@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/README.md
CHANGED
|
@@ -57,17 +57,112 @@ const sig = await signer.signMessage(new TextEncoder().encode('Hello'));
|
|
|
57
57
|
console.log('Signature bytes:', sig);
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
## Transport: iframe (default) vs popup
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
new EmbedWallet({ appId, walletOrigin, transport: 'iframe' | 'popup' });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`transport` defaults to `'iframe'`. This controls how the SDK talks to the
|
|
67
|
+
wallet UI at `${walletOrigin}/embed/wallet`:
|
|
68
|
+
|
|
69
|
+
- **`'iframe'` (default, `IframeTransport`).** The SDK injects a hidden,
|
|
70
|
+
zero-size `<iframe>` pinned to `walletOrigin` on first use. It stays
|
|
71
|
+
invisible until the wallet needs to show UI (a consent prompt or the
|
|
72
|
+
login "Continue" gesture — see below), at which point it grows into a
|
|
73
|
+
centered modal with a backdrop, then shrinks back to hidden. No
|
|
74
|
+
popup-blocker exposure, no separate window for the user to lose track of.
|
|
75
|
+
- **`'popup'` (`PopupTransport`, the original transport).** Opens
|
|
76
|
+
`${walletOrigin}/embed/wallet` in a real popup window via `window.open`.
|
|
77
|
+
Still fully supported — pass `transport: 'popup'` to opt back into it (e.g.
|
|
78
|
+
if your host page can't grant the iframe permission below, or you prefer
|
|
79
|
+
the popup UX).
|
|
80
|
+
|
|
81
|
+
Both implement the same `WalletTransport` interface, so `EmbedWallet`'s
|
|
82
|
+
public methods behave identically regardless of which one is active.
|
|
83
|
+
|
|
84
|
+
### `allow` / `Permissions-Policy` requirement (iframe transport only)
|
|
85
|
+
|
|
86
|
+
The injected iframe is created with:
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<iframe
|
|
90
|
+
src="https://wallet.example.com/embed/wallet?origin=..."
|
|
91
|
+
allow="publickey-credentials-get https://wallet.example.com"
|
|
92
|
+
/>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This delegates the WebAuthn **`get()`** (i.e. `login()`) ceremony into the
|
|
96
|
+
cross-origin iframe. Two things follow for host apps:
|
|
97
|
+
|
|
98
|
+
1. If your page sends a restrictive `Permissions-Policy` HTTP response
|
|
99
|
+
header, it must explicitly allow `publickey-credentials-get` for your
|
|
100
|
+
wallet origin (e.g. `Permissions-Policy: publickey-credentials-get=(self "https://wallet.example.com")`),
|
|
101
|
+
or the browser will block WebAuthn `get()` inside the iframe regardless of
|
|
102
|
+
the `allow` attribute. Pages with no `Permissions-Policy` header (the
|
|
103
|
+
common case) need no action — the `allow` attribute alone is sufficient.
|
|
104
|
+
2. **Only `-get` is delegated, not `-create`.** Safari blocks WebAuthn
|
|
105
|
+
`create()` (passkey registration) in cross-origin iframes outright, so
|
|
106
|
+
`create` is deliberately not requested in `allow`. See "Register uses a
|
|
107
|
+
synchronous popup" below.
|
|
108
|
+
|
|
109
|
+
### Register uses a synchronous popup
|
|
110
|
+
|
|
111
|
+
`EmbedWallet.register()` always performs its WebAuthn ceremony over a
|
|
112
|
+
`PopupTransport`, even when the SDK is configured with the (default) iframe
|
|
113
|
+
transport:
|
|
114
|
+
|
|
115
|
+
- If the configured transport is already `PopupTransport` (`transport: 'popup'`),
|
|
116
|
+
`register()` reuses it directly.
|
|
117
|
+
- Otherwise (iframe transport), `register()` opens a **transient popup
|
|
118
|
+
synchronously** — inside the same call stack as the consumer's click
|
|
119
|
+
handler — performs the `wallet:hello` (`intent: 'register'`) ceremony over
|
|
120
|
+
it, and closes it when done. "Synchronous" matters: Safari requires
|
|
121
|
+
`window.open` to happen within a real user-activation event, so `register()`
|
|
122
|
+
cannot defer opening the popup until after an `await`.
|
|
123
|
+
- `login()` and every other method (`getSigner`, `asSigner`,
|
|
124
|
+
`requestDelegation`, `saveApplication`, `findApplications`, `logout`) always
|
|
125
|
+
use the configured transport (iframe by default) unchanged — only
|
|
126
|
+
`register()` special-cases the popup punch-out.
|
|
127
|
+
- After the register popup completes, the persistent iframe adopts the new
|
|
128
|
+
session automatically via a same-origin `BroadcastChannel('scp-wallet-v2')`
|
|
129
|
+
handoff (`BroadcastSessionShare`) — no explicit `login()` call is needed
|
|
130
|
+
after `register()` resolves.
|
|
131
|
+
|
|
60
132
|
## Architecture
|
|
61
133
|
|
|
62
|
-
-
|
|
63
|
-
|
|
64
|
-
|
|
134
|
+
- Default transport (`IframeTransport`) injects a hidden, permission-scoped
|
|
135
|
+
iframe at `wallet.example.com/embed/wallet?origin=...` and shows/hides a
|
|
136
|
+
modal overlay on request from the wallet document (see "Transport" above).
|
|
137
|
+
- Opt-in transport (`PopupTransport`) opens `wallet.example.com/embed/wallet?origin=...`
|
|
138
|
+
in a separate popup window instead.
|
|
139
|
+
- Either way, the wallet surface runs the WebAuthn ceremony, signing, and
|
|
140
|
+
consent UI, and posts id-correlated results back over `postMessage`.
|
|
141
|
+
- Cross-tab session sharing via `BroadcastChannel('scp-wallet-v2')` so a second
|
|
142
|
+
host tab (or the register popup handing off to the iframe) discovers an
|
|
143
|
+
existing session.
|
|
65
144
|
|
|
66
145
|
Design specs and ADRs are maintained in the company memory bank.
|
|
67
146
|
|
|
147
|
+
## Public exports
|
|
148
|
+
|
|
149
|
+
- `EmbedWallet` — the SDK class (see Quick start).
|
|
150
|
+
- `WalletTransport` (type) — the transport interface both transports
|
|
151
|
+
implement; only relevant if you're injecting a custom transport via the
|
|
152
|
+
`__internal__` test seam.
|
|
153
|
+
- `PopupTransport` / `PopupTransportOptions` — the popup-window transport.
|
|
154
|
+
- `IframeTransport` / `IframeTransportOptions` — the default iframe transport.
|
|
155
|
+
Options: `walletOrigin`, `hostWindow?`, `document?`, `defaultTimeoutMs?`,
|
|
156
|
+
plus two test seams (`mountIframe?`, `onOverlay?`) not needed in production
|
|
157
|
+
use.
|
|
158
|
+
- `PopupHostBridge` / `PopupHostBridgeOptions`, `BroadcastSessionShare` /
|
|
159
|
+
`BroadcastSessionShareOptions` / `BroadcastSession` — wallet-side building
|
|
160
|
+
blocks (used by the wallet SPA itself, exported for advanced/embedding
|
|
161
|
+
scenarios); most host-app integrations never need these directly.
|
|
162
|
+
|
|
68
163
|
## Capabilities
|
|
69
164
|
|
|
70
|
-
- `register({ label? })` / `login()` — opens the popup, performs the WebAuthn ceremony, returns the session
|
|
165
|
+
- `register({ name?, email?, label? })` / `login()` — opens the configured transport surface (iframe by default; `register()` always punches out to a popup — see "Register uses a synchronous popup"), performs the WebAuthn ceremony, returns the session. `name` → the passkey's `user.displayName`, `email` → `user.name` (what OS/browser passkey managers show); `label` is a legacy single-field fallback for both.
|
|
71
166
|
- `getSigner({ chain })` — returns an `EthersSigner` (`'evm'`), `PolkadotSigner` (`'cere'`), or `SolanaSigner` (`'solana'`); each exposes `signMessage(bytes: Uint8Array): Promise<Uint8Array>`
|
|
72
167
|
- `requestDelegation({ capabilities, ttl, appId?, agentPubkey?, constraints? })` — requests a scoped delegation token; the popup shows a consent UI
|
|
73
168
|
- `saveApplication(record)` / `findApplications(filter)` — manage app context records
|
package/dist/index.cjs
CHANGED
|
@@ -107,8 +107,15 @@ var PopupTransport = class {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Close the popup if open. Immediately rejects any in-flight `request()`
|
|
112
|
+
* calls (mirrors `IframeTransport.close()`) rather than leaving them to
|
|
113
|
+
* time out — a caller that closes the popup mid-request (e.g.
|
|
114
|
+
* `EmbedWallet.register()`'s `finally` block) gets a prompt rejection
|
|
115
|
+
* instead of waiting out the full default 60s timeout.
|
|
116
|
+
*/
|
|
111
117
|
close() {
|
|
118
|
+
this.rejectAllPending("transport closed");
|
|
112
119
|
if (this.popup && !this.popup.closed) {
|
|
113
120
|
this.popup.close();
|
|
114
121
|
}
|
|
@@ -132,6 +139,14 @@ var PopupTransport = class {
|
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
// ---- internal -------------------------------------------------------------
|
|
142
|
+
/** Reject every in-flight request with a `WalletError('internal', reason)`. Idempotent. */
|
|
143
|
+
rejectAllPending(reason) {
|
|
144
|
+
for (const [id, pending] of this.pending) {
|
|
145
|
+
clearTimeout(pending.timer);
|
|
146
|
+
this.pending.delete(id);
|
|
147
|
+
pending.reject(new walletApiClient.WalletError("internal", reason));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
135
150
|
ensureListening() {
|
|
136
151
|
if (this.listener) return;
|
|
137
152
|
this.listener = (event) => this.onMessage(event);
|
|
@@ -189,6 +204,173 @@ function defaultOpener(url, features) {
|
|
|
189
204
|
const opened = w == null ? void 0 : w.open(url, "_blank", features);
|
|
190
205
|
return opened;
|
|
191
206
|
}
|
|
207
|
+
var IframeTransport = class {
|
|
208
|
+
constructor(options) {
|
|
209
|
+
this.iframe = null;
|
|
210
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
211
|
+
this.listener = null;
|
|
212
|
+
this.ready = false;
|
|
213
|
+
this.outbox = [];
|
|
214
|
+
this.backdrop = null;
|
|
215
|
+
this.hiddenCssText = "";
|
|
216
|
+
this.overlayVisible = false;
|
|
217
|
+
this.opts = __spreadValues({
|
|
218
|
+
hostWindow: globalThis.window,
|
|
219
|
+
document: globalThis.document,
|
|
220
|
+
defaultTimeoutMs: 6e4,
|
|
221
|
+
mountIframe: options.mountIframe,
|
|
222
|
+
onOverlay: options.onOverlay
|
|
223
|
+
}, options);
|
|
224
|
+
}
|
|
225
|
+
ensureIframe() {
|
|
226
|
+
var _a, _b, _c;
|
|
227
|
+
if (this.iframe) return;
|
|
228
|
+
const hostOrigin = (_b = (_a = globalThis.location) == null ? void 0 : _a.origin) != null ? _b : "";
|
|
229
|
+
const url = `${this.opts.walletOrigin}/embed/wallet?origin=${encodeURIComponent(hostOrigin)}`;
|
|
230
|
+
const allow = `publickey-credentials-get ${this.opts.walletOrigin}`;
|
|
231
|
+
if (this.opts.mountIframe) {
|
|
232
|
+
this.iframe = this.opts.mountIframe(url, allow);
|
|
233
|
+
} else {
|
|
234
|
+
const el = this.opts.document.createElement("iframe");
|
|
235
|
+
el.src = url;
|
|
236
|
+
el.setAttribute("allow", allow);
|
|
237
|
+
el.style.cssText = "position:fixed;border:0;width:0;height:0;left:-9999px;";
|
|
238
|
+
this.opts.document.body.appendChild(el);
|
|
239
|
+
this.iframe = el;
|
|
240
|
+
}
|
|
241
|
+
this.hiddenCssText = (_c = this.iframe.style.cssText) != null ? _c : "";
|
|
242
|
+
this.ready = false;
|
|
243
|
+
this.outbox = [];
|
|
244
|
+
this.overlayVisible = false;
|
|
245
|
+
this.ensureListening();
|
|
246
|
+
}
|
|
247
|
+
showOverlay() {
|
|
248
|
+
var _a, _b;
|
|
249
|
+
if (this.overlayVisible) return;
|
|
250
|
+
this.overlayVisible = true;
|
|
251
|
+
if (this.iframe) {
|
|
252
|
+
const backdrop = this.opts.document.createElement("div");
|
|
253
|
+
backdrop.setAttribute("data-wallet-overlay-backdrop", "");
|
|
254
|
+
backdrop.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:2147483646;";
|
|
255
|
+
this.opts.document.body.appendChild(backdrop);
|
|
256
|
+
this.backdrop = backdrop;
|
|
257
|
+
this.iframe.style.cssText = "position:fixed;border:0;z-index:2147483647;width:min(420px,100vw);height:min(640px,100vh);left:50%;top:50%;transform:translate(-50%,-50%);";
|
|
258
|
+
}
|
|
259
|
+
(_b = (_a = this.opts).onOverlay) == null ? void 0 : _b.call(_a, true);
|
|
260
|
+
}
|
|
261
|
+
hideOverlay() {
|
|
262
|
+
var _a, _b, _c;
|
|
263
|
+
if (!this.overlayVisible) return;
|
|
264
|
+
this.overlayVisible = false;
|
|
265
|
+
(_a = this.backdrop) == null ? void 0 : _a.remove();
|
|
266
|
+
this.backdrop = null;
|
|
267
|
+
if (this.iframe) {
|
|
268
|
+
this.iframe.style.cssText = this.hiddenCssText;
|
|
269
|
+
}
|
|
270
|
+
(_c = (_b = this.opts).onOverlay) == null ? void 0 : _c.call(_b, false);
|
|
271
|
+
}
|
|
272
|
+
ensureListening() {
|
|
273
|
+
if (this.listener) return;
|
|
274
|
+
this.listener = (e) => this.onMessage(e);
|
|
275
|
+
this.opts.hostWindow.addEventListener("message", this.listener);
|
|
276
|
+
}
|
|
277
|
+
request(_0, _1) {
|
|
278
|
+
return __async(this, arguments, function* (message, expectedTypes, init = {}) {
|
|
279
|
+
var _a;
|
|
280
|
+
this.ensureIframe();
|
|
281
|
+
const id = message.id;
|
|
282
|
+
const timeoutMs = (_a = init.timeoutMs) != null ? _a : this.opts.defaultTimeoutMs;
|
|
283
|
+
return new Promise((resolve, reject) => {
|
|
284
|
+
const timer = setTimeout(() => {
|
|
285
|
+
if (this.pending.has(id)) {
|
|
286
|
+
this.pending.delete(id);
|
|
287
|
+
this.outbox = this.outbox.filter((m) => m.id !== id);
|
|
288
|
+
reject(new walletApiClient.WalletError("internal", `request "${message.type}" (id=${id}) timed out after ${timeoutMs}ms`));
|
|
289
|
+
}
|
|
290
|
+
}, timeoutMs);
|
|
291
|
+
this.pending.set(id, { expectedTypes, resolve: (m) => resolve(m), reject, timer });
|
|
292
|
+
if (this.ready) this.postToIframe(message);
|
|
293
|
+
else this.outbox.push(message);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
postToIframe(message) {
|
|
298
|
+
var _a, _b;
|
|
299
|
+
(_b = (_a = this.iframe) == null ? void 0 : _a.contentWindow) == null ? void 0 : _b.postMessage(message, this.opts.walletOrigin);
|
|
300
|
+
}
|
|
301
|
+
close() {
|
|
302
|
+
var _a, _b;
|
|
303
|
+
this.rejectAllPending("transport closed");
|
|
304
|
+
(_a = this.backdrop) == null ? void 0 : _a.remove();
|
|
305
|
+
this.backdrop = null;
|
|
306
|
+
this.overlayVisible = false;
|
|
307
|
+
(_b = this.iframe) == null ? void 0 : _b.remove();
|
|
308
|
+
this.iframe = null;
|
|
309
|
+
this.ready = false;
|
|
310
|
+
this.outbox = [];
|
|
311
|
+
}
|
|
312
|
+
destroy() {
|
|
313
|
+
this.close();
|
|
314
|
+
if (this.listener) {
|
|
315
|
+
this.opts.hostWindow.removeEventListener("message", this.listener);
|
|
316
|
+
this.listener = null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
rejectAllPending(reason) {
|
|
320
|
+
for (const [id, p] of this.pending) {
|
|
321
|
+
clearTimeout(p.timer);
|
|
322
|
+
this.pending.delete(id);
|
|
323
|
+
p.reject(new walletApiClient.WalletError("internal", reason));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
onMessage(event) {
|
|
327
|
+
const data = event.data;
|
|
328
|
+
if (!isMatchingOrigin(event.origin, this.opts.walletOrigin)) {
|
|
329
|
+
if (data && typeof data.id === "string" && this.pending.has(data.id)) {
|
|
330
|
+
const p2 = this.pending.get(data.id);
|
|
331
|
+
this.pending.delete(data.id);
|
|
332
|
+
clearTimeout(p2.timer);
|
|
333
|
+
p2.reject(
|
|
334
|
+
new walletApiClient.WalletError(
|
|
335
|
+
"origin-mismatch",
|
|
336
|
+
`message from "${event.origin}" rejected (expected "${this.opts.walletOrigin}")`
|
|
337
|
+
)
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (!data || typeof data.type !== "string" || typeof data.id !== "string") return;
|
|
343
|
+
if (data.type === "wallet:ready") {
|
|
344
|
+
if (this.ready) return;
|
|
345
|
+
this.ready = true;
|
|
346
|
+
const flush = this.outbox;
|
|
347
|
+
this.outbox = [];
|
|
348
|
+
for (const m of flush) this.postToIframe(m);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (data.type === "wallet:ui:show") {
|
|
352
|
+
this.showOverlay();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (data.type === "wallet:ui:hide") {
|
|
356
|
+
this.hideOverlay();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const p = this.pending.get(data.id);
|
|
360
|
+
if (!p) return;
|
|
361
|
+
clearTimeout(p.timer);
|
|
362
|
+
this.pending.delete(data.id);
|
|
363
|
+
if (data.type === "wallet:error") {
|
|
364
|
+
p.reject(new walletApiClient.WalletError(data.code, data.message, data.traceId));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (!p.expectedTypes.includes(data.type)) {
|
|
368
|
+
p.reject(new walletApiClient.WalletError("internal", `received unexpected response type "${data.type}" for id "${data.id}"`));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
p.resolve(data);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
192
374
|
|
|
193
375
|
// src/EmbedWallet.ts
|
|
194
376
|
var EmbedWallet = class {
|
|
@@ -196,7 +378,7 @@ var EmbedWallet = class {
|
|
|
196
378
|
this._addresses = null;
|
|
197
379
|
this._credentialId = null;
|
|
198
380
|
this._listeners = {};
|
|
199
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
381
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
200
382
|
const injectedTransport = (_a = opts.__internal__) == null ? void 0 : _a.transport;
|
|
201
383
|
if (!injectedTransport && !opts.walletOrigin) {
|
|
202
384
|
throw new Error("EmbedWallet: `walletOrigin` is required (e.g. https://wallet.example.com).");
|
|
@@ -205,9 +387,8 @@ var EmbedWallet = class {
|
|
|
205
387
|
this.walletOrigin = (_b = opts.walletOrigin) != null ? _b : "";
|
|
206
388
|
this.popup = { width: (_d = (_c = opts.popup) == null ? void 0 : _c.width) != null ? _d : 420, height: (_f = (_e = opts.popup) == null ? void 0 : _e.height) != null ? _f : 640 };
|
|
207
389
|
this.appName = (_g = opts.appName) != null ? _g : "";
|
|
208
|
-
this.
|
|
209
|
-
|
|
210
|
-
});
|
|
390
|
+
this.regPopupOpener = (_h = opts.__internal__) == null ? void 0 : _h.registerPopupOpener;
|
|
391
|
+
this.transport = injectedTransport != null ? injectedTransport : opts.transport === "popup" ? new PopupTransport({ walletOrigin: this.walletOrigin }) : new IframeTransport({ walletOrigin: this.walletOrigin });
|
|
211
392
|
}
|
|
212
393
|
get isLoggedIn() {
|
|
213
394
|
return this._addresses !== null;
|
|
@@ -218,9 +399,27 @@ var EmbedWallet = class {
|
|
|
218
399
|
get credentialId() {
|
|
219
400
|
return this._credentialId;
|
|
220
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Safari blocks WebAuthn `create()` in cross-origin iframes, so `register()`
|
|
404
|
+
* always runs its `wallet:hello` ceremony over a `PopupTransport` — even
|
|
405
|
+
* when the configured transport is the iframe. When the main transport is
|
|
406
|
+
* already a `PopupTransport` it's reused; otherwise a transient one is
|
|
407
|
+
* opened synchronously (within the caller's click) and closed afterward.
|
|
408
|
+
* `login()` and all other ops use the configured transport unchanged.
|
|
409
|
+
*/
|
|
221
410
|
register() {
|
|
222
411
|
return __async(this, arguments, function* (opts = {}) {
|
|
223
|
-
|
|
412
|
+
if (this.transport instanceof PopupTransport) {
|
|
413
|
+
return this.helloAndAwaitLogin("register", opts);
|
|
414
|
+
}
|
|
415
|
+
const popup = new PopupTransport(__spreadValues({
|
|
416
|
+
walletOrigin: this.walletOrigin
|
|
417
|
+
}, this.regPopupOpener ? { openPopup: this.regPopupOpener } : {}));
|
|
418
|
+
try {
|
|
419
|
+
return yield this.helloAndAwaitLogin("register", opts, popup);
|
|
420
|
+
} finally {
|
|
421
|
+
popup.close();
|
|
422
|
+
}
|
|
224
423
|
});
|
|
225
424
|
}
|
|
226
425
|
login() {
|
|
@@ -379,16 +578,18 @@ var EmbedWallet = class {
|
|
|
379
578
|
return { appId: this.appId, name: this.appName, origin };
|
|
380
579
|
}
|
|
381
580
|
helloAndAwaitLogin(_0) {
|
|
382
|
-
return __async(this, arguments, function* (intent, opts = {}) {
|
|
581
|
+
return __async(this, arguments, function* (intent, opts = {}, transport = this.transport) {
|
|
383
582
|
const id = crypto.randomUUID();
|
|
384
583
|
const helloMessage = {
|
|
385
584
|
type: "wallet:hello",
|
|
386
585
|
id,
|
|
387
586
|
appContext: this.appContext(),
|
|
388
587
|
intent,
|
|
389
|
-
label: opts.label
|
|
588
|
+
label: opts.label,
|
|
589
|
+
name: opts.name,
|
|
590
|
+
email: opts.email
|
|
390
591
|
};
|
|
391
|
-
const result = yield
|
|
592
|
+
const result = yield transport.request(helloMessage, [
|
|
392
593
|
"wallet:login:ok"
|
|
393
594
|
]);
|
|
394
595
|
this._addresses = result.addresses;
|
|
@@ -421,7 +622,8 @@ var PopupHostBridge = class {
|
|
|
421
622
|
this.handlers = {};
|
|
422
623
|
this.listener = null;
|
|
423
624
|
this.opts = __spreadValues({
|
|
424
|
-
popupWindow: globalThis.window
|
|
625
|
+
popupWindow: globalThis.window,
|
|
626
|
+
replyTo: "opener"
|
|
425
627
|
}, options);
|
|
426
628
|
}
|
|
427
629
|
/** Register a handler for a specific message type. Replaces any prior handler. */
|
|
@@ -430,16 +632,16 @@ var PopupHostBridge = class {
|
|
|
430
632
|
return this;
|
|
431
633
|
}
|
|
432
634
|
/**
|
|
433
|
-
* Send a `PopupToHost` message back to the opener
|
|
434
|
-
* remember the host-window reference itself —
|
|
435
|
-
* each call, so it survives popup re-opens.
|
|
635
|
+
* Send a `PopupToHost` message back to the opener or parent, depending on
|
|
636
|
+
* `replyTo`. The bridge does not remember the host-window reference itself —
|
|
637
|
+
* it reads the target window each call, so it survives popup re-opens.
|
|
436
638
|
*/
|
|
437
639
|
send(message) {
|
|
438
|
-
const
|
|
439
|
-
if (!
|
|
640
|
+
const target = this.opts.replyTo === "parent" ? globalThis.parent : globalThis.opener;
|
|
641
|
+
if (!target || typeof target.postMessage !== "function") {
|
|
440
642
|
return;
|
|
441
643
|
}
|
|
442
|
-
|
|
644
|
+
target.postMessage(message, this.opts.hostOrigin);
|
|
443
645
|
}
|
|
444
646
|
/**
|
|
445
647
|
* Start listening for inbound messages.
|
|
@@ -525,8 +727,8 @@ var BroadcastSessionShare = class {
|
|
|
525
727
|
});
|
|
526
728
|
return;
|
|
527
729
|
}
|
|
528
|
-
if (msg.type === "
|
|
529
|
-
(_b = (_a = this.opts).
|
|
730
|
+
if (msg.type === "session-available") {
|
|
731
|
+
(_b = (_a = this.opts).onAvailable) == null ? void 0 : _b.call(_a);
|
|
530
732
|
return;
|
|
531
733
|
}
|
|
532
734
|
};
|
|
@@ -565,7 +767,13 @@ var BroadcastSessionShare = class {
|
|
|
565
767
|
});
|
|
566
768
|
});
|
|
567
769
|
}
|
|
568
|
-
/**
|
|
770
|
+
/**
|
|
771
|
+
* Begin offering this tab's session in response to requests, and invoke
|
|
772
|
+
* `onAvailable` on incoming `session-available` pushes. Callers that only
|
|
773
|
+
* care about `onAvailable` (no session to offer) still call `start()` —
|
|
774
|
+
* `getSession()` returning `null` simply means `request-session` replies
|
|
775
|
+
* are skipped.
|
|
776
|
+
*/
|
|
569
777
|
start() {
|
|
570
778
|
if (this.listening) return;
|
|
571
779
|
this.listening = true;
|
|
@@ -575,10 +783,24 @@ var BroadcastSessionShare = class {
|
|
|
575
783
|
this.channel.removeEventListener("message", this.onMessage);
|
|
576
784
|
this.listening = false;
|
|
577
785
|
}
|
|
578
|
-
/**
|
|
786
|
+
/**
|
|
787
|
+
* Notify peers that the user logged out. Kept for public-API compatibility;
|
|
788
|
+
* no `BroadcastSessionShare` instance currently handles the receive side
|
|
789
|
+
* (see class doc) — cross-tab logout is `CrossTabSync`'s responsibility.
|
|
790
|
+
*/
|
|
579
791
|
broadcastLogout() {
|
|
580
792
|
this.channel.postMessage({ type: "logout" });
|
|
581
793
|
}
|
|
794
|
+
/**
|
|
795
|
+
* Notify peers that this tab just established a session (register/login
|
|
796
|
+
* completed). Carries NO key material — it is purely a push trigger that
|
|
797
|
+
* tells session-less peers to issue their own `request()`. Key material
|
|
798
|
+
* still only ever moves as an `offer-session` reply to an explicit
|
|
799
|
+
* `request-session`; `announce()` does not bypass that gate.
|
|
800
|
+
*/
|
|
801
|
+
announce() {
|
|
802
|
+
this.channel.postMessage({ type: "session-available" });
|
|
803
|
+
}
|
|
582
804
|
/** Free the underlying channel handle. Call from unload handlers. */
|
|
583
805
|
close() {
|
|
584
806
|
this.stop();
|
|
@@ -590,6 +812,7 @@ exports.ALLOWED_ORIGIN_SCHEMES = ALLOWED_ORIGIN_SCHEMES;
|
|
|
590
812
|
exports.BROADCAST_CHANNEL_NAME = BROADCAST_CHANNEL_NAME;
|
|
591
813
|
exports.BroadcastSessionShare = BroadcastSessionShare;
|
|
592
814
|
exports.EmbedWallet = EmbedWallet;
|
|
815
|
+
exports.IframeTransport = IframeTransport;
|
|
593
816
|
exports.PopupHostBridge = PopupHostBridge;
|
|
594
817
|
exports.PopupTransport = PopupTransport;
|
|
595
818
|
exports.isMatchingOrigin = isMatchingOrigin;
|