@yeying-community/web3-bs 1.0.7 → 1.0.10

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.
@@ -7,10 +7,46 @@
7
7
  const YEYING_RDNS = 'io.github.yeying';
8
8
  const DEFAULT_TIMEOUT = 1000;
9
9
  const DEFAULT_ACCOUNT_STORAGE_KEY = 'yeying:last_account';
10
+ const DEFAULT_PROVIDER_POLL_INTERVAL = 100;
11
+ const DEFAULT_PROVIDER_MAX_POLLS = 20;
12
+ const requestAccountsInFlight = new WeakMap();
13
+ function isProvider(value) {
14
+ return !!value && typeof value.request === 'function';
15
+ }
10
16
  function getWindowEthereum() {
11
17
  if (typeof window === 'undefined')
12
18
  return null;
13
- return window.ethereum || null;
19
+ const ethereum = window.ethereum;
20
+ return isProvider(ethereum) ? ethereum : null;
21
+ }
22
+ function getWindowProviderCandidates() {
23
+ if (typeof window === 'undefined')
24
+ return [];
25
+ const source = window;
26
+ const candidates = [];
27
+ const addProvider = (provider) => {
28
+ if (isProvider(provider) && !candidates.includes(provider)) {
29
+ candidates.push(provider);
30
+ }
31
+ };
32
+ for (const name of [
33
+ 'ethereum',
34
+ 'yeeying',
35
+ 'yeying',
36
+ 'coinbaseWallet',
37
+ 'bitkeep',
38
+ 'tokenpocket',
39
+ '__YEYING_PROVIDER__',
40
+ ]) {
41
+ addProvider(source[name]);
42
+ }
43
+ const ethereum = getWindowEthereum();
44
+ if (Array.isArray(ethereum?.providers)) {
45
+ for (const provider of ethereum.providers) {
46
+ addProvider(provider);
47
+ }
48
+ }
49
+ return candidates;
14
50
  }
15
51
  function readStoredAccount(storageKey) {
16
52
  if (typeof localStorage === 'undefined')
@@ -54,7 +90,7 @@
54
90
  }
55
91
  function selectBestProvider(candidates, preferYeYing) {
56
92
  if (candidates.length === 0)
57
- return null;
93
+ return selectBestWindowProvider(preferYeYing);
58
94
  if (preferYeYing) {
59
95
  const yeying = candidates.find(c => isYeYingProvider(c.provider, c.info));
60
96
  if (yeying)
@@ -62,10 +98,21 @@
62
98
  }
63
99
  return candidates[0].provider;
64
100
  }
101
+ function selectBestWindowProvider(preferYeYing) {
102
+ const candidates = getWindowProviderCandidates();
103
+ if (candidates.length === 0)
104
+ return null;
105
+ if (preferYeYing) {
106
+ const yeying = candidates.find(provider => isYeYingProvider(provider));
107
+ if (yeying)
108
+ return yeying;
109
+ }
110
+ return candidates[0];
111
+ }
65
112
  async function getProvider(options = {}) {
66
113
  const preferYeYing = options.preferYeYing !== false;
67
114
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT;
68
- const windowProvider = getWindowEthereum();
115
+ const windowProvider = selectBestWindowProvider(preferYeYing);
69
116
  if (preferYeYing && isYeYingProvider(windowProvider)) {
70
117
  return windowProvider;
71
118
  }
@@ -98,7 +145,7 @@
98
145
  }
99
146
  };
100
147
  const onEthereumInitialized = () => {
101
- const injected = getWindowEthereum();
148
+ const injected = selectBestWindowProvider(preferYeYing);
102
149
  if (preferYeYing && isYeYingProvider(injected)) {
103
150
  safeResolve(injected);
104
151
  }
@@ -110,7 +157,7 @@
110
157
  return;
111
158
  const best = selectBestProvider(discovered, preferYeYing) ||
112
159
  windowProvider ||
113
- getWindowEthereum();
160
+ selectBestWindowProvider(preferYeYing);
114
161
  safeResolve(best || null);
115
162
  }, timeoutMs);
116
163
  try {
@@ -124,6 +171,107 @@
124
171
  }
125
172
  });
126
173
  }
174
+ function watchProvider(handler, options = {}) {
175
+ if (typeof window === 'undefined') {
176
+ handler({ provider: null, present: false });
177
+ return () => { };
178
+ }
179
+ const preferYeYing = options.preferYeYing !== false;
180
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_PROVIDER_POLL_INTERVAL;
181
+ const maxPolls = options.maxPolls ?? DEFAULT_PROVIDER_MAX_POLLS;
182
+ let stopped = false;
183
+ let lastProvider;
184
+ let pollCount = 0;
185
+ let pollTimer = null;
186
+ const emit = () => {
187
+ if (stopped)
188
+ return;
189
+ const provider = selectBestWindowProvider(preferYeYing);
190
+ if (provider === lastProvider)
191
+ return;
192
+ lastProvider = provider;
193
+ handler({ provider, present: !!provider });
194
+ };
195
+ const poll = () => {
196
+ if (stopped)
197
+ return;
198
+ emit();
199
+ pollCount += 1;
200
+ if (lastProvider || pollCount >= maxPolls)
201
+ return;
202
+ pollTimer = setTimeout(poll, pollIntervalMs);
203
+ };
204
+ const handleProviderReady = () => {
205
+ emit();
206
+ };
207
+ window.addEventListener('ethereum#initialized', handleProviderReady);
208
+ window.addEventListener('eip6963:announceProvider', handleProviderReady);
209
+ try {
210
+ window.dispatchEvent(new Event('eip6963:requestProvider'));
211
+ }
212
+ catch {
213
+ // Ignore unsupported event dispatch environments.
214
+ }
215
+ poll();
216
+ return () => {
217
+ stopped = true;
218
+ if (pollTimer) {
219
+ clearTimeout(pollTimer);
220
+ }
221
+ window.removeEventListener('ethereum#initialized', handleProviderReady);
222
+ window.removeEventListener('eip6963:announceProvider', handleProviderReady);
223
+ };
224
+ }
225
+ function getWalletErrorMessage(error) {
226
+ if (!error)
227
+ return '';
228
+ if (typeof error === 'string')
229
+ return error;
230
+ if (error instanceof Error)
231
+ return error.message || String(error);
232
+ const message = error.message;
233
+ if (typeof message === 'string')
234
+ return message;
235
+ return String(error);
236
+ }
237
+ function getWalletErrorCode(error) {
238
+ const code = Number(error?.code);
239
+ if (!Number.isNaN(code))
240
+ return code;
241
+ const causeCode = Number(error?.cause?.code);
242
+ if (!Number.isNaN(causeCode))
243
+ return causeCode;
244
+ return null;
245
+ }
246
+ function classifyWalletError(error) {
247
+ const code = getWalletErrorCode(error);
248
+ const message = getWalletErrorMessage(error);
249
+ const lowerMessage = message.toLowerCase();
250
+ if (code === 4001 || lowerMessage.includes('user rejected')) {
251
+ return { type: 'userRejected', code, message };
252
+ }
253
+ if (code === 4900 ||
254
+ lowerMessage.includes('disconnected') ||
255
+ lowerMessage.includes('reconnect') ||
256
+ lowerMessage.includes('not connected')) {
257
+ return { type: 'disconnected', code, message };
258
+ }
259
+ if (lowerMessage.includes('timeout')) {
260
+ return { type: 'timeout', code, message };
261
+ }
262
+ if (lowerMessage.includes('no injected wallet provider') ||
263
+ lowerMessage.includes('未检测到钱包')) {
264
+ return { type: 'notFound', code, message };
265
+ }
266
+ return { type: 'unknown', code, message };
267
+ }
268
+ function isUserRejectedWalletAction(error) {
269
+ return classifyWalletError(error).type === 'userRejected';
270
+ }
271
+ function isWalletReconnectError(error) {
272
+ const type = classifyWalletError(error).type;
273
+ return type === 'disconnected' || type === 'timeout';
274
+ }
127
275
  async function requireProvider(options = {}) {
128
276
  const provider = await getProvider(options);
129
277
  if (!provider) {
@@ -133,10 +281,24 @@
133
281
  }
134
282
  async function requestAccounts(options = {}) {
135
283
  const provider = options.provider || (await requireProvider());
136
- const accounts = (await provider.request({
284
+ const dedupe = options.dedupe !== false;
285
+ if (dedupe) {
286
+ const pending = requestAccountsInFlight.get(provider);
287
+ if (pending)
288
+ return pending;
289
+ }
290
+ const request = provider.request({
137
291
  method: 'eth_requestAccounts',
138
- }));
139
- return Array.isArray(accounts) ? accounts : [];
292
+ }).then(accounts => (Array.isArray(accounts) ? accounts : []));
293
+ if (!dedupe)
294
+ return request;
295
+ requestAccountsInFlight.set(provider, request);
296
+ try {
297
+ return await request;
298
+ }
299
+ finally {
300
+ requestAccountsInFlight.delete(provider);
301
+ }
140
302
  }
141
303
  async function getAccounts(provider) {
142
304
  const p = provider || (await requireProvider());
@@ -1441,6 +1603,9 @@
1441
1603
  const suffix = path.startsWith('/') ? path : `/${path}`;
1442
1604
  return `${base}${suffix}`;
1443
1605
  }
1606
+ function isRecord(value) {
1607
+ return typeof value === 'object' && value !== null;
1608
+ }
1444
1609
  function resolveAuthHeader(auth, token) {
1445
1610
  if (auth?.type === 'bearer') {
1446
1611
  return `Bearer ${auth.token}`;
@@ -1629,6 +1794,72 @@
1629
1794
  }
1630
1795
  return await res.json();
1631
1796
  }
1797
+ async requestApiJson(method, apiPath, body, options) {
1798
+ const headers = this.buildHeaders({
1799
+ auth: options?.auth,
1800
+ token: options?.token,
1801
+ contentType: body === undefined ? undefined : 'application/json',
1802
+ });
1803
+ const response = await this.fetcher(joinUrl(this.baseUrl, apiPath), {
1804
+ method,
1805
+ headers,
1806
+ body: body === undefined ? undefined : JSON.stringify(body),
1807
+ credentials: this.credentials,
1808
+ signal: options?.signal,
1809
+ });
1810
+ if (!response.ok) {
1811
+ throw new Error(`WebDAV ${method} ${apiPath} failed: ${response.status} ${response.statusText}`);
1812
+ }
1813
+ return await response.json();
1814
+ }
1815
+ getShareAccessUrl(token, fileName) {
1816
+ const normalizedToken = encodeURIComponent(String(token || '').trim());
1817
+ if (!normalizedToken) {
1818
+ throw new Error('Share token is required');
1819
+ }
1820
+ const encodedFileName = String(fileName || '').trim()
1821
+ ? `/${encodeURIComponent(String(fileName || '').trim())}`
1822
+ : '';
1823
+ return joinUrl(this.baseUrl, `/api/v1/public/share/${normalizedToken}${encodedFileName}`);
1824
+ }
1825
+ async createShareLink(options) {
1826
+ const normalizedPath = String(options.path || '').trim();
1827
+ if (!normalizedPath) {
1828
+ throw new Error('Share path is required');
1829
+ }
1830
+ const payload = await this.requestApiJson('POST', '/api/v1/public/share/create', {
1831
+ path: normalizedPath,
1832
+ expiresIn: options.expiresIn,
1833
+ expiresValue: options.expiresValue,
1834
+ expiresUnit: options.expiresUnit,
1835
+ }, options);
1836
+ if (!isRecord(payload)) {
1837
+ throw new Error('WebDAV share create response is invalid');
1838
+ }
1839
+ return payload;
1840
+ }
1841
+ async listShareLinks(options = {}) {
1842
+ const payload = await this.requestApiJson('GET', '/api/v1/public/share/list', undefined, options);
1843
+ if (!isRecord(payload)) {
1844
+ return [];
1845
+ }
1846
+ const items = payload.items;
1847
+ if (!Array.isArray(items)) {
1848
+ return [];
1849
+ }
1850
+ return items.filter(isRecord);
1851
+ }
1852
+ async revokeShareLink(token, options = {}) {
1853
+ const normalizedToken = String(token || '').trim();
1854
+ if (!normalizedToken) {
1855
+ throw new Error('Share token is required');
1856
+ }
1857
+ const payload = await this.requestApiJson('POST', '/api/v1/public/share/revoke', { token: normalizedToken }, options);
1858
+ if (!isRecord(payload)) {
1859
+ return {};
1860
+ }
1861
+ return payload;
1862
+ }
1632
1863
  }
1633
1864
  function createWebDavClient(options) {
1634
1865
  return new WebDavClient(options);
@@ -1945,6 +2176,7 @@
1945
2176
  exports.authCentralUcanFetch = authCentralUcanFetch;
1946
2177
  exports.authFetch = authFetch;
1947
2178
  exports.authUcanFetch = authUcanFetch;
2179
+ exports.classifyWalletError = classifyWalletError;
1948
2180
  exports.clearAccessToken = clearAccessToken;
1949
2181
  exports.clearCentralSessionToken = clearCentralSessionToken;
1950
2182
  exports.clearUcanSession = clearUcanSession;
@@ -1970,8 +2202,12 @@
1970
2202
  exports.getProvider = getProvider;
1971
2203
  exports.getStoredUcanRoot = getStoredUcanRoot;
1972
2204
  exports.getUcanSession = getUcanSession;
2205
+ exports.getWalletErrorCode = getWalletErrorCode;
2206
+ exports.getWalletErrorMessage = getWalletErrorMessage;
1973
2207
  exports.initDappSession = initDappSession;
1974
2208
  exports.initWebDavStorage = initWebDavStorage;
2209
+ exports.isUserRejectedWalletAction = isUserRejectedWalletAction;
2210
+ exports.isWalletReconnectError = isWalletReconnectError;
1975
2211
  exports.isYeYingProvider = isYeYingProvider;
1976
2212
  exports.issueCentralUcan = issueCentralUcan;
1977
2213
  exports.loginWithChallenge = loginWithChallenge;
@@ -1989,6 +2225,7 @@
1989
2225
  exports.signMessage = signMessage;
1990
2226
  exports.storeUcanRoot = storeUcanRoot;
1991
2227
  exports.watchAccounts = watchAccounts;
2228
+ exports.watchProvider = watchProvider;
1992
2229
 
1993
2230
  }));
1994
2231
  //# sourceMappingURL=web3-bs.umd.js.map