@synqauth/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/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
- import { InputHTMLAttributes, ReactNode } from "react";
3
- import { AccountComponents, ChallengeMethod, ChallengeProblem, DeniedReason, RedirectReason, SynqError } from "@synqauth/js";
2
+ import { CSSProperties, InputHTMLAttributes, KeyboardEvent, MouseEvent, PointerEvent, ReactNode, RefObject } from "react";
3
+ import { AccountComponents, AvatarInfo, AvatarPerson, ChallengeMethod, ChallengeProblem, DeniedReason, RedirectReason, SignInMethodInfo, SynqError } from "@synqauth/js";
4
4
  //#region src/sign-in.d.ts
5
5
  /** Where a sign-in is: what the form shows. */
6
6
  type SignInStep = 'start' | 'code' | 'verify-email' | 'done';
@@ -94,6 +94,232 @@ interface UseSignIn {
94
94
  /** One sign-in on the app's own screens, through the route mount's `sign-in/flow`. */
95
95
  export declare function useSignIn(options?: UseSignInOptions): UseSignIn;
96
96
  //#endregion
97
+ //#region src/types.d.ts
98
+ /** A call's outcome: its data, or the `SynqError` it failed with (branch on `error.code`). */
99
+ type SynqResult<T> = {
100
+ readonly data: T;
101
+ readonly error: null;
102
+ } | {
103
+ readonly data: null;
104
+ readonly error: SynqError;
105
+ };
106
+ /** A signed-in person, from the ID token Synq issued at sign-in. */
107
+ interface SynqUser {
108
+ /** The user's id at the brand (`usr_…`). */
109
+ readonly sub: string;
110
+ readonly email?: string;
111
+ readonly email_verified?: boolean;
112
+ readonly name?: string;
113
+ readonly preferred_username?: string;
114
+ /** The picture they chose: none, and your app shows its own default, from `avatar_seed`. */
115
+ readonly picture?: string;
116
+ /**
117
+ * Where `picture` came from (AERO-2700): `none`, `upload`, `url` or
118
+ * `linked:<provider>`. Absent from a token issued before Synq said.
119
+ */
120
+ readonly picture_source?: string;
121
+ /**
122
+ * What to draw their default avatar from (AERO-2704): theirs in every app
123
+ * of the brand, for as long as the account lasts. Absent from a token
124
+ * issued before seeds, where `sub` stands in (`useAvatar`).
125
+ */
126
+ readonly avatar_seed?: string;
127
+ /** The brand's own default avatar for them, where it uploaded a set. */
128
+ readonly default_picture?: string;
129
+ /** When they signed in, in seconds since the epoch. */
130
+ readonly auth_time?: number;
131
+ /** How they signed in: `pwd`, `otp`, `wallet`, `hwk`, `fed`... */
132
+ readonly amr?: readonly string[];
133
+ /**
134
+ * The way in of the sign-in this session came from, when the app turned
135
+ * its sign-in claims on (`sign_in_claims`, AERO-2691): `google`, `wallet`,
136
+ * `email`, `password`, `passkey`…
137
+ */
138
+ readonly sign_in_method?: string;
139
+ /** For a wallet sign-in, the address that signed and the wallet app's name, when it said. */
140
+ readonly sign_in_wallet?: {
141
+ readonly address: string;
142
+ readonly app: string | null;
143
+ };
144
+ /** Whether that sign-in made the account: true in its first ID token only. */
145
+ readonly new_user?: boolean;
146
+ readonly [claim: string]: unknown;
147
+ }
148
+ /** The way this browser last signed in to the app (AERO-2691): never who, never an address. */
149
+ interface LastUsedMethod {
150
+ /** The method's id: one of `SignInMethods`, or one Synq adds later. */
151
+ readonly method: SignInMethod | (string & Record<never, never>);
152
+ /** For a wallet, the wallet app's name as it announced itself (`Phantom`). */
153
+ readonly walletApp?: string;
154
+ }
155
+ /** A sign-in that just finished (AERO-2691): what `useSignInEvent()` tells the page once. */
156
+ interface SignInEvent {
157
+ /** This sign-in's own id: two sign-ins the same way can share `at`, never this. */
158
+ readonly id: string;
159
+ /** Whether it made the account: their first sign-in. */
160
+ readonly isNewUser: boolean;
161
+ /** The way in: one of `SignInMethods`, or one Synq adds later. */
162
+ readonly method: SignInMethod | (string & Record<never, never>);
163
+ /** For a wallet sign-in, the address that signed and its wallet app, when it said. */
164
+ readonly wallet: {
165
+ readonly address: string;
166
+ readonly app: string | null;
167
+ } | null;
168
+ /** When they signed in, in seconds since the epoch. */
169
+ readonly at: number;
170
+ }
171
+ /** Who is signed in, as the app's server tells the browser: `await synq.getSession()`, or the route mount's `session`. */
172
+ interface SynqSessionInfo {
173
+ readonly user: SynqUser | null;
174
+ /** When the session's main access token expires, in seconds since the epoch. */
175
+ readonly expiresAt: number | null;
176
+ /**
177
+ * The way this browser last signed in to the app, signed in or out, from
178
+ * the app's sign-in claims (AERO-2691). Absent from a mount that doesn't
179
+ * keep it.
180
+ */
181
+ readonly lastUsed?: LastUsedMethod | null;
182
+ /** The sign-in that just finished, until the page hears of it (AERO-2691). */
183
+ readonly event?: SignInEvent | null;
184
+ }
185
+ /**
186
+ * What the brand keeps for the person (the account API's `metadata`).
187
+ * Augment it to type what the app reads:
188
+ *
189
+ * ```ts
190
+ * declare module '@synqauth/react' {
191
+ * interface UserMetadata {
192
+ * plan?: 'free' | 'pro'
193
+ * }
194
+ * }
195
+ * ```
196
+ */
197
+ interface UserMetadata {
198
+ readonly [field: string]: unknown;
199
+ }
200
+ /**
201
+ * The ways a brand signs people in, as `synq_method` names them: the hosted
202
+ * page goes straight to one. A brand's own connection joins them the same way:
203
+ *
204
+ * ```ts
205
+ * declare module '@synqauth/react' {
206
+ * interface SignInMethods {
207
+ * okta: true
208
+ * }
209
+ * }
210
+ * ```
211
+ */
212
+ interface SignInMethods {
213
+ readonly password: true;
214
+ readonly email: true;
215
+ readonly passkey: true;
216
+ readonly wallet: true;
217
+ readonly google: true;
218
+ readonly github: true;
219
+ readonly microsoft: true;
220
+ readonly apple: true;
221
+ readonly facebook: true;
222
+ readonly x: true;
223
+ readonly discord: true;
224
+ readonly telegram: true;
225
+ readonly matrica: true;
226
+ }
227
+ /** One of the brand's sign-in methods. */
228
+ type SignInMethod = Extract<keyof SignInMethods, string>;
229
+ /** How to send the person to sign in. */
230
+ interface SignInOptions {
231
+ /** Where to come back to: a path on the app, the current page by default. */
232
+ readonly returnTo?: string;
233
+ /** Go straight to one sign-in method. */
234
+ readonly method?: SignInMethod;
235
+ /** Fills in their email address. */
236
+ readonly loginHint?: string;
237
+ /** `create` opens the hosted page on its sign-up screen. */
238
+ readonly prompt?: 'login' | 'none' | 'consent' | 'select_account' | 'create';
239
+ /** Ask them to sign in again when their last sign-in is older than this many seconds. */
240
+ readonly maxAge?: number;
241
+ /** Offer a new passkey once they've signed in again. */
242
+ readonly intent?: 'add-passkey';
243
+ }
244
+ /** The person's profile at the brand. */
245
+ type Profile = AccountComponents['schemas']['Profile'];
246
+ /** Changes to it: only the fields given change, and `null` clears one. */
247
+ type ProfileUpdate = AccountComponents['schemas']['ProfileUpdate'];
248
+ /** A Solana wallet the person signs in with. */
249
+ type Wallet = AccountComponents['schemas']['Wallet'];
250
+ /** A way the person signs in: their email address, a wallet, a Google account... */
251
+ type LinkedAccount = AccountComponents['schemas']['Identity'];
252
+ /** One of the person's passkeys: `synced` when it lives in a password manager across their devices. */
253
+ type Passkey = AccountComponents['schemas']['Passkey'];
254
+ /** One of the person's sessions at the brand, on some device. */
255
+ type AccountSession = AccountComponents['schemas']['Session'];
256
+ /** One of the person's API keys for the brand's APIs (AERO-1693): never the key itself. */
257
+ type ApiKey = AccountComponents['schemas']['UserApiKey'];
258
+ /** A key just made: `key` is the key itself, in this answer only. */
259
+ type NewApiKey = AccountComponents['schemas']['NewUserApiKey'];
260
+ /** What making an API key takes: the API's identifier, a name, its scopes, an expiry if any. */
261
+ type ApiKeyInput = AccountComponents['schemas']['UserApiKeyCreate'];
262
+ /** One of the brand's APIs the person may make keys for, with the scopes it defines. */
263
+ type KeyableApi = AccountComponents['schemas']['KeyableApi'];
264
+ /** A way to sign in the person can add (AERO-2080): what `useAuth().link` takes, with its name. */
265
+ type LinkableMethod = AccountComponents['schemas']['LinkableMethod'];
266
+ /** How `signOut` signs the person out. */
267
+ interface SignOutOptions {
268
+ /**
269
+ * Stay on the page (AERO-2684): the app's server ends the brand's session
270
+ * itself and the browser goes nowhere. For an app whose server holds a
271
+ * secret (`@synqauth/nextjs` does). By default the browser goes to the
272
+ * brand's sign-out page, which asks first.
273
+ */
274
+ readonly stay?: boolean;
275
+ }
276
+ //#endregion
277
+ //#region src/username.d.ts
278
+ /** Where the draft stands: nothing typed, being checked, free, taken, not a username, or theirs already. */
279
+ type UsernameStatus = 'idle' | 'checking' | 'available' | 'taken' | 'format' | 'same';
280
+ /** What to say of the draft: its status, or for `format` which rule it breaks. */
281
+ type UsernameCode = 'same' | 'available' | 'taken' |
282
+ /** Fewer than 3 characters. */
283
+ 'too_short' |
284
+ /** More than 64. */
285
+ 'too_long' |
286
+ /** Something other than a letter, digit, `.`, `_` or `-`. */
287
+ 'characters' |
288
+ /** Starting or ending with `.`, `_` or `-`. */
289
+ 'edges';
290
+ /** A username the brand takes: 3 to 64 of `a-z 0-9 . _ -`, starting and ending with a letter or digit. */
291
+ export declare const USERNAME_FORMAT: RegExp;
292
+ /** The draft as the brand keeps usernames: no spaces around it, no leading at sign, lowercase. */
293
+ export declare function normalizeUsername(value: string): string;
294
+ /** Which format rule `username` breaks, or null when it is one. */
295
+ export declare function usernameFormatCode(username: string): Extract<UsernameCode, 'too_short' | 'too_long' | 'characters' | 'edges'> | null;
296
+ /** What `useUsernameField()` answers. */
297
+ interface UseUsernameField {
298
+ /** The username being typed, as the brand would keep it; their own until they type. */
299
+ readonly draft: string;
300
+ /** Takes what they typed (or a suggestion they tapped), and checks it again. */
301
+ readonly setDraft: (value: string) => void;
302
+ readonly status: UsernameStatus;
303
+ /** What to say of it, by code: `messages.usernameCodes` has the words. */
304
+ readonly code: UsernameCode | null;
305
+ /** That, in the provider's words (`<SynqProvider messages>`), or null when there is nothing to say. */
306
+ readonly message: string | null;
307
+ /** When it is taken: up to three free ones like it, from the brand, to offer. */
308
+ readonly suggestions: readonly string[];
309
+ /**
310
+ * Makes the draft their username. `same` answers the profile as it is; a
311
+ * draft that isn't a username answers `invalid_username`; one taken since
312
+ * the check answers `username_taken`, and the field shows it with its
313
+ * suggestions.
314
+ */
315
+ readonly save: () => Promise<SynqResult<Profile>>;
316
+ readonly saving: boolean;
317
+ /** Why the last check or save failed outright (the network, say). */
318
+ readonly error: SynqError | null;
319
+ }
320
+ /** A username field for the signed-in person (AERO-2693): live availability, suggestions and saving. */
321
+ export declare function useUsernameField(): UseUsernameField;
322
+ //#endregion
97
323
  //#region src/messages.d.ts
98
324
  /** Every word the components show, for `<SynqProvider messages>`; any left out stay English. */
99
325
  interface SynqMessages {
@@ -111,6 +337,14 @@ interface SynqMessages {
111
337
  readonly name: string;
112
338
  readonly username: string;
113
339
  readonly waysToSignIn: string;
340
+ /** Choosing their picture (AERO-2700): uploading one. */
341
+ readonly uploadPicture: string;
342
+ readonly uploadingPicture: string;
343
+ /** Using a linked account's picture: "Use my Google picture". */
344
+ readonly usePictureFrom: (provider: string) => string;
345
+ /** The same, for one of two or more accounts at a provider: "Use my GitHub picture (@ana)". */
346
+ readonly usePictureFromAccount: (provider: string, account: string) => string;
347
+ readonly removePicture: string;
114
348
  readonly remove: string;
115
349
  /** The label of the button that stops a way signing them in. */
116
350
  readonly removeAccount: (provider: string, subject: string) => string;
@@ -193,111 +427,148 @@ interface SynqMessages {
193
427
  readonly signInProblems: Readonly<Partial<Record<SignInProblem['code'], string>>>;
194
428
  /** Why this person can't sign in here, by `SignInProblem.reason`. */
195
429
  readonly signInRefusals: Readonly<Partial<Record<DeniedReason, string>>>;
430
+ /** The button that adds a wallet: Synq asks them to sign in again, then for the wallet. */
431
+ readonly connectWallet: string;
432
+ /**
433
+ * The mark on a way to sign in they proved is theirs: every wallet (by a
434
+ * signature) and provider account, and their email address once verified.
435
+ */
436
+ readonly verified: string;
437
+ /** When a wallet or a way to sign in was added. */
438
+ readonly connectedOn: (date: string) => string;
439
+ readonly copyAddress: (address: string) => string;
440
+ readonly removeWallet: (name: string) => string;
441
+ /** The question before a wallet goes. */
442
+ readonly removeWalletTitle: string;
443
+ readonly removeWalletBody: string;
444
+ /** The question before a way to sign in goes. */
445
+ readonly removeAccountTitle: string;
446
+ readonly removeAccountBody: string;
447
+ /** The heading over the ways they could add. */
448
+ readonly addWayToSignIn: string;
449
+ /** Under that heading: adding one asks them to sign in again first. */
450
+ readonly signInAgainToAdd: string;
451
+ /** The label of the button that adds a way to sign in. */
452
+ readonly linkMethod: (provider: string) => string;
453
+ readonly connect: string;
454
+ readonly passkeys: string;
455
+ /** What passkeys are for, under the heading. */
456
+ readonly passkeysHint: string;
457
+ readonly noPasskeys: string;
458
+ readonly addPasskey: string;
459
+ /** A passkey's name: when it was made. */
460
+ readonly passkeyMade: (date: string) => string;
461
+ /** The mark on a passkey that a password manager keeps on all their devices. */
462
+ readonly synced: string;
463
+ readonly removePasskey: (name: string) => string;
464
+ readonly deleteAccount: string;
465
+ /**
466
+ * What deleting the account does, beside the button and in the question
467
+ * before it. It promises no way back: asking signs them out everywhere,
468
+ * and unless the brand lets a sign-in cancel it, they can't sign in again.
469
+ */
470
+ readonly deleteAccountBody: string;
471
+ /** The word they type to confirm the deletion. */
472
+ readonly deleteConfirmWord: string;
473
+ readonly typeToConfirm: (word: string) => string;
474
+ readonly deletionScheduled: (date: string) => string;
475
+ readonly keepAccount: string;
476
+ /** Why a change needs them to sign in again: their last sign-in isn't recent enough. */
477
+ readonly freshSignInNeeded: string;
478
+ /** What `useUsernameField()` says of the draft, by its `code` (AERO-2693). */
479
+ readonly usernameCodes: Readonly<Record<UsernameCode, (username: string) => string>>;
480
+ /** `<AccountPanel>` (AERO-2696): the trigger's label once signed in, with their first name when known. */
481
+ readonly yourProfile: (name: string | null) => string;
482
+ readonly close: string;
483
+ readonly back: string;
484
+ /** A way to sign in, on its button: "Continue with Google". */
485
+ readonly continueWith: (method: string) => string;
486
+ /** The wallet method with nothing connected. */
487
+ readonly continueWithWallet: string;
488
+ /** Under the connected wallet's button: what signing in with it takes. */
489
+ readonly walletSignInHint: string;
490
+ readonly useAnotherWallet: string;
491
+ readonly pickWallet: string;
492
+ /** Under a wallet in the picker. */
493
+ readonly walletDetected: string;
494
+ readonly noWalletHere: string;
495
+ /** Signing in on the brand's hosted page instead, where only it can. */
496
+ readonly useSignInPage: string;
497
+ readonly moreOptions: string;
498
+ readonly fewerOptions: string;
499
+ /** The mark on the way this browser last signed in. */
500
+ readonly lastUsedMethod: string;
501
+ /** Where a provider's sign-in can't run: another app's built-in browser. */
502
+ readonly openInBrowser: (method: string) => string;
503
+ /** A sign-in under way: a wallet's prompt, a popup, a redirect, a passkey. */
504
+ readonly approveIn: (walletApp: string | null) => string;
505
+ readonly finishInWindow: (method: string) => string;
506
+ readonly opening: (method: string) => string;
507
+ readonly usePasskeyNow: string;
508
+ readonly signingYouIn: string;
509
+ /** A sign-in that couldn't reach the app's server or Synq. */
510
+ readonly signInOffline: string;
511
+ readonly signInFailed: string;
512
+ readonly tryAgain: string;
513
+ readonly securedBy: string;
514
+ /** A first sign-in's greeting, with their first name when known. */
515
+ readonly youreIn: (name: string | null) => string;
516
+ readonly makeItYours: string;
517
+ /** A returning sign-in's greeting. */
518
+ readonly goodToSeeYou: (name: string | null) => string;
519
+ /** How they signed in: "Signed in with Google", "Signed in with 7xKX…3fQ". */
520
+ readonly signedInWith: (way: string) => string;
521
+ readonly viewProfile: string;
522
+ readonly edit: string;
523
+ readonly changePicture: string;
524
+ readonly yourPicture: string;
525
+ /** Beside the large avatar on the picture screen. */
526
+ readonly pictureHint: string;
527
+ /** The app's own default, drawn for them. */
528
+ readonly defaultPicture: string;
529
+ readonly defaultPictureHint: string;
530
+ readonly uploadPhoto: string;
531
+ readonly uploadAnotherPhoto: string;
532
+ readonly pictureTypes: string;
533
+ readonly uploadedPicture: string;
534
+ /** A linked account's picture: "Your Google picture". */
535
+ readonly pictureFrom: (provider: string) => string;
536
+ /** The mark on the picture showing now. */
537
+ readonly showing: string;
538
+ readonly accountSettings: string;
539
+ readonly signOutFailed: string;
540
+ readonly usernamePlaceholder: string;
541
+ readonly checking: string;
542
+ /** The connected wallet, when it isn't on their profile. */
543
+ readonly walletNotOnProfile: (address: string) => string;
544
+ readonly add: string;
545
+ readonly connected: string;
546
+ readonly signedInWithThis: string;
547
+ readonly addWallet: string;
548
+ /** Under a wallet in the add picker. */
549
+ readonly connectAndAdd: string;
550
+ readonly walletOptions: (name: string) => string;
551
+ readonly copyAddressShort: string;
552
+ readonly removeFromProfile: string;
553
+ readonly removeWalletQuestion: (name: string) => string;
554
+ readonly keep: string;
555
+ readonly confirmItsYou: string;
556
+ readonly onlyWayIn: string;
557
+ readonly walletInUse: string;
558
+ readonly moveHere: string;
559
+ readonly linked: string;
560
+ readonly link: string;
561
+ readonly notLinked: string;
562
+ /** The session ended while the panel was open, and not by its Sign out. */
563
+ readonly signedOutAgain: string;
564
+ /** Confirming it's them, they signed in as someone else, who is never let in. */
565
+ readonly notYourAccount: string;
566
+ /** A change that wants a recent sign-in, before "Confirm it's you". */
567
+ readonly confirmToGoOn: string;
196
568
  }
197
569
  /** The components' English words, which an app's own messages override one by one. */
198
570
  export declare const DEFAULT_MESSAGES: SynqMessages;
199
571
  //#endregion
200
- //#region src/types.d.ts
201
- /** A call's outcome: its data, or the `SynqError` it failed with (branch on `error.code`). */
202
- type SynqResult<T> = {
203
- readonly data: T;
204
- readonly error: null;
205
- } | {
206
- readonly data: null;
207
- readonly error: SynqError;
208
- };
209
- /** A signed-in person, from the ID token Synq issued at sign-in. */
210
- interface SynqUser {
211
- /** The user's id at the brand (`usr_…`). */
212
- readonly sub: string;
213
- readonly email?: string;
214
- readonly email_verified?: boolean;
215
- readonly name?: string;
216
- readonly preferred_username?: string;
217
- readonly picture?: string;
218
- /** When they signed in, in seconds since the epoch. */
219
- readonly auth_time?: number;
220
- /** How they signed in: `pwd`, `otp`, `wallet`, `hwk`, `fed`... */
221
- readonly amr?: readonly string[];
222
- readonly [claim: string]: unknown;
223
- }
224
- /** Who is signed in, as the app's server tells the browser: `await synq.getSession()`, or the route mount's `session`. */
225
- interface SynqSessionInfo {
226
- readonly user: SynqUser | null;
227
- /** When the session's main access token expires, in seconds since the epoch. */
228
- readonly expiresAt: number | null;
229
- }
230
- /**
231
- * What the brand keeps for the person (the account API's `metadata`).
232
- * Augment it to type what the app reads:
233
- *
234
- * ```ts
235
- * declare module '@synqauth/react' {
236
- * interface UserMetadata {
237
- * plan?: 'free' | 'pro'
238
- * }
239
- * }
240
- * ```
241
- */
242
- interface UserMetadata {
243
- readonly [field: string]: unknown;
244
- }
245
- /**
246
- * The ways a brand signs people in, as `synq_method` names them: the hosted
247
- * page goes straight to one. A brand's own connection joins them the same way:
248
- *
249
- * ```ts
250
- * declare module '@synqauth/react' {
251
- * interface SignInMethods {
252
- * okta: true
253
- * }
254
- * }
255
- * ```
256
- */
257
- interface SignInMethods {
258
- readonly password: true;
259
- readonly email: true;
260
- readonly passkey: true;
261
- readonly wallet: true;
262
- readonly google: true;
263
- readonly microsoft: true;
264
- readonly discord: true;
265
- }
266
- /** One of the brand's sign-in methods. */
267
- type SignInMethod = Extract<keyof SignInMethods, string>;
268
- /** How to send the person to sign in. */
269
- interface SignInOptions {
270
- /** Where to come back to: a path on the app, the current page by default. */
271
- readonly returnTo?: string;
272
- /** Go straight to one sign-in method. */
273
- readonly method?: SignInMethod;
274
- /** Fills in their email address. */
275
- readonly loginHint?: string;
276
- readonly prompt?: 'login' | 'none' | 'consent' | 'select_account';
277
- /** Ask them to sign in again when their last sign-in is older than this many seconds. */
278
- readonly maxAge?: number;
279
- /** Offer a new passkey once they've signed in again. */
280
- readonly intent?: 'add-passkey';
281
- }
282
- /** The person's profile at the brand. */
283
- type Profile = AccountComponents['schemas']['Profile'];
284
- /** Changes to it: only the fields given change, and `null` clears one. */
285
- type ProfileUpdate = AccountComponents['schemas']['ProfileUpdate'];
286
- /** A Solana wallet the person signs in with. */
287
- type Wallet = AccountComponents['schemas']['Wallet'];
288
- /** A way the person signs in: their email address, a wallet, a Google account... */
289
- type LinkedAccount = AccountComponents['schemas']['Identity'];
290
- /** One of the person's sessions at the brand, on some device. */
291
- type AccountSession = AccountComponents['schemas']['Session'];
292
- /** One of the person's API keys for the brand's APIs (AERO-1693): never the key itself. */
293
- type ApiKey = AccountComponents['schemas']['UserApiKey'];
294
- /** A key just made: `key` is the key itself, in this answer only. */
295
- type NewApiKey = AccountComponents['schemas']['NewUserApiKey'];
296
- /** What making an API key takes: the API's identifier, a name, its scopes, an expiry if any. */
297
- type ApiKeyInput = AccountComponents['schemas']['UserApiKeyCreate'];
298
- /** One of the brand's APIs the person may make keys for, with the scopes it defines. */
299
- type KeyableApi = AccountComponents['schemas']['KeyableApi'];
300
- //#endregion
301
572
  //#region src/mount.d.ts
302
573
  /** Calls the app's route mount: `request('session')`, `request('account/v1/profile', { method: 'PATCH', ... })`. */
303
574
  type SynqMountRequest = (path: string, init?: RequestInit) => Promise<Response>;
@@ -311,10 +582,90 @@ interface SynqClientPlugin<Id extends string = string, Actions = unknown> {
311
582
  actions(request: SynqMountRequest): Actions;
312
583
  }
313
584
  //#endregion
585
+ //#region src/redirect.d.ts
586
+ /** What left the page: a sign-in, a link, or signing the person in again. */
587
+ type RedirectKind = 'sign-in' | 'link' | 'reauthenticate';
588
+ /** The outcome of a sign-in or link that left the page, once the app is back on it. */
589
+ interface RedirectResult {
590
+ /**
591
+ * What left the page: `sign-in` (`useAuth().signIn`), `link` (`useLink`,
592
+ * `useAuth().link`) or `reauthenticate` (`useAuth().reauthenticate`).
593
+ */
594
+ readonly kind: RedirectKind;
595
+ /** The method it went for, if one was named. */
596
+ readonly method: SignInMethod | null;
597
+ /** What the app asked to keep (`keep`), as it gave it. */
598
+ readonly kept: unknown;
599
+ /** Why it didn't finish, as the app's callback said (`synq_error`), or null. */
600
+ readonly error: SynqError | null;
601
+ /** A link's: the ways in the person had before it. */
602
+ readonly before: readonly string[] | null;
603
+ }
604
+ //#endregion
314
605
  //#region src/store.d.ts
315
606
  /** `error` when the app couldn't be asked who is signed in, and nothing is known yet: the provider asks again, waiting longer each time. */
316
607
  type SessionStatus = 'loading' | 'signed-in' | 'signed-out' | 'error';
317
608
  //#endregion
609
+ //#region src/components/styling.d.ts
610
+ /** What every component takes for its look: Synq's classes, or `unstyled`. */
611
+ interface SynqStyleProps {
612
+ /** The same elements, roles and labels, with none of Synq's classes: the app styles them. */
613
+ readonly unstyled?: boolean;
614
+ /** Added to the component's outer element. */
615
+ readonly className?: string;
616
+ }
617
+ //#endregion
618
+ //#region src/components/Avatar.d.ts
619
+ /**
620
+ * Where a picture came from, as `sources` names it: `linked` is any linked
621
+ * account's. A token says `linked:<provider>` only with the
622
+ * `linked_accounts` scope, and `linked` without it; the profile always does.
623
+ */
624
+ type AvatarSource = 'upload' | 'url' | 'linked' | `linked:${string}`;
625
+ /**
626
+ * What shows for someone with no picture to show: Synq's `aura` (the
627
+ * default) or `monogram`, or the app's own, from the person's seed: an
628
+ * image's URL, or an element. Nothing (null) goes on to the brand's default.
629
+ */
630
+ type AvatarFallback = 'aura' | 'monogram' | ((seed: string, user: AvatarPerson) => ReactNode | string);
631
+ /** How an app draws avatars: once for the app (`SynqProvider avatar`), or per component. */
632
+ interface AvatarSettings {
633
+ /** What shows for someone with no picture to show: `aura` by default. */
634
+ readonly fallback?: AvatarFallback;
635
+ /**
636
+ * The pictures that show, by where they came from: all of them by
637
+ * default. `['upload']` shows only pictures people uploaded here, and the
638
+ * app's own art over their linked accounts' pictures.
639
+ */
640
+ readonly sources?: readonly AvatarSource[];
641
+ }
642
+ /** What `useAvatar()` answers: what an avatar is drawn from (`avatarOf` in @synqauth/js). */
643
+ type UseAvatar = AvatarInfo;
644
+ /**
645
+ * What the avatar of `user` is drawn from: the signed-in person by default,
646
+ * with their profile's picture once the account API was read, so a picture
647
+ * they just chose shows at once. `user` is anyone as Synq names them: an ID
648
+ * token's claims, or the management API's user (`id`, `username`).
649
+ */
650
+ export declare function useAvatar(user?: AvatarPerson | null): UseAvatar | null;
651
+ /** What `Avatar` takes: whose, how large, and how it falls back. */
652
+ interface AvatarProps extends SynqStyleProps, AvatarSettings {
653
+ /** Whose: the signed-in person by default, or anyone as Synq names them (`useAvatar`). */
654
+ readonly user?: AvatarPerson | null;
655
+ /**
656
+ * Its width and height in pixels: 32 by default. Styled, it sets
657
+ * `--synq-avatar-size`, which Synq's stylesheet sizes it by; your own CSS
658
+ * still wins.
659
+ */
660
+ readonly size?: number;
661
+ }
662
+ /**
663
+ * A person's avatar: their picture, or the app's default for them. Drawn
664
+ * for the eye alone (`alt=""`, `aria-hidden`): their name beside it says
665
+ * who it is. Nothing while there is nobody.
666
+ */
667
+ export declare function Avatar({ user, size, fallback, sources, unstyled, className }: AvatarProps): ReactNode;
668
+ //#endregion
318
669
  //#region src/provider.d.ts
319
670
  /** What `SynqProvider` takes: the server's session, the route mount, the words and the plugins. */
320
671
  interface SynqProviderProps {
@@ -339,13 +690,56 @@ interface SynqProviderProps {
339
690
  readonly navigate?: (url: string) => void;
340
691
  /** The components' words, in the app's language: any left out are English. */
341
692
  readonly messages?: Partial<SynqMessages>;
693
+ /**
694
+ * How the app draws avatars (AERO-2704), once for every component: its
695
+ * own default for someone with no picture (`fallback`, from their seed),
696
+ * and which pictures show (`sources`). A component's `avatar` overrides it.
697
+ */
698
+ readonly avatar?: AvatarSettings;
342
699
  readonly children?: ReactNode;
343
700
  }
344
701
  /** Synq for the components under it. Its route mount and `fetch` are read once, when it mounts. */
345
- export declare function SynqProvider({ initialSession, basePath, plugins, fetch: fetcher, navigate, messages, children }: SynqProviderProps): ReactNode;
702
+ export declare function SynqProvider({ initialSession, basePath, plugins, fetch: fetcher, navigate, messages, avatar, children }: SynqProviderProps): ReactNode;
346
703
  /** A browser plugin's actions, typed by the plugin, which must be one of the provider's `plugins`. */
347
704
  export declare function usePlugin<Actions>(plugin: SynqClientPlugin<string, Actions>): Actions;
348
705
  //#endregion
706
+ //#region src/popup.d.ts
707
+ /** How a provider's sign-in or link opens: `auto` is a popup where one fits, a redirect elsewhere. */
708
+ type SignInDisplay = 'auto' | 'popup' | 'redirect';
709
+ //#endregion
710
+ //#region src/handoff.d.ts
711
+ /** How a sign-in or a link opens, and the app's own state to have back afterwards. */
712
+ interface HandoffOptions {
713
+ /**
714
+ * `redirect` leaves the page and comes back to `returnTo`; `popup` opens
715
+ * one over it, and the page carries on once the person finishes there;
716
+ * `auto` is a popup where one fits (a fine pointer, from 640px wide, a
717
+ * real browser), else a redirect. A popup the browser blocks becomes a
718
+ * redirect by itself.
719
+ */
720
+ readonly display?: SignInDisplay;
721
+ /**
722
+ * Small state of the app's own (at most 4 KB of JSON), kept in this tab
723
+ * across a redirect and handed back by `useRedirectResult().kept`, or by
724
+ * the call's own answer after a popup: the screen the person was on, say.
725
+ */
726
+ readonly keep?: unknown;
727
+ }
728
+ /** A sign-in that finished on this page, in a popup. */
729
+ interface SignInDone {
730
+ /** The method it went straight to, if one was named. */
731
+ readonly method: SignInMethod | null;
732
+ /** What the app asked to keep (`keep`), as it gave it. */
733
+ readonly kept: unknown;
734
+ }
735
+ /** What `useAuth().reauthenticate()` takes: the way to sign in again with, and how it opens. */
736
+ interface ReauthenticateOptions extends HandoffOptions {
737
+ /** The way to sign in again with (`google`, `x`…): the brand's page shows them all without it. */
738
+ readonly method?: SignInMethod;
739
+ /** Where a redirect comes back to: this page by default. */
740
+ readonly returnTo?: string;
741
+ }
742
+ //#endregion
349
743
  //#region src/hooks.d.ts
350
744
  /** What `useSession()` answers: who is signed in, and whether the app said yet. */
351
745
  interface UseSession {
@@ -363,7 +757,7 @@ interface UseSession {
363
757
  }
364
758
  /** Who is signed in. It reads the session alone, and asks the account API nothing. */
365
759
  export declare function useSession(): UseSession;
366
- /** What the account hooks (`useWallets`, `useLinkedAccounts`, `useAccountSessions`) answer: the data, the error, and whether it loaded. */
760
+ /** What the account hooks (`useWallets`, `useLinkedAccounts`, `useAccountSessions`, `usePasskeys`) answer: the data, the error, and whether it loaded. */
367
761
  interface UseAccountData<T> {
368
762
  readonly data: T | null;
369
763
  readonly error: SynqError | null;
@@ -382,6 +776,60 @@ export declare function useAccountSessions(): UseAccountData<readonly AccountSes
382
776
  export declare function useApiKeys(): UseAccountData<readonly ApiKey[]>;
383
777
  /** The brand's APIs that let the person make keys, with the scopes each defines. */
384
778
  export declare function useKeyableApis(): UseAccountData<readonly KeyableApi[]>;
779
+ /** The person's passkeys. */
780
+ export declare function usePasskeys(): UseAccountData<readonly Passkey[]>;
781
+ /**
782
+ * The ways to sign in the person can add, in the brand's order (AERO-2080):
783
+ * each a `method` for `useAuth().link`, with its name. A passkey is listed
784
+ * only when offered; link('passkey') uses the hosted add-passkey intent.
785
+ */
786
+ export declare function useLinkableMethods(): UseAccountData<readonly LinkableMethod[]>;
787
+ /** What `useSignInMethods()` answers: the app's methods, in the brand's order, and whether the brand shows "Last used". */
788
+ interface UseSignInMethods extends UseAccountData<readonly SignInMethodInfo[]> {
789
+ /** Whether the brand marks the way this browser last signed in: off where people share devices. */
790
+ readonly showLastUsed: boolean;
791
+ }
792
+ /**
793
+ * The ways the app's sign-in offers, in the brand's order (AERO-2690), for a
794
+ * sign-in panel of the app's own: each one's id (what `signIn({ method })`
795
+ * takes), kind, name and artwork, whether it finishes on the app's own
796
+ * screens (`in_place`, through `useSignIn()`) and whether it signs in at
797
+ * another site (`popup`). Read from the route mount's `sign-in/methods`
798
+ * once for every component, signed in or out; a method the brand turns off
799
+ * is gone within a minute.
800
+ */
801
+ export declare function useSignInMethods(): UseSignInMethods;
802
+ /**
803
+ * The way this browser last signed in to the app, for a "Last used" mark on
804
+ * it (AERO-2691), or null: the method and, for a wallet, the wallet app's
805
+ * name, never an address or who it was. The app's server keeps it on the
806
+ * app's own host from its sign-in claims (`sign_in_claims` on the app), so
807
+ * it survives signing out and the issuer's cookies. Null where the brand
808
+ * turned "Last used" off, or no longer offers that method here, once
809
+ * `useSignInMethods()` says so.
810
+ */
811
+ export declare function useLastUsedMethod(): LastUsedMethod | null;
812
+ /** What `useSignInEvent()` answers: the sign-in, what the app kept for it, and `consume` to say the page heard of it. */
813
+ type UseSignInEvent = SignInEvent & {
814
+ /**
815
+ * What the app asked to keep (`keep`) when it started this sign-in in a
816
+ * popup or by a redirect (AERO-2692), as it gave it: the screen to come
817
+ * back to, say. Undefined when it kept nothing.
818
+ */
819
+ readonly kept: unknown;
820
+ /** The page heard of it: it's gone, here and on the app's server, so a reload doesn't tell it again. */
821
+ readonly consume: () => void;
822
+ };
823
+ /**
824
+ * The sign-in that just finished, once (AERO-2691): whether it made the
825
+ * account (`isNewUser`, for a first-profile screen or a welcome), how, with
826
+ * which wallet, and what the app kept for it (`kept`). It stays until
827
+ * `consume()`, or five minutes. Needs the app's sign-in claims on
828
+ * (`sign_in_claims`); null otherwise, and when there is none. Null in the
829
+ * server's render and the browser's first too: it is told just after, once
830
+ * the provider has read what a redirect kept for it.
831
+ */
832
+ export declare function useSignInEvent(): UseSignInEvent | null;
385
833
  /** What `useUser()` answers: the person's claims, profile and metadata, and `update` for the profile. */
386
834
  interface UseUser {
387
835
  readonly isLoaded: boolean;
@@ -396,37 +844,122 @@ interface UseUser {
396
844
  readonly error: SynqError | null;
397
845
  /** Changes their profile: only the fields given, `null` clearing one. */
398
846
  readonly update: (changes: ProfileUpdate) => Promise<SynqResult<Profile>>;
847
+ /**
848
+ * Uploads a picture and makes it theirs (AERO-2700): a PNG, JPEG, WebP or
849
+ * GIF, which Synq keeps as a WebP square. A large one is made smaller in
850
+ * the browser first, to at most 1024 px, so a phone’s photo fits the 5 MiB
851
+ * Synq takes. It goes through the app's route mount
852
+ * (`<base>/account/picture`), whose server sends it on to Synq's storage.
853
+ * Offer it only where the profile's `picture_choices` is true.
854
+ */
855
+ readonly uploadPicture: (picture: Blob) => Promise<SynqResult<Profile>>;
856
+ /**
857
+ * Makes a linked account's picture theirs, by its id (`useLinkedAccounts`,
858
+ * `has_picture`): Synq copies it into the brand's files.
859
+ */
860
+ readonly setPictureFrom: (identity: string) => Promise<SynqResult<Profile>>;
861
+ /** Removes their picture: the app's default shows. */
862
+ readonly removePicture: () => Promise<SynqResult<Profile>>;
399
863
  /** Reads the profile and metadata again. */
400
864
  readonly reload: () => Promise<void>;
401
865
  }
402
866
  /** The signed-in person: their ID token's claims at once, then their profile and metadata. */
403
867
  export declare function useUser(): UseUser;
404
- /** What `useAuth()` answers: `signIn`, `signOut`, `link`, and the account's wallets, ways to sign in and sessions. */
868
+ /** What `useAuth()` answers: `signIn`, `signOut`, `link`, and the account's wallets, ways to sign in, sessions, passkeys and deletion. */
405
869
  interface UseAuth {
406
870
  readonly isLoaded: boolean;
407
871
  readonly isSignedIn: boolean;
408
872
  readonly userId: string | null;
409
- /** Sends the browser to sign in, coming back to `returnTo` (this page by default). */
410
- readonly signIn: (options?: SignInOptions) => void;
873
+ /**
874
+ * Signs the person in: by default sends the browser to sign in, coming
875
+ * back to `returnTo` (this page by default), and answers `null` as the
876
+ * page leaves. With `display: 'popup'` (or `'auto'`, a popup where one
877
+ * fits) it opens one over the page, inside the tap, and answers once the
878
+ * session is the new one's, or the error it ended with (`cancelled` when
879
+ * the person closed it). A blocked popup becomes a redirect by itself.
880
+ */
881
+ readonly signIn: (options?: SignInOptions & HandoffOptions) => Promise<SynqResult<SignInDone | null>>;
411
882
  /** Where `signIn` sends the browser: the route mount's `sign-in`. */
412
883
  readonly signInUrl: (options?: SignInOptions) => string;
413
- /** Ends the session in the app and at the brand, then goes where the brand sends the browser. */
414
- readonly signOut: () => Promise<SynqResult<null>>;
884
+ /**
885
+ * Ends the session in the app and at the brand, then goes where the brand
886
+ * sends the browser. With `stay` (AERO-2684), the app's server ends the
887
+ * brand's session itself and the page stays where it is: show the person
888
+ * they're signed out. A failure leaves them signed in, to try again.
889
+ */
890
+ readonly signOut: (options?: SignOutOptions) => Promise<SynqResult<null>>;
415
891
  /**
416
892
  * Adds a way to sign in to the person's account: Synq asks them to sign in
417
- * again, then for `method`, and the browser comes back to `returnTo` (this
418
- * page by default). Answers only when it can't start: the method isn't
419
- * offered, or they have as many ways in as one may.
893
+ * again (unless they signed in within the brand's `link_reauth_window`),
894
+ * then for `method`, and the browser comes back to `returnTo` (this page
895
+ * by default), answering `null` as it leaves, or the problem when it can't
896
+ * start: the method isn't offered, or they have as many ways in as one
897
+ * may. With `display: 'popup'` or `'auto'` it runs in a popup and answers
898
+ * the way in that joined; `cancelled` when none did (closed, "Not now",
899
+ * or one they already had), and `account_switched` when they signed in to
900
+ * the account that has it instead. `useLink(method)` keeps its state for
901
+ * a button.
420
902
  */
421
903
  readonly link: (method: SignInMethod, options?: {
422
904
  returnTo?: string;
423
- }) => Promise<SynqResult<null>>;
905
+ } & HandoffOptions) => Promise<SynqResult<LinkedAccount | null>>;
906
+ /**
907
+ * Signs the person signed in now in again, for a change that wants a
908
+ * recent sign-in (`fresh_sign_in_required`), through the brand's page:
909
+ * with `method` (`google`, `x`…), straight to it. In a popup where one fits
910
+ * (`display: 'auto'`, the default), answering once the session is the
911
+ * fresh one, so the change can be tried again at once; else by a
912
+ * redirect that comes back to `returnTo` (this page), answering `null` as
913
+ * the page leaves. Call it from a tap's own handler, before anything is
914
+ * awaited: a popup opened later may be blocked, and it runs as a redirect.
915
+ * Only the same person counts: anyone else who signs in there is signed
916
+ * out again at once, at the brand too, the session stays as it was, and
917
+ * it answers `other_account`. `cancelled` when they closed the popup;
918
+ * `not_signed_in` with nobody signed in, or once they were signed out
919
+ * before it finished. With a wallet on the profile,
920
+ * `useWalletSignIn().reauthenticate()` from `@synqauth/solana/react` does
921
+ * it with one signature and no page.
922
+ */
923
+ readonly reauthenticate: (options?: ReauthenticateOptions) => Promise<SynqResult<SignInDone | null>>;
424
924
  readonly wallets: {
425
925
  readonly list: () => Promise<SynqResult<readonly Wallet[]>>;
426
926
  /** Names a wallet; `null` takes its name away. */
427
927
  readonly rename: (id: string, label: string | null) => Promise<SynqResult<Wallet>>;
428
928
  /** Makes it their primary wallet, in place of the one that was. */
429
929
  readonly setPrimary: (id: string) => Promise<SynqResult<Wallet>>;
930
+ /** Stops a wallet signing them in; the last way in can't go (`last_sign_in_method`). */
931
+ readonly remove: (id: string) => Promise<SynqResult<null>>;
932
+ /**
933
+ * The issuer and app the page asks for a wallet's sign-in request with
934
+ * (`POST <issuer>/siws/nonce`), from the route mount's `sign-in/wallet`:
935
+ * what `@synqauth/solana/react` signs with. Read once, then kept.
936
+ */
937
+ readonly requestTarget: () => Promise<SynqResult<WalletRequestTarget>>;
938
+ /**
939
+ * Signs in with a wallet's signature over that request (`siws`, as
940
+ * `@synqauth/solana`'s `encodeSiws` writes it), in place: the route
941
+ * mount trades it for the session with no page (AERO-2681), and the
942
+ * session here follows. `reauthenticate` signs the person signed in now
943
+ * in again, for a route that wants a recent sign-in: Synq signs in only
944
+ * them, and refuses another account's wallet, or one nobody has, before
945
+ * anything changes (`other_account`). A signature that didn't
946
+ * count is `wallet_failed` or `wallet_expired` (ask for another
947
+ * request); what only the hosted page can do is `redirect_to_web`.
948
+ */
949
+ readonly signIn: (siws: string, options?: {
950
+ reauthenticate?: boolean;
951
+ returnTo?: string;
952
+ }) => Promise<SynqResult<WalletSignedIn>>;
953
+ /**
954
+ * Adds the wallet that signed `siws` to their account (AERO-2683): one
955
+ * signature, no hosted page. It needs a recent sign-in
956
+ * (`fresh_sign_in_required`). A wallet another account has is
957
+ * `wallet_in_use`: `walletConflict(error)` from `@synqauth/js` says
958
+ * whether it can move here, and `move` moves it.
959
+ */
960
+ readonly add: (siws: string) => Promise<SynqResult<Wallet>>;
961
+ /** Moves a wallet here with the `move_ticket` of a `wallet_in_use` conflict. */
962
+ readonly move: (ticket: string) => Promise<SynqResult<Wallet>>;
430
963
  };
431
964
  /** The ways they sign in. */
432
965
  readonly accounts: {
@@ -451,12 +984,91 @@ interface UseAuth {
451
984
  /** Revokes a key at once. */
452
985
  readonly revoke: (id: string) => Promise<SynqResult<null>>;
453
986
  };
987
+ readonly passkeys: {
988
+ readonly list: () => Promise<SynqResult<readonly Passkey[]>>;
989
+ /** Sends the browser to Synq, which asks them to sign in again and then makes a passkey, coming back to `returnTo` (this page by default). */
990
+ readonly add: (options?: {
991
+ returnTo?: string;
992
+ }) => void;
993
+ /** Removes a passkey; the last way in can't go (`last_sign_in_method`). */
994
+ readonly remove: (id: string) => Promise<SynqResult<null>>;
995
+ };
996
+ /** Deleting the account: after a grace period, which the profile's `deletion_scheduled_for` ends. */
997
+ readonly deletion: {
998
+ /** Asks for the account to be deleted. Someone who owns an org can't (`conflict`) until they hand it over. */
999
+ readonly schedule: () => Promise<SynqResult<Profile>>;
1000
+ /** Takes the request back while the grace period lasts. */
1001
+ readonly cancel: () => Promise<SynqResult<Profile>>;
1002
+ };
454
1003
  /** Each browser plugin's actions, by its id (`usePlugin` types them). */
455
1004
  readonly plugins: Readonly<Record<string, unknown>>;
456
1005
  }
1006
+ /** Where the page asks for a wallet's sign-in request: the brand's issuer, as the app's client. */
1007
+ interface WalletRequestTarget {
1008
+ readonly issuer: string;
1009
+ readonly clientId: string;
1010
+ }
1011
+ /** A wallet's sign-in in place: who is signed in now, and where the app meant to go. */
1012
+ interface WalletSignedIn {
1013
+ readonly user: SynqUser | null;
1014
+ /** Where the sign-in was to land: the app decides whether to go there. */
1015
+ readonly returnTo: string;
1016
+ }
457
1017
  /** Everything the signed-in person can do. */
458
1018
  export declare function useAuth(): UseAuth;
459
1019
  //#endregion
1020
+ //#region src/link.d.ts
1021
+ /** Where `useLink` is: nothing yet, under way, linked, or failed. */
1022
+ type LinkStatus = 'idle' | 'pending' | 'linked' | 'error';
1023
+ /** What `useLink(method)` answers. */
1024
+ interface UseLink {
1025
+ /**
1026
+ * Adds `method`'s way in: in a popup where one fits (`display: 'auto'`, the
1027
+ * default), else by a redirect that comes back to `returnTo` (this page).
1028
+ * Call it from the tap itself. Answers the way in that joined, `null` as
1029
+ * the page leaves for a redirect, or the error it ended with: `cancelled`
1030
+ * when the person closed the popup, went on without adding it or proved
1031
+ * one they already had, and `account_switched` when they signed in to the
1032
+ * account that has it instead.
1033
+ */
1034
+ readonly link: (options?: {
1035
+ returnTo?: string;
1036
+ } & HandoffOptions) => Promise<SynqResult<LinkedAccount | null>>;
1037
+ /**
1038
+ * `linked` while the person has a way in of this kind, `pending` while one
1039
+ * is being added (a popup open, or a redirect on its way back), `error`
1040
+ * when the last try failed, else `idle`.
1041
+ */
1042
+ readonly status: LinkStatus;
1043
+ /** Their way in of this kind: the one just added, else their newest. */
1044
+ readonly identity: LinkedAccount | null;
1045
+ /**
1046
+ * Why the last try failed; a closed popup is no failure. A redirect's
1047
+ * error shows until the next try, or until one of this kind joins.
1048
+ */
1049
+ readonly problem: SynqError | null;
1050
+ /** Gives up on a popup still open, closing it where the browser lets the page. */
1051
+ readonly cancel: () => void;
1052
+ }
1053
+ /**
1054
+ * Linking one way in from a button of the app's own (AERO-2695): `link()`
1055
+ * from the tap, and the `status` and `identity` to show. A redirect's return
1056
+ * shows here too: the hook reads the way in that joined, or the error the
1057
+ * app's callback came back with.
1058
+ */
1059
+ export declare function useLink(method: SignInMethod): UseLink;
1060
+ /** What `useRedirectResult()` answers: the result, and `consume()` to forget it once handled. */
1061
+ type UseRedirectResult = (RedirectResult & {
1062
+ readonly consume: () => void;
1063
+ }) | null;
1064
+ /**
1065
+ * A sign-in or link that left the page for a redirect (AERO-2692), once the
1066
+ * app is back: what it was, the state the app kept for it (`keep`), and the
1067
+ * error the app's callback came back with, if any. Null until there is one,
1068
+ * and after `consume()`. Who is signed in now is `useSession()`'s.
1069
+ */
1070
+ export declare function useRedirectResult(): UseRedirectResult;
1071
+ //#endregion
460
1072
  //#region src/components/SignedIn.d.ts
461
1073
  /** Shows its children only while someone is signed in. It renders no element of its own. */
462
1074
  export declare function SignedIn({ children }: {
@@ -469,15 +1081,6 @@ export declare function SignedOut({ children }: {
469
1081
  readonly children?: ReactNode;
470
1082
  }): ReactNode;
471
1083
  //#endregion
472
- //#region src/components/styling.d.ts
473
- /** What every component takes for its look: Synq's classes, or `unstyled`. */
474
- interface SynqStyleProps {
475
- /** The same elements, roles and labels, with none of Synq's classes: the app styles them. */
476
- readonly unstyled?: boolean;
477
- /** Added to the component's outer element. */
478
- readonly className?: string;
479
- }
480
- //#endregion
481
1084
  //#region src/components/SignInButton.d.ts
482
1085
  /** What `SignInButton` takes: the sign-in's options, its look and its words. */
483
1086
  interface SignInButtonProps extends SignInOptions, SynqStyleProps {
@@ -538,33 +1141,49 @@ interface OneTimeCodeInputProps extends Omit<InputHTMLAttributes<HTMLInputElemen
538
1141
  export declare function OneTimeCodeInput({ label, unstyled, className, ...input }: OneTimeCodeInputProps): ReactNode;
539
1142
  //#endregion
540
1143
  //#region src/components/UserButton.d.ts
541
- /** What `UserButton` takes: where its account link goes, and its look. */
1144
+ /** What `UserButton` takes: where its account link goes, how it draws the avatar, and its look. */
542
1145
  interface UserButtonProps extends SynqStyleProps {
543
1146
  /** The app's page with `<AccountManager>`, linked as "Manage account". */
544
1147
  readonly accountHref?: string;
1148
+ /** How its avatar falls back and which pictures show: the provider's `avatar` by default. */
1149
+ readonly avatar?: AvatarSettings;
545
1150
  }
546
1151
  /**
547
1152
  * The signed-in person's picture and name, showing a panel to manage their
548
1153
  * account or sign out: a disclosure (a button that shows and hides it), so
549
1154
  * its link and button are reached with Tab as anywhere else. Escape or a
550
- * click elsewhere hides it. Nothing while nobody is signed in.
1155
+ * click elsewhere hides it. Its menu opens under the trigger and stays on
1156
+ * screen: on a narrow window it moves sideways to keep 8px clear of each edge
1157
+ * (AERO-2734). Nothing while nobody is signed in.
551
1158
  */
552
- export declare function UserButton({ unstyled, className, accountHref }: UserButtonProps): ReactNode;
1159
+ export declare function UserButton({ unstyled, className, accountHref, avatar }: UserButtonProps): ReactNode;
553
1160
  //#endregion
554
1161
  //#region src/components/AccountManager.d.ts
555
- /** One part of `AccountManager`: the profile, the ways to sign in, the sessions, or the API keys. */
556
- type AccountSection = 'profile' | 'accounts' | 'sessions' | 'apiKeys';
557
- /** What `AccountManager` takes: which sections it shows, and its look. */
1162
+ /** One part of `AccountManager`: the profile, the ways to sign in, the passkeys, the sessions, the API keys, or deleting the account. */
1163
+ type AccountSection = 'profile' | 'accounts' | 'passkeys' | 'sessions' | 'apiKeys' | 'deletion';
1164
+ /** What `AccountManager` takes: which sections it shows, the ways to sign in it offers to add, and its look. */
558
1165
  interface AccountManagerProps extends SynqStyleProps {
559
1166
  /**
560
- * The parts it shows, in this order: all four by default. The API keys
1167
+ * The parts it shows, in this order: all six by default. The API keys
561
1168
  * show only where one of the brand's APIs lets people make keys, or the
562
1169
  * person has some.
563
1170
  */
564
1171
  readonly sections?: readonly AccountSection[];
1172
+ /**
1173
+ * Limits the ways to sign in it offers to add to these. By default it
1174
+ * offers every way the brand turns on for adding, as the account API lists
1175
+ * them (`useLinkableMethods`), and never one the brand doesn't. A way they
1176
+ * already have is left out, except a wallet or a passkey, of which they may
1177
+ * have several. A passkey is offered in the passkeys section when that
1178
+ * shows, else among the ways to add; either button makes one on Synq's
1179
+ * add-passkey page, and only where the brand offers passkeys.
1180
+ */
1181
+ readonly linkMethods?: readonly SignInMethod[];
1182
+ /** How the profile's avatar falls back and which pictures show: the provider's `avatar` by default. */
1183
+ readonly avatar?: AvatarSettings;
565
1184
  }
566
1185
  /** Manage the signed-in person's account. Nothing while nobody is signed in. */
567
- export declare function AccountManager({ unstyled, className, sections }: AccountManagerProps): ReactNode;
1186
+ export declare function AccountManager({ unstyled, className, sections, linkMethods, avatar }: AccountManagerProps): ReactNode;
568
1187
  //#endregion
569
1188
  //#region src/components/WalletManager.d.ts
570
1189
  /** What `WalletManager` takes: its look. */
@@ -572,4 +1191,428 @@ type WalletManagerProps = SynqStyleProps;
572
1191
  /** Manage the signed-in person's wallets. Nothing while nobody is signed in. */
573
1192
  export declare function WalletManager({ unstyled, className }: WalletManagerProps): ReactNode;
574
1193
  //#endregion
575
- export type { AccountManagerProps, AccountSection, AccountSession, ApiKey, ApiKeyInput, KeyableApi, LinkedAccount, NewApiKey, OneTimeCodeInputProps, Profile, ProfileUpdate, SessionStatus, SignInButtonProps, SignInFormProps, SignInHandover, SignInMethod, SignInMethods, SignInOptions, SignInPartProps, SignInProblem, SignInStep, SynqClientPlugin, SynqMessages, SynqMountRequest, SynqProviderProps, SynqResult, SynqSessionInfo, SynqStyleProps, SynqUser, UseAccountData, UseAuth, UseSession, UseSignIn, UseSignInOptions, UseUser, UserButtonProps, UserMetadata, Wallet, WalletManagerProps };
1194
+ //#region src/account-panel.d.ts
1195
+ /** What the panel shows: the ways to sign in, one under way, a first sign-in's onboarding, or the profile. */
1196
+ type AccountPanelView = 'sign-in' | 'progress' | 'onboarding' | 'profile';
1197
+ /** How the panel sits: a popover anchored under its trigger from 640px wide, a bottom sheet below. */
1198
+ type AccountPanelLayout = 'popover' | 'sheet';
1199
+ /** A sign-in the panel started and waits on. */
1200
+ interface AccountPanelPending {
1201
+ /** The method: `google`, `wallet`… */
1202
+ readonly method: string;
1203
+ /** Its name, as people know it: `Google`. */
1204
+ readonly name: string;
1205
+ /**
1206
+ * How it runs: in a `popup` over the page, a `redirect` that leaves it, or
1207
+ * `in-place` (a wallet's signature, a passkey's prompt) with no page at all.
1208
+ */
1209
+ readonly display: 'popup' | 'redirect' | 'in-place';
1210
+ /** For a wallet's signature: the wallet app it waits on, when known. */
1211
+ readonly walletApp: string | null;
1212
+ }
1213
+ /** What `useAccountPanel()` takes. */
1214
+ interface UseAccountPanelOptions {
1215
+ /**
1216
+ * The query parameter that mirrors the panel being open
1217
+ * (`?<urlParam>=account`): `panel` by default; null mirrors nothing. With
1218
+ * it, Back closes the panel, a reload opens it again, and a provider's
1219
+ * redirect comes back into it.
1220
+ */
1221
+ readonly urlParam?: string | null;
1222
+ /**
1223
+ * Small state of the app's own to have back after a sign-in that leaves
1224
+ * the page (at most 4 KB of JSON; `useRedirectResult().kept`): a value, or
1225
+ * a function the tap calls for it.
1226
+ */
1227
+ readonly keep?: unknown;
1228
+ /** Called once the person signed out from the panel, which stays on the page: the app's toast. */
1229
+ readonly onSignedOut?: () => void;
1230
+ /**
1231
+ * Whether it moves the dialog as it opens and closes, with Synq's own
1232
+ * motion (WAAPI on `panelProps`' element): no by default, so a dialog of
1233
+ * the app's own moves as the app makes it. `<AccountPanel>` says yes.
1234
+ */
1235
+ readonly animate?: boolean;
1236
+ }
1237
+ /** The props of the button that opens the panel. */
1238
+ interface AccountPanelTriggerProps {
1239
+ readonly ref: RefObject<HTMLButtonElement | null>;
1240
+ readonly type: 'button';
1241
+ readonly 'aria-haspopup': 'dialog';
1242
+ readonly 'aria-expanded': boolean;
1243
+ readonly 'aria-controls': string;
1244
+ readonly onClick: () => void;
1245
+ }
1246
+ /** The props of the panel itself: a dialog that keeps the focus inside. */
1247
+ interface AccountPanelDialogProps {
1248
+ readonly ref: RefObject<HTMLDivElement | null>;
1249
+ readonly id: string;
1250
+ readonly role: 'dialog';
1251
+ readonly 'aria-modal': true;
1252
+ readonly 'aria-labelledby': string;
1253
+ readonly tabIndex: -1;
1254
+ readonly 'data-layout': AccountPanelLayout;
1255
+ readonly 'data-view': AccountPanelView;
1256
+ /** Where a popover sits, as `--synq-panel-top`, `--synq-panel-right` and `--synq-panel-max-height`. */
1257
+ readonly style: CSSProperties;
1258
+ readonly onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
1259
+ /**
1260
+ * Marks a press as inside the panel by React's tree, not the page's: a
1261
+ * popover, select or tooltip a card portals into `<body>` is still inside,
1262
+ * and pressing it leaves the panel open.
1263
+ */
1264
+ readonly onPointerDownCapture: (event: PointerEvent<HTMLElement>) => void;
1265
+ /** A link inside closes the panel as it navigates, without going Back first. */
1266
+ readonly onClickCapture: (event: MouseEvent<HTMLElement>) => void;
1267
+ }
1268
+ /** The props of a sheet's grabber, which a drag down closes. */
1269
+ interface AccountPanelGrabberProps {
1270
+ readonly 'aria-hidden': true;
1271
+ readonly onPointerDown: (event: PointerEvent<HTMLElement>) => void;
1272
+ readonly onPointerMove: (event: PointerEvent<HTMLElement>) => void;
1273
+ readonly onPointerUp: () => void;
1274
+ readonly onPointerCancel: () => void;
1275
+ }
1276
+ /** What `useAccountPanel()` answers. */
1277
+ interface UseAccountPanel {
1278
+ readonly open: boolean;
1279
+ /** Opens or closes it; closing gives the focus back to the trigger and calls off a popup under way. */
1280
+ readonly setOpen: (open: boolean) => void;
1281
+ /**
1282
+ * Closes it. With `navigating`, before the page goes elsewhere (a
1283
+ * `router.push` of the app's): it takes `?panel=account` out of the
1284
+ * address in place instead of going Back, so the navigation lands where
1285
+ * it meant to. A link inside the panel does this by itself.
1286
+ */
1287
+ readonly close: (options?: {
1288
+ readonly navigating?: boolean;
1289
+ }) => void;
1290
+ /** Whether to render the panel: open, or still closing. */
1291
+ readonly present: boolean;
1292
+ readonly view: AccountPanelView;
1293
+ readonly layout: AccountPanelLayout;
1294
+ /** The sign-in under way, started from the panel. */
1295
+ readonly pending: AccountPanelPending | null;
1296
+ /**
1297
+ * Why the last sign-in from the panel failed, in the page or by a redirect
1298
+ * that came back: a closed popup, a declined signature or a way back the
1299
+ * person took (`access_denied`) is none.
1300
+ */
1301
+ readonly problem: SynqError | null;
1302
+ /** The method `problem` is for (`google`, `wallet`…), to show it there. */
1303
+ readonly problemMethod: string | null;
1304
+ /**
1305
+ * The session ended while the panel was open, and not by its own Sign
1306
+ * out (it expired, say): the panel is back on the ways in, and says so.
1307
+ */
1308
+ readonly signedOut: boolean;
1309
+ /** Calls off the sign-in under way: closes its popup, and stops waiting on a wallet. */
1310
+ readonly cancel: () => void;
1311
+ /**
1312
+ * Signs in with `method` from a tap: a provider in a popup where one fits
1313
+ * (else a redirect that comes back into the panel), a wallet in place with
1314
+ * the wallet layer's signature (`walletApp` names one), anything else at
1315
+ * the hosted page. Call it from the tap itself, so a popup can open.
1316
+ */
1317
+ readonly signIn: (method: string, options?: {
1318
+ readonly walletApp?: string;
1319
+ }) => Promise<void>;
1320
+ /**
1321
+ * The sign-in the panel is showing: a new account's (`isNewUser`, the
1322
+ * onboarding) or a returning person's (the greeting), until it closes.
1323
+ */
1324
+ readonly event: SignInEvent | null;
1325
+ /** Shows the profile in place of a returning sign-in's greeting. */
1326
+ readonly showProfile: () => void;
1327
+ /** Signs out and stays on the page (`signOut({ stay: true })`), closing the panel and calling `onSignedOut`. */
1328
+ readonly signOut: () => Promise<SynqResult<null>>;
1329
+ /**
1330
+ * Keeps the panel open on a click outside while `busy`, as while a
1331
+ * wallet's prompt in the page waits: a card inside calls it through
1332
+ * `useAccountPanelSection()`.
1333
+ */
1334
+ readonly hold: (busy: boolean) => () => void;
1335
+ /** The id of the panel's title, which labels the dialog. */
1336
+ readonly titleId: string;
1337
+ readonly triggerProps: AccountPanelTriggerProps;
1338
+ readonly panelProps: AccountPanelDialogProps;
1339
+ readonly grabberProps: AccountPanelGrabberProps;
1340
+ }
1341
+ /**
1342
+ * The account panel's state and behavior, for a panel of the app's own:
1343
+ * spread `triggerProps` on its button and `panelProps` on its dialog.
1344
+ */
1345
+ export declare function useAccountPanel(options?: UseAccountPanelOptions): UseAccountPanel;
1346
+ //#endregion
1347
+ //#region src/components/account-panel-sections.d.ts
1348
+ /** What a card of the app's own inside `<AccountPanel>` reads of it: `useAccountPanelSection()`. */
1349
+ interface AccountPanelSection {
1350
+ /** The onboarding of a first sign-in, which keeps to the essentials, rather than the profile. */
1351
+ readonly firstRun: boolean;
1352
+ readonly view: AccountPanelView;
1353
+ /**
1354
+ * Closes the panel. With `navigating`, before a `router.push` of the
1355
+ * card's: it goes from the address in place rather than Back, so the
1356
+ * navigation lands where it meant to. A link inside does this by itself.
1357
+ */
1358
+ readonly close: (options?: {
1359
+ readonly navigating?: boolean;
1360
+ }) => void;
1361
+ /**
1362
+ * Keeps the panel open on a click outside while `busy` (a prompt of the
1363
+ * card's in the page): `useEffect(() => hold(busy), [hold, busy])`.
1364
+ */
1365
+ readonly hold: (busy: boolean) => () => void;
1366
+ }
1367
+ /** The panel a card of the app's own is in: its view, and closing or holding it. */
1368
+ export declare function useAccountPanelSection(): AccountPanelSection;
1369
+ /** What `AccountPanel.Card` takes. */
1370
+ interface AccountPanelCardProps {
1371
+ /** Its heading, which names the card for screen readers too. */
1372
+ readonly title: ReactNode;
1373
+ /** Beside the heading: a count, say. */
1374
+ readonly badge?: ReactNode;
1375
+ readonly children?: ReactNode;
1376
+ }
1377
+ /** A card of the app's own, drawn as the panel's sections are: a heading, then what the app puts in it. */
1378
+ declare function Card({ title, badge, children }: AccountPanelCardProps): ReactNode;
1379
+ /** What `AccountPanel.Row` takes. */
1380
+ interface AccountPanelRowProps {
1381
+ /**
1382
+ * The provider the row stands for, when it's one: inside
1383
+ * `AccountPanel.Linked`, the profile then lists that provider's linked
1384
+ * account here alone, not again as a read-only row (an app's own
1385
+ * "Event reminders" row for Telegram, say).
1386
+ */
1387
+ readonly provider?: SignInMethod;
1388
+ /** On its tile at the start: an icon, 20px. */
1389
+ readonly icon?: ReactNode;
1390
+ readonly title: ReactNode;
1391
+ /** The line under its title. */
1392
+ readonly line?: ReactNode;
1393
+ /** At its end: a button of the app's (a 44px target). */
1394
+ readonly action?: ReactNode;
1395
+ }
1396
+ /**
1397
+ * A row of the app's own, drawn as a `LinkRow` is: an `<li>`, for inside
1398
+ * `AccountPanel.Linked` or a `<ul>` of the app's own card.
1399
+ */
1400
+ declare function Row({ provider, icon, title, line, action }: AccountPanelRowProps): ReactNode;
1401
+ /** What `AccountPanel.Username` takes. */
1402
+ interface AccountPanelUsernameProps {
1403
+ /** The card's heading: "Username" by default. */
1404
+ readonly title?: ReactNode;
1405
+ /**
1406
+ * A line under the field while there's nothing to say about the name
1407
+ * typed: what the username is for, in the app's words ("Your name on
1408
+ * Acme Guild. Change it any time."). None by default.
1409
+ */
1410
+ readonly description?: ReactNode;
1411
+ }
1412
+ /**
1413
+ * The username card of a first sign-in's onboarding: free names checked as
1414
+ * they type, three suggestions for a taken one, saved with Save or Enter.
1415
+ * In the profile, "Edit" beside their name changes it.
1416
+ */
1417
+ declare function Username({ title, description }: AccountPanelUsernameProps): ReactNode;
1418
+ /** What `AccountPanel.Wallets` takes. */
1419
+ interface AccountPanelWalletsProps {
1420
+ /**
1421
+ * More to show under a wallet, from the app's own data (its rank, its
1422
+ * balance): `firstRun` is a first sign-in's onboarding, which keeps to
1423
+ * the essentials.
1424
+ */
1425
+ readonly extra?: (wallet: Wallet, info: {
1426
+ readonly firstRun: boolean;
1427
+ }) => ReactNode;
1428
+ /** The card's heading: "Wallets" by default. */
1429
+ readonly title?: ReactNode;
1430
+ }
1431
+ /**
1432
+ * The profile's wallets: which one is connected, which signed in, which is
1433
+ * primary; connecting one with no signature, adding one with one, and in
1434
+ * the profile, a menu to make one primary, rename, copy or remove it.
1435
+ * Through the wallet layer (`<SynqWalletProvider>`) where there is one,
1436
+ * else adding one goes through the hosted page.
1437
+ */
1438
+ declare function Wallets({ extra, title }: AccountPanelWalletsProps): ReactNode;
1439
+ /** What `AccountPanel.Linked` takes. */
1440
+ interface AccountPanelLinkedProps {
1441
+ /** The card's heading: "Linked" by default. */
1442
+ readonly title?: ReactNode;
1443
+ /**
1444
+ * Its rows: `AccountPanel.LinkRow`s and the app's own, which render
1445
+ * inside a list, so each is an `<li>` (`AccountPanel.Row` is one). By
1446
+ * default, a LinkRow for each provider the brand offers the app.
1447
+ */
1448
+ readonly children?: ReactNode;
1449
+ }
1450
+ /**
1451
+ * The accounts linked to the profile: the rows the app puts in it, and in
1452
+ * the profile, each other linked account, read only.
1453
+ */
1454
+ declare function Linked({ title, children }: AccountPanelLinkedProps): ReactNode;
1455
+ /** What `AccountPanel.LinkRow` takes. */
1456
+ interface AccountPanelLinkRowProps {
1457
+ /** The provider to link: `telegram`, `x`, `google`… */
1458
+ readonly provider: SignInMethod;
1459
+ /** The row's title: the provider's name by default. */
1460
+ readonly title?: ReactNode;
1461
+ /** The line under it while it isn't linked: "Not linked" by default. */
1462
+ readonly description?: ReactNode;
1463
+ /** What it says once linked, in place of the handle: an app's own status ("Alerts on"). */
1464
+ readonly linkedLine?: (account: LinkedAccount) => ReactNode;
1465
+ }
1466
+ /**
1467
+ * One provider to link: "Link", then "Finish in the X window" in a popup on
1468
+ * a laptop (or a redirect on a phone that comes back into the panel), then
1469
+ * their handle. Signing in again is skipped right after a sign-in.
1470
+ */
1471
+ declare function LinkRow({ provider, title, description, linkedLine }: AccountPanelLinkRowProps): ReactNode;
1472
+ //#endregion
1473
+ //#region src/components/AccountPanel.d.ts
1474
+ /** What `AccountPanel` takes. */
1475
+ interface AccountPanelProps extends SynqStyleProps, Omit<UseAccountPanelOptions, 'animate'> {
1476
+ /**
1477
+ * The ways in that lead the panel, in this order, of those the brand
1478
+ * offers the app: its first four by default. The brand's others go under
1479
+ * "More options", in the brand's order; the one this browser last used is
1480
+ * lifted out of it.
1481
+ */
1482
+ readonly methods?: readonly string[];
1483
+ /** Whether a sign-in plays the avatar's moment, the ring and embers: yes by default. */
1484
+ readonly moment?: boolean;
1485
+ /** How many embers rise in the moment: 6 by default. */
1486
+ readonly sparks?: number;
1487
+ /** A line under the sign-in title, in the app's words. */
1488
+ readonly description?: ReactNode;
1489
+ /** The app's page with `<AccountManager>`, linked as "Account settings" under the profile. */
1490
+ readonly accountHref?: string;
1491
+ /** How avatars fall back (the app's own default, from the person's seed) and which pictures show. */
1492
+ readonly avatar?: AvatarSettings;
1493
+ /**
1494
+ * Where the panel renders: `document.body` by default, so no ancestor's
1495
+ * transform, filter or overflow clips it; null renders it beside its
1496
+ * button. Out of the button's tree, it takes the theme variables set on
1497
+ * `.synq`, which it carries, or on `panelClassName`.
1498
+ */
1499
+ readonly container?: Element | null;
1500
+ /**
1501
+ * Added to the panel's layer, the full-window element that holds the
1502
+ * dialog (and on a phone, its backdrop): where scoped theme variables go.
1503
+ * `className` is the button's wrapper's alone.
1504
+ */
1505
+ readonly panelClassName?: string;
1506
+ /**
1507
+ * What the onboarding and the profile show, in this order: the panel's
1508
+ * sections and the app's own cards between them. Username, Wallets and
1509
+ * Linked by default.
1510
+ */
1511
+ readonly children?: ReactNode;
1512
+ }
1513
+ /**
1514
+ * The account button and panel: sign in, a first sign-in's onboarding, and
1515
+ * the profile, in one popover or sheet. Its sections are `AccountPanel.*`.
1516
+ */
1517
+ export declare function AccountPanel({ methods, moment, sparks, description, accountHref, avatar, container, urlParam, keep, onSignedOut, unstyled, className, panelClassName, children }: AccountPanelProps): ReactNode;
1518
+ export declare namespace AccountPanel {
1519
+ var Username: typeof Username;
1520
+ var Wallets: typeof Wallets;
1521
+ var Linked: typeof Linked;
1522
+ var LinkRow: typeof LinkRow;
1523
+ var Card: typeof Card;
1524
+ var Row: typeof Row;
1525
+ }
1526
+ //#endregion
1527
+ //#region src/components/AvatarMoment.d.ts
1528
+ /** What `AvatarMoment` takes: the sign-in to mark, how large, and how many embers. */
1529
+ interface AvatarMomentProps extends SynqStyleProps, AvatarSettings {
1530
+ /**
1531
+ * The sign-in to mark (`useSignInEvent()`, or the panel's `event`): the
1532
+ * ring and embers play once for its `id`. Null shows the avatar alone.
1533
+ */
1534
+ readonly event: {
1535
+ readonly id: string;
1536
+ } | null;
1537
+ /** Whose avatar: the signed-in person by default. */
1538
+ readonly user?: AvatarPerson | null;
1539
+ /** The avatar's width and height in pixels: 64 by default. */
1540
+ readonly size?: number;
1541
+ /** How many embers rise: 6 by default, 0 for the ring alone. */
1542
+ readonly sparks?: number;
1543
+ }
1544
+ /**
1545
+ * The person's avatar with the moment of a sign-in: a ring that sweeps
1546
+ * around it once and embers that rise off it, by `--synq-moment-1..3`.
1547
+ * Drawn for the eye alone; the greeting beside it says what happened.
1548
+ */
1549
+ export declare function AvatarMoment({ event, user, size, sparks, fallback, sources, unstyled, className }: AvatarMomentProps): ReactNode;
1550
+ //#endregion
1551
+ //#region src/wallet-layer.d.ts
1552
+ /** A wallet app as people see it: its name, and its icon when the browser has it. */
1553
+ interface WalletAppInfo {
1554
+ readonly name: string;
1555
+ /** A data URL, as the wallet announced it; null when the browser hasn't it. */
1556
+ readonly icon: string | null;
1557
+ }
1558
+ /** What a wallet layer lends `<AccountPanel>`: see `WalletLayerProvider`. */
1559
+ interface WalletLayer {
1560
+ /** The account the app uses now, and its wallet app; null when none is connected. */
1561
+ readonly connected: {
1562
+ readonly address: string;
1563
+ readonly app: WalletAppInfo | null;
1564
+ } | null;
1565
+ /** The browser's wallets that can sign someone in, for a picker. */
1566
+ readonly wallets: readonly WalletAppInfo[];
1567
+ /**
1568
+ * Signs in with a wallet, in place: the connected one, or the wallet app
1569
+ * called `walletApp`. One signature; the wallet stays connected. A
1570
+ * declined signature is `wallet_rejected`, and what only the hosted page
1571
+ * can do is `redirect_to_web`.
1572
+ */
1573
+ readonly signIn: (walletApp?: string) => Promise<SynqResult<unknown>>;
1574
+ /**
1575
+ * Adds a wallet to the signed-in profile with one signature: the
1576
+ * connected one, or the wallet app called `walletApp`, which connects it.
1577
+ * A wallet on another profile is `wallet_in_use`; `move()` moves it here
1578
+ * where the brand lets it.
1579
+ */
1580
+ readonly add: (walletApp?: string) => Promise<SynqResult<Wallet>>;
1581
+ /** Moves the wallet of the last add's `wallet_in_use` here, where the brand lets it (`canMove`). */
1582
+ readonly move: () => Promise<SynqResult<Wallet>>;
1583
+ /** Whether the last add's conflict carries a ticket that moves the wallet here. */
1584
+ readonly canMove: boolean;
1585
+ /**
1586
+ * Connects one of the profile's wallets, signing nothing: at `address`,
1587
+ * in the wallet app called `walletApp` when the browser has it. Another
1588
+ * account selected in the wallet app is `account_mismatch`.
1589
+ */
1590
+ readonly connect: (address: string, walletApp: string | null) => Promise<SynqResult<unknown>>;
1591
+ /** The address this session signed in with, when the layer knows it. */
1592
+ readonly signedInWith: string | null;
1593
+ /**
1594
+ * Confirms it's them for a change that wants a recent sign-in, with a
1595
+ * wallet on their profile: one signature, in place, no page.
1596
+ */
1597
+ readonly reauthenticate: () => Promise<SynqResult<unknown>>;
1598
+ /**
1599
+ * Asks for the next signature's request now, and keeps it fresh until the
1600
+ * returned function is called, so a tap on a wallet goes straight to the
1601
+ * wallet (AERO-2679). The account panel calls it while it is open. A
1602
+ * layer without it asks after the tap.
1603
+ */
1604
+ readonly prepare?: () => () => void;
1605
+ }
1606
+ /**
1607
+ * Lends a wallet layer to the components under it. `<SynqWalletProvider>`
1608
+ * from `@synqauth/solana/react` does this itself; a wallet layer of the
1609
+ * app's own (another chain's, say) passes its own `value`.
1610
+ */
1611
+ export declare function WalletLayerProvider({ value, children }: {
1612
+ readonly value: WalletLayer | null;
1613
+ readonly children?: ReactNode;
1614
+ }): ReactNode;
1615
+ /** The wallet layer lent to this component (`WalletLayerProvider`), or null when there is none. */
1616
+ export declare function useWalletLayer(): WalletLayer | null;
1617
+ //#endregion
1618
+ export { type AccountManagerProps, Card as AccountPanelCard, type AccountPanelCardProps, type AccountPanelDialogProps, type AccountPanelGrabberProps, type AccountPanelLayout, LinkRow as AccountPanelLinkRow, type AccountPanelLinkRowProps, Linked as AccountPanelLinked, type AccountPanelLinkedProps, type AccountPanelPending, type AccountPanelProps, Row as AccountPanelRow, type AccountPanelRowProps, type AccountPanelSection, type AccountPanelTriggerProps, Username as AccountPanelUsername, type AccountPanelUsernameProps, type AccountPanelView, Wallets as AccountPanelWallets, type AccountPanelWalletsProps, type AccountSection, type AccountSession, type ApiKey, type ApiKeyInput, type AvatarFallback, type AvatarMomentProps, type AvatarProps, type AvatarSettings, type AvatarSource, type HandoffOptions, type KeyableApi, type LastUsedMethod, type LinkStatus, type LinkableMethod, type LinkedAccount, type NewApiKey, type OneTimeCodeInputProps, type Passkey, type Profile, type ProfileUpdate, type ReauthenticateOptions, type RedirectKind, type RedirectResult, type SessionStatus, type SignInButtonProps, type SignInDisplay, type SignInDone, type SignInEvent, type SignInFormProps, type SignInHandover, type SignInMethod, type SignInMethodInfo, type SignInMethods, type SignInOptions, type SignInPartProps, type SignInProblem, type SignInStep, type SignOutOptions, type SynqClientPlugin, type SynqMessages, type SynqMountRequest, type SynqProviderProps, type SynqResult, type SynqSessionInfo, type SynqStyleProps, type SynqUser, type UseAccountData, type UseAccountPanel, type UseAccountPanelOptions, type UseAuth, type UseAvatar, type UseLink, type UseRedirectResult, type UseSession, type UseSignIn, type UseSignInEvent, type UseSignInMethods, type UseSignInOptions, type UseUser, type UseUsernameField, type UserButtonProps, type UserMetadata, type UsernameCode, type UsernameStatus, type Wallet, type WalletAppInfo, type WalletLayer, type WalletManagerProps, type WalletRequestTarget, type WalletSignedIn };