@zautha/react 0.2.0 → 0.3.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/README.md +21 -0
- package/dist/index.cjs +55 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -3
- package/dist/index.d.mts +14 -3
- package/dist/index.mjs +32 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -30,6 +30,27 @@ export function App() {
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## Account management
|
|
34
|
+
|
|
35
|
+
`<UserProfile />` is the signed-in user's account page: name and username, email addresses, password, active
|
|
36
|
+
devices and (when the instance allows it) account deletion. `<UserButton />`'s "Manage account" opens it in a modal, or
|
|
37
|
+
goes to `userProfileUrl` when you pass one (e.g. your instance's hosted `/user` page).
|
|
38
|
+
|
|
39
|
+
## Sensitive actions (reverification)
|
|
40
|
+
|
|
41
|
+
`useReverification(fetcher)` wraps an action that may need a recent identity check. When `fetcher` throws
|
|
42
|
+
`ReverificationRequiredError` (e.g. `user.updatePassword`) or resolves to your backend's `403 reverification_required`
|
|
43
|
+
response (`requireReverification` in `@zautha/backend`, `reverificationError` in `@zautha/nextjs`,
|
|
44
|
+
`RequireZauthaReverification` in `Zautha.AspNetCore`), a modal asks the user to verify and the action runs once more.
|
|
45
|
+
Closing the modal rejects with `ReverificationCancelledError`.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
const { getToken } = useAuth();
|
|
49
|
+
const transfer = useReverification(async () =>
|
|
50
|
+
fetch("/api/transfer", { method: "POST", headers: { Authorization: `Bearer ${await getToken()}` } }),
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
33
54
|
## License
|
|
34
55
|
|
|
35
56
|
MIT, see [LICENSE](./LICENSE).
|
package/dist/index.cjs
CHANGED
|
@@ -89,14 +89,36 @@ function useAuth() {
|
|
|
89
89
|
has
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
+
/** The signed-in user with its self-service actions (`user.update`, `user.createEmailAddress`, …). */
|
|
92
93
|
function useUser() {
|
|
94
|
+
const zautha = useZautha();
|
|
93
95
|
const state = useZauthaState();
|
|
96
|
+
const user = state.user === null ? null : zautha?.user ?? null;
|
|
94
97
|
return {
|
|
95
98
|
isLoaded: isLoadedStatus(state.status),
|
|
96
99
|
isSignedIn: state.session !== null,
|
|
97
|
-
user
|
|
100
|
+
user
|
|
98
101
|
};
|
|
99
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Wraps a sensitive action so that it completes after an identity check when it needs one: when `fetcher` throws
|
|
105
|
+
* `ReverificationRequiredError` (e.g. `user.updatePassword`) or resolves to a `Response` with the `403
|
|
106
|
+
* reverification_required` body (your backend with @zautha/backend, @zautha/nextjs or Zautha.AspNetCore), the
|
|
107
|
+
* reverification modal opens and, once the user has verified, `fetcher` runs once more with the same arguments (its
|
|
108
|
+
* token is fresh by then). Closing the modal rejects with `ReverificationCancelledError`. Needs `ui` from @zautha/ui on
|
|
109
|
+
* the provider. The returned function is stable; it always calls the latest `fetcher`.
|
|
110
|
+
*/
|
|
111
|
+
function useReverification(fetcher) {
|
|
112
|
+
const zautha = useZautha();
|
|
113
|
+
const latest = (0, react.useRef)(fetcher);
|
|
114
|
+
(0, react.useLayoutEffect)(() => {
|
|
115
|
+
latest.current = fetcher;
|
|
116
|
+
});
|
|
117
|
+
return (0, react.useCallback)((...args) => {
|
|
118
|
+
const call = (...callArgs) => latest.current(...callArgs);
|
|
119
|
+
return zautha === null ? call(...args) : (0, _zautha_js.withReverification)(zautha, call)(...args);
|
|
120
|
+
}, [zautha]);
|
|
121
|
+
}
|
|
100
122
|
function useSession() {
|
|
101
123
|
const state = useZauthaState();
|
|
102
124
|
return {
|
|
@@ -281,6 +303,12 @@ function UserButton(props) {
|
|
|
281
303
|
componentProps: props
|
|
282
304
|
});
|
|
283
305
|
}
|
|
306
|
+
function UserProfile(props) {
|
|
307
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Mounted, {
|
|
308
|
+
name: "UserProfile",
|
|
309
|
+
componentProps: props
|
|
310
|
+
});
|
|
311
|
+
}
|
|
284
312
|
function OAuthConsent(props) {
|
|
285
313
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Mounted, {
|
|
286
314
|
name: "OAuthConsent",
|
|
@@ -291,6 +319,18 @@ function OAuthConsent(props) {
|
|
|
291
319
|
exports.OAuthConsent = OAuthConsent;
|
|
292
320
|
exports.RedirectToSignIn = RedirectToSignIn;
|
|
293
321
|
exports.RedirectToSignUp = RedirectToSignUp;
|
|
322
|
+
Object.defineProperty(exports, "ReverificationCancelledError", {
|
|
323
|
+
enumerable: true,
|
|
324
|
+
get: function() {
|
|
325
|
+
return _zautha_js.ReverificationCancelledError;
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
Object.defineProperty(exports, "ReverificationRequiredError", {
|
|
329
|
+
enumerable: true,
|
|
330
|
+
get: function() {
|
|
331
|
+
return _zautha_js.ReverificationRequiredError;
|
|
332
|
+
}
|
|
333
|
+
});
|
|
294
334
|
exports.Show = Show;
|
|
295
335
|
exports.SignIn = SignIn;
|
|
296
336
|
exports.SignInButton = SignInButton;
|
|
@@ -298,6 +338,7 @@ exports.SignOutButton = SignOutButton;
|
|
|
298
338
|
exports.SignUp = SignUp;
|
|
299
339
|
exports.SignUpButton = SignUpButton;
|
|
300
340
|
exports.UserButton = UserButton;
|
|
341
|
+
exports.UserProfile = UserProfile;
|
|
301
342
|
Object.defineProperty(exports, "ZauthaApiError", {
|
|
302
343
|
enumerable: true,
|
|
303
344
|
get: function() {
|
|
@@ -327,6 +368,18 @@ Object.defineProperty(exports, "ZauthaOfflineError", {
|
|
|
327
368
|
}
|
|
328
369
|
});
|
|
329
370
|
exports.ZauthaProvider = ZauthaProvider;
|
|
371
|
+
Object.defineProperty(exports, "isReverificationCancelledError", {
|
|
372
|
+
enumerable: true,
|
|
373
|
+
get: function() {
|
|
374
|
+
return _zautha_js.isReverificationCancelledError;
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
Object.defineProperty(exports, "isReverificationRequiredError", {
|
|
378
|
+
enumerable: true,
|
|
379
|
+
get: function() {
|
|
380
|
+
return _zautha_js.isReverificationRequiredError;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
330
383
|
Object.defineProperty(exports, "isZauthaApiError", {
|
|
331
384
|
enumerable: true,
|
|
332
385
|
get: function() {
|
|
@@ -352,6 +405,7 @@ Object.defineProperty(exports, "isZauthaOfflineError", {
|
|
|
352
405
|
}
|
|
353
406
|
});
|
|
354
407
|
exports.useAuth = useAuth;
|
|
408
|
+
exports.useReverification = useReverification;
|
|
355
409
|
exports.useSession = useSession;
|
|
356
410
|
exports.useSignIn = useSignIn;
|
|
357
411
|
exports.useSignUp = useSignUp;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["createContext","useState","Zautha","useMemo","useContext","useSyncExternalStore","useCallback","useSyncExternalStore","Children","isValidElement","cloneElement","useRef"],"sources":["../src/provider.tsx","../src/hooks.ts","../src/control.tsx","../src/buttons.tsx","../src/prebuilt.tsx"],"sourcesContent":["import { Zautha, type ZauthaOptions, type ZauthaState } from \"@zautha/js\";\nimport { createContext, useContext, useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from \"react\";\n\ninterface ZauthaContextValue {\n readonly zautha: Zautha | null;\n /** The snapshot before the instance exists and during server rendering (carries `initialState`). */\n readonly pending: ZauthaState;\n}\n\nconst ZauthaContext = createContext<ZauthaContextValue | undefined>(undefined);\n\nexport interface ZauthaProviderProps extends Omit<ZauthaOptions, \"fetch\"> {\n publishableKey: string;\n children?: ReactNode;\n}\n\n/**\n * Creates the Zautha instance after mount (never during render, so StrictMode's double mount and SSR\n * create no timers or channels), loads it, and disposes it on unmount. Until then hooks report\n * `isLoaded: false`, unless `initialState` (the server's view, e.g. from @zautha/nextjs) says who is signed in.\n */\nexport function ZauthaProvider({ publishableKey, children, ...options }: ZauthaProviderProps): ReactNode {\n const [zautha, setZautha] = useState<Zautha | null>(null);\n const { ui, appearance, localization, signInUrl, signUpUrl, navigate, initialState } = options;\n const [pending] = useState<ZauthaState>(() => ({ ...NO_STATE, initialState: initialState ?? null }));\n\n useEffect(() => {\n const instance = new Zautha(publishableKey, {\n ...(ui === undefined ? {} : { ui }),\n ...(appearance === undefined ? {} : { appearance }),\n ...(localization === undefined ? {} : { localization }),\n ...(signInUrl === undefined ? {} : { signInUrl }),\n ...(signUpUrl === undefined ? {} : { signUpUrl }),\n ...(navigate === undefined ? {} : { navigate }),\n ...(initialState === undefined ? {} : { initialState }),\n });\n // The instance is an external system: it is created here, never during render, and published through state.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setZautha(instance);\n void instance.load();\n return () => {\n instance.dispose();\n setZautha(null);\n };\n // Appearance/localization objects are read at mount; changing the key re-creates the instance.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [publishableKey]);\n\n const value = useMemo(() => ({ zautha, pending }), [zautha, pending]);\n return <ZauthaContext.Provider value={value}>{children}</ZauthaContext.Provider>;\n}\n\n/** The Zautha instance, or null before the provider has created it. */\nexport function useZautha(): Zautha | null {\n return useZauthaContext().zautha;\n}\n\nfunction useZauthaContext(): ZauthaContextValue {\n const value = useContext(ZauthaContext);\n if (value === undefined) throw new Error(\"Zautha hooks and components must be used inside <ZauthaProvider>.\");\n return value;\n}\n\nconst NO_STATE: ZauthaState = {\n status: \"loading\",\n environment: null,\n client: null,\n session: null,\n user: null,\n initialState: null,\n};\nconst noop = (): (() => void) => () => undefined;\n\n/** Reactive snapshot of the instance state (the loading state, with any initial state, before the instance exists). */\nexport function useZauthaState(): ZauthaState {\n const { zautha, pending } = useZauthaContext();\n return useSyncExternalStore(\n zautha?.subscribe ?? noop,\n () => zautha?.getSnapshot() ?? pending,\n () => pending,\n );\n}\n","import type { AttemptErrors, FetchStatus, Session, SignInResource, SignUpResource, User } from \"@zautha/js\";\nimport { useCallback, useSyncExternalStore } from \"react\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\n/** Authorization check. In 3a there are no organization claims yet, so role/permission checks are false. */\nexport type HasParams = { role?: string; permission?: string };\nexport type Has = (params: HasParams) => boolean;\n\nexport interface UseAuthResult {\n isLoaded: boolean;\n isSignedIn: boolean;\n userId: string | null;\n sessionId: string | null;\n getToken: (options?: { skipCache?: boolean }) => Promise<string | null>;\n signOut: (options?: { redirectUrl?: string }) => Promise<void>;\n has: Has;\n}\n\nconst isLoadedStatus = (status: string): boolean => status === \"ready\" || status === \"degraded\";\n\nexport function useAuth(): UseAuthResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const getToken = useCallback(\n (options?: { skipCache?: boolean }) => zautha?.getToken(options) ?? Promise.resolve(null),\n [zautha],\n );\n const signOut = useCallback(\n (options?: { redirectUrl?: string }) => zautha?.signOut(options) ?? Promise.resolve(),\n [zautha],\n );\n // Until the first load settles, the server's initial state (if any) answers, so nothing flickers.\n const initial = state.status === \"loading\" ? state.initialState : null;\n const userId = initial === null ? (state.session?.userId ?? null) : initial.userId;\n const sessionId = initial === null ? (state.session?.id ?? null) : initial.sessionId;\n const signedIn = userId !== null;\n const has = useCallback<Has>(\n (params) => signedIn && params.role === undefined && params.permission === undefined,\n [signedIn],\n );\n return {\n isLoaded: isLoadedStatus(state.status) || initial !== null,\n isSignedIn: signedIn,\n userId,\n sessionId,\n getToken,\n signOut,\n has,\n };\n}\n\nexport function useUser(): { isLoaded: boolean; isSignedIn: boolean; user: User | null } {\n const state = useZauthaState();\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, user: state.user };\n}\n\nexport function useSession(): { isLoaded: boolean; isSignedIn: boolean; session: Session | null } {\n const state = useZauthaState();\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, session: state.session };\n}\n\nconst noopSubscribe = (): (() => void) => () => undefined;\n\nfunction useResource<T extends SignInResource | SignUpResource>(resource: T | null) {\n const snapshot = useSyncExternalStore(\n resource?.subscribe ?? noopSubscribe,\n () => resource?.snapshot ?? null,\n () => null,\n );\n return snapshot;\n}\n\nexport interface UseSignInResult {\n isLoaded: boolean;\n /** The sign-in resource; null until the provider has created the instance. */\n signIn: SignInResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nconst NO_ERRORS: AttemptErrors = { fields: {}, global: [] };\n\nexport function useSignIn(): UseSignInResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signIn ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signIn: zautha?.signIn ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n\nexport interface UseSignUpResult {\n isLoaded: boolean;\n signUp: SignUpResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nexport function useSignUp(): UseSignUpResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signUp ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signUp: zautha?.signUp ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n","import { useEffect, type ReactNode } from \"react\";\nimport { useAuth, type Has, type HasParams } from \"./hooks\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\nexport type ShowWhen = \"signed-in\" | \"signed-out\" | HasParams | ((has: Has) => boolean);\n\n/**\n * Renders `children` when the condition holds, else `fallback`. Renders nothing until Zautha has\n * loaded, so a signed-in user never sees a signed-out flash.\n */\nexport function Show(props: { when: ShowWhen; fallback?: ReactNode; children?: ReactNode }): ReactNode {\n const { isLoaded, isSignedIn, has } = useAuth();\n if (!isLoaded) return null;\n const { when } = props;\n const pass =\n when === \"signed-in\"\n ? isSignedIn\n : when === \"signed-out\"\n ? !isSignedIn\n : typeof when === \"function\"\n ? when(has)\n : has(when);\n return pass ? props.children : (props.fallback ?? null);\n}\n\nexport function ZauthaLoading(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"loading\" ? props.children : null;\n}\n\n/** Loaded, including degraded (network trouble after load: last known state is shown). */\nexport function ZauthaLoaded(props: { children?: ReactNode }): ReactNode {\n const { status } = useZauthaState();\n return status === \"ready\" || status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaDegraded(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaFailed(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"failed\" ? props.children : null;\n}\n\n/** Sends a signed-out visitor to sign-in once loaded; renders nothing. */\nexport function RedirectToSignIn(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignIn(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n\nexport function RedirectToSignUp(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignUp(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n","import { Children, cloneElement, isValidElement, type MouseEvent, type ReactElement, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\ninterface ButtonProps {\n /** `redirect` (default) goes to the sign-in page; `modal` opens the prebuilt component (needs `ui`). */\n mode?: \"redirect\" | \"modal\";\n redirectUrl?: string;\n children?: ReactNode;\n}\n\n/** Wraps its single child (or renders a plain button) and wires the click. Unstyled by design. */\nfunction Trigger(props: { children: ReactNode; label: string; onClick: () => void }): ReactNode {\n const child = Children.count(props.children) === 1 ? Children.only(props.children) : null;\n if (isValidElement<{ onClick?: (event: MouseEvent) => void }>(child)) {\n const original = child.props.onClick;\n return cloneElement(child as ReactElement<{ onClick?: (event: MouseEvent) => void }>, {\n onClick: (event: MouseEvent) => {\n original?.(event);\n if (!event.defaultPrevented) props.onClick();\n },\n });\n }\n return (\n <button type=\"button\" onClick={props.onClick}>\n {props.children ?? props.label}\n </button>\n );\n}\n\nexport function SignInButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign in\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignIn(options) : zautha?.redirectToSignIn(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignUpButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign up\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignUp(options) : zautha?.redirectToSignUp(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignOutButton(props: { redirectUrl?: string; children?: ReactNode }): ReactNode {\n const zautha = useZautha();\n return (\n <Trigger\n label=\"Sign out\"\n onClick={() => void zautha?.signOut(props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl })}\n >\n {props.children}\n </Trigger>\n );\n}\n","import type { UiComponentName } from \"@zautha/js\";\nimport { useEffect, useRef, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\n/**\n * Mounts a @zautha/ui component into a div once the instance exists; later prop changes update it in\n * place. SSR renders only the empty div.\n */\nfunction Mounted(props: { name: UiComponentName; componentProps: object }): ReactNode {\n const zautha = useZautha();\n const ref = useRef<HTMLDivElement>(null);\n const mountedWith = useRef<object | null>(null);\n const { name, componentProps } = props;\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null) return;\n zautha.mount(name, element, componentProps);\n mountedWith.current = componentProps;\n return () => {\n mountedWith.current = null;\n zautha.unmount(element);\n };\n // componentProps changes are applied by the effect below without remounting.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [zautha, name]);\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null || mountedWith.current === null || mountedWith.current === componentProps)\n return;\n mountedWith.current = componentProps;\n zautha.updateMounted(element, componentProps);\n }, [zautha, componentProps]);\n\n return <div ref={ref} />;\n}\n\n// Prop types live in @zautha/ui; they are structural here so @zautha/react has no runtime dependency on it.\nexport function SignIn(props: import(\"@zautha/ui\").SignInProps): ReactNode {\n return <Mounted name=\"SignIn\" componentProps={props} />;\n}\n\nexport function SignUp(props: import(\"@zautha/ui\").SignUpProps): ReactNode {\n return <Mounted name=\"SignUp\" componentProps={props} />;\n}\n\nexport function UserButton(props: import(\"@zautha/ui\").UserButtonProps): ReactNode {\n return <Mounted name=\"UserButton\" componentProps={props} />;\n}\n\nexport function OAuthConsent(props: import(\"@zautha/ui\").OAuthConsentProps): ReactNode {\n return <Mounted name=\"OAuthConsent\" componentProps={props} />;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createContext","useState","Zautha","useMemo","useContext","useSyncExternalStore","useCallback","useRef","withReverification","useSyncExternalStore","Children","isValidElement","cloneElement","useRef"],"sources":["../src/provider.tsx","../src/hooks.ts","../src/control.tsx","../src/buttons.tsx","../src/prebuilt.tsx"],"sourcesContent":["import { Zautha, type ZauthaOptions, type ZauthaState } from \"@zautha/js\";\nimport { createContext, useContext, useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from \"react\";\n\ninterface ZauthaContextValue {\n readonly zautha: Zautha | null;\n /** The snapshot before the instance exists and during server rendering (carries `initialState`). */\n readonly pending: ZauthaState;\n}\n\nconst ZauthaContext = createContext<ZauthaContextValue | undefined>(undefined);\n\nexport interface ZauthaProviderProps extends Omit<ZauthaOptions, \"fetch\"> {\n publishableKey: string;\n children?: ReactNode;\n}\n\n/**\n * Creates the Zautha instance after mount (never during render, so StrictMode's double mount and SSR\n * create no timers or channels), loads it, and disposes it on unmount. Until then hooks report\n * `isLoaded: false`, unless `initialState` (the server's view, e.g. from @zautha/nextjs) says who is signed in.\n */\nexport function ZauthaProvider({ publishableKey, children, ...options }: ZauthaProviderProps): ReactNode {\n const [zautha, setZautha] = useState<Zautha | null>(null);\n const { ui, appearance, localization, signInUrl, signUpUrl, navigate, initialState } = options;\n const [pending] = useState<ZauthaState>(() => ({ ...NO_STATE, initialState: initialState ?? null }));\n\n useEffect(() => {\n const instance = new Zautha(publishableKey, {\n ...(ui === undefined ? {} : { ui }),\n ...(appearance === undefined ? {} : { appearance }),\n ...(localization === undefined ? {} : { localization }),\n ...(signInUrl === undefined ? {} : { signInUrl }),\n ...(signUpUrl === undefined ? {} : { signUpUrl }),\n ...(navigate === undefined ? {} : { navigate }),\n ...(initialState === undefined ? {} : { initialState }),\n });\n // The instance is an external system: it is created here, never during render, and published through state.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setZautha(instance);\n void instance.load();\n return () => {\n instance.dispose();\n setZautha(null);\n };\n // Appearance/localization objects are read at mount; changing the key re-creates the instance.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [publishableKey]);\n\n const value = useMemo(() => ({ zautha, pending }), [zautha, pending]);\n return <ZauthaContext.Provider value={value}>{children}</ZauthaContext.Provider>;\n}\n\n/** The Zautha instance, or null before the provider has created it. */\nexport function useZautha(): Zautha | null {\n return useZauthaContext().zautha;\n}\n\nfunction useZauthaContext(): ZauthaContextValue {\n const value = useContext(ZauthaContext);\n if (value === undefined) throw new Error(\"Zautha hooks and components must be used inside <ZauthaProvider>.\");\n return value;\n}\n\nconst NO_STATE: ZauthaState = {\n status: \"loading\",\n environment: null,\n client: null,\n session: null,\n user: null,\n initialState: null,\n};\nconst noop = (): (() => void) => () => undefined;\n\n/** Reactive snapshot of the instance state (the loading state, with any initial state, before the instance exists). */\nexport function useZauthaState(): ZauthaState {\n const { zautha, pending } = useZauthaContext();\n return useSyncExternalStore(\n zautha?.subscribe ?? noop,\n () => zautha?.getSnapshot() ?? pending,\n () => pending,\n );\n}\n","import {\n withReverification,\n type AttemptErrors,\n type FetchStatus,\n type Session,\n type SignInResource,\n type SignUpResource,\n type UserResource,\n} from \"@zautha/js\";\nimport { useCallback, useLayoutEffect, useRef, useSyncExternalStore } from \"react\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\n/** Authorization check. In 3a there are no organization claims yet, so role/permission checks are false. */\nexport type HasParams = { role?: string; permission?: string };\nexport type Has = (params: HasParams) => boolean;\n\nexport interface UseAuthResult {\n isLoaded: boolean;\n isSignedIn: boolean;\n userId: string | null;\n sessionId: string | null;\n getToken: (options?: { skipCache?: boolean }) => Promise<string | null>;\n signOut: (options?: { redirectUrl?: string }) => Promise<void>;\n has: Has;\n}\n\nconst isLoadedStatus = (status: string): boolean => status === \"ready\" || status === \"degraded\";\n\nexport function useAuth(): UseAuthResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const getToken = useCallback(\n (options?: { skipCache?: boolean }) => zautha?.getToken(options) ?? Promise.resolve(null),\n [zautha],\n );\n const signOut = useCallback(\n (options?: { redirectUrl?: string }) => zautha?.signOut(options) ?? Promise.resolve(),\n [zautha],\n );\n // Until the first load settles, the server's initial state (if any) answers, so nothing flickers.\n const initial = state.status === \"loading\" ? state.initialState : null;\n const userId = initial === null ? (state.session?.userId ?? null) : initial.userId;\n const sessionId = initial === null ? (state.session?.id ?? null) : initial.sessionId;\n const signedIn = userId !== null;\n const has = useCallback<Has>(\n (params) => signedIn && params.role === undefined && params.permission === undefined,\n [signedIn],\n );\n return {\n isLoaded: isLoadedStatus(state.status) || initial !== null,\n isSignedIn: signedIn,\n userId,\n sessionId,\n getToken,\n signOut,\n has,\n };\n}\n\n/** The signed-in user with its self-service actions (`user.update`, `user.createEmailAddress`, …). */\nexport function useUser(): { isLoaded: boolean; isSignedIn: boolean; user: UserResource | null } {\n const zautha = useZautha();\n const state = useZauthaState();\n // zautha.user is rebuilt only when state.user changes, so the resource is stable between renders.\n const user = state.user === null ? null : (zautha?.user ?? null);\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, user };\n}\n\n/**\n * Wraps a sensitive action so that it completes after an identity check when it needs one: when `fetcher` throws\n * `ReverificationRequiredError` (e.g. `user.updatePassword`) or resolves to a `Response` with the `403\n * reverification_required` body (your backend with @zautha/backend, @zautha/nextjs or Zautha.AspNetCore), the\n * reverification modal opens and, once the user has verified, `fetcher` runs once more with the same arguments (its\n * token is fresh by then). Closing the modal rejects with `ReverificationCancelledError`. Needs `ui` from @zautha/ui on\n * the provider. The returned function is stable; it always calls the latest `fetcher`.\n */\nexport function useReverification<Args extends unknown[], Result>(\n fetcher: (...args: Args) => Promise<Result>,\n): (...args: Args) => Promise<Result> {\n const zautha = useZautha();\n const latest = useRef(fetcher);\n useLayoutEffect(() => {\n latest.current = fetcher;\n });\n return useCallback(\n (...args: Args) => {\n const call = (...callArgs: Args): Promise<Result> => latest.current(...callArgs);\n // Before the provider has created the instance no modal can open: the action runs as it is.\n return zautha === null ? call(...args) : withReverification(zautha, call)(...args);\n },\n [zautha],\n );\n}\n\nexport function useSession(): { isLoaded: boolean; isSignedIn: boolean; session: Session | null } {\n const state = useZauthaState();\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, session: state.session };\n}\n\nconst noopSubscribe = (): (() => void) => () => undefined;\n\nfunction useResource<T extends SignInResource | SignUpResource>(resource: T | null) {\n const snapshot = useSyncExternalStore(\n resource?.subscribe ?? noopSubscribe,\n () => resource?.snapshot ?? null,\n () => null,\n );\n return snapshot;\n}\n\nexport interface UseSignInResult {\n isLoaded: boolean;\n /** The sign-in resource; null until the provider has created the instance. */\n signIn: SignInResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nconst NO_ERRORS: AttemptErrors = { fields: {}, global: [] };\n\nexport function useSignIn(): UseSignInResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signIn ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signIn: zautha?.signIn ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n\nexport interface UseSignUpResult {\n isLoaded: boolean;\n signUp: SignUpResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nexport function useSignUp(): UseSignUpResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signUp ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signUp: zautha?.signUp ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n","import { useEffect, type ReactNode } from \"react\";\nimport { useAuth, type Has, type HasParams } from \"./hooks\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\nexport type ShowWhen = \"signed-in\" | \"signed-out\" | HasParams | ((has: Has) => boolean);\n\n/**\n * Renders `children` when the condition holds, else `fallback`. Renders nothing until Zautha has\n * loaded, so a signed-in user never sees a signed-out flash.\n */\nexport function Show(props: { when: ShowWhen; fallback?: ReactNode; children?: ReactNode }): ReactNode {\n const { isLoaded, isSignedIn, has } = useAuth();\n if (!isLoaded) return null;\n const { when } = props;\n const pass =\n when === \"signed-in\"\n ? isSignedIn\n : when === \"signed-out\"\n ? !isSignedIn\n : typeof when === \"function\"\n ? when(has)\n : has(when);\n return pass ? props.children : (props.fallback ?? null);\n}\n\nexport function ZauthaLoading(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"loading\" ? props.children : null;\n}\n\n/** Loaded, including degraded (network trouble after load: last known state is shown). */\nexport function ZauthaLoaded(props: { children?: ReactNode }): ReactNode {\n const { status } = useZauthaState();\n return status === \"ready\" || status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaDegraded(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaFailed(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"failed\" ? props.children : null;\n}\n\n/** Sends a signed-out visitor to sign-in once loaded; renders nothing. */\nexport function RedirectToSignIn(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignIn(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n\nexport function RedirectToSignUp(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignUp(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n","import { Children, cloneElement, isValidElement, type MouseEvent, type ReactElement, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\ninterface ButtonProps {\n /** `redirect` (default) goes to the sign-in page; `modal` opens the prebuilt component (needs `ui`). */\n mode?: \"redirect\" | \"modal\";\n redirectUrl?: string;\n children?: ReactNode;\n}\n\n/** Wraps its single child (or renders a plain button) and wires the click. Unstyled by design. */\nfunction Trigger(props: { children: ReactNode; label: string; onClick: () => void }): ReactNode {\n const child = Children.count(props.children) === 1 ? Children.only(props.children) : null;\n if (isValidElement<{ onClick?: (event: MouseEvent) => void }>(child)) {\n const original = child.props.onClick;\n return cloneElement(child as ReactElement<{ onClick?: (event: MouseEvent) => void }>, {\n onClick: (event: MouseEvent) => {\n original?.(event);\n if (!event.defaultPrevented) props.onClick();\n },\n });\n }\n return (\n <button type=\"button\" onClick={props.onClick}>\n {props.children ?? props.label}\n </button>\n );\n}\n\nexport function SignInButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign in\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignIn(options) : zautha?.redirectToSignIn(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignUpButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign up\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignUp(options) : zautha?.redirectToSignUp(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignOutButton(props: { redirectUrl?: string; children?: ReactNode }): ReactNode {\n const zautha = useZautha();\n return (\n <Trigger\n label=\"Sign out\"\n onClick={() => void zautha?.signOut(props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl })}\n >\n {props.children}\n </Trigger>\n );\n}\n","import type { UiComponentName } from \"@zautha/js\";\nimport { useEffect, useRef, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\n/**\n * Mounts a @zautha/ui component into a div once the instance exists; later prop changes update it in\n * place. SSR renders only the empty div.\n */\nfunction Mounted(props: { name: UiComponentName; componentProps: object }): ReactNode {\n const zautha = useZautha();\n const ref = useRef<HTMLDivElement>(null);\n const mountedWith = useRef<object | null>(null);\n const { name, componentProps } = props;\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null) return;\n zautha.mount(name, element, componentProps);\n mountedWith.current = componentProps;\n return () => {\n mountedWith.current = null;\n zautha.unmount(element);\n };\n // componentProps changes are applied by the effect below without remounting.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [zautha, name]);\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null || mountedWith.current === null || mountedWith.current === componentProps)\n return;\n mountedWith.current = componentProps;\n zautha.updateMounted(element, componentProps);\n }, [zautha, componentProps]);\n\n return <div ref={ref} />;\n}\n\n// Prop types live in @zautha/ui; they are structural here so @zautha/react has no runtime dependency on it.\nexport function SignIn(props: import(\"@zautha/ui\").SignInProps): ReactNode {\n return <Mounted name=\"SignIn\" componentProps={props} />;\n}\n\nexport function SignUp(props: import(\"@zautha/ui\").SignUpProps): ReactNode {\n return <Mounted name=\"SignUp\" componentProps={props} />;\n}\n\nexport function UserButton(props: import(\"@zautha/ui\").UserButtonProps): ReactNode {\n return <Mounted name=\"UserButton\" componentProps={props} />;\n}\n\nexport function UserProfile(props: import(\"@zautha/ui\").UserProfileProps): ReactNode {\n return <Mounted name=\"UserProfile\" componentProps={props} />;\n}\n\nexport function OAuthConsent(props: import(\"@zautha/ui\").OAuthConsentProps): ReactNode {\n return <Mounted name=\"OAuthConsent\" componentProps={props} />;\n}\n"],"mappings":";;;;;;AASA,MAAM,iBAAA,GAAgBA,MAAAA,cAAAA,CAA8C,KAAA,CAAS;;;;;;AAY7E,SAAgB,eAAe,EAAE,gBAAgB,UAAU,GAAG,WAA2C;CACvG,MAAM,CAAC,QAAQ,cAAA,GAAaC,MAAAA,SAAAA,CAAwB,IAAI;CACxD,MAAM,EAAE,IAAI,YAAY,cAAc,WAAW,WAAW,UAAU,iBAAiB;CACvF,MAAM,CAAC,YAAA,GAAWA,MAAAA,SAAAA,QAA6B;EAAE,GAAG;EAAU,cAAc,gBAAgB;CAAK,EAAE;CAEnG,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,WAAW,IAAIC,WAAAA,OAAO,gBAAgB;GAC1C,GAAI,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,GAAG;GACjC,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;GACjD,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;GACrD,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;GAC/C,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;GAC/C,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;GAC7C,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACvD,CAAC;EAGD,UAAU,QAAQ;EAClB,SAAc,KAAK;EACnB,aAAa;GACX,SAAS,QAAQ;GACjB,UAAU,IAAI;EAChB;CAGF,GAAG,CAAC,cAAc,CAAC;CAEnB,MAAM,SAAA,GAAQC,MAAAA,QAAAA,QAAe;EAAE;EAAQ;CAAQ,IAAI,CAAC,QAAQ,OAAO,CAAC;CACpE,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAc,UAAf;EAA+B;EAAQ;CAAiC,CAAA;AACjF;;AAGA,SAAgB,YAA2B;CACzC,OAAO,iBAAiB,CAAC,CAAC;AAC5B;AAEA,SAAS,mBAAuC;CAC9C,MAAM,SAAA,GAAQC,MAAAA,WAAAA,CAAW,aAAa;CACtC,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,MAAM,mEAAmE;CAC5G,OAAO;AACT;AAEA,MAAM,WAAwB;CAC5B,QAAQ;CACR,aAAa;CACb,QAAQ;CACR,SAAS;CACT,MAAM;CACN,cAAc;AAChB;AACA,MAAM,mBAAiC,KAAA;;AAGvC,SAAgB,iBAA8B;CAC5C,MAAM,EAAE,QAAQ,YAAY,iBAAiB;CAC7C,QAAA,GAAOC,MAAAA,qBAAAA,CACL,QAAQ,aAAa,YACf,QAAQ,YAAY,KAAK,eACzB,OACR;AACF;;;ACvDA,MAAM,kBAAkB,WAA4B,WAAW,WAAW,WAAW;AAErF,SAAgB,UAAyB;CACvC,MAAM,SAAS,UAAU;CACzB,MAAM,QAAQ,eAAe;CAC7B,MAAM,YAAA,GAAWC,MAAAA,YAAAA,EACd,YAAsC,QAAQ,SAAS,OAAO,KAAK,QAAQ,QAAQ,IAAI,GACxF,CAAC,MAAM,CACT;CACA,MAAM,WAAA,GAAUA,MAAAA,YAAAA,EACb,YAAuC,QAAQ,QAAQ,OAAO,KAAK,QAAQ,QAAQ,GACpF,CAAC,MAAM,CACT;CAEA,MAAM,UAAU,MAAM,WAAW,YAAY,MAAM,eAAe;CAClE,MAAM,SAAS,YAAY,OAAQ,MAAM,SAAS,UAAU,OAAQ,QAAQ;CAC5E,MAAM,YAAY,YAAY,OAAQ,MAAM,SAAS,MAAM,OAAQ,QAAQ;CAC3E,MAAM,WAAW,WAAW;CAC5B,MAAM,OAAA,GAAMA,MAAAA,YAAAA,EACT,WAAW,YAAY,OAAO,SAAS,KAAA,KAAa,OAAO,eAAe,KAAA,GAC3E,CAAC,QAAQ,CACX;CACA,OAAO;EACL,UAAU,eAAe,MAAM,MAAM,KAAK,YAAY;EACtD,YAAY;EACZ;EACA;EACA;EACA;EACA;CACF;AACF;;AAGA,SAAgB,UAAiF;CAC/F,MAAM,SAAS,UAAU;CACzB,MAAM,QAAQ,eAAe;CAE7B,MAAM,OAAO,MAAM,SAAS,OAAO,OAAQ,QAAQ,QAAQ;CAC3D,OAAO;EAAE,UAAU,eAAe,MAAM,MAAM;EAAG,YAAY,MAAM,YAAY;EAAM;CAAK;AAC5F;;;;;;;;;AAUA,SAAgB,kBACd,SACoC;CACpC,MAAM,SAAS,UAAU;CACzB,MAAM,UAAA,GAASC,MAAAA,OAAAA,CAAO,OAAO;CAC7B,CAAA,GAAA,MAAA,gBAAA,OAAsB;EACpB,OAAO,UAAU;CACnB,CAAC;CACD,QAAA,GAAOD,MAAAA,YAAAA,EACJ,GAAG,SAAe;EACjB,MAAM,QAAQ,GAAG,aAAoC,OAAO,QAAQ,GAAG,QAAQ;EAE/E,OAAO,WAAW,OAAO,KAAK,GAAG,IAAI,KAAA,GAAIE,WAAAA,mBAAAA,CAAmB,QAAQ,IAAI,CAAC,CAAC,GAAG,IAAI;CACnF,GACA,CAAC,MAAM,CACT;AACF;AAEA,SAAgB,aAAkF;CAChG,MAAM,QAAQ,eAAe;CAC7B,OAAO;EAAE,UAAU,eAAe,MAAM,MAAM;EAAG,YAAY,MAAM,YAAY;EAAM,SAAS,MAAM;CAAQ;AAC9G;AAEA,MAAM,4BAA0C,KAAA;AAEhD,SAAS,YAAuD,UAAoB;CAMlF,QAAA,GALiBC,MAAAA,qBAAAA,CACf,UAAU,aAAa,qBACjB,UAAU,YAAY,YACtB,IAEM;AAChB;AAUA,MAAM,YAA2B;CAAE,QAAQ,CAAC;CAAG,QAAQ,CAAC;AAAE;AAE1D,SAAgB,YAA6B;CAC3C,MAAM,SAAS,UAAU;CACzB,MAAM,QAAQ,eAAe;CAC7B,MAAM,WAAW,YAAY,QAAQ,UAAU,IAAI;CACnD,OAAO;EACL,UAAU,eAAe,MAAM,MAAM;EACrC,QAAQ,QAAQ,UAAU;EAC1B,QAAQ,UAAU,UAAU;EAC5B,aAAa,UAAU,eAAe;CACxC;AACF;AASA,SAAgB,YAA6B;CAC3C,MAAM,SAAS,UAAU;CACzB,MAAM,QAAQ,eAAe;CAC7B,MAAM,WAAW,YAAY,QAAQ,UAAU,IAAI;CACnD,OAAO;EACL,UAAU,eAAe,MAAM,MAAM;EACrC,QAAQ,QAAQ,UAAU;EAC1B,QAAQ,UAAU,UAAU;EAC5B,aAAa,UAAU,eAAe;CACxC;AACF;;;;;;;AC3IA,SAAgB,KAAK,OAAkF;CACrG,MAAM,EAAE,UAAU,YAAY,QAAQ,QAAQ;CAC9C,IAAI,CAAC,UAAU,OAAO;CACtB,MAAM,EAAE,SAAS;CASjB,QAPE,SAAS,cACL,aACA,SAAS,eACP,CAAC,aACD,OAAO,SAAS,aACd,KAAK,GAAG,IACR,IAAI,IAAI,KACJ,MAAM,WAAY,MAAM,YAAY;AACpD;AAEA,SAAgB,cAAc,OAA4C;CACxE,OAAO,eAAe,CAAC,CAAC,WAAW,YAAY,MAAM,WAAW;AAClE;;AAGA,SAAgB,aAAa,OAA4C;CACvE,MAAM,EAAE,WAAW,eAAe;CAClC,OAAO,WAAW,WAAW,WAAW,aAAa,MAAM,WAAW;AACxE;AAEA,SAAgB,eAAe,OAA4C;CACzE,OAAO,eAAe,CAAC,CAAC,WAAW,aAAa,MAAM,WAAW;AACnE;AAEA,SAAgB,aAAa,OAA4C;CACvE,OAAO,eAAe,CAAC,CAAC,WAAW,WAAW,MAAM,WAAW;AACjE;;AAGA,SAAgB,iBAAiB,OAA4C;CAC3E,MAAM,SAAS,UAAU;CACzB,MAAM,EAAE,UAAU,eAAe,QAAQ;CACzC,MAAM,EAAE,gBAAgB;CACxB,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,WAAW,QAAQ,YAAY,CAAC,YAClC,OAAO,iBAAiB,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,CAAC;CAC5E,GAAG;EAAC;EAAQ;EAAU;EAAY;CAAW,CAAC;CAC9C,OAAO;AACT;AAEA,SAAgB,iBAAiB,OAA4C;CAC3E,MAAM,SAAS,UAAU;CACzB,MAAM,EAAE,UAAU,eAAe,QAAQ;CACzC,MAAM,EAAE,gBAAgB;CACxB,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,WAAW,QAAQ,YAAY,CAAC,YAClC,OAAO,iBAAiB,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,CAAC;CAC5E,GAAG;EAAC;EAAQ;EAAU;EAAY;CAAW,CAAC;CAC9C,OAAO;AACT;;;;ACrDA,SAAS,QAAQ,OAA+E;CAC9F,MAAM,QAAQC,MAAAA,SAAS,MAAM,MAAM,QAAQ,MAAM,IAAIA,MAAAA,SAAS,KAAK,MAAM,QAAQ,IAAI;CACrF,KAAA,GAAIC,MAAAA,eAAAA,CAA0D,KAAK,GAAG;EACpE,MAAM,WAAW,MAAM,MAAM;EAC7B,QAAA,GAAOC,MAAAA,aAAAA,CAAa,OAAkE,EACpF,UAAU,UAAsB;GAC9B,WAAW,KAAK;GAChB,IAAI,CAAC,MAAM,kBAAkB,MAAM,QAAQ;EAC7C,EACF,CAAC;CACH;CACA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;EAAQ,MAAK;EAAS,SAAS,MAAM;EAClC,UAAA,MAAM,YAAY,MAAM;CACnB,CAAA;AAEZ;AAEA,SAAgB,aAAa,OAA+B;CAC1D,MAAM,SAAS,UAAU;CACzB,MAAM,UAAU,MAAM,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,MAAM,YAAY;CACxF,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EACE,OAAM;EACN,eAAgB,MAAM,SAAS,UAAU,QAAQ,WAAW,OAAO,IAAI,QAAQ,iBAAiB,OAAO;EAEtG,UAAA,MAAM;CACA,CAAA;AAEb;AAEA,SAAgB,aAAa,OAA+B;CAC1D,MAAM,SAAS,UAAU;CACzB,MAAM,UAAU,MAAM,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,MAAM,YAAY;CACxF,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EACE,OAAM;EACN,eAAgB,MAAM,SAAS,UAAU,QAAQ,WAAW,OAAO,IAAI,QAAQ,iBAAiB,OAAO;EAEtG,UAAA,MAAM;CACA,CAAA;AAEb;AAEA,SAAgB,cAAc,OAAkE;CAC9F,MAAM,SAAS,UAAU;CACzB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EACE,OAAM;EACN,eAAe,KAAK,QAAQ,QAAQ,MAAM,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,MAAM,YAAY,CAAC;EAE5G,UAAA,MAAM;CACA,CAAA;AAEb;;;;;;;ACzDA,SAAS,QAAQ,OAAqE;CACpF,MAAM,SAAS,UAAU;CACzB,MAAM,OAAA,GAAMC,MAAAA,OAAAA,CAAuB,IAAI;CACvC,MAAM,eAAA,GAAcA,MAAAA,OAAAA,CAAsB,IAAI;CAC9C,MAAM,EAAE,MAAM,mBAAmB;CAEjC,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,UAAU,IAAI;EACpB,IAAI,WAAW,QAAQ,YAAY,MAAM;EACzC,OAAO,MAAM,MAAM,SAAS,cAAc;EAC1C,YAAY,UAAU;EACtB,aAAa;GACX,YAAY,UAAU;GACtB,OAAO,QAAQ,OAAO;EACxB;CAGF,GAAG,CAAC,QAAQ,IAAI,CAAC;CAEjB,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,UAAU,IAAI;EACpB,IAAI,WAAW,QAAQ,YAAY,QAAQ,YAAY,YAAY,QAAQ,YAAY,YAAY,gBACjG;EACF,YAAY,UAAU;EACtB,OAAO,cAAc,SAAS,cAAc;CAC9C,GAAG,CAAC,QAAQ,cAAc,CAAC;CAE3B,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD,EAAU,IAAM,CAAA;AACzB;AAGA,SAAgB,OAAO,OAAoD;CACzE,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EAAS,MAAK;EAAS,gBAAgB;CAAQ,CAAA;AACxD;AAEA,SAAgB,OAAO,OAAoD;CACzE,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EAAS,MAAK;EAAS,gBAAgB;CAAQ,CAAA;AACxD;AAEA,SAAgB,WAAW,OAAwD;CACjF,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EAAS,MAAK;EAAa,gBAAgB;CAAQ,CAAA;AAC5D;AAEA,SAAgB,YAAY,OAAyD;CACnF,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EAAS,MAAK;EAAc,gBAAgB;CAAQ,CAAA;AAC7D;AAEA,SAAgB,aAAa,OAA0D;CACrF,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;EAAS,MAAK;EAAe,gBAAgB;CAAQ,CAAA;AAC9D"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { AttemptErrors, Environment, FetchStatus, Session, Session as Session$1, SignInResource, SignInResource as SignInResource$1, SignUpResource, SignUpResource as SignUpResource$1, User,
|
|
2
|
+
import { AttemptErrors, EmailAddress, Environment, FetchStatus, ReverificationCancelledError, ReverificationRequiredError, ReverificationRequirement, Session, Session as Session$1, SessionWithActivity, SignInResource, SignInResource as SignInResource$1, SignUpResource, SignUpResource as SignUpResource$1, User, UserResource, UserResource as UserResource$1, Zautha, ZauthaApiError, ZauthaError, ZauthaErrorCode, ZauthaNetworkError, ZauthaOfflineError, ZauthaOptions, isReverificationCancelledError, isReverificationRequiredError, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError } from "@zautha/js";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
//#region src/provider.d.ts
|
|
5
5
|
interface ZauthaProviderProps extends Omit<ZauthaOptions, "fetch"> {
|
|
@@ -36,11 +36,21 @@ interface UseAuthResult {
|
|
|
36
36
|
has: Has;
|
|
37
37
|
}
|
|
38
38
|
export declare function useAuth(): UseAuthResult;
|
|
39
|
+
/** The signed-in user with its self-service actions (`user.update`, `user.createEmailAddress`, …). */
|
|
39
40
|
export declare function useUser(): {
|
|
40
41
|
isLoaded: boolean;
|
|
41
42
|
isSignedIn: boolean;
|
|
42
|
-
user:
|
|
43
|
+
user: UserResource$1 | null;
|
|
43
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Wraps a sensitive action so that it completes after an identity check when it needs one: when `fetcher` throws
|
|
47
|
+
* `ReverificationRequiredError` (e.g. `user.updatePassword`) or resolves to a `Response` with the `403
|
|
48
|
+
* reverification_required` body (your backend with @zautha/backend, @zautha/nextjs or Zautha.AspNetCore), the
|
|
49
|
+
* reverification modal opens and, once the user has verified, `fetcher` runs once more with the same arguments (its
|
|
50
|
+
* token is fresh by then). Closing the modal rejects with `ReverificationCancelledError`. Needs `ui` from @zautha/ui on
|
|
51
|
+
* the provider. The returned function is stable; it always calls the latest `fetcher`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function useReverification<Args extends unknown[], Result>(fetcher: (...args: Args) => Promise<Result>): (...args: Args) => Promise<Result>;
|
|
44
54
|
export declare function useSession(): {
|
|
45
55
|
isLoaded: boolean;
|
|
46
56
|
isSignedIn: boolean;
|
|
@@ -112,7 +122,8 @@ export declare function SignOutButton(props: {
|
|
|
112
122
|
export declare function SignIn(props: import("@zautha/ui").SignInProps): ReactNode;
|
|
113
123
|
export declare function SignUp(props: import("@zautha/ui").SignUpProps): ReactNode;
|
|
114
124
|
export declare function UserButton(props: import("@zautha/ui").UserButtonProps): ReactNode;
|
|
125
|
+
export declare function UserProfile(props: import("@zautha/ui").UserProfileProps): ReactNode;
|
|
115
126
|
export declare function OAuthConsent(props: import("@zautha/ui").OAuthConsentProps): ReactNode;
|
|
116
127
|
//#endregion
|
|
117
|
-
export { type Environment, type Has, type HasParams, type Session, type ShowWhen, type SignInResource, type SignUpResource, type UseAuthResult, type UseSignInResult, type UseSignUpResult, type User, ZauthaApiError, ZauthaError, type ZauthaErrorCode, ZauthaNetworkError, ZauthaOfflineError, type ZauthaProviderProps, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError };
|
|
128
|
+
export { type EmailAddress, type Environment, type Has, type HasParams, ReverificationCancelledError, ReverificationRequiredError, type ReverificationRequirement, type Session, type SessionWithActivity, type ShowWhen, type SignInResource, type SignUpResource, type UseAuthResult, type UseSignInResult, type UseSignUpResult, type User, type UserResource, ZauthaApiError, ZauthaError, type ZauthaErrorCode, ZauthaNetworkError, ZauthaOfflineError, type ZauthaProviderProps, isReverificationCancelledError, isReverificationRequiredError, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError };
|
|
118
129
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { AttemptErrors, Environment, FetchStatus, Session, Session as Session$1, SignInResource, SignInResource as SignInResource$1, SignUpResource, SignUpResource as SignUpResource$1, User,
|
|
2
|
+
import { AttemptErrors, EmailAddress, Environment, FetchStatus, ReverificationCancelledError, ReverificationRequiredError, ReverificationRequirement, Session, Session as Session$1, SessionWithActivity, SignInResource, SignInResource as SignInResource$1, SignUpResource, SignUpResource as SignUpResource$1, User, UserResource, UserResource as UserResource$1, Zautha, ZauthaApiError, ZauthaError, ZauthaErrorCode, ZauthaNetworkError, ZauthaOfflineError, ZauthaOptions, isReverificationCancelledError, isReverificationRequiredError, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError } from "@zautha/js";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
//#region src/provider.d.ts
|
|
5
5
|
interface ZauthaProviderProps extends Omit<ZauthaOptions, "fetch"> {
|
|
@@ -36,11 +36,21 @@ interface UseAuthResult {
|
|
|
36
36
|
has: Has;
|
|
37
37
|
}
|
|
38
38
|
export declare function useAuth(): UseAuthResult;
|
|
39
|
+
/** The signed-in user with its self-service actions (`user.update`, `user.createEmailAddress`, …). */
|
|
39
40
|
export declare function useUser(): {
|
|
40
41
|
isLoaded: boolean;
|
|
41
42
|
isSignedIn: boolean;
|
|
42
|
-
user:
|
|
43
|
+
user: UserResource$1 | null;
|
|
43
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Wraps a sensitive action so that it completes after an identity check when it needs one: when `fetcher` throws
|
|
47
|
+
* `ReverificationRequiredError` (e.g. `user.updatePassword`) or resolves to a `Response` with the `403
|
|
48
|
+
* reverification_required` body (your backend with @zautha/backend, @zautha/nextjs or Zautha.AspNetCore), the
|
|
49
|
+
* reverification modal opens and, once the user has verified, `fetcher` runs once more with the same arguments (its
|
|
50
|
+
* token is fresh by then). Closing the modal rejects with `ReverificationCancelledError`. Needs `ui` from @zautha/ui on
|
|
51
|
+
* the provider. The returned function is stable; it always calls the latest `fetcher`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function useReverification<Args extends unknown[], Result>(fetcher: (...args: Args) => Promise<Result>): (...args: Args) => Promise<Result>;
|
|
44
54
|
export declare function useSession(): {
|
|
45
55
|
isLoaded: boolean;
|
|
46
56
|
isSignedIn: boolean;
|
|
@@ -112,7 +122,8 @@ export declare function SignOutButton(props: {
|
|
|
112
122
|
export declare function SignIn(props: import("@zautha/ui").SignInProps): ReactNode;
|
|
113
123
|
export declare function SignUp(props: import("@zautha/ui").SignUpProps): ReactNode;
|
|
114
124
|
export declare function UserButton(props: import("@zautha/ui").UserButtonProps): ReactNode;
|
|
125
|
+
export declare function UserProfile(props: import("@zautha/ui").UserProfileProps): ReactNode;
|
|
115
126
|
export declare function OAuthConsent(props: import("@zautha/ui").OAuthConsentProps): ReactNode;
|
|
116
127
|
//#endregion
|
|
117
|
-
export { type Environment, type Has, type HasParams, type Session, type ShowWhen, type SignInResource, type SignUpResource, type UseAuthResult, type UseSignInResult, type UseSignUpResult, type User, ZauthaApiError, ZauthaError, type ZauthaErrorCode, ZauthaNetworkError, ZauthaOfflineError, type ZauthaProviderProps, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError };
|
|
128
|
+
export { type EmailAddress, type Environment, type Has, type HasParams, ReverificationCancelledError, ReverificationRequiredError, type ReverificationRequirement, type Session, type SessionWithActivity, type ShowWhen, type SignInResource, type SignUpResource, type UseAuthResult, type UseSignInResult, type UseSignUpResult, type User, type UserResource, ZauthaApiError, ZauthaError, type ZauthaErrorCode, ZauthaNetworkError, ZauthaOfflineError, type ZauthaProviderProps, isReverificationCancelledError, isReverificationRequiredError, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError };
|
|
118
129
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { Zautha, ZauthaApiError, ZauthaError, ZauthaNetworkError, ZauthaOfflineError, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError } from "@zautha/js";
|
|
3
|
-
import { Children, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { ReverificationCancelledError, ReverificationRequiredError, Zautha, ZauthaApiError, ZauthaError, ZauthaNetworkError, ZauthaOfflineError, isReverificationCancelledError, isReverificationRequiredError, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError, withReverification } from "@zautha/js";
|
|
3
|
+
import { Children, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/provider.tsx
|
|
6
6
|
const ZauthaContext = createContext(void 0);
|
|
@@ -88,14 +88,36 @@ function useAuth() {
|
|
|
88
88
|
has
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
+
/** The signed-in user with its self-service actions (`user.update`, `user.createEmailAddress`, …). */
|
|
91
92
|
function useUser() {
|
|
93
|
+
const zautha = useZautha();
|
|
92
94
|
const state = useZauthaState();
|
|
95
|
+
const user = state.user === null ? null : zautha?.user ?? null;
|
|
93
96
|
return {
|
|
94
97
|
isLoaded: isLoadedStatus(state.status),
|
|
95
98
|
isSignedIn: state.session !== null,
|
|
96
|
-
user
|
|
99
|
+
user
|
|
97
100
|
};
|
|
98
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Wraps a sensitive action so that it completes after an identity check when it needs one: when `fetcher` throws
|
|
104
|
+
* `ReverificationRequiredError` (e.g. `user.updatePassword`) or resolves to a `Response` with the `403
|
|
105
|
+
* reverification_required` body (your backend with @zautha/backend, @zautha/nextjs or Zautha.AspNetCore), the
|
|
106
|
+
* reverification modal opens and, once the user has verified, `fetcher` runs once more with the same arguments (its
|
|
107
|
+
* token is fresh by then). Closing the modal rejects with `ReverificationCancelledError`. Needs `ui` from @zautha/ui on
|
|
108
|
+
* the provider. The returned function is stable; it always calls the latest `fetcher`.
|
|
109
|
+
*/
|
|
110
|
+
function useReverification(fetcher) {
|
|
111
|
+
const zautha = useZautha();
|
|
112
|
+
const latest = useRef(fetcher);
|
|
113
|
+
useLayoutEffect(() => {
|
|
114
|
+
latest.current = fetcher;
|
|
115
|
+
});
|
|
116
|
+
return useCallback((...args) => {
|
|
117
|
+
const call = (...callArgs) => latest.current(...callArgs);
|
|
118
|
+
return zautha === null ? call(...args) : withReverification(zautha, call)(...args);
|
|
119
|
+
}, [zautha]);
|
|
120
|
+
}
|
|
99
121
|
function useSession() {
|
|
100
122
|
const state = useZauthaState();
|
|
101
123
|
return {
|
|
@@ -280,6 +302,12 @@ function UserButton(props) {
|
|
|
280
302
|
componentProps: props
|
|
281
303
|
});
|
|
282
304
|
}
|
|
305
|
+
function UserProfile(props) {
|
|
306
|
+
return /* @__PURE__ */ jsx(Mounted, {
|
|
307
|
+
name: "UserProfile",
|
|
308
|
+
componentProps: props
|
|
309
|
+
});
|
|
310
|
+
}
|
|
283
311
|
function OAuthConsent(props) {
|
|
284
312
|
return /* @__PURE__ */ jsx(Mounted, {
|
|
285
313
|
name: "OAuthConsent",
|
|
@@ -287,6 +315,6 @@ function OAuthConsent(props) {
|
|
|
287
315
|
});
|
|
288
316
|
}
|
|
289
317
|
//#endregion
|
|
290
|
-
export { OAuthConsent, RedirectToSignIn, RedirectToSignUp, Show, SignIn, SignInButton, SignOutButton, SignUp, SignUpButton, UserButton, ZauthaApiError, ZauthaDegraded, ZauthaError, ZauthaFailed, ZauthaLoaded, ZauthaLoading, ZauthaNetworkError, ZauthaOfflineError, ZauthaProvider, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError, useAuth, useSession, useSignIn, useSignUp, useUser, useZautha };
|
|
318
|
+
export { OAuthConsent, RedirectToSignIn, RedirectToSignUp, ReverificationCancelledError, ReverificationRequiredError, Show, SignIn, SignInButton, SignOutButton, SignUp, SignUpButton, UserButton, UserProfile, ZauthaApiError, ZauthaDegraded, ZauthaError, ZauthaFailed, ZauthaLoaded, ZauthaLoading, ZauthaNetworkError, ZauthaOfflineError, ZauthaProvider, isReverificationCancelledError, isReverificationRequiredError, isZauthaApiError, isZauthaError, isZauthaNetworkError, isZauthaOfflineError, useAuth, useReverification, useSession, useSignIn, useSignUp, useUser, useZautha };
|
|
291
319
|
|
|
292
320
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/provider.tsx","../src/hooks.ts","../src/control.tsx","../src/buttons.tsx","../src/prebuilt.tsx"],"sourcesContent":["import { Zautha, type ZauthaOptions, type ZauthaState } from \"@zautha/js\";\nimport { createContext, useContext, useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from \"react\";\n\ninterface ZauthaContextValue {\n readonly zautha: Zautha | null;\n /** The snapshot before the instance exists and during server rendering (carries `initialState`). */\n readonly pending: ZauthaState;\n}\n\nconst ZauthaContext = createContext<ZauthaContextValue | undefined>(undefined);\n\nexport interface ZauthaProviderProps extends Omit<ZauthaOptions, \"fetch\"> {\n publishableKey: string;\n children?: ReactNode;\n}\n\n/**\n * Creates the Zautha instance after mount (never during render, so StrictMode's double mount and SSR\n * create no timers or channels), loads it, and disposes it on unmount. Until then hooks report\n * `isLoaded: false`, unless `initialState` (the server's view, e.g. from @zautha/nextjs) says who is signed in.\n */\nexport function ZauthaProvider({ publishableKey, children, ...options }: ZauthaProviderProps): ReactNode {\n const [zautha, setZautha] = useState<Zautha | null>(null);\n const { ui, appearance, localization, signInUrl, signUpUrl, navigate, initialState } = options;\n const [pending] = useState<ZauthaState>(() => ({ ...NO_STATE, initialState: initialState ?? null }));\n\n useEffect(() => {\n const instance = new Zautha(publishableKey, {\n ...(ui === undefined ? {} : { ui }),\n ...(appearance === undefined ? {} : { appearance }),\n ...(localization === undefined ? {} : { localization }),\n ...(signInUrl === undefined ? {} : { signInUrl }),\n ...(signUpUrl === undefined ? {} : { signUpUrl }),\n ...(navigate === undefined ? {} : { navigate }),\n ...(initialState === undefined ? {} : { initialState }),\n });\n // The instance is an external system: it is created here, never during render, and published through state.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setZautha(instance);\n void instance.load();\n return () => {\n instance.dispose();\n setZautha(null);\n };\n // Appearance/localization objects are read at mount; changing the key re-creates the instance.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [publishableKey]);\n\n const value = useMemo(() => ({ zautha, pending }), [zautha, pending]);\n return <ZauthaContext.Provider value={value}>{children}</ZauthaContext.Provider>;\n}\n\n/** The Zautha instance, or null before the provider has created it. */\nexport function useZautha(): Zautha | null {\n return useZauthaContext().zautha;\n}\n\nfunction useZauthaContext(): ZauthaContextValue {\n const value = useContext(ZauthaContext);\n if (value === undefined) throw new Error(\"Zautha hooks and components must be used inside <ZauthaProvider>.\");\n return value;\n}\n\nconst NO_STATE: ZauthaState = {\n status: \"loading\",\n environment: null,\n client: null,\n session: null,\n user: null,\n initialState: null,\n};\nconst noop = (): (() => void) => () => undefined;\n\n/** Reactive snapshot of the instance state (the loading state, with any initial state, before the instance exists). */\nexport function useZauthaState(): ZauthaState {\n const { zautha, pending } = useZauthaContext();\n return useSyncExternalStore(\n zautha?.subscribe ?? noop,\n () => zautha?.getSnapshot() ?? pending,\n () => pending,\n );\n}\n","import type { AttemptErrors, FetchStatus, Session, SignInResource, SignUpResource, User } from \"@zautha/js\";\nimport { useCallback, useSyncExternalStore } from \"react\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\n/** Authorization check. In 3a there are no organization claims yet, so role/permission checks are false. */\nexport type HasParams = { role?: string; permission?: string };\nexport type Has = (params: HasParams) => boolean;\n\nexport interface UseAuthResult {\n isLoaded: boolean;\n isSignedIn: boolean;\n userId: string | null;\n sessionId: string | null;\n getToken: (options?: { skipCache?: boolean }) => Promise<string | null>;\n signOut: (options?: { redirectUrl?: string }) => Promise<void>;\n has: Has;\n}\n\nconst isLoadedStatus = (status: string): boolean => status === \"ready\" || status === \"degraded\";\n\nexport function useAuth(): UseAuthResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const getToken = useCallback(\n (options?: { skipCache?: boolean }) => zautha?.getToken(options) ?? Promise.resolve(null),\n [zautha],\n );\n const signOut = useCallback(\n (options?: { redirectUrl?: string }) => zautha?.signOut(options) ?? Promise.resolve(),\n [zautha],\n );\n // Until the first load settles, the server's initial state (if any) answers, so nothing flickers.\n const initial = state.status === \"loading\" ? state.initialState : null;\n const userId = initial === null ? (state.session?.userId ?? null) : initial.userId;\n const sessionId = initial === null ? (state.session?.id ?? null) : initial.sessionId;\n const signedIn = userId !== null;\n const has = useCallback<Has>(\n (params) => signedIn && params.role === undefined && params.permission === undefined,\n [signedIn],\n );\n return {\n isLoaded: isLoadedStatus(state.status) || initial !== null,\n isSignedIn: signedIn,\n userId,\n sessionId,\n getToken,\n signOut,\n has,\n };\n}\n\nexport function useUser(): { isLoaded: boolean; isSignedIn: boolean; user: User | null } {\n const state = useZauthaState();\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, user: state.user };\n}\n\nexport function useSession(): { isLoaded: boolean; isSignedIn: boolean; session: Session | null } {\n const state = useZauthaState();\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, session: state.session };\n}\n\nconst noopSubscribe = (): (() => void) => () => undefined;\n\nfunction useResource<T extends SignInResource | SignUpResource>(resource: T | null) {\n const snapshot = useSyncExternalStore(\n resource?.subscribe ?? noopSubscribe,\n () => resource?.snapshot ?? null,\n () => null,\n );\n return snapshot;\n}\n\nexport interface UseSignInResult {\n isLoaded: boolean;\n /** The sign-in resource; null until the provider has created the instance. */\n signIn: SignInResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nconst NO_ERRORS: AttemptErrors = { fields: {}, global: [] };\n\nexport function useSignIn(): UseSignInResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signIn ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signIn: zautha?.signIn ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n\nexport interface UseSignUpResult {\n isLoaded: boolean;\n signUp: SignUpResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nexport function useSignUp(): UseSignUpResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signUp ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signUp: zautha?.signUp ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n","import { useEffect, type ReactNode } from \"react\";\nimport { useAuth, type Has, type HasParams } from \"./hooks\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\nexport type ShowWhen = \"signed-in\" | \"signed-out\" | HasParams | ((has: Has) => boolean);\n\n/**\n * Renders `children` when the condition holds, else `fallback`. Renders nothing until Zautha has\n * loaded, so a signed-in user never sees a signed-out flash.\n */\nexport function Show(props: { when: ShowWhen; fallback?: ReactNode; children?: ReactNode }): ReactNode {\n const { isLoaded, isSignedIn, has } = useAuth();\n if (!isLoaded) return null;\n const { when } = props;\n const pass =\n when === \"signed-in\"\n ? isSignedIn\n : when === \"signed-out\"\n ? !isSignedIn\n : typeof when === \"function\"\n ? when(has)\n : has(when);\n return pass ? props.children : (props.fallback ?? null);\n}\n\nexport function ZauthaLoading(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"loading\" ? props.children : null;\n}\n\n/** Loaded, including degraded (network trouble after load: last known state is shown). */\nexport function ZauthaLoaded(props: { children?: ReactNode }): ReactNode {\n const { status } = useZauthaState();\n return status === \"ready\" || status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaDegraded(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaFailed(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"failed\" ? props.children : null;\n}\n\n/** Sends a signed-out visitor to sign-in once loaded; renders nothing. */\nexport function RedirectToSignIn(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignIn(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n\nexport function RedirectToSignUp(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignUp(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n","import { Children, cloneElement, isValidElement, type MouseEvent, type ReactElement, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\ninterface ButtonProps {\n /** `redirect` (default) goes to the sign-in page; `modal` opens the prebuilt component (needs `ui`). */\n mode?: \"redirect\" | \"modal\";\n redirectUrl?: string;\n children?: ReactNode;\n}\n\n/** Wraps its single child (or renders a plain button) and wires the click. Unstyled by design. */\nfunction Trigger(props: { children: ReactNode; label: string; onClick: () => void }): ReactNode {\n const child = Children.count(props.children) === 1 ? Children.only(props.children) : null;\n if (isValidElement<{ onClick?: (event: MouseEvent) => void }>(child)) {\n const original = child.props.onClick;\n return cloneElement(child as ReactElement<{ onClick?: (event: MouseEvent) => void }>, {\n onClick: (event: MouseEvent) => {\n original?.(event);\n if (!event.defaultPrevented) props.onClick();\n },\n });\n }\n return (\n <button type=\"button\" onClick={props.onClick}>\n {props.children ?? props.label}\n </button>\n );\n}\n\nexport function SignInButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign in\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignIn(options) : zautha?.redirectToSignIn(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignUpButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign up\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignUp(options) : zautha?.redirectToSignUp(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignOutButton(props: { redirectUrl?: string; children?: ReactNode }): ReactNode {\n const zautha = useZautha();\n return (\n <Trigger\n label=\"Sign out\"\n onClick={() => void zautha?.signOut(props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl })}\n >\n {props.children}\n </Trigger>\n );\n}\n","import type { UiComponentName } from \"@zautha/js\";\nimport { useEffect, useRef, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\n/**\n * Mounts a @zautha/ui component into a div once the instance exists; later prop changes update it in\n * place. SSR renders only the empty div.\n */\nfunction Mounted(props: { name: UiComponentName; componentProps: object }): ReactNode {\n const zautha = useZautha();\n const ref = useRef<HTMLDivElement>(null);\n const mountedWith = useRef<object | null>(null);\n const { name, componentProps } = props;\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null) return;\n zautha.mount(name, element, componentProps);\n mountedWith.current = componentProps;\n return () => {\n mountedWith.current = null;\n zautha.unmount(element);\n };\n // componentProps changes are applied by the effect below without remounting.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [zautha, name]);\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null || mountedWith.current === null || mountedWith.current === componentProps)\n return;\n mountedWith.current = componentProps;\n zautha.updateMounted(element, componentProps);\n }, [zautha, componentProps]);\n\n return <div ref={ref} />;\n}\n\n// Prop types live in @zautha/ui; they are structural here so @zautha/react has no runtime dependency on it.\nexport function SignIn(props: import(\"@zautha/ui\").SignInProps): ReactNode {\n return <Mounted name=\"SignIn\" componentProps={props} />;\n}\n\nexport function SignUp(props: import(\"@zautha/ui\").SignUpProps): ReactNode {\n return <Mounted name=\"SignUp\" componentProps={props} />;\n}\n\nexport function UserButton(props: import(\"@zautha/ui\").UserButtonProps): ReactNode {\n return <Mounted name=\"UserButton\" componentProps={props} />;\n}\n\nexport function OAuthConsent(props: import(\"@zautha/ui\").OAuthConsentProps): ReactNode {\n return <Mounted name=\"OAuthConsent\" componentProps={props} />;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/provider.tsx","../src/hooks.ts","../src/control.tsx","../src/buttons.tsx","../src/prebuilt.tsx"],"sourcesContent":["import { Zautha, type ZauthaOptions, type ZauthaState } from \"@zautha/js\";\nimport { createContext, useContext, useEffect, useMemo, useState, useSyncExternalStore, type ReactNode } from \"react\";\n\ninterface ZauthaContextValue {\n readonly zautha: Zautha | null;\n /** The snapshot before the instance exists and during server rendering (carries `initialState`). */\n readonly pending: ZauthaState;\n}\n\nconst ZauthaContext = createContext<ZauthaContextValue | undefined>(undefined);\n\nexport interface ZauthaProviderProps extends Omit<ZauthaOptions, \"fetch\"> {\n publishableKey: string;\n children?: ReactNode;\n}\n\n/**\n * Creates the Zautha instance after mount (never during render, so StrictMode's double mount and SSR\n * create no timers or channels), loads it, and disposes it on unmount. Until then hooks report\n * `isLoaded: false`, unless `initialState` (the server's view, e.g. from @zautha/nextjs) says who is signed in.\n */\nexport function ZauthaProvider({ publishableKey, children, ...options }: ZauthaProviderProps): ReactNode {\n const [zautha, setZautha] = useState<Zautha | null>(null);\n const { ui, appearance, localization, signInUrl, signUpUrl, navigate, initialState } = options;\n const [pending] = useState<ZauthaState>(() => ({ ...NO_STATE, initialState: initialState ?? null }));\n\n useEffect(() => {\n const instance = new Zautha(publishableKey, {\n ...(ui === undefined ? {} : { ui }),\n ...(appearance === undefined ? {} : { appearance }),\n ...(localization === undefined ? {} : { localization }),\n ...(signInUrl === undefined ? {} : { signInUrl }),\n ...(signUpUrl === undefined ? {} : { signUpUrl }),\n ...(navigate === undefined ? {} : { navigate }),\n ...(initialState === undefined ? {} : { initialState }),\n });\n // The instance is an external system: it is created here, never during render, and published through state.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setZautha(instance);\n void instance.load();\n return () => {\n instance.dispose();\n setZautha(null);\n };\n // Appearance/localization objects are read at mount; changing the key re-creates the instance.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [publishableKey]);\n\n const value = useMemo(() => ({ zautha, pending }), [zautha, pending]);\n return <ZauthaContext.Provider value={value}>{children}</ZauthaContext.Provider>;\n}\n\n/** The Zautha instance, or null before the provider has created it. */\nexport function useZautha(): Zautha | null {\n return useZauthaContext().zautha;\n}\n\nfunction useZauthaContext(): ZauthaContextValue {\n const value = useContext(ZauthaContext);\n if (value === undefined) throw new Error(\"Zautha hooks and components must be used inside <ZauthaProvider>.\");\n return value;\n}\n\nconst NO_STATE: ZauthaState = {\n status: \"loading\",\n environment: null,\n client: null,\n session: null,\n user: null,\n initialState: null,\n};\nconst noop = (): (() => void) => () => undefined;\n\n/** Reactive snapshot of the instance state (the loading state, with any initial state, before the instance exists). */\nexport function useZauthaState(): ZauthaState {\n const { zautha, pending } = useZauthaContext();\n return useSyncExternalStore(\n zautha?.subscribe ?? noop,\n () => zautha?.getSnapshot() ?? pending,\n () => pending,\n );\n}\n","import {\n withReverification,\n type AttemptErrors,\n type FetchStatus,\n type Session,\n type SignInResource,\n type SignUpResource,\n type UserResource,\n} from \"@zautha/js\";\nimport { useCallback, useLayoutEffect, useRef, useSyncExternalStore } from \"react\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\n/** Authorization check. In 3a there are no organization claims yet, so role/permission checks are false. */\nexport type HasParams = { role?: string; permission?: string };\nexport type Has = (params: HasParams) => boolean;\n\nexport interface UseAuthResult {\n isLoaded: boolean;\n isSignedIn: boolean;\n userId: string | null;\n sessionId: string | null;\n getToken: (options?: { skipCache?: boolean }) => Promise<string | null>;\n signOut: (options?: { redirectUrl?: string }) => Promise<void>;\n has: Has;\n}\n\nconst isLoadedStatus = (status: string): boolean => status === \"ready\" || status === \"degraded\";\n\nexport function useAuth(): UseAuthResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const getToken = useCallback(\n (options?: { skipCache?: boolean }) => zautha?.getToken(options) ?? Promise.resolve(null),\n [zautha],\n );\n const signOut = useCallback(\n (options?: { redirectUrl?: string }) => zautha?.signOut(options) ?? Promise.resolve(),\n [zautha],\n );\n // Until the first load settles, the server's initial state (if any) answers, so nothing flickers.\n const initial = state.status === \"loading\" ? state.initialState : null;\n const userId = initial === null ? (state.session?.userId ?? null) : initial.userId;\n const sessionId = initial === null ? (state.session?.id ?? null) : initial.sessionId;\n const signedIn = userId !== null;\n const has = useCallback<Has>(\n (params) => signedIn && params.role === undefined && params.permission === undefined,\n [signedIn],\n );\n return {\n isLoaded: isLoadedStatus(state.status) || initial !== null,\n isSignedIn: signedIn,\n userId,\n sessionId,\n getToken,\n signOut,\n has,\n };\n}\n\n/** The signed-in user with its self-service actions (`user.update`, `user.createEmailAddress`, …). */\nexport function useUser(): { isLoaded: boolean; isSignedIn: boolean; user: UserResource | null } {\n const zautha = useZautha();\n const state = useZauthaState();\n // zautha.user is rebuilt only when state.user changes, so the resource is stable between renders.\n const user = state.user === null ? null : (zautha?.user ?? null);\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, user };\n}\n\n/**\n * Wraps a sensitive action so that it completes after an identity check when it needs one: when `fetcher` throws\n * `ReverificationRequiredError` (e.g. `user.updatePassword`) or resolves to a `Response` with the `403\n * reverification_required` body (your backend with @zautha/backend, @zautha/nextjs or Zautha.AspNetCore), the\n * reverification modal opens and, once the user has verified, `fetcher` runs once more with the same arguments (its\n * token is fresh by then). Closing the modal rejects with `ReverificationCancelledError`. Needs `ui` from @zautha/ui on\n * the provider. The returned function is stable; it always calls the latest `fetcher`.\n */\nexport function useReverification<Args extends unknown[], Result>(\n fetcher: (...args: Args) => Promise<Result>,\n): (...args: Args) => Promise<Result> {\n const zautha = useZautha();\n const latest = useRef(fetcher);\n useLayoutEffect(() => {\n latest.current = fetcher;\n });\n return useCallback(\n (...args: Args) => {\n const call = (...callArgs: Args): Promise<Result> => latest.current(...callArgs);\n // Before the provider has created the instance no modal can open: the action runs as it is.\n return zautha === null ? call(...args) : withReverification(zautha, call)(...args);\n },\n [zautha],\n );\n}\n\nexport function useSession(): { isLoaded: boolean; isSignedIn: boolean; session: Session | null } {\n const state = useZauthaState();\n return { isLoaded: isLoadedStatus(state.status), isSignedIn: state.session !== null, session: state.session };\n}\n\nconst noopSubscribe = (): (() => void) => () => undefined;\n\nfunction useResource<T extends SignInResource | SignUpResource>(resource: T | null) {\n const snapshot = useSyncExternalStore(\n resource?.subscribe ?? noopSubscribe,\n () => resource?.snapshot ?? null,\n () => null,\n );\n return snapshot;\n}\n\nexport interface UseSignInResult {\n isLoaded: boolean;\n /** The sign-in resource; null until the provider has created the instance. */\n signIn: SignInResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nconst NO_ERRORS: AttemptErrors = { fields: {}, global: [] };\n\nexport function useSignIn(): UseSignInResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signIn ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signIn: zautha?.signIn ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n\nexport interface UseSignUpResult {\n isLoaded: boolean;\n signUp: SignUpResource | null;\n errors: AttemptErrors;\n fetchStatus: FetchStatus;\n}\n\nexport function useSignUp(): UseSignUpResult {\n const zautha = useZautha();\n const state = useZauthaState();\n const snapshot = useResource(zautha?.signUp ?? null);\n return {\n isLoaded: isLoadedStatus(state.status),\n signUp: zautha?.signUp ?? null,\n errors: snapshot?.errors ?? NO_ERRORS,\n fetchStatus: snapshot?.fetchStatus ?? \"idle\",\n };\n}\n","import { useEffect, type ReactNode } from \"react\";\nimport { useAuth, type Has, type HasParams } from \"./hooks\";\nimport { useZautha, useZauthaState } from \"./provider\";\n\nexport type ShowWhen = \"signed-in\" | \"signed-out\" | HasParams | ((has: Has) => boolean);\n\n/**\n * Renders `children` when the condition holds, else `fallback`. Renders nothing until Zautha has\n * loaded, so a signed-in user never sees a signed-out flash.\n */\nexport function Show(props: { when: ShowWhen; fallback?: ReactNode; children?: ReactNode }): ReactNode {\n const { isLoaded, isSignedIn, has } = useAuth();\n if (!isLoaded) return null;\n const { when } = props;\n const pass =\n when === \"signed-in\"\n ? isSignedIn\n : when === \"signed-out\"\n ? !isSignedIn\n : typeof when === \"function\"\n ? when(has)\n : has(when);\n return pass ? props.children : (props.fallback ?? null);\n}\n\nexport function ZauthaLoading(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"loading\" ? props.children : null;\n}\n\n/** Loaded, including degraded (network trouble after load: last known state is shown). */\nexport function ZauthaLoaded(props: { children?: ReactNode }): ReactNode {\n const { status } = useZauthaState();\n return status === \"ready\" || status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaDegraded(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"degraded\" ? props.children : null;\n}\n\nexport function ZauthaFailed(props: { children?: ReactNode }): ReactNode {\n return useZauthaState().status === \"failed\" ? props.children : null;\n}\n\n/** Sends a signed-out visitor to sign-in once loaded; renders nothing. */\nexport function RedirectToSignIn(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignIn(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n\nexport function RedirectToSignUp(props: { redirectUrl?: string }): ReactNode {\n const zautha = useZautha();\n const { isLoaded, isSignedIn } = useAuth();\n const { redirectUrl } = props;\n useEffect(() => {\n if (zautha !== null && isLoaded && !isSignedIn)\n zautha.redirectToSignUp(redirectUrl === undefined ? {} : { redirectUrl });\n }, [zautha, isLoaded, isSignedIn, redirectUrl]);\n return null;\n}\n","import { Children, cloneElement, isValidElement, type MouseEvent, type ReactElement, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\ninterface ButtonProps {\n /** `redirect` (default) goes to the sign-in page; `modal` opens the prebuilt component (needs `ui`). */\n mode?: \"redirect\" | \"modal\";\n redirectUrl?: string;\n children?: ReactNode;\n}\n\n/** Wraps its single child (or renders a plain button) and wires the click. Unstyled by design. */\nfunction Trigger(props: { children: ReactNode; label: string; onClick: () => void }): ReactNode {\n const child = Children.count(props.children) === 1 ? Children.only(props.children) : null;\n if (isValidElement<{ onClick?: (event: MouseEvent) => void }>(child)) {\n const original = child.props.onClick;\n return cloneElement(child as ReactElement<{ onClick?: (event: MouseEvent) => void }>, {\n onClick: (event: MouseEvent) => {\n original?.(event);\n if (!event.defaultPrevented) props.onClick();\n },\n });\n }\n return (\n <button type=\"button\" onClick={props.onClick}>\n {props.children ?? props.label}\n </button>\n );\n}\n\nexport function SignInButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign in\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignIn(options) : zautha?.redirectToSignIn(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignUpButton(props: ButtonProps): ReactNode {\n const zautha = useZautha();\n const options = props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl };\n return (\n <Trigger\n label=\"Sign up\"\n onClick={() => (props.mode === \"modal\" ? zautha?.openSignUp(options) : zautha?.redirectToSignUp(options))}\n >\n {props.children}\n </Trigger>\n );\n}\n\nexport function SignOutButton(props: { redirectUrl?: string; children?: ReactNode }): ReactNode {\n const zautha = useZautha();\n return (\n <Trigger\n label=\"Sign out\"\n onClick={() => void zautha?.signOut(props.redirectUrl === undefined ? {} : { redirectUrl: props.redirectUrl })}\n >\n {props.children}\n </Trigger>\n );\n}\n","import type { UiComponentName } from \"@zautha/js\";\nimport { useEffect, useRef, type ReactNode } from \"react\";\nimport { useZautha } from \"./provider\";\n\n/**\n * Mounts a @zautha/ui component into a div once the instance exists; later prop changes update it in\n * place. SSR renders only the empty div.\n */\nfunction Mounted(props: { name: UiComponentName; componentProps: object }): ReactNode {\n const zautha = useZautha();\n const ref = useRef<HTMLDivElement>(null);\n const mountedWith = useRef<object | null>(null);\n const { name, componentProps } = props;\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null) return;\n zautha.mount(name, element, componentProps);\n mountedWith.current = componentProps;\n return () => {\n mountedWith.current = null;\n zautha.unmount(element);\n };\n // componentProps changes are applied by the effect below without remounting.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [zautha, name]);\n\n useEffect(() => {\n const element = ref.current;\n if (zautha === null || element === null || mountedWith.current === null || mountedWith.current === componentProps)\n return;\n mountedWith.current = componentProps;\n zautha.updateMounted(element, componentProps);\n }, [zautha, componentProps]);\n\n return <div ref={ref} />;\n}\n\n// Prop types live in @zautha/ui; they are structural here so @zautha/react has no runtime dependency on it.\nexport function SignIn(props: import(\"@zautha/ui\").SignInProps): ReactNode {\n return <Mounted name=\"SignIn\" componentProps={props} />;\n}\n\nexport function SignUp(props: import(\"@zautha/ui\").SignUpProps): ReactNode {\n return <Mounted name=\"SignUp\" componentProps={props} />;\n}\n\nexport function UserButton(props: import(\"@zautha/ui\").UserButtonProps): ReactNode {\n return <Mounted name=\"UserButton\" componentProps={props} />;\n}\n\nexport function UserProfile(props: import(\"@zautha/ui\").UserProfileProps): ReactNode {\n return <Mounted name=\"UserProfile\" componentProps={props} />;\n}\n\nexport function OAuthConsent(props: import(\"@zautha/ui\").OAuthConsentProps): ReactNode {\n return <Mounted name=\"OAuthConsent\" componentProps={props} />;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zautha/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "React bindings for Zautha: provider, hooks, <Show>, buttons and prebuilt components.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"LICENSE"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@zautha/js": "^0.
|
|
34
|
+
"@zautha/js": "^0.3.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"react": "^19.0.0",
|
|
38
38
|
"react-dom": "^19.0.0",
|
|
39
|
-
"@zautha/ui": "^0.
|
|
39
|
+
"@zautha/ui": "^0.3.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@size-limit/preset-small-lib": "14.0.1",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@testing-library/user-event": "14.6.7",
|
|
45
45
|
"@types/react": "19.3.0",
|
|
46
46
|
"@types/react-dom": "19.3.0",
|
|
47
|
-
"@zautha/ui": "0.
|
|
47
|
+
"@zautha/ui": "0.3.0",
|
|
48
48
|
"happy-dom": "20.14.5",
|
|
49
49
|
"msw": "2.15.0",
|
|
50
50
|
"react": "19.3.0",
|