@chipi-stack/starknet-connector 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/transport.js DELETED
@@ -1,217 +0,0 @@
1
- /**
2
- * Client-side (dApp) transport: opens the Chipi hosted wallet in a popup,
3
- * performs the ready handshake, and forwards get-starknet `wallet_*` RPC calls
4
- * over `postMessage`, correlating responses by id. The popup is reused across
5
- * requests; if the user closed it, the next request reopens it.
6
- */
7
- import { CHIPI_WC, isChipiWalletMessage, } from "./protocol.js";
8
- function deferred() {
9
- let resolve;
10
- let reject;
11
- const promise = new Promise((res, rej) => {
12
- resolve = res;
13
- reject = rej;
14
- });
15
- return { promise, resolve, reject };
16
- }
17
- function randomId() {
18
- // App-runtime ids; uniqueness only needs to hold within one open popup.
19
- return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
20
- }
21
- export class ChipiPopupTransport {
22
- constructor(opts) {
23
- this.opts = opts;
24
- this.target = null;
25
- this.ownsTarget = false;
26
- this.ready = false;
27
- this.listening = false;
28
- this.pending = new Map();
29
- this.handlers = new Map();
30
- this.readyDeferred = null;
31
- this.openPromise = null;
32
- this.closeWatch = null;
33
- this.onMessage = (e) => {
34
- if (e.origin !== this.walletOrigin || !isChipiWalletMessage(e.data))
35
- return;
36
- // Isolate to OUR wallet window — without this, a second Chipi popup (another
37
- // dApp, same origin) could leak its `ready`/`event` into this transport.
38
- if (this.target && e.source !== this.target)
39
- return;
40
- const msg = e.data;
41
- if (msg.type === "ready") {
42
- this.ready = true;
43
- this.readyDeferred?.resolve();
44
- this.readyDeferred = null;
45
- // A fresh /connect re-announces "ready" — e.g. after the user signs in,
46
- // which reloads the popup onto a new page. The request we already sent went
47
- // to the previous page, which is gone. Re-send any in-flight requests so
48
- // they reach the new page. The hosted wallet de-dupes by request id, so a
49
- // repeat (e.g. from the popup's multi-post ready) is harmless.
50
- if (this.target) {
51
- for (const p of this.pending.values()) {
52
- this.target.postMessage(p.message, this.walletOrigin);
53
- }
54
- }
55
- return;
56
- }
57
- if (msg.type === "rpc-result") {
58
- const p = this.pending.get(msg.id);
59
- if (!p)
60
- return;
61
- this.pending.delete(msg.id);
62
- if (msg.ok)
63
- p.resolve(msg.result);
64
- else
65
- p.reject(Object.assign(new Error(msg.error.message), { code: msg.error.code }));
66
- return;
67
- }
68
- if (msg.type === "event") {
69
- this.handlers.get(msg.event)?.forEach((h) => h(msg.data));
70
- }
71
- };
72
- this.url = opts.walletUrl.replace(/\/$/, "") + "/connect";
73
- this.walletOrigin = new URL(opts.walletUrl).origin;
74
- }
75
- on(event, handler) {
76
- (this.handlers.get(event) ?? this.handlers.set(event, new Set()).get(event)).add(handler);
77
- }
78
- off(event, handler) {
79
- this.handlers.get(event)?.delete(handler);
80
- }
81
- ensureListener() {
82
- if (this.listening || typeof window === "undefined")
83
- return;
84
- window.addEventListener("message", this.onMessage);
85
- this.listening = true;
86
- }
87
- /** Resolve "auto" → "parent" when iframed, else "popup". */
88
- resolveMode() {
89
- const mode = this.opts.mode ?? "popup";
90
- if (mode !== "auto")
91
- return mode;
92
- return typeof window !== "undefined" && window.parent !== window ? "parent" : "popup";
93
- }
94
- /** Open (or reuse) the wallet (popup window OR the parent frame) and wait for
95
- * its ready handshake. Concurrent calls share ONE handshake. */
96
- async open() {
97
- if (typeof window === "undefined")
98
- throw new Error("chipi_connector_requires_browser");
99
- this.ensureListener();
100
- const isParent = this.resolveMode() === "parent";
101
- if (this.ready && this.target && (isParent || !this.target.closed))
102
- return;
103
- if (this.openPromise)
104
- return this.openPromise;
105
- this.openPromise = this.doOpen(isParent);
106
- try {
107
- await this.openPromise;
108
- }
109
- finally {
110
- this.openPromise = null;
111
- }
112
- }
113
- async doOpen(isParent) {
114
- if (isParent) {
115
- if (window.parent === window)
116
- throw new Error("chipi_not_in_iframe");
117
- this.target = window.parent;
118
- this.ownsTarget = false;
119
- }
120
- else {
121
- this.ready = false;
122
- // Unique per-open window name so a reconnect can never reuse a stale popup
123
- // that still holds a previous session's pending approval.
124
- const name = this.opts.windowName ?? `chipi-wallet-${randomId()}`;
125
- this.target = window.open(this.url, name, this.opts.windowFeatures ?? "popup=yes,width=420,height=680");
126
- this.ownsTarget = true;
127
- if (!this.target)
128
- throw Object.assign(new Error("Popup blocked"), { code: "popup_blocked" });
129
- }
130
- this.readyDeferred = deferred();
131
- await this.awaitReady();
132
- }
133
- async awaitReady() {
134
- // Generous: a cold login (Clerk / passkey) can take a while. We only reject
135
- // here on an outright stall — the popup-closed watcher handles the real
136
- // "user gave up / closed it" case quickly.
137
- const timeoutMs = this.opts.openTimeoutMs ?? 180000;
138
- let timer;
139
- const timeout = new Promise((_, reject) => {
140
- timer = setTimeout(() => reject(new Error("wallet_open_timeout")), timeoutMs);
141
- });
142
- try {
143
- await Promise.race([this.readyDeferred.promise, timeout]);
144
- }
145
- finally {
146
- if (timer)
147
- clearTimeout(timer);
148
- }
149
- }
150
- /** Reject every in-flight request (popup closed / disconnected). */
151
- rejectAllPending(message) {
152
- for (const [, p] of this.pending)
153
- p.reject(Object.assign(new Error(message), { code: 4001 }));
154
- this.pending.clear();
155
- }
156
- /** While requests are in flight, watch for the user closing the popup and
157
- * reject them — otherwise the dApp's promise would hang forever. */
158
- startCloseWatch() {
159
- if (this.closeWatch || !this.ownsTarget)
160
- return;
161
- this.closeWatch = setInterval(() => {
162
- if (this.target?.closed) {
163
- this.ready = false;
164
- this.target = null;
165
- this.rejectAllPending("User closed the wallet");
166
- this.stopCloseWatch();
167
- }
168
- else if (this.pending.size === 0) {
169
- this.stopCloseWatch();
170
- }
171
- }, 500);
172
- }
173
- stopCloseWatch() {
174
- if (this.closeWatch) {
175
- clearInterval(this.closeWatch);
176
- this.closeWatch = null;
177
- }
178
- }
179
- /** Forward a get-starknet RPC call to the wallet, awaiting its response. */
180
- async request(call) {
181
- await this.open();
182
- if (this.ownsTarget)
183
- this.target?.focus();
184
- const id = randomId();
185
- const message = {
186
- channel: CHIPI_WC,
187
- type: "rpc",
188
- id,
189
- origin: window.location.origin,
190
- call,
191
- };
192
- return new Promise((resolve, reject) => {
193
- this.pending.set(id, { resolve, reject, message });
194
- this.target.postMessage(message, this.walletOrigin);
195
- this.startCloseWatch();
196
- });
197
- }
198
- /** Close the popup (if we own one) + reject in-flight requests. */
199
- close() {
200
- this.stopCloseWatch();
201
- try {
202
- if (this.ownsTarget)
203
- this.target?.close();
204
- }
205
- catch {
206
- // ignore
207
- }
208
- this.target = null;
209
- this.ready = false;
210
- this.rejectAllPending("disconnected");
211
- if (this.listening && typeof window !== "undefined") {
212
- window.removeEventListener("message", this.onMessage);
213
- this.listening = false;
214
- }
215
- }
216
- }
217
- //# sourceMappingURL=transport.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}