@vertz/ui 0.2.12 → 0.2.13
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/README.md +70 -6
- package/dist/shared/{chunk-bjcpcq5j.js → chunk-2sth83bd.js} +1 -1
- package/dist/shared/{chunk-9e92w0wt.js → chunk-83g4h38e.js} +13 -0
- package/dist/shared/{chunk-2rs8a26p.js → chunk-8hsz5y4a.js} +92 -33
- package/dist/shared/{chunk-55tgkc7s.js → chunk-c30eg6wn.js} +1 -1
- package/dist/shared/{chunk-kg898f92.js → chunk-c9xxsrat.js} +7 -2
- package/dist/shared/{chunk-wn4gv1qd.js → chunk-dksg08fq.js} +1 -1
- package/dist/shared/{chunk-g4rch80a.js → chunk-h89w580h.js} +7 -0
- package/dist/shared/chunk-hw67ckr3.js +1212 -0
- package/dist/shared/{chunk-662f9zrb.js → chunk-j6qyxfdc.js} +7 -7
- package/dist/shared/{chunk-g1gf16fz.js → chunk-mj7b4t40.js} +107 -41
- package/dist/shared/{chunk-18jzqefd.js → chunk-nn9v1zmk.js} +4 -4
- package/dist/src/auth/public.d.ts +303 -0
- package/dist/src/auth/public.js +773 -0
- package/dist/src/css/public.js +22 -0
- package/dist/{form → src/form}/public.js +2 -2
- package/dist/{index.d.ts → src/index.d.ts} +218 -14
- package/dist/{index.js → src/index.js} +79 -229
- package/dist/{internals.d.ts → src/internals.d.ts} +265 -3
- package/dist/{internals.js → src/internals.js} +18 -10
- package/dist/{jsx-runtime → src/jsx-runtime}/index.js +1 -1
- package/dist/{query → src/query}/public.d.ts +3 -1
- package/dist/src/query/public.js +15 -0
- package/dist/{router → src/router}/public.d.ts +25 -4
- package/dist/{router → src/router}/public.js +9 -9
- package/dist/{test → src/test}/index.d.ts +12 -2
- package/dist/{test → src/test}/index.js +4 -4
- package/package.json +31 -25
- package/reactivity.json +67 -0
- package/dist/css/public.js +0 -22
- package/dist/query/public.js +0 -15
- package/dist/shared/chunk-9k2z3jfx.js +0 -528
- /package/dist/{css → src/css}/public.d.ts +0 -0
- /package/dist/{form → src/form}/public.d.ts +0 -0
- /package/dist/{jsx-runtime → src/jsx-runtime}/index.d.ts +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAdapter,
|
|
3
|
-
isRenderNode
|
|
4
|
-
} from "./chunk-g4rch80a.js";
|
|
5
|
-
import {
|
|
6
|
-
domEffect
|
|
7
|
-
} from "./chunk-2rs8a26p.js";
|
|
8
1
|
import {
|
|
9
2
|
SVG_NS,
|
|
10
3
|
isSVGTag,
|
|
11
4
|
normalizeSVGAttr
|
|
12
5
|
} from "./chunk-prj7nm08.js";
|
|
6
|
+
import {
|
|
7
|
+
getAdapter,
|
|
8
|
+
isRenderNode
|
|
9
|
+
} from "./chunk-h89w580h.js";
|
|
10
|
+
import {
|
|
11
|
+
domEffect
|
|
12
|
+
} from "./chunk-8hsz5y4a.js";
|
|
13
13
|
|
|
14
14
|
// src/hydrate/hydration-context.ts
|
|
15
15
|
var isHydrating = false;
|
|
@@ -1,26 +1,66 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeLoaders,
|
|
3
3
|
matchRoute
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-83g4h38e.js";
|
|
5
5
|
import {
|
|
6
6
|
prefetchNavData
|
|
7
7
|
} from "./chunk-jrtrk5z4.js";
|
|
8
8
|
import {
|
|
9
|
+
getSSRContext,
|
|
9
10
|
signal
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-8hsz5y4a.js";
|
|
11
12
|
|
|
12
13
|
// src/router/navigate.ts
|
|
13
14
|
var DEFAULT_NAV_THRESHOLD_MS = 500;
|
|
14
15
|
function createRouter(routes, initialUrl, options) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const ssrCtx = getSSRContext();
|
|
17
|
+
const isSSR = ssrCtx !== undefined;
|
|
18
|
+
if (isSSR || typeof window === "undefined") {
|
|
19
|
+
const ssrUrl = initialUrl ?? ssrCtx?.url ?? "/";
|
|
20
|
+
const fallbackMatch = matchRoute(routes, ssrUrl);
|
|
21
|
+
return {
|
|
22
|
+
current: {
|
|
23
|
+
get value() {
|
|
24
|
+
const ctx = getSSRContext();
|
|
25
|
+
if (ctx)
|
|
26
|
+
return matchRoute(routes, ctx.url);
|
|
27
|
+
return fallbackMatch;
|
|
28
|
+
},
|
|
29
|
+
peek() {
|
|
30
|
+
const ctx = getSSRContext();
|
|
31
|
+
if (ctx)
|
|
32
|
+
return matchRoute(routes, ctx.url);
|
|
33
|
+
return fallbackMatch;
|
|
34
|
+
},
|
|
35
|
+
notify() {}
|
|
36
|
+
},
|
|
37
|
+
searchParams: {
|
|
38
|
+
get value() {
|
|
39
|
+
const ctx = getSSRContext();
|
|
40
|
+
if (ctx) {
|
|
41
|
+
const m = matchRoute(routes, ctx.url);
|
|
42
|
+
return m?.search ?? {};
|
|
43
|
+
}
|
|
44
|
+
return fallbackMatch?.search ?? {};
|
|
45
|
+
},
|
|
46
|
+
peek() {
|
|
47
|
+
const ctx = getSSRContext();
|
|
48
|
+
if (ctx) {
|
|
49
|
+
const m = matchRoute(routes, ctx.url);
|
|
50
|
+
return m?.search ?? {};
|
|
51
|
+
}
|
|
52
|
+
return fallbackMatch?.search ?? {};
|
|
53
|
+
},
|
|
54
|
+
notify() {}
|
|
55
|
+
},
|
|
56
|
+
loaderData: { value: [], peek: () => [], notify() {} },
|
|
57
|
+
loaderError: { value: null, peek: () => null, notify() {} },
|
|
58
|
+
navigate: () => Promise.resolve(),
|
|
59
|
+
revalidate: () => Promise.resolve(),
|
|
60
|
+
dispose: () => {}
|
|
61
|
+
};
|
|
23
62
|
}
|
|
63
|
+
const url = initialUrl ?? window.location.pathname + window.location.search;
|
|
24
64
|
const initialMatch = matchRoute(routes, url);
|
|
25
65
|
function normalizeUrl(rawUrl) {
|
|
26
66
|
const qIdx = rawUrl.indexOf("?");
|
|
@@ -35,10 +75,54 @@ function createRouter(routes, initialUrl, options) {
|
|
|
35
75
|
const visitedUrls = new Set;
|
|
36
76
|
if (initialMatch)
|
|
37
77
|
visitedUrls.add(normalizeUrl(url));
|
|
38
|
-
const
|
|
78
|
+
const _current = signal(initialMatch);
|
|
39
79
|
const loaderData = signal([]);
|
|
40
80
|
const loaderError = signal(null);
|
|
41
|
-
const
|
|
81
|
+
const _searchParams = signal(initialMatch?.search ?? {});
|
|
82
|
+
const current = {
|
|
83
|
+
get value() {
|
|
84
|
+
const ctx = getSSRContext();
|
|
85
|
+
if (ctx)
|
|
86
|
+
return matchRoute(routes, ctx.url);
|
|
87
|
+
return _current.value;
|
|
88
|
+
},
|
|
89
|
+
set value(v) {
|
|
90
|
+
_current.value = v;
|
|
91
|
+
},
|
|
92
|
+
peek() {
|
|
93
|
+
const ctx = getSSRContext();
|
|
94
|
+
if (ctx)
|
|
95
|
+
return matchRoute(routes, ctx.url);
|
|
96
|
+
return _current.peek();
|
|
97
|
+
},
|
|
98
|
+
notify() {
|
|
99
|
+
_current.notify();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const searchParams = {
|
|
103
|
+
get value() {
|
|
104
|
+
const ctx = getSSRContext();
|
|
105
|
+
if (ctx) {
|
|
106
|
+
const match = matchRoute(routes, ctx.url);
|
|
107
|
+
return match?.search ?? {};
|
|
108
|
+
}
|
|
109
|
+
return _searchParams.value;
|
|
110
|
+
},
|
|
111
|
+
set value(v) {
|
|
112
|
+
_searchParams.value = v;
|
|
113
|
+
},
|
|
114
|
+
peek() {
|
|
115
|
+
const ctx = getSSRContext();
|
|
116
|
+
if (ctx) {
|
|
117
|
+
const match = matchRoute(routes, ctx.url);
|
|
118
|
+
return match?.search ?? {};
|
|
119
|
+
}
|
|
120
|
+
return _searchParams.peek();
|
|
121
|
+
},
|
|
122
|
+
notify() {
|
|
123
|
+
_searchParams.notify();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
42
126
|
let navigationGen = 0;
|
|
43
127
|
let navigateGen = 0;
|
|
44
128
|
let currentAbort = null;
|
|
@@ -71,17 +155,6 @@ function createRouter(routes, initialUrl, options) {
|
|
|
71
155
|
return;
|
|
72
156
|
await Promise.race([target, new Promise((r) => setTimeout(r, DEFAULT_NAV_THRESHOLD_MS))]);
|
|
73
157
|
}
|
|
74
|
-
if (isSSR) {
|
|
75
|
-
const g = globalThis;
|
|
76
|
-
const prev = g.__VERTZ_SSR_SYNC_ROUTER__;
|
|
77
|
-
g.__VERTZ_SSR_SYNC_ROUTER__ = (ssrUrl) => {
|
|
78
|
-
if (typeof prev === "function")
|
|
79
|
-
prev(ssrUrl);
|
|
80
|
-
const match = matchRoute(routes, ssrUrl);
|
|
81
|
-
current.value = match;
|
|
82
|
-
searchParams.value = match?.search ?? {};
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
158
|
if (initialMatch) {
|
|
86
159
|
const gen = ++navigationGen;
|
|
87
160
|
const abort = new AbortController;
|
|
@@ -125,12 +198,10 @@ function createRouter(routes, initialUrl, options) {
|
|
|
125
198
|
async function navigate(navUrl, navOptions) {
|
|
126
199
|
const gen = ++navigateGen;
|
|
127
200
|
const handle = startPrefetch(navUrl);
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
window.history.pushState(null, "", navUrl);
|
|
133
|
-
}
|
|
201
|
+
if (navOptions?.replace) {
|
|
202
|
+
window.history.replaceState(null, "", navUrl);
|
|
203
|
+
} else {
|
|
204
|
+
window.history.pushState(null, "", navUrl);
|
|
134
205
|
}
|
|
135
206
|
const isCachedNav = visitedUrls.has(normalizeUrl(navUrl));
|
|
136
207
|
if (!isCachedNav && (handle?.firstEvent || handle?.done)) {
|
|
@@ -152,19 +223,14 @@ function createRouter(routes, initialUrl, options) {
|
|
|
152
223
|
await runLoaders(match, gen, abort.signal);
|
|
153
224
|
}
|
|
154
225
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
};
|
|
162
|
-
window.addEventListener("popstate", onPopState);
|
|
163
|
-
}
|
|
226
|
+
const onPopState = () => {
|
|
227
|
+
const popUrl = window.location.pathname + window.location.search;
|
|
228
|
+
startPrefetch(popUrl);
|
|
229
|
+
applyNavigation(popUrl).catch(() => {});
|
|
230
|
+
};
|
|
231
|
+
window.addEventListener("popstate", onPopState);
|
|
164
232
|
function dispose() {
|
|
165
|
-
|
|
166
|
-
window.removeEventListener("popstate", onPopState);
|
|
167
|
-
}
|
|
233
|
+
window.removeEventListener("popstate", onPopState);
|
|
168
234
|
if (currentAbort) {
|
|
169
235
|
currentAbort.abort();
|
|
170
236
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__classList,
|
|
3
3
|
__on
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-dksg08fq.js";
|
|
5
5
|
import {
|
|
6
6
|
__append,
|
|
7
7
|
__element,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
__exitChildren,
|
|
10
10
|
__staticText,
|
|
11
11
|
getIsHydrating
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-j6qyxfdc.js";
|
|
13
13
|
import {
|
|
14
14
|
_tryOnCleanup,
|
|
15
15
|
createContext,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
signal,
|
|
21
21
|
untrack,
|
|
22
22
|
useContext
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-8hsz5y4a.js";
|
|
24
24
|
|
|
25
25
|
// src/router/link.ts
|
|
26
26
|
var DANGEROUS_SCHEMES = ["javascript:", "data:", "vbscript:"];
|
|
@@ -103,7 +103,7 @@ function useParams() {
|
|
|
103
103
|
if (!router) {
|
|
104
104
|
throw new Error("useParams() must be called within RouterContext.Provider");
|
|
105
105
|
}
|
|
106
|
-
return router.current?.params ?? {};
|
|
106
|
+
return router.current?.parsedParams ?? router.current?.params ?? {};
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/router/outlet.ts
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A reactive signal that holds a value and notifies subscribers on change.
|
|
3
|
+
*/
|
|
4
|
+
interface Signal<T> {
|
|
5
|
+
/** Get the current value and subscribe to changes (when inside a tracking context). */
|
|
6
|
+
get value(): T;
|
|
7
|
+
/** Set the current value and notify subscribers if changed. */
|
|
8
|
+
set value(newValue: T);
|
|
9
|
+
/** Read the current value without subscribing (no tracking). */
|
|
10
|
+
peek(): T;
|
|
11
|
+
/** Manually notify all subscribers (useful after mutating the value in place). */
|
|
12
|
+
notify(): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A read-only reactive value derived from other signals.
|
|
16
|
+
*/
|
|
17
|
+
interface ReadonlySignal<T> {
|
|
18
|
+
/** Get the current value and subscribe to changes. */
|
|
19
|
+
readonly value: T;
|
|
20
|
+
/** Read the current value without subscribing. */
|
|
21
|
+
peek(): T;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Unwraps a ReadonlySignal to its value type.
|
|
25
|
+
* Used by signal APIs (like query()) to expose plain values in TypeScript
|
|
26
|
+
* while the compiler auto-unwraps them at runtime.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* type UnwrappedData = Unwrapped<ReadonlySignal<Task | undefined>>; // → Task | undefined
|
|
30
|
+
*/
|
|
31
|
+
type Unwrapped<T> = T extends ReadonlySignal<infer U> ? U : T;
|
|
32
|
+
/**
|
|
33
|
+
* Unwraps all signal properties of an object type.
|
|
34
|
+
* Properties that are signals become their inner value type.
|
|
35
|
+
* Non-signal properties and primitive types pass through unchanged.
|
|
36
|
+
*
|
|
37
|
+
* Used by `useContext` to present context values without the Signal wrapper.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* type Settings = { theme: Signal<string>; setTheme: (t: string) => void };
|
|
41
|
+
* type Unwrapped = UnwrapSignals<Settings>; // { theme: string; setTheme: (t: string) => void }
|
|
42
|
+
*/
|
|
43
|
+
type UnwrapSignals<T> = T extends object ? { [K in keyof T] : Unwrapped<T[K]> } : T;
|
|
44
|
+
/**
|
|
45
|
+
* Props for the JSX pattern of Context.Provider.
|
|
46
|
+
*
|
|
47
|
+
* `children` accepts both raw values (what TypeScript sees in JSX) and
|
|
48
|
+
* thunks (what the compiler produces). At compile time the compiler wraps
|
|
49
|
+
* JSX children in `() => ...`, but TypeScript checks the pre-compilation
|
|
50
|
+
* source where children are plain elements.
|
|
51
|
+
*/
|
|
52
|
+
interface ProviderJsxProps<T> {
|
|
53
|
+
value: T;
|
|
54
|
+
children: (() => unknown) | unknown;
|
|
55
|
+
}
|
|
56
|
+
/** A context object created by `createContext`. */
|
|
57
|
+
interface Context<T> {
|
|
58
|
+
/** Provide a value via callback pattern. */
|
|
59
|
+
Provider(value: T, fn: () => void): void;
|
|
60
|
+
/** Provide a value via JSX pattern (single-arg object with children thunk). */
|
|
61
|
+
Provider(props: ProviderJsxProps<T>): HTMLElement;
|
|
62
|
+
/** @internal — current value stack */
|
|
63
|
+
_stack: T[];
|
|
64
|
+
/** @internal — default value */
|
|
65
|
+
_default: T | undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Client-side access set types.
|
|
69
|
+
*
|
|
70
|
+
* Mirrors server types (no server imports). See "Known Limitations"
|
|
71
|
+
* in the design doc for drift risk documentation.
|
|
72
|
+
*/
|
|
73
|
+
type DenialReason = "plan_required" | "role_required" | "limit_reached" | "flag_disabled" | "hierarchy_denied" | "step_up_required" | "not_authenticated";
|
|
74
|
+
interface DenialMeta {
|
|
75
|
+
requiredPlans?: string[];
|
|
76
|
+
requiredRoles?: string[];
|
|
77
|
+
disabledFlags?: string[];
|
|
78
|
+
limit?: {
|
|
79
|
+
max: number;
|
|
80
|
+
consumed: number;
|
|
81
|
+
remaining: number;
|
|
82
|
+
};
|
|
83
|
+
fvaMaxAge?: number;
|
|
84
|
+
}
|
|
85
|
+
interface AccessCheckData {
|
|
86
|
+
allowed: boolean;
|
|
87
|
+
reasons: DenialReason[];
|
|
88
|
+
reason?: DenialReason;
|
|
89
|
+
meta?: DenialMeta;
|
|
90
|
+
}
|
|
91
|
+
interface AccessSet {
|
|
92
|
+
entitlements: Record<string, AccessCheckData>;
|
|
93
|
+
flags: Record<string, boolean>;
|
|
94
|
+
plan: string | null;
|
|
95
|
+
computedAt: string;
|
|
96
|
+
}
|
|
97
|
+
/** Public return type of can(). All properties are ReadonlySignal (compiler auto-unwraps). */
|
|
98
|
+
interface AccessCheck {
|
|
99
|
+
readonly allowed: boolean;
|
|
100
|
+
readonly reasons: DenialReason[];
|
|
101
|
+
readonly reason: DenialReason | undefined;
|
|
102
|
+
readonly meta: DenialMeta | undefined;
|
|
103
|
+
readonly loading: boolean;
|
|
104
|
+
}
|
|
105
|
+
interface AccessContextValue {
|
|
106
|
+
accessSet: Signal<AccessSet | null>;
|
|
107
|
+
loading: Signal<boolean>;
|
|
108
|
+
}
|
|
109
|
+
declare const AccessContext: Context<AccessContextValue>;
|
|
110
|
+
/**
|
|
111
|
+
* Read the access context. Returns getter-wrapped object (wrapSignalProps
|
|
112
|
+
* applied by Provider). Throws if no provider — consistent with useRouter(),
|
|
113
|
+
* useDialogStack(), and all other use* hooks.
|
|
114
|
+
*/
|
|
115
|
+
declare function useAccessContext(): UnwrapSignals<AccessContextValue>;
|
|
116
|
+
/**
|
|
117
|
+
* Check if the current user has a specific entitlement.
|
|
118
|
+
*
|
|
119
|
+
* Must be called in the component body (like query()/form()).
|
|
120
|
+
* Returns an AccessCheck with ReadonlySignal properties that the
|
|
121
|
+
* compiler auto-unwraps via signal-api registration.
|
|
122
|
+
*
|
|
123
|
+
* **UI-advisory only.** Server always re-validates before mutations.
|
|
124
|
+
* `can()` controls UI visibility, not authorization.
|
|
125
|
+
*
|
|
126
|
+
* @param entitlement - The entitlement to check
|
|
127
|
+
* @param entity - Optional entity with pre-computed `__access` metadata
|
|
128
|
+
*/
|
|
129
|
+
declare function can(entitlement: string, entity?: {
|
|
130
|
+
__access?: Record<string, AccessCheckData>;
|
|
131
|
+
}): 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
|
+
/** Client-side access events (no orgId/userId — server scopes the broadcast) */
|
|
139
|
+
type ClientAccessEvent = {
|
|
140
|
+
type: "access:flag_toggled";
|
|
141
|
+
flag: string;
|
|
142
|
+
enabled: boolean;
|
|
143
|
+
} | {
|
|
144
|
+
type: "access:limit_updated";
|
|
145
|
+
entitlement: string;
|
|
146
|
+
consumed: number;
|
|
147
|
+
remaining: number;
|
|
148
|
+
max: number;
|
|
149
|
+
} | {
|
|
150
|
+
type: "access:role_changed";
|
|
151
|
+
} | {
|
|
152
|
+
type: "access:plan_changed";
|
|
153
|
+
};
|
|
154
|
+
interface AccessEventClientOptions {
|
|
155
|
+
/** WebSocket URL. Defaults to deriving from window.location. */
|
|
156
|
+
url?: string;
|
|
157
|
+
/** Called for each access event received from the server. */
|
|
158
|
+
onEvent: (event: ClientAccessEvent) => void;
|
|
159
|
+
/** Called after a successful reconnection (not the initial connect). */
|
|
160
|
+
onReconnect: () => void;
|
|
161
|
+
}
|
|
162
|
+
interface AccessEventClient {
|
|
163
|
+
connect(): void;
|
|
164
|
+
disconnect(): void;
|
|
165
|
+
dispose(): void;
|
|
166
|
+
}
|
|
167
|
+
declare function createAccessEventClient(options: AccessEventClientOptions): AccessEventClient;
|
|
168
|
+
interface AccessGateProps {
|
|
169
|
+
fallback?: () => unknown;
|
|
170
|
+
children: (() => unknown) | unknown;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Gate component that blocks children while the access set is loading.
|
|
174
|
+
* Use this to prevent flicker on initial render when access data
|
|
175
|
+
* hasn't been hydrated yet.
|
|
176
|
+
*/
|
|
177
|
+
declare function AccessGate({ fallback, children }: AccessGateProps): ReadonlySignal<unknown> | unknown;
|
|
178
|
+
import { Result } from "@vertz/fetch";
|
|
179
|
+
/**
|
|
180
|
+
* Minimal schema interface compatible with @vertz/schema.
|
|
181
|
+
* Any object with a `parse(data: unknown): Result` method satisfies this.
|
|
182
|
+
*/
|
|
183
|
+
interface FormSchema<T> {
|
|
184
|
+
parse(data: unknown): {
|
|
185
|
+
ok: true;
|
|
186
|
+
data: T;
|
|
187
|
+
} | {
|
|
188
|
+
ok: false;
|
|
189
|
+
error: unknown;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* An SDK method with endpoint metadata attached.
|
|
194
|
+
* Generated SDK methods expose `.url` and `.method` for progressive enhancement.
|
|
195
|
+
*/
|
|
196
|
+
interface SdkMethod<
|
|
197
|
+
TBody,
|
|
198
|
+
TResult
|
|
199
|
+
> {
|
|
200
|
+
(body: TBody): PromiseLike<Result<TResult, Error>>;
|
|
201
|
+
url: string;
|
|
202
|
+
method: string;
|
|
203
|
+
meta?: {
|
|
204
|
+
bodySchema?: FormSchema<TBody>;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* An SDK method with embedded schema metadata.
|
|
209
|
+
* Generated by `@vertz/codegen` — carries `.meta.bodySchema` for auto-validation.
|
|
210
|
+
*/
|
|
211
|
+
interface SdkMethodWithMeta<
|
|
212
|
+
TBody,
|
|
213
|
+
TResult
|
|
214
|
+
> extends SdkMethod<TBody, TResult> {
|
|
215
|
+
meta: {
|
|
216
|
+
bodySchema: FormSchema<TBody>;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
interface User {
|
|
220
|
+
id: string;
|
|
221
|
+
email: string;
|
|
222
|
+
role: string;
|
|
223
|
+
emailVerified?: boolean;
|
|
224
|
+
[key: string]: unknown;
|
|
225
|
+
}
|
|
226
|
+
type AuthStatus = "idle" | "loading" | "authenticated" | "unauthenticated" | "mfa_required" | "error";
|
|
227
|
+
type AuthErrorCode = "INVALID_CREDENTIALS" | "USER_EXISTS" | "USER_NOT_FOUND" | "INVALID_TOKEN" | "TOKEN_EXPIRED" | "MFA_REQUIRED" | "INVALID_MFA_CODE" | "RATE_LIMITED" | "NETWORK_ERROR" | "SERVER_ERROR";
|
|
228
|
+
interface AuthClientError {
|
|
229
|
+
code: AuthErrorCode;
|
|
230
|
+
message: string;
|
|
231
|
+
statusCode: number;
|
|
232
|
+
retryAfter?: number;
|
|
233
|
+
}
|
|
234
|
+
interface SignInInput {
|
|
235
|
+
email: string;
|
|
236
|
+
password: string;
|
|
237
|
+
}
|
|
238
|
+
interface SignUpInput {
|
|
239
|
+
email: string;
|
|
240
|
+
password: string;
|
|
241
|
+
[key: string]: unknown;
|
|
242
|
+
}
|
|
243
|
+
interface MfaInput {
|
|
244
|
+
code: string;
|
|
245
|
+
}
|
|
246
|
+
interface ForgotInput {
|
|
247
|
+
email: string;
|
|
248
|
+
}
|
|
249
|
+
interface ResetInput {
|
|
250
|
+
token: string;
|
|
251
|
+
password: string;
|
|
252
|
+
}
|
|
253
|
+
interface AuthResponse {
|
|
254
|
+
user: User;
|
|
255
|
+
expiresAt: number;
|
|
256
|
+
}
|
|
257
|
+
interface AuthContextValue {
|
|
258
|
+
user: Signal<User | null>;
|
|
259
|
+
status: Signal<AuthStatus>;
|
|
260
|
+
isAuthenticated: ReadonlySignal<boolean>;
|
|
261
|
+
isLoading: ReadonlySignal<boolean>;
|
|
262
|
+
error: Signal<AuthClientError | null>;
|
|
263
|
+
signIn: SdkMethodWithMeta<SignInInput, AuthResponse>;
|
|
264
|
+
signUp: SdkMethodWithMeta<SignUpInput, AuthResponse>;
|
|
265
|
+
signOut: () => Promise<void>;
|
|
266
|
+
refresh: () => Promise<void>;
|
|
267
|
+
mfaChallenge: SdkMethodWithMeta<MfaInput, AuthResponse>;
|
|
268
|
+
forgotPassword: SdkMethodWithMeta<ForgotInput, void>;
|
|
269
|
+
resetPassword: SdkMethodWithMeta<ResetInput, void>;
|
|
270
|
+
}
|
|
271
|
+
declare const AuthContext: Context<AuthContextValue>;
|
|
272
|
+
declare function useAuth(): UnwrapSignals<AuthContextValue>;
|
|
273
|
+
interface AuthProviderProps {
|
|
274
|
+
basePath?: string;
|
|
275
|
+
accessControl?: boolean;
|
|
276
|
+
/** Enable WebSocket-based real-time access event updates. Requires accessControl. */
|
|
277
|
+
accessEvents?: boolean;
|
|
278
|
+
/** WebSocket URL for access events. Defaults to deriving from window.location. */
|
|
279
|
+
accessEventsUrl?: string;
|
|
280
|
+
/** Map of entitlement names to their required flags. Used for inline flag toggle updates. */
|
|
281
|
+
flagEntitlementMap?: Record<string, string[]>;
|
|
282
|
+
children: (() => unknown) | unknown;
|
|
283
|
+
}
|
|
284
|
+
declare function AuthProvider({ basePath, accessControl, accessEvents, accessEventsUrl, flagEntitlementMap, children }: AuthProviderProps): HTMLElement;
|
|
285
|
+
interface AuthGateProps {
|
|
286
|
+
fallback?: () => unknown;
|
|
287
|
+
children: (() => unknown) | unknown;
|
|
288
|
+
}
|
|
289
|
+
declare function AuthGate({ fallback, children }: AuthGateProps): ReadonlySignal<unknown> | unknown;
|
|
290
|
+
/**
|
|
291
|
+
* Create an AccessContextValue for use with AccessContext.Provider.
|
|
292
|
+
* Hydrates from `window.__VERTZ_ACCESS_SET__` when available (SSR).
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```tsx
|
|
296
|
+
* const accessValue = createAccessProvider();
|
|
297
|
+
* <AccessContext.Provider value={accessValue}>
|
|
298
|
+
* <App />
|
|
299
|
+
* </AccessContext.Provider>
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
declare function createAccessProvider(): AccessContextValue;
|
|
303
|
+
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 };
|