@cef-ai/wallet 1.0.0 → 1.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/README.md +99 -4
- package/dist/index.cjs +243 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +180 -53
- package/dist/index.d.ts +180 -53
- package/dist/index.js +243 -21
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ type HostToPopup = {
|
|
|
41
41
|
appContext: AppContext;
|
|
42
42
|
intent: 'register' | 'login';
|
|
43
43
|
label?: string;
|
|
44
|
+
name?: string;
|
|
45
|
+
email?: string;
|
|
44
46
|
} | {
|
|
45
47
|
type: 'wallet:requestDelegation';
|
|
46
48
|
id: string;
|
|
@@ -90,6 +92,12 @@ type PopupToHost = {
|
|
|
90
92
|
code: WalletErrorCode;
|
|
91
93
|
message: string;
|
|
92
94
|
traceId?: string;
|
|
95
|
+
} | {
|
|
96
|
+
type: 'wallet:ui:show';
|
|
97
|
+
id: string;
|
|
98
|
+
} | {
|
|
99
|
+
type: 'wallet:ui:hide';
|
|
100
|
+
id: string;
|
|
93
101
|
};
|
|
94
102
|
/** Spec §5.5. Channel name: `scp-wallet-v2`. Wallet-origin tabs only. */
|
|
95
103
|
type BroadcastChannelMessage = {
|
|
@@ -104,6 +112,8 @@ type BroadcastChannelMessage = {
|
|
|
104
112
|
/** absolute Unix epoch ms when session keys expire */ expMs: number;
|
|
105
113
|
} | {
|
|
106
114
|
type: 'logout';
|
|
115
|
+
} | {
|
|
116
|
+
type: 'session-available';
|
|
107
117
|
};
|
|
108
118
|
declare const BROADCAST_CHANNEL_NAME = "scp-wallet-v2";
|
|
109
119
|
|
|
@@ -136,54 +146,20 @@ type PopupMessageHandler<T extends HostToPopup = HostToPopup> = (message: T, met
|
|
|
136
146
|
origin: string;
|
|
137
147
|
}) => Promise<void> | void;
|
|
138
148
|
|
|
139
|
-
interface PopupTransportOptions {
|
|
140
|
-
/** Expected origin of the wallet popup, e.g. 'https://wallet.example.com'. */
|
|
141
|
-
walletOrigin: string;
|
|
142
|
-
/** Defaults to `(url, features) => window.open(url, '_blank', features)`. */
|
|
143
|
-
openPopup?: PopupOpener;
|
|
144
|
-
/** Window object hosting the event listener. Defaults to `globalThis.window`. */
|
|
145
|
-
hostWindow?: Window;
|
|
146
|
-
/** Default per-request timeout. Defaults to 60_000 ms. */
|
|
147
|
-
defaultTimeoutMs?: number;
|
|
148
|
-
}
|
|
149
149
|
/**
|
|
150
|
-
* Host-side
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* `request()` after it's been closed, and correlates each in-flight request
|
|
155
|
-
* by its message `id`.
|
|
156
|
-
*
|
|
157
|
-
* Wire handshake: every freshly opened popup must send `wallet:ready` before
|
|
158
|
-
* this transport will post any `HostToPopup` envelope. Without the handshake,
|
|
159
|
-
* `postMessage` may fire before the popup document has executed its `message`
|
|
160
|
-
* listener and the message is dropped. Outbound messages are queued in
|
|
161
|
-
* `outbox` while `popupReady === false`; `wallet:ready` flushes them.
|
|
150
|
+
* Host-side transport contract used by `EmbedWallet`. Both `PopupTransport`
|
|
151
|
+
* (separate window) and `IframeTransport` (in-page cross-origin iframe)
|
|
152
|
+
* implement it — `EmbedWallet` depends only on this, never on a concrete
|
|
153
|
+
* transport's popup/iframe mechanics.
|
|
162
154
|
*/
|
|
163
|
-
|
|
164
|
-
private readonly opts;
|
|
165
|
-
private popup;
|
|
166
|
-
private pending;
|
|
167
|
-
private listener;
|
|
168
|
-
private popupReady;
|
|
169
|
-
private outbox;
|
|
170
|
-
constructor(options: PopupTransportOptions);
|
|
155
|
+
interface WalletTransport {
|
|
171
156
|
request<T extends PopupToHost>(message: HostToPopup, expectedTypes: T['type'][], init?: {
|
|
172
157
|
timeoutMs?: number;
|
|
173
158
|
}): Promise<T>;
|
|
174
|
-
/**
|
|
159
|
+
/** Tear down the visible surface (popup window / iframe element); keep the instance reusable. */
|
|
175
160
|
close(): void;
|
|
176
|
-
/**
|
|
177
|
-
* Tear down the transport entirely. Closes the popup AND removes the host-
|
|
178
|
-
* window message listener. After `destroy()`, the transport instance cannot
|
|
179
|
-
* be reused — callers should drop the reference.
|
|
180
|
-
*
|
|
181
|
-
* Use `close()` if you only want to close the popup but keep the transport
|
|
182
|
-
* alive for a later re-open.
|
|
183
|
-
*/
|
|
161
|
+
/** Tear down entirely (surface + host-window listener); instance not reusable after. */
|
|
184
162
|
destroy(): void;
|
|
185
|
-
private ensureListening;
|
|
186
|
-
private onMessage;
|
|
187
163
|
}
|
|
188
164
|
|
|
189
165
|
interface EmbedWalletOptions {
|
|
@@ -202,12 +178,21 @@ interface EmbedWalletOptions {
|
|
|
202
178
|
};
|
|
203
179
|
/** Optional name for the appContext sent via wallet:hello. Defaults to ''. */
|
|
204
180
|
appName?: string;
|
|
181
|
+
/** Transport surface. 'iframe' (default) embeds the wallet inline; 'popup' opens a window. */
|
|
182
|
+
transport?: 'iframe' | 'popup';
|
|
205
183
|
/**
|
|
206
184
|
* Test seam. Injects an alternative `PopupTransport` instance. Production
|
|
207
185
|
* callers leave this undefined and the SDK constructs its own transport.
|
|
208
186
|
*/
|
|
209
187
|
__internal__?: {
|
|
210
|
-
transport?:
|
|
188
|
+
transport?: WalletTransport;
|
|
189
|
+
/**
|
|
190
|
+
* Test seam. Injects the `openPopup` used by the transient `PopupTransport`
|
|
191
|
+
* that `register()` opens when the main transport is not already a popup
|
|
192
|
+
* (see `EmbedWallet.register`). Production callers leave this undefined and
|
|
193
|
+
* the transient popup uses the real `window.open` default.
|
|
194
|
+
*/
|
|
195
|
+
registerPopupOpener?: PopupOpener;
|
|
211
196
|
};
|
|
212
197
|
}
|
|
213
198
|
|
|
@@ -285,12 +270,23 @@ declare class EmbedWallet {
|
|
|
285
270
|
private _credentialId;
|
|
286
271
|
private _listeners;
|
|
287
272
|
private readonly transport;
|
|
273
|
+
private readonly regPopupOpener?;
|
|
288
274
|
constructor(opts: EmbedWalletOptions);
|
|
289
275
|
get isLoggedIn(): boolean;
|
|
290
276
|
get addresses(): Addresses | null;
|
|
291
277
|
get credentialId(): string | null;
|
|
278
|
+
/**
|
|
279
|
+
* Safari blocks WebAuthn `create()` in cross-origin iframes, so `register()`
|
|
280
|
+
* always runs its `wallet:hello` ceremony over a `PopupTransport` — even
|
|
281
|
+
* when the configured transport is the iframe. When the main transport is
|
|
282
|
+
* already a `PopupTransport` it's reused; otherwise a transient one is
|
|
283
|
+
* opened synchronously (within the caller's click) and closed afterward.
|
|
284
|
+
* `login()` and all other ops use the configured transport unchanged.
|
|
285
|
+
*/
|
|
292
286
|
register(opts?: {
|
|
293
287
|
label?: string;
|
|
288
|
+
name?: string;
|
|
289
|
+
email?: string;
|
|
294
290
|
}): Promise<LoginResult>;
|
|
295
291
|
login(): Promise<LoginResult>;
|
|
296
292
|
logout(): Promise<void>;
|
|
@@ -348,11 +344,106 @@ declare const ALLOWED_ORIGIN_SCHEMES: readonly string[];
|
|
|
348
344
|
*/
|
|
349
345
|
declare function isMatchingOrigin(a: string | undefined, b: string | undefined): boolean;
|
|
350
346
|
|
|
347
|
+
interface PopupTransportOptions {
|
|
348
|
+
/** Expected origin of the wallet popup, e.g. 'https://wallet.example.com'. */
|
|
349
|
+
walletOrigin: string;
|
|
350
|
+
/** Defaults to `(url, features) => window.open(url, '_blank', features)`. */
|
|
351
|
+
openPopup?: PopupOpener;
|
|
352
|
+
/** Window object hosting the event listener. Defaults to `globalThis.window`. */
|
|
353
|
+
hostWindow?: Window;
|
|
354
|
+
/** Default per-request timeout. Defaults to 60_000 ms. */
|
|
355
|
+
defaultTimeoutMs?: number;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Host-side popup transport. Spec §5.
|
|
359
|
+
*
|
|
360
|
+
* Use one `PopupTransport` instance per `EmbedWallet`. The instance owns a
|
|
361
|
+
* single popup window at a time, opens it on demand, auto-reopens on the next
|
|
362
|
+
* `request()` after it's been closed, and correlates each in-flight request
|
|
363
|
+
* by its message `id`.
|
|
364
|
+
*
|
|
365
|
+
* Wire handshake: every freshly opened popup must send `wallet:ready` before
|
|
366
|
+
* this transport will post any `HostToPopup` envelope. Without the handshake,
|
|
367
|
+
* `postMessage` may fire before the popup document has executed its `message`
|
|
368
|
+
* listener and the message is dropped. Outbound messages are queued in
|
|
369
|
+
* `outbox` while `popupReady === false`; `wallet:ready` flushes them.
|
|
370
|
+
*/
|
|
371
|
+
declare class PopupTransport implements WalletTransport {
|
|
372
|
+
private readonly opts;
|
|
373
|
+
private popup;
|
|
374
|
+
private pending;
|
|
375
|
+
private listener;
|
|
376
|
+
private popupReady;
|
|
377
|
+
private outbox;
|
|
378
|
+
constructor(options: PopupTransportOptions);
|
|
379
|
+
request<T extends PopupToHost>(message: HostToPopup, expectedTypes: T['type'][], init?: {
|
|
380
|
+
timeoutMs?: number;
|
|
381
|
+
}): Promise<T>;
|
|
382
|
+
/**
|
|
383
|
+
* Close the popup if open. Immediately rejects any in-flight `request()`
|
|
384
|
+
* calls (mirrors `IframeTransport.close()`) rather than leaving them to
|
|
385
|
+
* time out — a caller that closes the popup mid-request (e.g.
|
|
386
|
+
* `EmbedWallet.register()`'s `finally` block) gets a prompt rejection
|
|
387
|
+
* instead of waiting out the full default 60s timeout.
|
|
388
|
+
*/
|
|
389
|
+
close(): void;
|
|
390
|
+
/**
|
|
391
|
+
* Tear down the transport entirely. Closes the popup AND removes the host-
|
|
392
|
+
* window message listener. After `destroy()`, the transport instance cannot
|
|
393
|
+
* be reused — callers should drop the reference.
|
|
394
|
+
*
|
|
395
|
+
* Use `close()` if you only want to close the popup but keep the transport
|
|
396
|
+
* alive for a later re-open.
|
|
397
|
+
*/
|
|
398
|
+
destroy(): void;
|
|
399
|
+
/** Reject every in-flight request with a `WalletError('internal', reason)`. Idempotent. */
|
|
400
|
+
private rejectAllPending;
|
|
401
|
+
private ensureListening;
|
|
402
|
+
private onMessage;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
interface IframeTransportOptions {
|
|
406
|
+
walletOrigin: string;
|
|
407
|
+
hostWindow?: Window;
|
|
408
|
+
document?: Document;
|
|
409
|
+
defaultTimeoutMs?: number;
|
|
410
|
+
/** Test seam: build the iframe element (happy-dom's contentWindow is null). */
|
|
411
|
+
mountIframe?: (url: string, allow: string) => HTMLIFrameElement;
|
|
412
|
+
/** Test seam: observe overlay visibility transitions without relying on real DOM layout. */
|
|
413
|
+
onOverlay?: (visible: boolean) => void;
|
|
414
|
+
}
|
|
415
|
+
declare class IframeTransport implements WalletTransport {
|
|
416
|
+
private readonly opts;
|
|
417
|
+
private iframe;
|
|
418
|
+
private pending;
|
|
419
|
+
private listener;
|
|
420
|
+
private ready;
|
|
421
|
+
private outbox;
|
|
422
|
+
private backdrop;
|
|
423
|
+
private hiddenCssText;
|
|
424
|
+
private overlayVisible;
|
|
425
|
+
constructor(options: IframeTransportOptions);
|
|
426
|
+
private ensureIframe;
|
|
427
|
+
private showOverlay;
|
|
428
|
+
private hideOverlay;
|
|
429
|
+
private ensureListening;
|
|
430
|
+
request<T extends PopupToHost>(message: HostToPopup, expectedTypes: T['type'][], init?: {
|
|
431
|
+
timeoutMs?: number;
|
|
432
|
+
}): Promise<T>;
|
|
433
|
+
private postToIframe;
|
|
434
|
+
close(): void;
|
|
435
|
+
destroy(): void;
|
|
436
|
+
private rejectAllPending;
|
|
437
|
+
private onMessage;
|
|
438
|
+
}
|
|
439
|
+
|
|
351
440
|
interface PopupHostBridgeOptions {
|
|
352
441
|
/** Expected origin of the host page (the page that opened this popup). */
|
|
353
442
|
hostOrigin: string;
|
|
354
443
|
/** The window this bridge runs in. Defaults to `globalThis`. */
|
|
355
444
|
popupWindow?: Window;
|
|
445
|
+
/** Which window to post PopupToHost messages to. 'opener' (popup, default) or 'parent' (iframe). */
|
|
446
|
+
replyTo?: 'opener' | 'parent';
|
|
356
447
|
}
|
|
357
448
|
/**
|
|
358
449
|
* Popup-side bridge. Spec §5.
|
|
@@ -375,9 +466,9 @@ declare class PopupHostBridge {
|
|
|
375
466
|
type: T;
|
|
376
467
|
}>>): this;
|
|
377
468
|
/**
|
|
378
|
-
* Send a `PopupToHost` message back to the opener
|
|
379
|
-
* remember the host-window reference itself —
|
|
380
|
-
* each call, so it survives popup re-opens.
|
|
469
|
+
* Send a `PopupToHost` message back to the opener or parent, depending on
|
|
470
|
+
* `replyTo`. The bridge does not remember the host-window reference itself —
|
|
471
|
+
* it reads the target window each call, so it survives popup re-opens.
|
|
381
472
|
*/
|
|
382
473
|
send(message: PopupToHost): void;
|
|
383
474
|
/**
|
|
@@ -410,18 +501,36 @@ interface BroadcastSessionShareOptions {
|
|
|
410
501
|
*/
|
|
411
502
|
getSession: () => BroadcastSession | null;
|
|
412
503
|
/**
|
|
413
|
-
* Called when another tab broadcasts
|
|
414
|
-
*
|
|
504
|
+
* Called when another tab broadcasts `session-available` (a keyless,
|
|
505
|
+
* fire-and-forget "a session just got established" push — see
|
|
506
|
+
* `announce()`). Session-less listeners should react by issuing a fresh
|
|
507
|
+
* `request()`; this callback never receives key material itself.
|
|
415
508
|
*/
|
|
416
|
-
|
|
509
|
+
onAvailable?: () => void;
|
|
417
510
|
}
|
|
418
511
|
/**
|
|
419
512
|
* Popup-side cross-tab session sharing. Spec §5.5.
|
|
420
513
|
*
|
|
421
514
|
* - `request()`: broadcast `request-session`, wait up to 200ms for an offer.
|
|
422
515
|
* - `start()`: listen for incoming requests; reply with `offer-session` if we
|
|
423
|
-
* have a non-expired session and `getSession()` returns it.
|
|
424
|
-
* - `
|
|
516
|
+
* have a non-expired session and `getSession()` returns it. Also listens
|
|
517
|
+
* for `session-available` pushes (see `announce()`) and invokes
|
|
518
|
+
* `onAvailable` so a session-less peer can react without polling.
|
|
519
|
+
* - `broadcastLogout()`: notify peers to wipe their sessions too. NOTE: no
|
|
520
|
+
* `BroadcastSessionShare` call site wires a receive-side handler for this
|
|
521
|
+
* today — cross-tab logout is owned exclusively by `IdentityImpl` via its
|
|
522
|
+
* own `CrossTabSync` (`packages/identity/src/CrossTabSync.ts`), which
|
|
523
|
+
* shares this same channel name/message shape by convention. This method
|
|
524
|
+
* (and the `'logout'` message type) stays for public-API compatibility
|
|
525
|
+
* (`@cef-ai/wallet` is published) but intentionally has no paired
|
|
526
|
+
* `onLogout` option — wiring one would double-handle logout alongside
|
|
527
|
+
* `CrossTabSync`. See `ARCHITECTURE.md` Key invariants.
|
|
528
|
+
* - `announce()`: notify peers that THIS tab just established a session, so a
|
|
529
|
+
* session-less peer can immediately issue its own `request()` rather than
|
|
530
|
+
* waiting on a one-shot timer that may have already elapsed. Carries no key
|
|
531
|
+
* material — only `request()`/`offer-session` ever move keys, and only in
|
|
532
|
+
* direct response to an explicit request. This keeps the push trigger and
|
|
533
|
+
* the key transfer on two separate, independently-gated messages.
|
|
425
534
|
*
|
|
426
535
|
* Channel name is the spec-locked `scp-wallet-v2`. Only wallet-origin popups
|
|
427
536
|
* join. Host pages do not participate.
|
|
@@ -439,14 +548,32 @@ declare class BroadcastSessionShare {
|
|
|
439
548
|
request(opts?: {
|
|
440
549
|
timeoutMs?: number;
|
|
441
550
|
}): Promise<BroadcastSession | null>;
|
|
442
|
-
/**
|
|
551
|
+
/**
|
|
552
|
+
* Begin offering this tab's session in response to requests, and invoke
|
|
553
|
+
* `onAvailable` on incoming `session-available` pushes. Callers that only
|
|
554
|
+
* care about `onAvailable` (no session to offer) still call `start()` —
|
|
555
|
+
* `getSession()` returning `null` simply means `request-session` replies
|
|
556
|
+
* are skipped.
|
|
557
|
+
*/
|
|
443
558
|
start(): void;
|
|
444
559
|
stop(): void;
|
|
445
|
-
/**
|
|
560
|
+
/**
|
|
561
|
+
* Notify peers that the user logged out. Kept for public-API compatibility;
|
|
562
|
+
* no `BroadcastSessionShare` instance currently handles the receive side
|
|
563
|
+
* (see class doc) — cross-tab logout is `CrossTabSync`'s responsibility.
|
|
564
|
+
*/
|
|
446
565
|
broadcastLogout(): void;
|
|
566
|
+
/**
|
|
567
|
+
* Notify peers that this tab just established a session (register/login
|
|
568
|
+
* completed). Carries NO key material — it is purely a push trigger that
|
|
569
|
+
* tells session-less peers to issue their own `request()`. Key material
|
|
570
|
+
* still only ever moves as an `offer-session` reply to an explicit
|
|
571
|
+
* `request-session`; `announce()` does not bypass that gate.
|
|
572
|
+
*/
|
|
573
|
+
announce(): void;
|
|
447
574
|
/** Free the underlying channel handle. Call from unload handlers. */
|
|
448
575
|
close(): void;
|
|
449
576
|
private onMessage;
|
|
450
577
|
}
|
|
451
578
|
|
|
452
|
-
export { ALLOWED_ORIGIN_SCHEMES, type AppContext, type ApplicationBody, type ApplicationFilter, type ApplicationRecord, BROADCAST_CHANNEL_NAME, type BroadcastChannelMessage, type BroadcastSession, BroadcastSessionShare, type BroadcastSessionShareOptions, type CefSigner, type DelegationRequest, type DelegationResult, type DelegationScope, EmbedWallet, type EmbedWalletOptions, type HostMessageListener, type HostToPopup, type LoginResult, type PopupHandle, PopupHostBridge, type PopupHostBridgeOptions, type PopupMessageHandler, type PopupOpener, type PopupToHost, PopupTransport, type PopupTransportOptions, type SignIntent, type SignerSpec, type SignerType, type WalletEvent, type WalletEventListener, isMatchingOrigin };
|
|
579
|
+
export { ALLOWED_ORIGIN_SCHEMES, type AppContext, type ApplicationBody, type ApplicationFilter, type ApplicationRecord, BROADCAST_CHANNEL_NAME, type BroadcastChannelMessage, type BroadcastSession, BroadcastSessionShare, type BroadcastSessionShareOptions, type CefSigner, type DelegationRequest, type DelegationResult, type DelegationScope, EmbedWallet, type EmbedWalletOptions, type HostMessageListener, type HostToPopup, IframeTransport, type IframeTransportOptions, type LoginResult, type PopupHandle, PopupHostBridge, type PopupHostBridgeOptions, type PopupMessageHandler, type PopupOpener, type PopupToHost, PopupTransport, type PopupTransportOptions, type SignIntent, type SignerSpec, type SignerType, type WalletEvent, type WalletEventListener, type WalletTransport, isMatchingOrigin };
|