@segmentstream/app 0.0.0-stage → 0.38.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/LICENSE +201 -0
- package/NOTICE +2 -0
- package/README.md +1236 -2
- package/dist/agent.d.ts +56 -0
- package/dist/agent.js +203 -0
- package/dist/app.d.ts +82 -0
- package/dist/app.js +434 -0
- package/dist/browser-auth-capability.d.ts +2 -0
- package/dist/browser-auth-capability.js +2 -0
- package/dist/browser-revision.d.ts +19 -0
- package/dist/browser-revision.js +140 -0
- package/dist/channel-protocol.d.ts +16 -0
- package/dist/channel-protocol.js +17 -0
- package/dist/channel-session.d.ts +22 -0
- package/dist/channel-session.js +89 -0
- package/dist/config.d.ts +53 -0
- package/dist/config.js +112 -0
- package/dist/context.d.ts +10 -0
- package/dist/context.js +1 -0
- package/dist/cookies.d.ts +56 -0
- package/dist/cookies.js +131 -0
- package/dist/data.d.ts +233 -0
- package/dist/data.js +206 -0
- package/dist/decision-pages.d.ts +14 -0
- package/dist/decision-pages.js +87 -0
- package/dist/deployment.d.ts +30 -0
- package/dist/deployment.js +72 -0
- package/dist/errors.d.ts +60 -0
- package/dist/errors.js +123 -0
- package/dist/federation.d.ts +24 -0
- package/dist/federation.js +613 -0
- package/dist/groups.d.ts +95 -0
- package/dist/groups.js +280 -0
- package/dist/handlers.d.ts +10 -0
- package/dist/handlers.js +161 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +29 -0
- package/dist/jobs.d.ts +49 -0
- package/dist/jobs.js +40 -0
- package/dist/legacy.d.ts +77 -0
- package/dist/legacy.js +57 -0
- package/dist/logo.d.ts +1 -0
- package/dist/logo.js +25 -0
- package/dist/me.d.ts +42 -0
- package/dist/me.js +54 -0
- package/dist/navigation/build.d.ts +68 -0
- package/dist/navigation/build.js +290 -0
- package/dist/navigation/cli.d.ts +19 -0
- package/dist/navigation/cli.js +37 -0
- package/dist/navigation/generate.d.ts +43 -0
- package/dist/navigation/generate.js +138 -0
- package/dist/navigation/next.d.ts +10 -0
- package/dist/navigation/next.js +18 -0
- package/dist/navigation-protocol.d.ts +55 -0
- package/dist/navigation-protocol.js +298 -0
- package/dist/neon.d.ts +101 -0
- package/dist/neon.js +539 -0
- package/dist/oauth-completion.d.ts +6 -0
- package/dist/oauth-completion.js +106 -0
- package/dist/oauth.d.ts +59 -0
- package/dist/oauth.js +129 -0
- package/dist/ontology-names.d.ts +10 -0
- package/dist/ontology-names.js +38 -0
- package/dist/ontology-transport.d.ts +34 -0
- package/dist/ontology-transport.js +130 -0
- package/dist/ontology-wire.d.ts +66 -0
- package/dist/ontology-wire.js +355 -0
- package/dist/ontology.d.ts +381 -0
- package/dist/ontology.js +367 -0
- package/dist/pages.d.ts +6 -0
- package/dist/pages.js +6 -0
- package/dist/parameters.d.ts +55 -0
- package/dist/parameters.js +118 -0
- package/dist/permissions.d.ts +19 -0
- package/dist/permissions.js +55 -0
- package/dist/pkce.d.ts +8 -0
- package/dist/pkce.js +40 -0
- package/dist/proxy.d.ts +25 -0
- package/dist/proxy.js +135 -0
- package/dist/request-timing.d.ts +6 -0
- package/dist/request-timing.js +53 -0
- package/dist/return-to.d.ts +2 -0
- package/dist/return-to.js +24 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +80 -0
- package/dist/sessions.d.ts +27 -0
- package/dist/sessions.js +72 -0
- package/dist/sign-in.d.ts +37 -0
- package/dist/sign-in.js +95 -0
- package/dist/storage.d.ts +79 -0
- package/dist/storage.js +180 -0
- package/dist/telegram-launch.d.ts +7 -0
- package/dist/telegram-launch.js +134 -0
- package/dist/telegram.d.ts +2 -0
- package/dist/telegram.js +6 -0
- package/dist/tokens.d.ts +51 -0
- package/dist/tokens.js +70 -0
- package/dist/ui/agent-connect.d.ts +18 -0
- package/dist/ui/agent-connect.js +23 -0
- package/dist/ui/components.d.ts +27 -0
- package/dist/ui/components.js +71 -0
- package/dist/ui/hooks.d.ts +19 -0
- package/dist/ui/hooks.js +56 -0
- package/dist/ui/index.d.ts +22 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/navigation.d.ts +5 -0
- package/dist/ui/navigation.js +8 -0
- package/dist/ui/segmentstream-logo.d.ts +2 -0
- package/dist/ui/segmentstream-logo.js +7 -0
- package/dist/ui/sign-in-check.d.ts +14 -0
- package/dist/ui/sign-in-check.js +41 -0
- package/dist/ui/sign-in.d.ts +17 -0
- package/dist/ui/sign-in.js +65 -0
- package/dist/ui/store.d.ts +80 -0
- package/dist/ui/store.js +280 -0
- package/dist/ui/styles.css +249 -0
- package/dist/ui/telegram-sign-in.d.ts +16 -0
- package/dist/ui/telegram-sign-in.js +21 -0
- package/dist/ui/use-browser-sign-in.d.ts +52 -0
- package/dist/ui/use-browser-sign-in.js +169 -0
- package/dist/ui/use-channel-sign-in.d.ts +10 -0
- package/dist/ui/use-channel-sign-in.js +118 -0
- package/dist/ui/use-sign-in.d.ts +109 -0
- package/dist/ui/use-sign-in.js +304 -0
- package/package.json +95 -4
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { safeReturnTo } from "../return-to.js";
|
|
4
|
+
import { appSignInConfiguration, authClient } from "./store.js";
|
|
5
|
+
/** Headless browser-method discovery. The workspace supplies every message and control. */
|
|
6
|
+
export function useBrowserSignIn(options = {}) {
|
|
7
|
+
const [configuration, setConfiguration] = useState();
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
let active = true;
|
|
10
|
+
void appSignInConfiguration().then((value) => {
|
|
11
|
+
if (active)
|
|
12
|
+
setConfiguration(value);
|
|
13
|
+
});
|
|
14
|
+
return () => {
|
|
15
|
+
active = false;
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
const query = typeof window === "undefined" ? undefined : new URLSearchParams(window.location.search);
|
|
19
|
+
const returnTo = safeReturnTo(options.returnTo ?? query?.get("returnTo"));
|
|
20
|
+
const error = options.error ?? query?.get("error") ?? null;
|
|
21
|
+
const methods = configuration?.browser?.methods ?? [
|
|
22
|
+
...(configuration?.neon ? ["email"] : []),
|
|
23
|
+
...(configuration?.enabled ? ["segmentstream"] : []),
|
|
24
|
+
];
|
|
25
|
+
return {
|
|
26
|
+
status: configuration === undefined
|
|
27
|
+
? "loading"
|
|
28
|
+
: configuration === null
|
|
29
|
+
? "unavailable"
|
|
30
|
+
: "ready",
|
|
31
|
+
methods,
|
|
32
|
+
primary: configuration?.browser?.primary ?? (configuration?.neon ? "email" : "segmentstream"),
|
|
33
|
+
returnTo,
|
|
34
|
+
error,
|
|
35
|
+
segmentstreamSignInUrl: methods.includes("segmentstream")
|
|
36
|
+
? `/api/segmentstream/sign-in?returnTo=${encodeURIComponent(returnTo)}`
|
|
37
|
+
: null,
|
|
38
|
+
connectAccountUrl: methods.includes("email") && methods.includes("segmentstream")
|
|
39
|
+
? `/api/segmentstream/connect?returnTo=${encodeURIComponent(returnTo)}`
|
|
40
|
+
: null,
|
|
41
|
+
accountSwitchUrl: methods.includes("segmentstream")
|
|
42
|
+
? `/api/segmentstream/sign-in?account=select&returnTo=${encodeURIComponent(returnTo)}`
|
|
43
|
+
: null,
|
|
44
|
+
emailSignInUrl: methods.includes("email")
|
|
45
|
+
? `/sign-in?method=email&returnTo=${encodeURIComponent(returnTo)}`
|
|
46
|
+
: null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Linking never confirms during a GET. Post the form to confirmationUrl after explicit consent. */
|
|
50
|
+
export function useIdentityLink() {
|
|
51
|
+
const [state, setState] = useState({
|
|
52
|
+
status: "loading",
|
|
53
|
+
pending: false,
|
|
54
|
+
proofReady: false,
|
|
55
|
+
emailEnabled: false,
|
|
56
|
+
identity: null,
|
|
57
|
+
workspaceUser: null,
|
|
58
|
+
expiresAt: null,
|
|
59
|
+
returnTo: null,
|
|
60
|
+
});
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
let active = true;
|
|
63
|
+
void fetch("/api/segmentstream/link", { credentials: "same-origin", cache: "no-store" })
|
|
64
|
+
.then(async (response) => {
|
|
65
|
+
if (!response.ok)
|
|
66
|
+
throw new Error("identity_link_unavailable");
|
|
67
|
+
const value = (await response.json());
|
|
68
|
+
if (active)
|
|
69
|
+
setState({
|
|
70
|
+
status: "ready",
|
|
71
|
+
pending: value.pending === true,
|
|
72
|
+
proofReady: value.proof_ready === true,
|
|
73
|
+
emailEnabled: value.email_enabled === true,
|
|
74
|
+
identity: typeof value.identity?.id === "string" ? value.identity : null,
|
|
75
|
+
workspaceUser: typeof value.workspaceUser?.id === "string" ? value.workspaceUser : null,
|
|
76
|
+
expiresAt: typeof value.expiresAt === "number" ? value.expiresAt : null,
|
|
77
|
+
returnTo: typeof value.returnTo === "string" ? safeReturnTo(value.returnTo) : null,
|
|
78
|
+
});
|
|
79
|
+
})
|
|
80
|
+
.catch(() => {
|
|
81
|
+
if (active)
|
|
82
|
+
setState({
|
|
83
|
+
status: "unavailable",
|
|
84
|
+
pending: false,
|
|
85
|
+
proofReady: false,
|
|
86
|
+
emailEnabled: false,
|
|
87
|
+
identity: null,
|
|
88
|
+
workspaceUser: null,
|
|
89
|
+
expiresAt: null,
|
|
90
|
+
returnTo: null,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
return () => {
|
|
94
|
+
active = false;
|
|
95
|
+
};
|
|
96
|
+
}, []);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (state.expiresAt === null)
|
|
99
|
+
return;
|
|
100
|
+
const deadline = state.expiresAt;
|
|
101
|
+
const timer = window.setTimeout(() => setState((current) => current.expiresAt === deadline
|
|
102
|
+
? { ...current, pending: false, proofReady: false, identity: null, workspaceUser: null }
|
|
103
|
+
: current), Math.max(0, deadline - Date.now()));
|
|
104
|
+
return () => window.clearTimeout(timer);
|
|
105
|
+
}, [state.expiresAt]);
|
|
106
|
+
return {
|
|
107
|
+
...state,
|
|
108
|
+
confirmationUrl: "/api/segmentstream/link",
|
|
109
|
+
emailProofUrl: `/sign-in?method=email&returnTo=${encodeURIComponent(`/sign-in/link?returnTo=${encodeURIComponent(state.returnTo ?? "/")}`)}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** Explicit reverse linking. Email possession is proof only; the OIDC session selects the user. */
|
|
113
|
+
export function useEmailIdentityLink() {
|
|
114
|
+
const [status, setStatus] = useState("idle");
|
|
115
|
+
const [error, setError] = useState(null);
|
|
116
|
+
async function confirm() {
|
|
117
|
+
setStatus("pending");
|
|
118
|
+
setError(null);
|
|
119
|
+
try {
|
|
120
|
+
const response = await fetch("/api/segmentstream/link-email", {
|
|
121
|
+
method: "POST",
|
|
122
|
+
credentials: "same-origin",
|
|
123
|
+
cache: "no-store",
|
|
124
|
+
});
|
|
125
|
+
const body = (await response.json());
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
setError(typeof body.error === "string" ? body.error : "identity_link_failed");
|
|
128
|
+
setStatus("error");
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
setStatus("linked");
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
setError("identity_link_unavailable");
|
|
136
|
+
setStatus("error");
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function requestProof(email, returnTo = "/sign-in/link-email") {
|
|
141
|
+
setStatus("pending");
|
|
142
|
+
setError(null);
|
|
143
|
+
try {
|
|
144
|
+
const configuration = await appSignInConfiguration();
|
|
145
|
+
if (!configuration?.neon || !configuration.enabled) {
|
|
146
|
+
setError("identity_method_unavailable");
|
|
147
|
+
setStatus("error");
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const result = await authClient().signIn.magicLink({
|
|
151
|
+
email,
|
|
152
|
+
callbackURL: new URL(safeReturnTo(returnTo), window.location.origin).href,
|
|
153
|
+
});
|
|
154
|
+
if (result.error) {
|
|
155
|
+
setError("identity_proof_failed");
|
|
156
|
+
setStatus("error");
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
setStatus("idle");
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
setError("identity_proof_unavailable");
|
|
164
|
+
setStatus("error");
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { status, error, confirm, requestProof };
|
|
169
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ChannelSignInState = {
|
|
2
|
+
readonly status: "loading" | "disabled" | "idle" | "pending" | "signed_in" | "error";
|
|
3
|
+
readonly error: string | null;
|
|
4
|
+
};
|
|
5
|
+
export interface ChannelSignInOptions {
|
|
6
|
+
readonly assertion: () => string | undefined;
|
|
7
|
+
readonly redirectTo?: string;
|
|
8
|
+
}
|
|
9
|
+
/** One exchange per assertion, with an absolute expiry timer. Errors are app-rendered codes. */
|
|
10
|
+
export declare function useChannelSignIn(provider: string, options: ChannelSignInOptions): ChannelSignInState;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { channelSignInPath } from "../channel-protocol.js";
|
|
4
|
+
import { safeReturnTo } from "../return-to.js";
|
|
5
|
+
import { useSession } from "./hooks.js";
|
|
6
|
+
import { navigate } from "./navigation.js";
|
|
7
|
+
import { appSignInConfiguration, getSnapshot, refreshSession, sessionWasEnded } from "./store.js";
|
|
8
|
+
// A layout and the SDK sign-in page may mount together. Retain only the latest attempt per provider,
|
|
9
|
+
// in memory, so both share one exchange/discovery and never retry the same launch assertion.
|
|
10
|
+
const attempts = new Map();
|
|
11
|
+
function exchange(provider, assertion) {
|
|
12
|
+
const previous = attempts.get(provider);
|
|
13
|
+
if (previous?.assertion === assertion) {
|
|
14
|
+
return previous.result;
|
|
15
|
+
}
|
|
16
|
+
const result = (async () => {
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetch(channelSignInPath(provider), {
|
|
19
|
+
method: "POST",
|
|
20
|
+
credentials: "same-origin",
|
|
21
|
+
headers: { "content-type": "application/json" },
|
|
22
|
+
body: JSON.stringify({ assertion }),
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const body = (await response.json());
|
|
26
|
+
return typeof body.error === "string" ? body.error : "sign_in_failed";
|
|
27
|
+
}
|
|
28
|
+
await refreshSession();
|
|
29
|
+
return getSnapshot().session.status === "signed-in" ? null : "session_unavailable";
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return "sign_in_unavailable";
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
attempts.set(provider, { assertion, result });
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
/** One exchange per assertion, with an absolute expiry timer. Errors are app-rendered codes. */
|
|
39
|
+
export function useChannelSignIn(provider, options) {
|
|
40
|
+
const session = useSession();
|
|
41
|
+
const [enabled, setEnabled] = useState(undefined);
|
|
42
|
+
const [settled, setSettled] = useState();
|
|
43
|
+
const [expired, setExpired] = useState();
|
|
44
|
+
const redirected = useRef(false);
|
|
45
|
+
const raw = enabled && typeof window !== "undefined" ? options.assertion() : undefined;
|
|
46
|
+
const redirectTo = options.redirectTo;
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
let mounted = true;
|
|
49
|
+
void appSignInConfiguration().then((config) => {
|
|
50
|
+
if (mounted) {
|
|
51
|
+
setEnabled(config === null ? null : config.channels[provider] === true);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return () => {
|
|
55
|
+
mounted = false;
|
|
56
|
+
};
|
|
57
|
+
}, [provider, session]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!enabled ||
|
|
60
|
+
session.status !== "signed-in" ||
|
|
61
|
+
session.source !== "channel" ||
|
|
62
|
+
!session.expiresAt ||
|
|
63
|
+
expired === session.sessionId) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const timer = window.setTimeout(() => {
|
|
67
|
+
setExpired(session.sessionId);
|
|
68
|
+
void refreshSession();
|
|
69
|
+
}, Math.max(0, Date.parse(session.expiresAt) - Date.now()));
|
|
70
|
+
return () => window.clearTimeout(timer);
|
|
71
|
+
}, [session, enabled, expired]);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!enabled || session.status === "loading" || session.status === "unavailable") {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (session.status === "signed-in" && expired !== session.sessionId) {
|
|
77
|
+
if (raw && redirectTo !== undefined && !redirected.current) {
|
|
78
|
+
redirected.current = true;
|
|
79
|
+
navigate(safeReturnTo(redirectTo));
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (sessionWasEnded() || !raw) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let mounted = true;
|
|
87
|
+
void exchange(provider, raw).then((error) => {
|
|
88
|
+
if (mounted) {
|
|
89
|
+
setSettled({ assertion: raw, error });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return () => {
|
|
93
|
+
mounted = false;
|
|
94
|
+
};
|
|
95
|
+
}, [session, enabled, provider, raw, redirectTo, expired]);
|
|
96
|
+
if (enabled === undefined || session.status === "loading") {
|
|
97
|
+
return { status: "loading", error: null };
|
|
98
|
+
}
|
|
99
|
+
if (enabled === null) {
|
|
100
|
+
return { status: "error", error: "configuration_unavailable" };
|
|
101
|
+
}
|
|
102
|
+
if (!enabled) {
|
|
103
|
+
return { status: "disabled", error: null };
|
|
104
|
+
}
|
|
105
|
+
if (session.status === "unavailable") {
|
|
106
|
+
return { status: "error", error: "session_unavailable" };
|
|
107
|
+
}
|
|
108
|
+
if (session.status === "signed-in" && expired !== session.sessionId) {
|
|
109
|
+
return { status: "signed_in", error: null };
|
|
110
|
+
}
|
|
111
|
+
if (sessionWasEnded() || !raw) {
|
|
112
|
+
return { status: "idle", error: null };
|
|
113
|
+
}
|
|
114
|
+
if (settled?.assertion === raw) {
|
|
115
|
+
return { status: "error", error: settled.error ?? "fresh_assertion_required" };
|
|
116
|
+
}
|
|
117
|
+
return { status: "pending", error: null };
|
|
118
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export interface SignInOptions {
|
|
2
|
+
/** Where to go once signed in. Defaults to the page's `returnTo` parameter, then `/`. */
|
|
3
|
+
readonly returnTo?: string;
|
|
4
|
+
/**
|
|
5
|
+
* Why the page was opened: `not_invited` when `requireUser()` found no invitation,
|
|
6
|
+
* `sign_in_link_invalid` when an emailed sign-in link was expired, used or unknown. Defaults to
|
|
7
|
+
* the page's `error` parameter.
|
|
8
|
+
*/
|
|
9
|
+
readonly error?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Why the last call failed. `not_invited`: the ontology allows no sign-in with this address, so no
|
|
13
|
+
* email was sent. Apps that show another language word their message from it.
|
|
14
|
+
*/
|
|
15
|
+
export type SignInErrorCode = "email_required" | "email_invalid" | "not_invited" | "code_incomplete" | "code_invalid" | "code_expired" | "too_many_attempts" | "rate_limited" | "unavailable" | "failed";
|
|
16
|
+
/** What the last call confirmed: another code, or another sign-in link, was sent. */
|
|
17
|
+
export type SignInNoticeCode = "code_resent" | "link_resent";
|
|
18
|
+
/** The SDK's sentence for each {@link SignInErrorCode}. */
|
|
19
|
+
export declare const SIGN_IN_ERROR_MESSAGES: Readonly<Record<SignInErrorCode, string>>;
|
|
20
|
+
/** Email sign-in for a form the app renders itself. The calls take values, not form events. */
|
|
21
|
+
export interface SignInController {
|
|
22
|
+
readonly segmentstreamSignInUrl: string | null;
|
|
23
|
+
readonly emailEnabled: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* `email`, then `code` for a member with a Neon Auth account, or `link` when a sign-in link was
|
|
26
|
+
* emailed instead: a first sign-in, whose link creates the account. The link step has nothing to
|
|
27
|
+
* submit; the person opens the link from the email.
|
|
28
|
+
*/
|
|
29
|
+
readonly step: "email" | "code" | "link";
|
|
30
|
+
readonly email: string;
|
|
31
|
+
readonly setEmail: (value: string) => void;
|
|
32
|
+
readonly code: string;
|
|
33
|
+
readonly setCode: (value: string) => void;
|
|
34
|
+
/** The call in flight; disable the form while it is not `null`. */
|
|
35
|
+
readonly pending: "send" | "verify" | "resend" | "sign-out" | null;
|
|
36
|
+
/** A sentence for the person, from the last failed call. */
|
|
37
|
+
readonly error: string | null;
|
|
38
|
+
/** Why the last call failed, for apps that word `error` themselves. */
|
|
39
|
+
readonly errorCode: SignInErrorCode | null;
|
|
40
|
+
/** A confirmation, such as a new code having been sent. */
|
|
41
|
+
readonly notice: string | null;
|
|
42
|
+
/** What `notice` confirms, for apps that word it themselves. */
|
|
43
|
+
readonly noticeCode: SignInNoticeCode | null;
|
|
44
|
+
/** Seconds until another code or link may be requested; 0 when it may. */
|
|
45
|
+
readonly resendIn: number;
|
|
46
|
+
/** Digits in a code: 6. */
|
|
47
|
+
readonly codeLength: number;
|
|
48
|
+
/** The same-origin path the person returns to after signing in. */
|
|
49
|
+
readonly destination: string;
|
|
50
|
+
/**
|
|
51
|
+
* A session exists in this browser and the person has not chosen another account: offer to
|
|
52
|
+
* continue to `destination` instead of the email step.
|
|
53
|
+
*/
|
|
54
|
+
readonly signedIn: boolean;
|
|
55
|
+
/** The page was opened for a signed-in person the app has not invited. */
|
|
56
|
+
readonly notInvited: boolean;
|
|
57
|
+
/** The emailed sign-in link was expired, used or unknown: say so on the email step. */
|
|
58
|
+
readonly linkInvalid: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The page was opened from the link in a sign-in code email, for apps that declare
|
|
61
|
+
* `signIn.codeLink`: the hook is on the `code` step with that email's address and code. The hook
|
|
62
|
+
* never submits it. Calling `submitCode()` once when this turns true signs in with one click; a
|
|
63
|
+
* mail scanner that runs the page's scripts can then spend the code first, and the person asks
|
|
64
|
+
* for a new one. An app that would rather avoid that asks the person to confirm instead. It turns
|
|
65
|
+
* false once the code is cleared.
|
|
66
|
+
*/
|
|
67
|
+
readonly codeFromLink: boolean;
|
|
68
|
+
/** The signed-in address or name, for the not-invited state; null when signed out. */
|
|
69
|
+
readonly sessionEmail: string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Asks the ontology whether `email` may sign in, then emails a code or a sign-in link and moves to
|
|
72
|
+
* `code` or `link`. An address the ontology refuses gets `not_invited`, and no email is sent. Call
|
|
73
|
+
* it from the form's submit handler.
|
|
74
|
+
*/
|
|
75
|
+
readonly submitEmail: () => Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Verifies `value`, or the current `code`, and opens `destination`. A wrong code clears `code`;
|
|
78
|
+
* a second call while one is verifying is ignored, so the last digit and Enter may both submit.
|
|
79
|
+
*/
|
|
80
|
+
readonly submitCode: (value?: string) => Promise<void>;
|
|
81
|
+
/** Sends another code, or another link on the link step, to the same address. */
|
|
82
|
+
readonly resend: () => Promise<void>;
|
|
83
|
+
/** Returns to the email step. */
|
|
84
|
+
readonly changeEmail: () => void;
|
|
85
|
+
/** Signs the current session out and returns to the email step. */
|
|
86
|
+
readonly switchAccount: () => Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The address and code that a sign-in code email's link carries in the sign-in page's fragment, as
|
|
90
|
+
* the platform writes it for apps that declare `signIn.codeLink`:
|
|
91
|
+
* `/sign-in#code=482915&email=ada%40example.com`. Browsers never send the fragment to a server.
|
|
92
|
+
*/
|
|
93
|
+
export declare function signInCodeFromLink(hash: string): {
|
|
94
|
+
readonly email: string;
|
|
95
|
+
readonly code: string;
|
|
96
|
+
} | null;
|
|
97
|
+
/**
|
|
98
|
+
* Email sign-in with Neon Auth, without markup. The ontology first says whether the address may
|
|
99
|
+
* sign in (`POST /api/ontology/sign-in/check`): a member with a Neon Auth account gets a six-digit
|
|
100
|
+
* code, a first sign-in gets a sign-in link, which creates the account, and an address the ontology
|
|
101
|
+
* refuses gets `not_invited` without an email. Without an ontology to ask, every address gets a
|
|
102
|
+
* code. `<SignIn />` renders it with the SDK's own styles; apps with their own design system render
|
|
103
|
+
* the controller themselves.
|
|
104
|
+
*/
|
|
105
|
+
export declare function useSignIn({ returnTo, error: reason }?: SignInOptions): SignInController;
|
|
106
|
+
/** Why a Neon Auth (Better Auth) call failed. */
|
|
107
|
+
export declare function signInErrorCode(error: unknown): SignInErrorCode;
|
|
108
|
+
/** A sentence for a Neon Auth (Better Auth) failure. */
|
|
109
|
+
export declare function signInErrorMessage(error: unknown): string;
|