@vertz/ui 0.2.15 → 0.2.17

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 (33) hide show
  1. package/README.md +49 -0
  2. package/dist/shared/{chunk-dksg08fq.js → chunk-07bh4m1e.js} +1 -1
  3. package/dist/shared/chunk-14eqne2a.js +10 -0
  4. package/dist/shared/{chunk-nn9v1zmk.js → chunk-2wtb9x81.js} +83 -20
  5. package/dist/shared/{chunk-8hsz5y4a.js → chunk-4fwcwxn6.js} +14 -4
  6. package/dist/shared/{chunk-4txc67nd.js → chunk-6jyt4ycw.js} +67 -2
  7. package/dist/shared/{chunk-83g4h38e.js → chunk-6wd36w21.js} +1 -0
  8. package/dist/shared/{chunk-h89w580h.js → chunk-afawz764.js} +1 -1
  9. package/dist/shared/{chunk-1wby7nex.js → chunk-dhehvmj0.js} +161 -9
  10. package/dist/shared/{chunk-wymw818z.js → chunk-fkbgbf3n.js} +48 -9
  11. package/dist/shared/{chunk-hw67ckr3.js → chunk-fs3eec4b.js} +230 -19
  12. package/dist/shared/{chunk-5dbq8jp9.js → chunk-j09yyh34.js} +72 -6
  13. package/dist/shared/chunk-mtsvrj9e.js +23 -0
  14. package/dist/shared/{chunk-j6qyxfdc.js → chunk-vndfjfdy.js} +3 -3
  15. package/dist/src/auth/public.d.ts +40 -24
  16. package/dist/src/auth/public.js +110 -52
  17. package/dist/src/css/public.d.ts +110 -2
  18. package/dist/src/css/public.js +8 -4
  19. package/dist/src/form/public.d.ts +29 -6
  20. package/dist/src/form/public.js +2 -2
  21. package/dist/src/index.d.ts +284 -13
  22. package/dist/src/index.js +160 -14
  23. package/dist/src/internals.d.ts +168 -5
  24. package/dist/src/internals.js +14 -8
  25. package/dist/src/jsx-runtime/index.d.ts +5 -0
  26. package/dist/src/jsx-runtime/index.js +8 -1
  27. package/dist/src/query/public.js +4 -3
  28. package/dist/src/router/public.d.ts +17 -4
  29. package/dist/src/router/public.js +16 -11
  30. package/dist/src/test/index.d.ts +5 -0
  31. package/dist/src/test/index.js +4 -3
  32. package/package.json +3 -3
  33. package/reactivity.json +1 -11
@@ -0,0 +1,23 @@
1
+ import {
2
+ createContext,
3
+ useContext
4
+ } from "./chunk-4fwcwxn6.js";
5
+
6
+ // src/router/router-context.ts
7
+ var RouterContext = createContext(undefined, "@vertz/ui::RouterContext");
8
+ function useRouter() {
9
+ const router = useContext(RouterContext);
10
+ if (!router) {
11
+ throw new Error("useRouter() must be called within RouterContext.Provider");
12
+ }
13
+ return router;
14
+ }
15
+ function useParams() {
16
+ const router = useContext(RouterContext);
17
+ if (!router) {
18
+ throw new Error("useParams() must be called within RouterContext.Provider");
19
+ }
20
+ return router.current?.parsedParams ?? router.current?.params ?? {};
21
+ }
22
+
23
+ export { RouterContext, useRouter, useParams };
@@ -6,10 +6,10 @@ import {
6
6
  import {
7
7
  getAdapter,
8
8
  isRenderNode
9
- } from "./chunk-h89w580h.js";
9
+ } from "./chunk-afawz764.js";
10
10
  import {
11
11
  domEffect
12
- } from "./chunk-8hsz5y4a.js";
12
+ } from "./chunk-4fwcwxn6.js";
13
13
 
14
14
  // src/hydrate/hydration-context.ts
15
15
  var isHydrating = false;
@@ -371,4 +371,4 @@ function __exitChildren() {
371
371
  }
372
372
  }
373
373
 
374
- export { startHydration, endHydration, getIsHydrating, claimText, claimComment, __text, __child, __insert, __element, __append, __staticText, __enterChildren, __exitChildren };
374
+ export { startHydration, endHydration, getIsHydrating, claimElement, claimText, claimComment, enterChildren, exitChildren, __text, __child, __insert, __element, __append, __staticText, __enterChildren, __exitChildren };
@@ -114,6 +114,13 @@ declare const AccessContext: Context<AccessContextValue>;
114
114
  */
115
115
  declare function useAccessContext(): UnwrapSignals<AccessContextValue>;
116
116
  /**
117
+ * Entitlement registry — augmented by @vertz/codegen to narrow entitlement strings.
118
+ * When empty (no codegen), Entitlement falls back to `string`.
119
+ */
120
+ interface EntitlementRegistry {}
121
+ /** Entitlement type — narrows to literal union when codegen populates EntitlementRegistry. */
122
+ type Entitlement = keyof EntitlementRegistry extends never ? string : Extract<keyof EntitlementRegistry, string>;
123
+ /**
117
124
  * Check if the current user has a specific entitlement.
118
125
  *
119
126
  * Must be called in the component body (like query()/form()).
@@ -126,15 +133,9 @@ declare function useAccessContext(): UnwrapSignals<AccessContextValue>;
126
133
  * @param entitlement - The entitlement to check
127
134
  * @param entity - Optional entity with pre-computed `__access` metadata
128
135
  */
129
- declare function can(entitlement: string, entity?: {
136
+ declare function can(entitlement: Entitlement, entity?: {
130
137
  __access?: Record<string, AccessCheckData>;
131
138
  }): AccessCheck;
132
- /**
133
- * Access Event Client — WebSocket client for real-time access invalidation.
134
- *
135
- * Connects to the access event WebSocket endpoint, handles reconnection
136
- * with exponential backoff, and delivers parsed events to the caller.
137
- */
138
139
  /** Client-side access events (no orgId/userId — server scopes the broadcast) */
139
140
  type ClientAccessEvent = {
140
141
  type: "access:flag_toggled";
@@ -178,16 +179,6 @@ interface AccessEventClient {
178
179
  dispose(): void;
179
180
  }
180
181
  declare function createAccessEventClient(options: AccessEventClientOptions): AccessEventClient;
181
- interface AccessGateProps {
182
- fallback?: () => unknown;
183
- children: (() => unknown) | unknown;
184
- }
185
- /**
186
- * Gate component that blocks children while the access set is loading.
187
- * Use this to prevent flicker on initial render when access data
188
- * hasn't been hydrated yet.
189
- */
190
- declare function AccessGate({ fallback, children }: AccessGateProps): ReadonlySignal<unknown> | unknown;
191
182
  import { Result } from "@vertz/fetch";
192
183
  /**
193
184
  * Minimal schema interface compatible with @vertz/schema.
@@ -263,6 +254,15 @@ interface ResetInput {
263
254
  token: string;
264
255
  password: string;
265
256
  }
257
+ interface SignOutOptions {
258
+ /** Path to navigate to after sign-out completes. Uses SPA navigation (replace). */
259
+ redirectTo?: string;
260
+ }
261
+ interface OAuthProviderInfo {
262
+ id: string;
263
+ name: string;
264
+ authUrl: string;
265
+ }
266
266
  interface AuthResponse {
267
267
  user: User;
268
268
  expiresAt: number;
@@ -275,11 +275,12 @@ interface AuthContextValue {
275
275
  error: Signal<AuthClientError | null>;
276
276
  signIn: SdkMethodWithMeta<SignInInput, AuthResponse>;
277
277
  signUp: SdkMethodWithMeta<SignUpInput, AuthResponse>;
278
- signOut: () => Promise<void>;
278
+ signOut: (options?: SignOutOptions) => Promise<void>;
279
279
  refresh: () => Promise<void>;
280
280
  mfaChallenge: SdkMethodWithMeta<MfaInput, AuthResponse>;
281
281
  forgotPassword: SdkMethodWithMeta<ForgotInput, void>;
282
282
  resetPassword: SdkMethodWithMeta<ResetInput, void>;
283
+ providers: Signal<OAuthProviderInfo[]>;
283
284
  }
284
285
  declare const AuthContext: Context<AuthContextValue>;
285
286
  declare function useAuth(): UnwrapSignals<AuthContextValue>;
@@ -295,11 +296,6 @@ interface AuthProviderProps {
295
296
  children: (() => unknown) | unknown;
296
297
  }
297
298
  declare function AuthProvider({ basePath, accessControl, accessEvents, accessEventsUrl, flagEntitlementMap, children }: AuthProviderProps): HTMLElement;
298
- interface AuthGateProps {
299
- fallback?: () => unknown;
300
- children: (() => unknown) | unknown;
301
- }
302
- declare function AuthGate({ fallback, children }: AuthGateProps): ReadonlySignal<unknown> | unknown;
303
299
  /**
304
300
  * Create an AccessContextValue for use with AccessContext.Provider.
305
301
  * Hydrates from `window.__VERTZ_ACCESS_SET__` when available (SSR).
@@ -313,4 +309,24 @@ declare function AuthGate({ fallback, children }: AuthGateProps): ReadonlySignal
313
309
  * ```
314
310
  */
315
311
  declare function createAccessProvider(): AccessContextValue;
316
- export { useAuth, useAccessContext, createAccessProvider, createAccessEventClient, can, User, SignUpInput, SignInInput, ResetInput, MfaInput, ForgotInput, DenialReason, DenialMeta, ClientAccessEvent, AuthStatus, AuthResponse, AuthProviderProps, AuthProvider, AuthGateProps, AuthGate, AuthErrorCode, AuthContextValue, AuthContext, AuthClientError, AccessSet, AccessGateProps, AccessGate, AccessEventClientOptions, AccessEventClient, AccessContextValue, AccessContext, AccessCheckData, AccessCheck };
312
+ /**
313
+ * Get an SVG icon string for a provider.
314
+ * Returns the built-in icon for known providers, or a generic fallback for unknown.
315
+ */
316
+ declare function getProviderIcon(providerId: string, size: number): string;
317
+ /**
318
+ * Get a display name for a user with a fallback chain.
319
+ * Chain: user.name (if string & non-empty) → user.email → fallback (default: 'Unknown')
320
+ */
321
+ declare function getUserDisplayName(user: User | null | undefined, fallback?: string): string;
322
+ /**
323
+ * Get initials from a user's name or email (max 2 characters).
324
+ * Chain: first + last word initials from name → first char of email → '?'
325
+ */
326
+ declare function getUserInitials(user: User | null | undefined): string;
327
+ /**
328
+ * Default user silhouette icon for avatar fallbacks.
329
+ * Returns an inline SVG string — no external requests, works in SSR.
330
+ */
331
+ declare function getUserIcon(size: number): string;
332
+ export { useAuth, useAccessContext, getUserInitials, getUserIcon, getUserDisplayName, getProviderIcon, createAccessProvider, createAccessEventClient, can, User, SignUpInput, SignOutOptions, SignInInput, ResetInput, OAuthProviderInfo, MfaInput, ForgotInput, EntitlementRegistry, Entitlement, DenialReason, DenialMeta, ClientAccessEvent, AuthStatus, AuthResponse, AuthProviderProps, AuthProvider, AuthErrorCode, AuthContextValue, AuthContext, AuthClientError, AccessSet, AccessEventClientOptions, AccessEventClient, AccessContextValue, AccessContext, AccessCheckData, AccessCheck };
@@ -1,10 +1,17 @@
1
+ import {
2
+ RouterContext
3
+ } from "../../shared/chunk-mtsvrj9e.js";
4
+ import {
5
+ isBrowser
6
+ } from "../../shared/chunk-14eqne2a.js";
1
7
  import {
2
8
  _tryOnCleanup,
3
9
  computed,
4
10
  createContext,
11
+ getSSRContext,
5
12
  signal,
6
13
  useContext
7
- } from "../../shared/chunk-8hsz5y4a.js";
14
+ } from "../../shared/chunk-4fwcwxn6.js";
8
15
 
9
16
  // src/auth/access-context.ts
10
17
  var AccessContext = createContext(undefined, "@vertz/ui::AccessContext");
@@ -69,7 +76,7 @@ function createAccessEventClient(options) {
69
76
  function getUrl() {
70
77
  if (options.url)
71
78
  return options.url;
72
- if (typeof window !== "undefined") {
79
+ if (isBrowser()) {
73
80
  const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
74
81
  return `${protocol}//${window.location.host}/api/auth/access-events`;
75
82
  }
@@ -139,26 +146,6 @@ function createAccessEventClient(options) {
139
146
  }
140
147
  return { connect, disconnect, dispose };
141
148
  }
142
- // src/auth/access-gate.ts
143
- function AccessGate({
144
- fallback,
145
- children
146
- }) {
147
- const ctx = useContext(AccessContext);
148
- if (!ctx) {
149
- return typeof children === "function" ? children() : children;
150
- }
151
- const isLoaded = computed(() => {
152
- const set = ctx.accessSet;
153
- return set !== null;
154
- });
155
- return computed(() => {
156
- if (isLoaded.value) {
157
- return typeof children === "function" ? children() : children;
158
- }
159
- return fallback ? fallback() : null;
160
- });
161
- }
162
149
  // src/auth/access-event-handler.ts
163
150
  function handleAccessEvent(accessSet, event, flagEntitlementMap) {
164
151
  const current = accessSet.value;
@@ -454,7 +441,7 @@ function createTokenRefresh({ onRefresh }) {
454
441
  pendingOfflineRefresh = false;
455
442
  }
456
443
  let visibilityHandler;
457
- if (typeof document !== "undefined") {
444
+ if (isBrowser()) {
458
445
  visibilityHandler = () => {
459
446
  if (document.visibilityState === "hidden") {
460
447
  clearTimer();
@@ -465,7 +452,7 @@ function createTokenRefresh({ onRefresh }) {
465
452
  document.addEventListener("visibilitychange", visibilityHandler);
466
453
  }
467
454
  let onlineHandler;
468
- if (typeof window !== "undefined") {
455
+ if (isBrowser()) {
469
456
  onlineHandler = () => {
470
457
  if (pendingOfflineRefresh) {
471
458
  pendingOfflineRefresh = false;
@@ -476,10 +463,10 @@ function createTokenRefresh({ onRefresh }) {
476
463
  }
477
464
  function dispose() {
478
465
  cancel();
479
- if (visibilityHandler && typeof document !== "undefined") {
466
+ if (visibilityHandler && isBrowser()) {
480
467
  document.removeEventListener("visibilitychange", visibilityHandler);
481
468
  }
482
- if (onlineHandler && typeof window !== "undefined") {
469
+ if (onlineHandler && isBrowser()) {
483
470
  window.removeEventListener("online", onlineHandler);
484
471
  }
485
472
  }
@@ -502,9 +489,19 @@ function AuthProvider({
502
489
  flagEntitlementMap,
503
490
  children
504
491
  }) {
492
+ const router = useContext(RouterContext);
505
493
  const userSignal = signal(null);
506
494
  const statusSignal = signal("idle");
507
495
  const errorSignal = signal(null);
496
+ const providersSignal = signal([]);
497
+ if (isBrowser()) {
498
+ setTimeout(() => {
499
+ fetch(`${basePath}/providers`).then((res) => res.ok ? res.json() : []).then((data) => {
500
+ providersSignal.value = data;
501
+ }).catch(() => {});
502
+ }, 0);
503
+ }
504
+ let deferredRefreshTimer = null;
508
505
  const isAuthenticated = computed(() => statusSignal.value === "authenticated");
509
506
  const isLoading = computed(() => statusSignal.value === "loading");
510
507
  const accessSetSignal = accessControl ? signal(null) : null;
@@ -565,6 +562,10 @@ function AuthProvider({
565
562
  onSuccess: handleAuthSuccess
566
563
  });
567
564
  const signIn = Object.assign(async (body) => {
565
+ if (deferredRefreshTimer) {
566
+ clearTimeout(deferredRefreshTimer);
567
+ deferredRefreshTimer = null;
568
+ }
568
569
  statusSignal.value = "loading";
569
570
  errorSignal.value = null;
570
571
  const result = await signInMethod(body);
@@ -585,6 +586,10 @@ function AuthProvider({
585
586
  onSuccess: handleAuthSuccess
586
587
  });
587
588
  const signUp = Object.assign(async (body) => {
589
+ if (deferredRefreshTimer) {
590
+ clearTimeout(deferredRefreshTimer);
591
+ deferredRefreshTimer = null;
592
+ }
588
593
  statusSignal.value = "loading";
589
594
  errorSignal.value = null;
590
595
  const result = await signUpMethod(body);
@@ -645,7 +650,7 @@ function AuthProvider({
645
650
  method: resetPasswordMethod.method,
646
651
  meta: resetPasswordMethod.meta
647
652
  });
648
- const signOut = async () => {
653
+ const signOut = async (options) => {
649
654
  tokenRefresh.cancel();
650
655
  try {
651
656
  await fetch(`${basePath}/signout`, {
@@ -658,9 +663,16 @@ function AuthProvider({
658
663
  statusSignal.value = "unauthenticated";
659
664
  errorSignal.value = null;
660
665
  clearAccessSet();
661
- if (typeof window !== "undefined") {
666
+ if (isBrowser()) {
662
667
  delete window.__VERTZ_SESSION__;
663
668
  }
669
+ if (options?.redirectTo) {
670
+ if (router) {
671
+ router.navigate({ to: options.redirectTo, replace: true }).catch(() => {});
672
+ } else if (typeof console !== "undefined") {
673
+ console.warn("[vertz] signOut({ redirectTo }) was called but no RouterContext is available. Navigation was skipped.");
674
+ }
675
+ }
664
676
  };
665
677
  let refreshInFlight = null;
666
678
  const doRefresh = async () => {
@@ -730,9 +742,10 @@ function AuthProvider({
730
742
  refresh,
731
743
  mfaChallenge,
732
744
  forgotPassword,
733
- resetPassword
745
+ resetPassword,
746
+ providers: providersSignal
734
747
  };
735
- if (typeof window !== "undefined") {
748
+ if (isBrowser()) {
736
749
  if (window.__VERTZ_SESSION__?.user) {
737
750
  const session = window.__VERTZ_SESSION__;
738
751
  userSignal.value = session.user;
@@ -741,11 +754,24 @@ function AuthProvider({
741
754
  tokenRefresh.schedule(session.expiresAt);
742
755
  }
743
756
  } else {
744
- statusSignal.value = "unauthenticated";
757
+ deferredRefreshTimer = setTimeout(() => {
758
+ deferredRefreshTimer = null;
759
+ refresh();
760
+ }, 0);
761
+ }
762
+ } else {
763
+ const ssrCtx = getSSRContext();
764
+ if (ssrCtx?.ssrAuth) {
765
+ if (ssrCtx.ssrAuth.status === "authenticated") {
766
+ userSignal.value = ssrCtx.ssrAuth.user;
767
+ statusSignal.value = "authenticated";
768
+ } else {
769
+ statusSignal.value = "unauthenticated";
770
+ }
745
771
  }
746
772
  }
747
773
  if (accessControl && accessSetSignal && accessLoadingSignal) {
748
- if (typeof window !== "undefined" && window.__VERTZ_ACCESS_SET__ && typeof window.__VERTZ_ACCESS_SET__.entitlements === "object" && window.__VERTZ_ACCESS_SET__.entitlements !== null) {
774
+ if (isBrowser() && window.__VERTZ_ACCESS_SET__ && typeof window.__VERTZ_ACCESS_SET__.entitlements === "object" && window.__VERTZ_ACCESS_SET__.entitlements !== null) {
749
775
  accessSetSignal.value = window.__VERTZ_ACCESS_SET__;
750
776
  accessLoadingSignal.value = false;
751
777
  }
@@ -760,42 +786,74 @@ function AuthProvider({
760
786
  }
761
787
  return AuthContext.Provider({ value: contextValue, children });
762
788
  }
763
- // src/auth/auth-gate.ts
764
- function AuthGate({ fallback, children }) {
765
- const ctx = useContext(AuthContext);
766
- if (!ctx) {
767
- return typeof children === "function" ? children() : children;
768
- }
769
- const isResolved = computed(() => {
770
- const status = ctx.status;
771
- return status !== "idle" && status !== "loading";
772
- });
773
- return computed(() => {
774
- if (isResolved.value) {
775
- return typeof children === "function" ? children() : children;
776
- }
777
- return fallback ? fallback() : null;
778
- });
779
- }
780
789
  // src/auth/create-access-provider.ts
781
790
  function createAccessProvider() {
782
791
  const accessSet = signal(null);
783
792
  const loading = signal(true);
784
- if (typeof window !== "undefined" && window.__VERTZ_ACCESS_SET__ && typeof window.__VERTZ_ACCESS_SET__.entitlements === "object" && window.__VERTZ_ACCESS_SET__.entitlements !== null) {
793
+ if (isBrowser() && window.__VERTZ_ACCESS_SET__ && typeof window.__VERTZ_ACCESS_SET__.entitlements === "object" && window.__VERTZ_ACCESS_SET__.entitlements !== null) {
785
794
  accessSet.value = window.__VERTZ_ACCESS_SET__;
786
795
  loading.value = false;
787
796
  }
788
797
  return { accessSet, loading };
789
798
  }
799
+ // src/auth/provider-icons.ts
800
+ var icons = {
801
+ github: (size) => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12Z"/></svg>`,
802
+ google: (size) => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1Z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23Z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62Z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53Z"/></svg>`,
803
+ discord: (size) => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03ZM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418Zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418Z"/></svg>`,
804
+ apple: (size) => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="currentColor"><path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09ZM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.32 2.32-1.55 4.25-3.74 4.25Z"/></svg>`,
805
+ microsoft: (size) => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24"><rect fill="#F25022" x="1" y="1" width="10" height="10"/><rect fill="#7FBA00" x="13" y="1" width="10" height="10"/><rect fill="#00A4EF" x="1" y="13" width="10" height="10"/><rect fill="#FFB900" x="13" y="13" width="10" height="10"/></svg>`,
806
+ twitter: (size) => `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>`
807
+ };
808
+ function fallbackIcon(size) {
809
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>`;
810
+ }
811
+ function getProviderIcon(providerId, size) {
812
+ const iconFn = icons[providerId];
813
+ return iconFn ? iconFn(size) : fallbackIcon(size);
814
+ }
815
+ // src/auth/user-display.ts
816
+ function getUserDisplayName(user, fallback = "Unknown") {
817
+ if (!user)
818
+ return fallback;
819
+ const name = user.name;
820
+ if (typeof name === "string" && name.trim().length > 0)
821
+ return name.trim();
822
+ if (user.email)
823
+ return user.email;
824
+ return fallback;
825
+ }
826
+ function getUserInitials(user) {
827
+ if (!user)
828
+ return "?";
829
+ const name = user.name;
830
+ if (typeof name === "string" && name.trim().length > 0) {
831
+ const words = name.trim().split(/\s+/);
832
+ const first = words[0] ?? "";
833
+ const last = words[words.length - 1] ?? "";
834
+ if (words.length === 1 || !last)
835
+ return first.charAt(0).toUpperCase() || "?";
836
+ return (first.charAt(0) + last.charAt(0)).toUpperCase();
837
+ }
838
+ if (user.email && user.email.length > 0)
839
+ return user.email.charAt(0).toUpperCase();
840
+ return "?";
841
+ }
842
+ // src/auth/user-icon.ts
843
+ function getUserIcon(size) {
844
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>`;
845
+ }
790
846
  export {
791
847
  useAuth,
792
848
  useAccessContext,
849
+ getUserInitials,
850
+ getUserIcon,
851
+ getUserDisplayName,
852
+ getProviderIcon,
793
853
  createAccessProvider,
794
854
  createAccessEventClient,
795
855
  can,
796
856
  AuthProvider,
797
- AuthGate,
798
857
  AuthContext,
799
- AccessGate,
800
858
  AccessContext
801
859
  };
@@ -27,6 +27,101 @@ type CSSOutput<T extends CSSInput = CSSInput> = { readonly [K in keyof T & strin
27
27
  * @returns Object with block names as keys (class name strings) and non-enumerable `css` property.
28
28
  */
29
29
  declare function css<T extends CSSInput>(input: T & { [K in keyof T & "css"]? : never }, filePath?: string): CSSOutput<T>;
30
+ interface FontSrc {
31
+ path: string;
32
+ weight?: string | number;
33
+ style?: "normal" | "italic";
34
+ }
35
+ type FallbackFontName = "Arial" | "Times New Roman" | "Courier New";
36
+ interface FontFallbackMetrics {
37
+ /** CSS ascent-override value, e.g., '94.52%' */
38
+ ascentOverride: string;
39
+ /** CSS descent-override value, e.g., '24.60%' */
40
+ descentOverride: string;
41
+ /** CSS line-gap-override value, e.g., '0.00%' */
42
+ lineGapOverride: string;
43
+ /** CSS size-adjust value, e.g., '104.88%' */
44
+ sizeAdjust: string;
45
+ /** System font used as fallback base. */
46
+ fallbackFont: FallbackFontName;
47
+ }
48
+ interface CompileFontsOptions {
49
+ /** Pre-computed fallback metrics per font key. Provided by @vertz/ui-server at build/SSR time. */
50
+ fallbackMetrics?: Record<string, FontFallbackMetrics>;
51
+ }
52
+ interface FontOptions {
53
+ /** Font weight: '100..1000' (variable) or 400 (fixed). */
54
+ weight: string | number;
55
+ /** Font style. @default 'normal' */
56
+ style?: "normal" | "italic";
57
+ /** Font-display strategy. @default 'swap' */
58
+ display?: "auto" | "block" | "swap" | "fallback" | "optional";
59
+ /** URL path(s) for local/self-hosted fonts. */
60
+ src?: string | FontSrc[];
61
+ /** Fallback font stack. */
62
+ fallback?: string[];
63
+ /** Font subsets (metadata only — subsetting is deferred to a future phase). @default ['latin'] */
64
+ subsets?: string[];
65
+ /** Unicode range for subsetting. */
66
+ unicodeRange?: string;
67
+ /**
68
+ * Control automatic fallback font metric adjustment for zero-CLS font loading.
69
+ * - true: auto-detect fallback base from `fallback` array (default)
70
+ * - false: disable
71
+ * - 'Arial' | 'Times New Roman' | 'Courier New': explicit base
72
+ * @default true
73
+ */
74
+ adjustFontFallback?: boolean | FallbackFontName;
75
+ }
76
+ type FontStyle = "normal" | "italic";
77
+ type FontDisplay = "auto" | "block" | "swap" | "fallback" | "optional";
78
+ interface FontDescriptor {
79
+ readonly __brand: "FontDescriptor";
80
+ readonly family: string;
81
+ readonly weight: string;
82
+ readonly style: FontStyle;
83
+ readonly display: FontDisplay;
84
+ readonly src?: string | FontSrc[];
85
+ readonly fallback: string[];
86
+ readonly subsets: string[];
87
+ readonly unicodeRange?: string;
88
+ readonly adjustFontFallback: boolean | FallbackFontName;
89
+ }
90
+ /** Structured description of a resource to preload. */
91
+ interface PreloadItem {
92
+ href: string;
93
+ as: "font" | "image" | "style" | "script";
94
+ type?: string;
95
+ crossorigin?: boolean;
96
+ }
97
+ interface CompiledFonts {
98
+ /** @font-face declarations. */
99
+ fontFaceCss: string;
100
+ /** :root { --font-<key>: ...; } block (for standalone use). */
101
+ cssVarsCss: string;
102
+ /** Individual CSS var lines (e.g., ' --font-sans: ...;') for merging into an existing :root. */
103
+ cssVarLines: string[];
104
+ /** <link rel="preload"> HTML tags for font files. */
105
+ preloadTags: string;
106
+ /** Structured preload data for generating HTTP Link headers. */
107
+ preloadItems: PreloadItem[];
108
+ }
109
+ /**
110
+ * Create a font descriptor for use in theme definitions.
111
+ *
112
+ * @param family - The font family name (e.g., 'DM Sans').
113
+ * @param options - Font configuration.
114
+ * @returns A FontDescriptor.
115
+ */
116
+ declare function font(family: string, options: FontOptions): FontDescriptor;
117
+ /**
118
+ * Compile font descriptors into CSS and preload tags.
119
+ *
120
+ * @param fonts - A map of token key → FontDescriptor.
121
+ * @param options - Optional compilation settings (e.g., pre-computed fallback metrics).
122
+ * @returns Compiled @font-face CSS, CSS var lines, and preload link tags.
123
+ */
124
+ declare function compileFonts(fonts: Record<string, FontDescriptor>, options?: CompileFontsOptions): CompiledFonts;
30
125
  /** Input to globalCss(): selector → property-value map. */
31
126
  type GlobalCSSInput = Record<string, Record<string, string>>;
32
127
  /** Output of globalCss(): extracted CSS string. */
@@ -65,6 +160,8 @@ interface ThemeInput {
65
160
  colors: ColorTokens;
66
161
  /** Spacing scale tokens. */
67
162
  spacing?: SpacingTokens;
163
+ /** Font descriptors keyed by token name (e.g., sans, mono, display). */
164
+ fonts?: Record<string, FontDescriptor>;
68
165
  }
69
166
  /** The structured theme object returned by defineTheme(). */
70
167
  interface Theme {
@@ -72,6 +169,8 @@ interface Theme {
72
169
  colors: ColorTokens;
73
170
  /** Spacing scale tokens. */
74
171
  spacing?: SpacingTokens;
172
+ /** Font descriptors keyed by token name. */
173
+ fonts?: Record<string, FontDescriptor>;
75
174
  }
76
175
  /** Output of compileTheme(). */
77
176
  interface CompiledTheme {
@@ -79,6 +178,15 @@ interface CompiledTheme {
79
178
  css: string;
80
179
  /** Flat list of token dot-paths (e.g., 'primary.500', 'background'). */
81
180
  tokens: string[];
181
+ /** Font preload link tags for injection into <head>. */
182
+ preloadTags: string;
183
+ /** Structured preload data for generating HTTP Link headers. */
184
+ preloadItems: PreloadItem[];
185
+ }
186
+ /** Options for compileTheme(). */
187
+ interface CompileThemeOptions {
188
+ /** Pre-computed font fallback metrics for zero-CLS font loading. */
189
+ fallbackMetrics?: CompileFontsOptions["fallbackMetrics"];
82
190
  }
83
191
  /**
84
192
  * Define a theme with raw and contextual design tokens.
@@ -97,7 +205,7 @@ declare function defineTheme(input: ThemeInput): Theme;
97
205
  * @param theme - A theme object from defineTheme().
98
206
  * @returns Compiled CSS and token list.
99
207
  */
100
- declare function compileTheme(theme: Theme): CompiledTheme;
208
+ declare function compileTheme(theme: Theme, options?: CompileThemeOptions): CompiledTheme;
101
209
  /** A child node: either a DOM Node or a string (text content). */
102
210
  type ThemeChild = Node | string;
103
211
  /** Props for ThemeProvider. */
@@ -151,4 +259,4 @@ interface VariantFunction<V extends VariantDefinitions> {
151
259
  * @returns A function that accepts variant props and returns a className string.
152
260
  */
153
261
  declare function variants<V extends VariantDefinitions>(config: VariantsConfig<V>): VariantFunction<V>;
154
- export { variants, s, globalCss, defineTheme, css, compileTheme, VariantsConfig, VariantProps, VariantFunction, ThemeProviderProps, ThemeProvider, ThemeInput, Theme, StyleEntry, GlobalCSSOutput, GlobalCSSInput, CompiledTheme, CSSOutput, CSSInput };
262
+ export { variants, s, globalCss, font, defineTheme, css, compileTheme, compileFonts, VariantsConfig, VariantProps, VariantFunction, ThemeProviderProps, ThemeProvider, ThemeInput, Theme, StyleEntry, PreloadItem, GlobalCSSOutput, GlobalCSSInput, FontSrc, FontOptions, FontFallbackMetrics, FontDescriptor, FallbackFontName, CompiledTheme, CompiledFonts, CompileThemeOptions, CompileFontsOptions, CSSOutput, CSSInput };
@@ -1,22 +1,26 @@
1
1
  import {
2
2
  ThemeProvider,
3
+ compileFonts,
3
4
  compileTheme,
4
5
  css,
5
6
  defineTheme,
7
+ font,
6
8
  globalCss,
7
9
  s,
8
10
  variants
9
- } from "../../shared/chunk-1wby7nex.js";
10
- import"../../shared/chunk-j6qyxfdc.js";
11
+ } from "../../shared/chunk-dhehvmj0.js";
12
+ import"../../shared/chunk-vndfjfdy.js";
11
13
  import"../../shared/chunk-prj7nm08.js";
12
- import"../../shared/chunk-h89w580h.js";
13
- import"../../shared/chunk-8hsz5y4a.js";
14
+ import"../../shared/chunk-afawz764.js";
15
+ import"../../shared/chunk-4fwcwxn6.js";
14
16
  export {
15
17
  variants,
16
18
  s,
17
19
  globalCss,
20
+ font,
18
21
  defineTheme,
19
22
  css,
20
23
  compileTheme,
24
+ compileFonts,
21
25
  ThemeProvider
22
26
  };