@formstr/signer 0.1.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 +21 -0
- package/README.md +275 -0
- package/dist/index.cjs +726 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +140 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.js +693 -0
- package/dist/index.js.map +1 -0
- package/dist/signer-DiKj4PR7.d.cts +377 -0
- package/dist/signer-DiKj4PR7.d.ts +377 -0
- package/dist/ui/index.cjs +334 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.cts +24 -0
- package/dist/ui/index.d.ts +24 -0
- package/dist/ui/index.js +298 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +85 -0
- package/styles/signer.css +263 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { EventTemplate, Event } from 'nostr-tools';
|
|
2
|
+
import { AbstractSimplePool } from 'nostr-tools/abstract-pool';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Subset of `nostr-signer-capacitor-plugin`'s exported `NostrSignerPlugin`
|
|
6
|
+
* that we depend on. Signatures intentionally mirror that library
|
|
7
|
+
* (positional args, per-call `packageName`) so the real plugin is
|
|
8
|
+
* structurally assignable here — and any mock written against this
|
|
9
|
+
* interface is a faithful stand-in. The conformance is enforced by a
|
|
10
|
+
* compile-time guard in `tests/helpers/mockAndroidPlugin.ts`.
|
|
11
|
+
*/
|
|
12
|
+
interface AndroidSignerAppInfo {
|
|
13
|
+
name: string;
|
|
14
|
+
packageName: string;
|
|
15
|
+
iconUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
interface AndroidSignerPlugin {
|
|
18
|
+
setPackageName(packageName: string): Promise<void>;
|
|
19
|
+
getInstalledSignerApps(): Promise<{
|
|
20
|
+
apps: AndroidSignerAppInfo[];
|
|
21
|
+
}>;
|
|
22
|
+
getPublicKey(packageName?: string, permissions?: string): Promise<{
|
|
23
|
+
npub: string;
|
|
24
|
+
package: string;
|
|
25
|
+
}>;
|
|
26
|
+
signEvent(packageName: string, eventJson: string, id: string, npub: string): Promise<{
|
|
27
|
+
signature: string;
|
|
28
|
+
id: string;
|
|
29
|
+
event: string;
|
|
30
|
+
}>;
|
|
31
|
+
nip04Encrypt(packageName: string, plainText: string, id: string, pubKey: string, npub: string): Promise<{
|
|
32
|
+
result: string;
|
|
33
|
+
id: string;
|
|
34
|
+
}>;
|
|
35
|
+
nip04Decrypt(packageName: string, encryptedText: string, id: string, pubKey: string, npub: string): Promise<{
|
|
36
|
+
result: string;
|
|
37
|
+
id: string;
|
|
38
|
+
}>;
|
|
39
|
+
nip44Encrypt(packageName: string, plainText: string, id: string, pubKey: string, npub: string): Promise<{
|
|
40
|
+
result: string;
|
|
41
|
+
id: string;
|
|
42
|
+
}>;
|
|
43
|
+
nip44Decrypt(packageName: string, encryptedText: string, id: string, pubKey: string, npub: string): Promise<{
|
|
44
|
+
result: string;
|
|
45
|
+
id: string;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
interface AndroidLoginOptions {
|
|
49
|
+
/** The Android package name of the external signer app (e.g. com.greenart7c3.nostrsigner). */
|
|
50
|
+
packageName?: string;
|
|
51
|
+
/** Override the plugin for this call. Falls back to SignerConfig.androidSignerPlugin. */
|
|
52
|
+
plugin?: AndroidSignerPlugin;
|
|
53
|
+
}
|
|
54
|
+
declare class AndroidSigner implements ActiveSigner {
|
|
55
|
+
#private;
|
|
56
|
+
constructor(plugin: AndroidSignerPlugin, packageName: string, npub: string, pubkey: string);
|
|
57
|
+
getPublicKey(): Promise<string>;
|
|
58
|
+
signEvent(event: EventTemplate): Promise<Event>;
|
|
59
|
+
nip04Encrypt(peerPubkey: string, plaintext: string): Promise<string>;
|
|
60
|
+
nip04Decrypt(peerPubkey: string, ciphertext: string): Promise<string>;
|
|
61
|
+
nip44Encrypt(peerPubkey: string, plaintext: string): Promise<string>;
|
|
62
|
+
nip44Decrypt(peerPubkey: string, ciphertext: string): Promise<string>;
|
|
63
|
+
}
|
|
64
|
+
interface AndroidLoginResult {
|
|
65
|
+
signer: AndroidSigner;
|
|
66
|
+
pubkey: string;
|
|
67
|
+
npub: string;
|
|
68
|
+
packageName: string;
|
|
69
|
+
}
|
|
70
|
+
declare function loginWithAndroidSigner(plugin: AndroidSignerPlugin, packageName?: string): Promise<AndroidLoginResult>;
|
|
71
|
+
|
|
72
|
+
interface StorageAdapter {
|
|
73
|
+
get(key: string): string | null;
|
|
74
|
+
set(key: string, value: string): void;
|
|
75
|
+
remove(key: string): void;
|
|
76
|
+
}
|
|
77
|
+
declare function localStorageAdapter(prefix?: string): StorageAdapter;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* How the user's key material is held for an account:
|
|
81
|
+
* - `extension`: NIP-07 browser extension (window.nostr).
|
|
82
|
+
* - `nip46`: NIP-46 remote signer (bunker URI or nostrconnect QR).
|
|
83
|
+
* - `ncryptsec`: NIP-49 encrypted nsec — decrypted into memory on unlock.
|
|
84
|
+
* - `android`: NIP-55 Android external signer app via a Capacitor plugin.
|
|
85
|
+
*/
|
|
86
|
+
type LoginMethod = 'extension' | 'nip46' | 'ncryptsec' | 'android';
|
|
87
|
+
/**
|
|
88
|
+
* Serialized account record persisted by the {@link StorageAdapter}.
|
|
89
|
+
*
|
|
90
|
+
* Survives reloads. Re-hydrates as **locked** — the account is present in
|
|
91
|
+
* `listAccounts()` and reachable via `getActiveAccount()`, but
|
|
92
|
+
* `getActiveSigner()` returns `null` until the user re-authenticates
|
|
93
|
+
* (passphrase for ncryptsec, page granted for extension, signer app for
|
|
94
|
+
* NIP-46/NIP-55).
|
|
95
|
+
*
|
|
96
|
+
* Method-specific fields:
|
|
97
|
+
* - `ncryptsec` — present when `method === 'ncryptsec'`. The encrypted nsec.
|
|
98
|
+
* - `nip46` — present when `method === 'nip46'`. URI, remote signer pubkey,
|
|
99
|
+
* relays, and the per-account client session keypair (hex). The client
|
|
100
|
+
* secret key is stored in plaintext on purpose — see the README's
|
|
101
|
+
* threat-model note.
|
|
102
|
+
* - `androidPackageName` — present when `method === 'android'`. Identifies
|
|
103
|
+
* which installed signer app fulfilled the login (e.g. Amber).
|
|
104
|
+
*/
|
|
105
|
+
interface StoredAccount {
|
|
106
|
+
npub: string;
|
|
107
|
+
pubkey: string;
|
|
108
|
+
method: LoginMethod;
|
|
109
|
+
ncryptsec?: string;
|
|
110
|
+
nip46?: {
|
|
111
|
+
uri: string;
|
|
112
|
+
remoteSignerPubkey: string;
|
|
113
|
+
relays: string[];
|
|
114
|
+
clientSecretKey: string;
|
|
115
|
+
};
|
|
116
|
+
androidPackageName?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The runtime signing surface exposed once an account is **unlocked**.
|
|
120
|
+
*
|
|
121
|
+
* Every concrete signer ({@link LocalSigner}, {@link ExtensionSigner},
|
|
122
|
+
* {@link BunkerSigner}, {@link AndroidSigner}) conforms to this. The
|
|
123
|
+
* abstraction has one deliberate omission: there is no `getPrivateKey()`.
|
|
124
|
+
* The raw secret key is never reachable through this interface — that is
|
|
125
|
+
* the package's central security invariant. Local signing holds the key
|
|
126
|
+
* in memory; the other methods sign remotely.
|
|
127
|
+
*
|
|
128
|
+
* `signEvent` accepts an unsigned {@link EventTemplate} (no `pubkey`,
|
|
129
|
+
* `id`, or `sig`) and returns a fully-signed {@link NostrEvent} —
|
|
130
|
+
* the implementation sets the `pubkey` to the active account's and fills
|
|
131
|
+
* in `id`/`sig`.
|
|
132
|
+
*
|
|
133
|
+
* `nip04Encrypt`/`nip44Encrypt` and their decrypt counterparts perform
|
|
134
|
+
* ECDH against `peerPubkey` (a 32-byte x-only hex pubkey). All four
|
|
135
|
+
* may throw if the remote signer (extension / bunker / Android) denies
|
|
136
|
+
* the operation.
|
|
137
|
+
*/
|
|
138
|
+
interface ActiveSigner {
|
|
139
|
+
getPublicKey(): Promise<string>;
|
|
140
|
+
signEvent(event: EventTemplate): Promise<Event>;
|
|
141
|
+
nip04Encrypt(peerPubkey: string, plaintext: string): Promise<string>;
|
|
142
|
+
nip04Decrypt(peerPubkey: string, ciphertext: string): Promise<string>;
|
|
143
|
+
nip44Encrypt(peerPubkey: string, plaintext: string): Promise<string>;
|
|
144
|
+
nip44Decrypt(peerPubkey: string, ciphertext: string): Promise<string>;
|
|
145
|
+
}
|
|
146
|
+
interface RelayMismatchInfo {
|
|
147
|
+
userRelays: string[];
|
|
148
|
+
bunkerRelays: string[];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Called after pairing if the bunker's preferred relays (via get_relays)
|
|
152
|
+
* differ from the user-supplied list. Return `true` to accept the bunker's
|
|
153
|
+
* list — it will be stored on the account for future sessions. Return
|
|
154
|
+
* `false` (or anything falsy) to keep the user's list. Either way, the
|
|
155
|
+
* current in-memory session keeps using the user's relays since those
|
|
156
|
+
* just worked for pairing.
|
|
157
|
+
*/
|
|
158
|
+
type RelayMismatchHandler = (info: RelayMismatchInfo) => boolean | Promise<boolean>;
|
|
159
|
+
interface BunkerLoginOptions {
|
|
160
|
+
pool?: AbstractSimplePool;
|
|
161
|
+
onAuth?: (url: string) => void;
|
|
162
|
+
/** Reuse a stored client session keypair to resume a NIP-46 connection. */
|
|
163
|
+
clientSecretKey?: Uint8Array;
|
|
164
|
+
onRelayMismatch?: RelayMismatchHandler;
|
|
165
|
+
/**
|
|
166
|
+
* NIP-46 permissions to request as part of the `connect` call
|
|
167
|
+
* (e.g. `['sign_event:1', 'nip44_encrypt']`). Without this, many
|
|
168
|
+
* bunker UIs (e.g. Amber) show no approve/deny prompt because the
|
|
169
|
+
* connect request has nothing concrete to authorize.
|
|
170
|
+
*/
|
|
171
|
+
perms?: string[];
|
|
172
|
+
}
|
|
173
|
+
interface NostrConnectOptions {
|
|
174
|
+
relays: string[];
|
|
175
|
+
metadata?: {
|
|
176
|
+
name?: string;
|
|
177
|
+
url?: string;
|
|
178
|
+
image?: string;
|
|
179
|
+
};
|
|
180
|
+
perms?: string[];
|
|
181
|
+
/** Called once with the generated nostrconnect URI so the caller can render it. */
|
|
182
|
+
onUri: (uri: string) => void;
|
|
183
|
+
pool?: AbstractSimplePool;
|
|
184
|
+
onAuth?: (url: string) => void;
|
|
185
|
+
signal?: AbortSignal;
|
|
186
|
+
timeoutMs?: number;
|
|
187
|
+
onRelayMismatch?: RelayMismatchHandler;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Emitted by {@link Signer.onChange}. Variants:
|
|
191
|
+
* - `login` — a new account became active (no previous active account).
|
|
192
|
+
* - `switch` — the active account changed (including unlocking an already
|
|
193
|
+
* hydrated account, since unlock re-asserts the active signer).
|
|
194
|
+
* - `logout` — `logout(pubkey)` removed an account; emitted with the
|
|
195
|
+
* removed account's `pubkey` (the account itself is already gone from
|
|
196
|
+
* `listAccounts()` by the time the event fires).
|
|
197
|
+
*/
|
|
198
|
+
type SignerEvent = {
|
|
199
|
+
type: 'login';
|
|
200
|
+
account: StoredAccount;
|
|
201
|
+
} | {
|
|
202
|
+
type: 'logout';
|
|
203
|
+
pubkey: string;
|
|
204
|
+
} | {
|
|
205
|
+
type: 'switch';
|
|
206
|
+
account: StoredAccount;
|
|
207
|
+
};
|
|
208
|
+
interface SignerConfig {
|
|
209
|
+
/**
|
|
210
|
+
* Persistence backend. Defaults to a `localStorage`-backed adapter.
|
|
211
|
+
* Provide a custom adapter to use sessionStorage, an in-memory map,
|
|
212
|
+
* IndexedDB, or any other key/value store. See {@link StorageAdapter}.
|
|
213
|
+
*/
|
|
214
|
+
storage?: StorageAdapter;
|
|
215
|
+
/** Prefix applied to all keys written by the default localStorage adapter. */
|
|
216
|
+
storageKeyPrefix?: string;
|
|
217
|
+
/**
|
|
218
|
+
* Human-readable app name used as the default `name` metadata in
|
|
219
|
+
* the nostrconnect:// URI generated by `loginWithNostrConnect`.
|
|
220
|
+
* Remote signers (Amber, etc.) display this on the consent screen.
|
|
221
|
+
* Overridden by a per-call `metadata.name`.
|
|
222
|
+
*/
|
|
223
|
+
appName?: string;
|
|
224
|
+
/**
|
|
225
|
+
* Canonical app URL used as the default `url` metadata in the
|
|
226
|
+
* nostrconnect:// URI. Overridden by a per-call `metadata.url`.
|
|
227
|
+
*/
|
|
228
|
+
appUrl?: string;
|
|
229
|
+
/**
|
|
230
|
+
* Icon URL used as the default `image` metadata in the
|
|
231
|
+
* nostrconnect:// URI. Overridden by a per-call `metadata.image`.
|
|
232
|
+
*/
|
|
233
|
+
appImage?: string;
|
|
234
|
+
/**
|
|
235
|
+
* Default Android signer plugin (NIP-55). Provide the host app's
|
|
236
|
+
* `nostr-signer-capacitor-plugin` instance or an equivalent stub that
|
|
237
|
+
* satisfies {@link import('../nip55.js').AndroidSignerPlugin}.
|
|
238
|
+
* Can be overridden per-call via `loginWithAndroidSigner({ plugin })`
|
|
239
|
+
* or `listAndroidSignerApps(plugin)`.
|
|
240
|
+
*/
|
|
241
|
+
androidSignerPlugin?: AndroidSignerPlugin;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Multi-account Nostr signer with persistence.
|
|
246
|
+
*
|
|
247
|
+
* **Hydration.** The constructor reads previously-saved accounts from
|
|
248
|
+
* the configured storage adapter. Every hydrated account starts
|
|
249
|
+
* **locked**: present in `listAccounts()` and (if it was the active one
|
|
250
|
+
* before) reachable via `getActiveAccount()`, but `getActiveSigner()`
|
|
251
|
+
* returns `null` until the user re-authenticates. The matching
|
|
252
|
+
* `loginWith*` method unlocks the active account.
|
|
253
|
+
*
|
|
254
|
+
* **Locked vs unlocked.** Use `getActiveAccount()` to render UI ("logged
|
|
255
|
+
* in as @alice") and `getActiveSigner()` to decide whether the user can
|
|
256
|
+
* actually sign. The pattern is "show the account always, gate signing
|
|
257
|
+
* on the signer."
|
|
258
|
+
*
|
|
259
|
+
* **Events.** Subscribe via `onChange()` to re-render when an account
|
|
260
|
+
* is added, switched, or removed. See {@link SignerEvent}.
|
|
261
|
+
*/
|
|
262
|
+
declare class Signer {
|
|
263
|
+
#private;
|
|
264
|
+
constructor(config?: SignerConfig);
|
|
265
|
+
/**
|
|
266
|
+
* Generate a brand-new nsec, encrypt it with `passphrase` (NIP-49),
|
|
267
|
+
* persist the resulting `ncryptsec` account, and activate it. Returns
|
|
268
|
+
* the new account's `npub` and `ncryptsec` — the caller must surface
|
|
269
|
+
* the `ncryptsec` to the user **immediately** since it is the only way
|
|
270
|
+
* back into the account on a fresh device.
|
|
271
|
+
*
|
|
272
|
+
* @throws if `passphrase` is empty.
|
|
273
|
+
*/
|
|
274
|
+
createAccount(passphrase: string): Promise<{
|
|
275
|
+
npub: string;
|
|
276
|
+
ncryptsec: string;
|
|
277
|
+
}>;
|
|
278
|
+
/**
|
|
279
|
+
* Decrypt an ncryptsec with the user's passphrase, persist the account
|
|
280
|
+
* (overwriting any previous entry for the same pubkey), and activate it.
|
|
281
|
+
*
|
|
282
|
+
* @throws if either argument is empty, or if the passphrase doesn't
|
|
283
|
+
* decrypt the ncryptsec.
|
|
284
|
+
*/
|
|
285
|
+
loginWithNcryptsec(ncryptsec: string, passphrase: string): Promise<StoredAccount>;
|
|
286
|
+
/**
|
|
287
|
+
* Connect via the NIP-07 browser extension exposed at `window.nostr`.
|
|
288
|
+
* The extension prompts the user for permission on first use.
|
|
289
|
+
*
|
|
290
|
+
* @throws if no extension is installed or the user denies the request.
|
|
291
|
+
*/
|
|
292
|
+
loginWithExtension(): Promise<StoredAccount>;
|
|
293
|
+
/**
|
|
294
|
+
* Connect to a NIP-46 remote signer via a `bunker://` URI. Relays are
|
|
295
|
+
* read from the URI itself — no hardcoded fallbacks. Pass a `pool`
|
|
296
|
+
* to reuse an existing relay connection; pass `clientSecretKey` to
|
|
297
|
+
* resume a previous session (the hex from `StoredAccount.nip46`).
|
|
298
|
+
*
|
|
299
|
+
* @throws if the URI is malformed, no relay is reachable, or the
|
|
300
|
+
* remote signer rejects pairing within the implementation's timeout.
|
|
301
|
+
*/
|
|
302
|
+
loginWithBunkerUri(uri: string, options?: BunkerLoginOptions): Promise<StoredAccount>;
|
|
303
|
+
/**
|
|
304
|
+
* Initiate a NIP-46 `nostrconnect://` pairing. Generates a client
|
|
305
|
+
* keypair, publishes a connect request to the supplied `relays`, and
|
|
306
|
+
* waits for a remote signer to pair. Call `options.onUri(uri)` to
|
|
307
|
+
* render the URI as a QR code; the returned promise resolves once
|
|
308
|
+
* pairing completes. Cancel by aborting `options.signal`.
|
|
309
|
+
*
|
|
310
|
+
* @throws if `relays` is empty, the user aborts, the pairing times
|
|
311
|
+
* out, or no signer responds.
|
|
312
|
+
*/
|
|
313
|
+
loginWithNostrConnect(options: NostrConnectOptions): Promise<StoredAccount>;
|
|
314
|
+
/**
|
|
315
|
+
* Enumerate NIP-55 signer apps installed on the device, via the
|
|
316
|
+
* configured Android plugin (or `plugin` if supplied). Useful for
|
|
317
|
+
* rendering a "pick your signer" list — the built-in UI does this
|
|
318
|
+
* automatically when the Android tab is selected.
|
|
319
|
+
*
|
|
320
|
+
* Only meaningful inside a Capacitor Android shell. On web/iOS the
|
|
321
|
+
* configured plugin is typically absent and this throws.
|
|
322
|
+
*
|
|
323
|
+
* @throws if no plugin is configured and none is passed in.
|
|
324
|
+
*/
|
|
325
|
+
listAndroidSignerApps(plugin?: AndroidSignerPlugin): Promise<AndroidSignerAppInfo[]>;
|
|
326
|
+
/**
|
|
327
|
+
* Sign in via a NIP-55 Android external signer (Amber, etc). If
|
|
328
|
+
* `options.packageName` is given, that specific signer app is invoked;
|
|
329
|
+
* otherwise the plugin picks a default (typically the only installed
|
|
330
|
+
* signer, or an OS chooser). Pass `options.plugin` to override the
|
|
331
|
+
* configured default for this call.
|
|
332
|
+
*
|
|
333
|
+
* @throws if no plugin is configured, the signer app cannot be
|
|
334
|
+
* resolved to a package name, or the user denies the request.
|
|
335
|
+
*/
|
|
336
|
+
loginWithAndroidSigner(options?: AndroidLoginOptions): Promise<StoredAccount>;
|
|
337
|
+
/** Snapshot of every persisted account, in insertion order. */
|
|
338
|
+
listAccounts(): StoredAccount[];
|
|
339
|
+
/**
|
|
340
|
+
* The currently selected account, or `null` if none. Present even when
|
|
341
|
+
* the account is locked (no active signer yet). Use this to render
|
|
342
|
+
* "logged in as @alice" — pair with {@link getActiveSigner} to decide
|
|
343
|
+
* whether signing is actually available.
|
|
344
|
+
*/
|
|
345
|
+
getActiveAccount(): StoredAccount | null;
|
|
346
|
+
/**
|
|
347
|
+
* The unlocked signer for the active account, or `null` if locked.
|
|
348
|
+
* After a fresh page load this is `null` for every account type
|
|
349
|
+
* (passphrase / extension grant / signer-app handshake all need to
|
|
350
|
+
* be redone). Calling the matching `loginWith*` method unlocks it.
|
|
351
|
+
*/
|
|
352
|
+
getActiveSigner(): ActiveSigner | null;
|
|
353
|
+
/**
|
|
354
|
+
* Make `pubkey` the active account. Clears the in-memory signer —
|
|
355
|
+
* the new account starts **locked** even if it was previously
|
|
356
|
+
* unlocked in this session.
|
|
357
|
+
*
|
|
358
|
+
* @throws if `pubkey` does not match any persisted account.
|
|
359
|
+
*/
|
|
360
|
+
switchAccount(pubkey: string): Promise<void>;
|
|
361
|
+
/**
|
|
362
|
+
* Remove an account from storage. `pubkey` defaults to the active
|
|
363
|
+
* account. If the active account is removed, the in-memory signer is
|
|
364
|
+
* cleared. No-op if there is nothing to remove.
|
|
365
|
+
*/
|
|
366
|
+
logout(pubkey?: string): Promise<void>;
|
|
367
|
+
/**
|
|
368
|
+
* Subscribe to account-state changes. Returns an unsubscribe function.
|
|
369
|
+
* Listener errors are swallowed so one bad listener can't break others.
|
|
370
|
+
* See {@link SignerEvent} for the variants.
|
|
371
|
+
*/
|
|
372
|
+
onChange(cb: (event: SignerEvent) => void): () => void;
|
|
373
|
+
}
|
|
374
|
+
/** Convenience wrapper around `new Signer(config)`. */
|
|
375
|
+
declare function createSigner(config?: SignerConfig): Signer;
|
|
376
|
+
|
|
377
|
+
export { type ActiveSigner as A, type BunkerLoginOptions as B, type LoginMethod as L, type NostrConnectOptions as N, type RelayMismatchHandler as R, Signer as S, type AndroidLoginOptions as a, type AndroidLoginResult as b, AndroidSigner as c, type AndroidSignerAppInfo as d, type AndroidSignerPlugin as e, type RelayMismatchInfo as f, type SignerConfig as g, type SignerEvent as h, type StorageAdapter as i, type StoredAccount as j, createSigner as k, localStorageAdapter as l, loginWithAndroidSigner as m };
|