@yeying-community/web3-bs 1.0.2
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 +265 -0
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/provider.d.ts +10 -0
- package/dist/auth/siwe.d.ts +17 -0
- package/dist/auth/types.d.ts +71 -0
- package/dist/auth/ucan.d.ts +92 -0
- package/dist/index.d.ts +2 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/webdav.d.ts +54 -0
- package/dist/web3-bs.esm.js +1012 -0
- package/dist/web3-bs.esm.js.map +1 -0
- package/dist/web3-bs.umd.js +1047 -0
- package/dist/web3-bs.umd.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,1012 @@
|
|
|
1
|
+
const YEYING_RDNS = 'io.github.yeying';
|
|
2
|
+
const DEFAULT_TIMEOUT = 1000;
|
|
3
|
+
function getWindowEthereum() {
|
|
4
|
+
if (typeof window === 'undefined')
|
|
5
|
+
return null;
|
|
6
|
+
return window.ethereum || null;
|
|
7
|
+
}
|
|
8
|
+
function isYeYingProvider(provider, info) {
|
|
9
|
+
if (!provider)
|
|
10
|
+
return false;
|
|
11
|
+
if (provider.isYeYing)
|
|
12
|
+
return true;
|
|
13
|
+
const name = (info?.name || '').toLowerCase();
|
|
14
|
+
const rdns = (info?.rdns || '').toLowerCase();
|
|
15
|
+
return rdns === YEYING_RDNS || name.includes('yeying');
|
|
16
|
+
}
|
|
17
|
+
function selectBestProvider(candidates, preferYeYing) {
|
|
18
|
+
if (candidates.length === 0)
|
|
19
|
+
return null;
|
|
20
|
+
if (preferYeYing) {
|
|
21
|
+
const yeying = candidates.find(c => isYeYingProvider(c.provider, c.info));
|
|
22
|
+
if (yeying)
|
|
23
|
+
return yeying.provider;
|
|
24
|
+
}
|
|
25
|
+
return candidates[0].provider;
|
|
26
|
+
}
|
|
27
|
+
async function getProvider(options = {}) {
|
|
28
|
+
const preferYeYing = options.preferYeYing !== false;
|
|
29
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT;
|
|
30
|
+
const windowProvider = getWindowEthereum();
|
|
31
|
+
if (preferYeYing && isYeYingProvider(windowProvider)) {
|
|
32
|
+
return windowProvider;
|
|
33
|
+
}
|
|
34
|
+
if (typeof window === 'undefined') {
|
|
35
|
+
return windowProvider;
|
|
36
|
+
}
|
|
37
|
+
const discovered = [];
|
|
38
|
+
let resolved = false;
|
|
39
|
+
return await new Promise(resolve => {
|
|
40
|
+
const cleanup = () => {
|
|
41
|
+
window.removeEventListener('eip6963:announceProvider', onAnnounce);
|
|
42
|
+
window.removeEventListener('ethereum#initialized', onEthereumInitialized);
|
|
43
|
+
if (timeoutId)
|
|
44
|
+
clearTimeout(timeoutId);
|
|
45
|
+
};
|
|
46
|
+
const safeResolve = (provider) => {
|
|
47
|
+
if (resolved)
|
|
48
|
+
return;
|
|
49
|
+
resolved = true;
|
|
50
|
+
cleanup();
|
|
51
|
+
resolve(provider);
|
|
52
|
+
};
|
|
53
|
+
const onAnnounce = (event) => {
|
|
54
|
+
const detail = event.detail;
|
|
55
|
+
if (!detail?.provider)
|
|
56
|
+
return;
|
|
57
|
+
discovered.push(detail);
|
|
58
|
+
if (preferYeYing && isYeYingProvider(detail.provider, detail.info)) {
|
|
59
|
+
safeResolve(detail.provider);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const onEthereumInitialized = () => {
|
|
63
|
+
const injected = getWindowEthereum();
|
|
64
|
+
if (preferYeYing && isYeYingProvider(injected)) {
|
|
65
|
+
safeResolve(injected);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
window.addEventListener('eip6963:announceProvider', onAnnounce);
|
|
69
|
+
window.addEventListener('ethereum#initialized', onEthereumInitialized, { once: true });
|
|
70
|
+
const timeoutId = setTimeout(() => {
|
|
71
|
+
if (resolved)
|
|
72
|
+
return;
|
|
73
|
+
const best = selectBestProvider(discovered, preferYeYing) ||
|
|
74
|
+
windowProvider ||
|
|
75
|
+
getWindowEthereum();
|
|
76
|
+
safeResolve(best || null);
|
|
77
|
+
}, timeoutMs);
|
|
78
|
+
try {
|
|
79
|
+
window.dispatchEvent(new Event('eip6963:requestProvider'));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Ignore if browser doesn't support CustomEvent target
|
|
83
|
+
}
|
|
84
|
+
if (!preferYeYing && windowProvider) {
|
|
85
|
+
safeResolve(windowProvider);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function requireProvider(options = {}) {
|
|
90
|
+
const provider = await getProvider(options);
|
|
91
|
+
if (!provider) {
|
|
92
|
+
throw new Error('No injected wallet provider found');
|
|
93
|
+
}
|
|
94
|
+
return provider;
|
|
95
|
+
}
|
|
96
|
+
async function requestAccounts(options = {}) {
|
|
97
|
+
const provider = options.provider || (await requireProvider());
|
|
98
|
+
const accounts = (await provider.request({
|
|
99
|
+
method: 'eth_requestAccounts',
|
|
100
|
+
}));
|
|
101
|
+
return Array.isArray(accounts) ? accounts : [];
|
|
102
|
+
}
|
|
103
|
+
async function getAccounts(provider) {
|
|
104
|
+
const p = provider || (await requireProvider());
|
|
105
|
+
const accounts = (await p.request({ method: 'eth_accounts' }));
|
|
106
|
+
return Array.isArray(accounts) ? accounts : [];
|
|
107
|
+
}
|
|
108
|
+
async function getChainId(provider) {
|
|
109
|
+
const p = provider || (await requireProvider());
|
|
110
|
+
const chainId = (await p.request({ method: 'eth_chainId' }));
|
|
111
|
+
return typeof chainId === 'string' ? chainId : null;
|
|
112
|
+
}
|
|
113
|
+
async function getBalance(provider, address, blockTag = 'latest') {
|
|
114
|
+
const p = provider || (await requireProvider());
|
|
115
|
+
let target = address;
|
|
116
|
+
if (!target) {
|
|
117
|
+
const accounts = await getAccounts(p);
|
|
118
|
+
target = accounts[0];
|
|
119
|
+
}
|
|
120
|
+
if (!target) {
|
|
121
|
+
throw new Error('No account available for balance');
|
|
122
|
+
}
|
|
123
|
+
const balance = (await p.request({
|
|
124
|
+
method: 'eth_getBalance',
|
|
125
|
+
params: [target, blockTag],
|
|
126
|
+
}));
|
|
127
|
+
if (typeof balance !== 'string') {
|
|
128
|
+
throw new Error('Invalid balance response');
|
|
129
|
+
}
|
|
130
|
+
return balance;
|
|
131
|
+
}
|
|
132
|
+
function onAccountsChanged(provider, handler) {
|
|
133
|
+
provider.on?.('accountsChanged', handler);
|
|
134
|
+
return () => provider.removeListener?.('accountsChanged', handler);
|
|
135
|
+
}
|
|
136
|
+
function onChainChanged(provider, handler) {
|
|
137
|
+
provider.on?.('chainChanged', handler);
|
|
138
|
+
return () => provider.removeListener?.('chainChanged', handler);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeBaseUrl$1(baseUrl) {
|
|
142
|
+
return baseUrl.replace(/\/+$/, '');
|
|
143
|
+
}
|
|
144
|
+
function joinUrl$1(baseUrl, path) {
|
|
145
|
+
const trimmed = path.replace(/^\/+/, '');
|
|
146
|
+
return `${normalizeBaseUrl$1(baseUrl)}/${trimmed}`;
|
|
147
|
+
}
|
|
148
|
+
const DEFAULT_TOKEN_KEY = 'authToken';
|
|
149
|
+
let cachedAccessToken = null;
|
|
150
|
+
let refreshInFlight = null;
|
|
151
|
+
function resolveTokenKey(options) {
|
|
152
|
+
return options?.tokenStorageKey || DEFAULT_TOKEN_KEY;
|
|
153
|
+
}
|
|
154
|
+
function shouldStoreToken(options) {
|
|
155
|
+
return options?.storeToken !== false;
|
|
156
|
+
}
|
|
157
|
+
function resolveFetcher(options) {
|
|
158
|
+
return options?.fetcher || fetch;
|
|
159
|
+
}
|
|
160
|
+
function resolveCredentials(options) {
|
|
161
|
+
return options?.credentials ?? 'include';
|
|
162
|
+
}
|
|
163
|
+
function readStoredToken(options) {
|
|
164
|
+
if (!shouldStoreToken(options))
|
|
165
|
+
return null;
|
|
166
|
+
if (typeof localStorage === 'undefined')
|
|
167
|
+
return null;
|
|
168
|
+
const key = resolveTokenKey(options);
|
|
169
|
+
return localStorage.getItem(key);
|
|
170
|
+
}
|
|
171
|
+
function persistToken(token, options) {
|
|
172
|
+
cachedAccessToken = token;
|
|
173
|
+
if (!shouldStoreToken(options))
|
|
174
|
+
return;
|
|
175
|
+
if (typeof localStorage === 'undefined')
|
|
176
|
+
return;
|
|
177
|
+
const key = resolveTokenKey(options);
|
|
178
|
+
if (!token) {
|
|
179
|
+
localStorage.removeItem(key);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
localStorage.setItem(key, token);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function getAccessToken(options) {
|
|
186
|
+
if (cachedAccessToken)
|
|
187
|
+
return cachedAccessToken;
|
|
188
|
+
const stored = readStoredToken(options);
|
|
189
|
+
if (stored) {
|
|
190
|
+
cachedAccessToken = stored;
|
|
191
|
+
}
|
|
192
|
+
return stored;
|
|
193
|
+
}
|
|
194
|
+
function setAccessToken(token, options) {
|
|
195
|
+
persistToken(token, options);
|
|
196
|
+
}
|
|
197
|
+
function clearAccessToken(options) {
|
|
198
|
+
cachedAccessToken = null;
|
|
199
|
+
if (typeof localStorage === 'undefined')
|
|
200
|
+
return;
|
|
201
|
+
const key = resolveTokenKey(options);
|
|
202
|
+
localStorage.removeItem(key);
|
|
203
|
+
}
|
|
204
|
+
async function resolveAddress$1(provider, address) {
|
|
205
|
+
if (address)
|
|
206
|
+
return address;
|
|
207
|
+
let accounts = await getAccounts(provider);
|
|
208
|
+
if (!accounts[0]) {
|
|
209
|
+
const requested = (await provider.request({
|
|
210
|
+
method: 'eth_requestAccounts',
|
|
211
|
+
}));
|
|
212
|
+
if (Array.isArray(requested)) {
|
|
213
|
+
accounts = requested;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!accounts[0]) {
|
|
217
|
+
throw new Error('No account available');
|
|
218
|
+
}
|
|
219
|
+
return accounts[0];
|
|
220
|
+
}
|
|
221
|
+
function extractChallenge(payload) {
|
|
222
|
+
if (!payload || typeof payload !== 'object')
|
|
223
|
+
return null;
|
|
224
|
+
const data = payload;
|
|
225
|
+
const envelope = data.data;
|
|
226
|
+
if (envelope) {
|
|
227
|
+
const value = envelope.challenge;
|
|
228
|
+
if (typeof value === 'string')
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
const direct = data.challenge || data.result;
|
|
232
|
+
if (typeof direct === 'string')
|
|
233
|
+
return direct;
|
|
234
|
+
if (direct && typeof direct === 'object') {
|
|
235
|
+
const nested = direct.challenge;
|
|
236
|
+
if (typeof nested === 'string')
|
|
237
|
+
return nested;
|
|
238
|
+
}
|
|
239
|
+
const body = data.body;
|
|
240
|
+
if (body) {
|
|
241
|
+
const bodyResult = body.result;
|
|
242
|
+
if (typeof bodyResult === 'string')
|
|
243
|
+
return bodyResult;
|
|
244
|
+
if (bodyResult && typeof bodyResult === 'object') {
|
|
245
|
+
const nested = bodyResult.challenge;
|
|
246
|
+
if (typeof nested === 'string')
|
|
247
|
+
return nested;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
function extractToken(payload) {
|
|
253
|
+
if (!payload || typeof payload !== 'object')
|
|
254
|
+
return null;
|
|
255
|
+
const data = payload;
|
|
256
|
+
const envelope = data.data;
|
|
257
|
+
if (envelope) {
|
|
258
|
+
const value = envelope.token;
|
|
259
|
+
if (typeof value === 'string')
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
const direct = data.token || data.result;
|
|
263
|
+
if (typeof direct === 'string')
|
|
264
|
+
return direct;
|
|
265
|
+
const body = data.body;
|
|
266
|
+
if (body) {
|
|
267
|
+
const bodyToken = body.token;
|
|
268
|
+
if (typeof bodyToken === 'string')
|
|
269
|
+
return bodyToken;
|
|
270
|
+
const bodyResult = body.result;
|
|
271
|
+
if (typeof bodyResult === 'string')
|
|
272
|
+
return bodyResult;
|
|
273
|
+
if (bodyResult && typeof bodyResult === 'object') {
|
|
274
|
+
const nested = bodyResult.token;
|
|
275
|
+
if (typeof nested === 'string')
|
|
276
|
+
return nested;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
async function signMessage(options) {
|
|
282
|
+
const provider = options.provider || (await requireProvider());
|
|
283
|
+
const address = await resolveAddress$1(provider, options.address);
|
|
284
|
+
const method = options.method || 'personal_sign';
|
|
285
|
+
const params = method === 'eth_sign'
|
|
286
|
+
? [address, options.message]
|
|
287
|
+
: [options.message, address];
|
|
288
|
+
const signature = await provider.request({
|
|
289
|
+
method,
|
|
290
|
+
params,
|
|
291
|
+
});
|
|
292
|
+
if (typeof signature !== 'string') {
|
|
293
|
+
throw new Error('Invalid signature response');
|
|
294
|
+
}
|
|
295
|
+
return signature;
|
|
296
|
+
}
|
|
297
|
+
async function loginWithChallenge(options = {}) {
|
|
298
|
+
const provider = options.provider || (await requireProvider());
|
|
299
|
+
const address = await resolveAddress$1(provider, options.address);
|
|
300
|
+
const fetcher = resolveFetcher(options);
|
|
301
|
+
const credentials = resolveCredentials(options);
|
|
302
|
+
const baseUrl = options.baseUrl || '/api/v1/public/auth';
|
|
303
|
+
const challengeUrl = joinUrl$1(baseUrl, options.challengePath || 'challenge');
|
|
304
|
+
const verifyUrl = joinUrl$1(baseUrl, options.verifyPath || 'verify');
|
|
305
|
+
const challengeBody = {
|
|
306
|
+
address,
|
|
307
|
+
};
|
|
308
|
+
const challengeRes = await fetcher(challengeUrl, {
|
|
309
|
+
method: 'POST',
|
|
310
|
+
headers: {
|
|
311
|
+
'Content-Type': 'application/json',
|
|
312
|
+
accept: 'application/json',
|
|
313
|
+
},
|
|
314
|
+
credentials,
|
|
315
|
+
body: JSON.stringify(challengeBody),
|
|
316
|
+
});
|
|
317
|
+
if (!challengeRes.ok) {
|
|
318
|
+
const text = await challengeRes.text();
|
|
319
|
+
throw new Error(`Challenge request failed: ${challengeRes.status} ${text}`);
|
|
320
|
+
}
|
|
321
|
+
const challengePayload = await challengeRes.json();
|
|
322
|
+
const challenge = extractChallenge(challengePayload);
|
|
323
|
+
if (!challenge) {
|
|
324
|
+
throw new Error('Challenge response missing challenge');
|
|
325
|
+
}
|
|
326
|
+
const signature = await signMessage({
|
|
327
|
+
provider,
|
|
328
|
+
address,
|
|
329
|
+
message: challenge,
|
|
330
|
+
method: options.signMethod || 'personal_sign',
|
|
331
|
+
});
|
|
332
|
+
const verifyBody = {
|
|
333
|
+
address,
|
|
334
|
+
signature,
|
|
335
|
+
};
|
|
336
|
+
const verifyRes = await fetcher(verifyUrl, {
|
|
337
|
+
method: 'POST',
|
|
338
|
+
headers: {
|
|
339
|
+
'Content-Type': 'application/json',
|
|
340
|
+
accept: 'application/json',
|
|
341
|
+
},
|
|
342
|
+
credentials,
|
|
343
|
+
body: JSON.stringify(verifyBody),
|
|
344
|
+
});
|
|
345
|
+
if (!verifyRes.ok) {
|
|
346
|
+
const text = await verifyRes.text();
|
|
347
|
+
throw new Error(`Verify request failed: ${verifyRes.status} ${text}`);
|
|
348
|
+
}
|
|
349
|
+
const verifyPayload = await verifyRes.json();
|
|
350
|
+
const token = extractToken(verifyPayload);
|
|
351
|
+
if (!token) {
|
|
352
|
+
throw new Error('Verify response missing token');
|
|
353
|
+
}
|
|
354
|
+
persistToken(token, options);
|
|
355
|
+
return {
|
|
356
|
+
token,
|
|
357
|
+
address,
|
|
358
|
+
signature,
|
|
359
|
+
challenge,
|
|
360
|
+
response: verifyPayload,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
async function refreshAccessToken(options = {}) {
|
|
364
|
+
if (refreshInFlight) {
|
|
365
|
+
return refreshInFlight;
|
|
366
|
+
}
|
|
367
|
+
const task = (async () => {
|
|
368
|
+
const fetcher = resolveFetcher(options);
|
|
369
|
+
const credentials = resolveCredentials(options);
|
|
370
|
+
const baseUrl = options.baseUrl || '/api/v1/public/auth';
|
|
371
|
+
const refreshUrl = joinUrl$1(baseUrl, options.refreshPath || 'refresh');
|
|
372
|
+
const refreshRes = await fetcher(refreshUrl, {
|
|
373
|
+
method: 'POST',
|
|
374
|
+
headers: {
|
|
375
|
+
accept: 'application/json',
|
|
376
|
+
},
|
|
377
|
+
credentials,
|
|
378
|
+
});
|
|
379
|
+
if (!refreshRes.ok) {
|
|
380
|
+
const text = await refreshRes.text();
|
|
381
|
+
throw new Error(`Refresh request failed: ${refreshRes.status} ${text}`);
|
|
382
|
+
}
|
|
383
|
+
const refreshPayload = await refreshRes.json();
|
|
384
|
+
const token = extractToken(refreshPayload);
|
|
385
|
+
if (!token) {
|
|
386
|
+
throw new Error('Refresh response missing token');
|
|
387
|
+
}
|
|
388
|
+
persistToken(token, options);
|
|
389
|
+
return { token, response: refreshPayload };
|
|
390
|
+
})();
|
|
391
|
+
refreshInFlight = task;
|
|
392
|
+
try {
|
|
393
|
+
return await task;
|
|
394
|
+
}
|
|
395
|
+
finally {
|
|
396
|
+
refreshInFlight = null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async function logout(options = {}) {
|
|
400
|
+
const fetcher = resolveFetcher(options);
|
|
401
|
+
const credentials = resolveCredentials(options);
|
|
402
|
+
const baseUrl = options.baseUrl || '/api/v1/public/auth';
|
|
403
|
+
const logoutUrl = joinUrl$1(baseUrl, options.logoutPath || 'logout');
|
|
404
|
+
const logoutRes = await fetcher(logoutUrl, {
|
|
405
|
+
method: 'POST',
|
|
406
|
+
headers: {
|
|
407
|
+
accept: 'application/json',
|
|
408
|
+
},
|
|
409
|
+
credentials,
|
|
410
|
+
});
|
|
411
|
+
if (!logoutRes.ok) {
|
|
412
|
+
const text = await logoutRes.text();
|
|
413
|
+
throw new Error(`Logout request failed: ${logoutRes.status} ${text}`);
|
|
414
|
+
}
|
|
415
|
+
let payload = null;
|
|
416
|
+
try {
|
|
417
|
+
payload = await logoutRes.json();
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
payload = null;
|
|
421
|
+
}
|
|
422
|
+
clearAccessToken(options);
|
|
423
|
+
return { response: payload };
|
|
424
|
+
}
|
|
425
|
+
async function authFetch(input, init = {}, options = {}) {
|
|
426
|
+
const fetcher = resolveFetcher(options);
|
|
427
|
+
const credentials = resolveCredentials(options);
|
|
428
|
+
const retryOnUnauthorized = options.retryOnUnauthorized !== false;
|
|
429
|
+
const performRequest = async (tokenOverride) => {
|
|
430
|
+
const headers = new Headers(init.headers || {});
|
|
431
|
+
const token = tokenOverride ?? options.accessToken ?? getAccessToken(options);
|
|
432
|
+
if (token && !headers.has('Authorization')) {
|
|
433
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
434
|
+
}
|
|
435
|
+
return fetcher(input, {
|
|
436
|
+
...init,
|
|
437
|
+
headers,
|
|
438
|
+
credentials,
|
|
439
|
+
});
|
|
440
|
+
};
|
|
441
|
+
const initialRes = await performRequest();
|
|
442
|
+
if (initialRes.status !== 401 || !retryOnUnauthorized) {
|
|
443
|
+
return initialRes;
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
const refreshed = await refreshAccessToken(options);
|
|
447
|
+
return await performRequest(refreshed.token);
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
return initialRes;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const DEFAULT_SESSION_ID = 'default';
|
|
455
|
+
const DEFAULT_SESSION_TTL = 24 * 60 * 60 * 1000;
|
|
456
|
+
const DEFAULT_UCAN_TTL = 5 * 60 * 1000;
|
|
457
|
+
const DB_NAME = 'yeying-web3';
|
|
458
|
+
const DB_STORE = 'ucan-sessions';
|
|
459
|
+
const textEncoder = new TextEncoder();
|
|
460
|
+
function toBase64Url(data) {
|
|
461
|
+
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
462
|
+
let binary = '';
|
|
463
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
464
|
+
binary += String.fromCharCode(bytes[i]);
|
|
465
|
+
}
|
|
466
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
467
|
+
}
|
|
468
|
+
function encodeJson(value) {
|
|
469
|
+
return toBase64Url(textEncoder.encode(JSON.stringify(value)));
|
|
470
|
+
}
|
|
471
|
+
function randomNonce(bytes = 16) {
|
|
472
|
+
const buffer = new Uint8Array(bytes);
|
|
473
|
+
crypto.getRandomValues(buffer);
|
|
474
|
+
return Array.from(buffer)
|
|
475
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
476
|
+
.join('');
|
|
477
|
+
}
|
|
478
|
+
function normalizeExpiry(exp, fallbackMs) {
|
|
479
|
+
return Date.now() + fallbackMs;
|
|
480
|
+
}
|
|
481
|
+
function openDb() {
|
|
482
|
+
if (typeof indexedDB === 'undefined') {
|
|
483
|
+
return Promise.reject(new Error('IndexedDB not available'));
|
|
484
|
+
}
|
|
485
|
+
return new Promise((resolve, reject) => {
|
|
486
|
+
const request = indexedDB.open(DB_NAME, 1);
|
|
487
|
+
request.onupgradeneeded = () => {
|
|
488
|
+
const db = request.result;
|
|
489
|
+
if (!db.objectStoreNames.contains(DB_STORE)) {
|
|
490
|
+
db.createObjectStore(DB_STORE, { keyPath: 'id' });
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
request.onsuccess = () => resolve(request.result);
|
|
494
|
+
request.onerror = () => reject(request.error);
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
async function readSessionRecord(id) {
|
|
498
|
+
try {
|
|
499
|
+
const db = await openDb();
|
|
500
|
+
return await new Promise((resolve, reject) => {
|
|
501
|
+
const tx = db.transaction(DB_STORE, 'readonly');
|
|
502
|
+
const store = tx.objectStore(DB_STORE);
|
|
503
|
+
const request = store.get(id);
|
|
504
|
+
request.onsuccess = () => resolve(request.result || null);
|
|
505
|
+
request.onerror = () => reject(request.error);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async function writeSessionRecord(record) {
|
|
513
|
+
try {
|
|
514
|
+
const db = await openDb();
|
|
515
|
+
await new Promise((resolve, reject) => {
|
|
516
|
+
const tx = db.transaction(DB_STORE, 'readwrite');
|
|
517
|
+
const store = tx.objectStore(DB_STORE);
|
|
518
|
+
const request = store.put(record);
|
|
519
|
+
request.onsuccess = () => resolve();
|
|
520
|
+
request.onerror = () => reject(request.error);
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
// ignore storage failures
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async function deleteSessionRecord(id) {
|
|
528
|
+
try {
|
|
529
|
+
const db = await openDb();
|
|
530
|
+
await new Promise((resolve, reject) => {
|
|
531
|
+
const tx = db.transaction(DB_STORE, 'readwrite');
|
|
532
|
+
const store = tx.objectStore(DB_STORE);
|
|
533
|
+
const request = store.delete(id);
|
|
534
|
+
request.onsuccess = () => resolve();
|
|
535
|
+
request.onerror = () => reject(request.error);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
// ignore storage failures
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
async function getUcanSession(id = DEFAULT_SESSION_ID, provider) {
|
|
543
|
+
const walletProvider = provider || (typeof window !== 'undefined'
|
|
544
|
+
? await getProvider({ preferYeYing: true })
|
|
545
|
+
: null);
|
|
546
|
+
if (!walletProvider)
|
|
547
|
+
return null;
|
|
548
|
+
try {
|
|
549
|
+
return await requestWalletUcanSession(walletProvider, { id });
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
async function requestWalletUcanSession(provider, options) {
|
|
556
|
+
const sessionId = options.id || DEFAULT_SESSION_ID;
|
|
557
|
+
const result = (await provider.request({
|
|
558
|
+
method: 'yeying_ucan_session',
|
|
559
|
+
params: [
|
|
560
|
+
{
|
|
561
|
+
sessionId,
|
|
562
|
+
expiresInMs: options.expiresInMs,
|
|
563
|
+
forceNew: options.forceNew,
|
|
564
|
+
},
|
|
565
|
+
],
|
|
566
|
+
}));
|
|
567
|
+
if (!result || typeof result.did !== 'string') {
|
|
568
|
+
throw new Error('Invalid wallet UCAN session response');
|
|
569
|
+
}
|
|
570
|
+
const createdAt = typeof result.createdAt === 'number' ? result.createdAt : Date.now();
|
|
571
|
+
const expiresAt = typeof result.expiresAt === 'number' ? result.expiresAt : null;
|
|
572
|
+
const existing = await readSessionRecord(sessionId);
|
|
573
|
+
const nextRecord = {
|
|
574
|
+
id: result.id || sessionId,
|
|
575
|
+
did: result.did,
|
|
576
|
+
createdAt,
|
|
577
|
+
expiresAt,
|
|
578
|
+
root: existing?.root,
|
|
579
|
+
};
|
|
580
|
+
if (nextRecord.root && nextRecord.root.aud && nextRecord.root.aud !== nextRecord.did) {
|
|
581
|
+
nextRecord.root = undefined;
|
|
582
|
+
}
|
|
583
|
+
await writeSessionRecord(nextRecord);
|
|
584
|
+
return {
|
|
585
|
+
id: result.id || sessionId,
|
|
586
|
+
did: result.did,
|
|
587
|
+
createdAt,
|
|
588
|
+
expiresAt,
|
|
589
|
+
signer: async (signingInput, payload) => {
|
|
590
|
+
const signatureResult = (await provider.request({
|
|
591
|
+
method: 'yeying_ucan_sign',
|
|
592
|
+
params: [
|
|
593
|
+
{
|
|
594
|
+
sessionId,
|
|
595
|
+
signingInput,
|
|
596
|
+
payload,
|
|
597
|
+
},
|
|
598
|
+
],
|
|
599
|
+
}));
|
|
600
|
+
if (typeof signatureResult === 'string') {
|
|
601
|
+
return signatureResult;
|
|
602
|
+
}
|
|
603
|
+
if (signatureResult && typeof signatureResult.signature === 'string') {
|
|
604
|
+
return signatureResult.signature;
|
|
605
|
+
}
|
|
606
|
+
throw new Error('Invalid wallet UCAN signature response');
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
async function createUcanSession(options = {}) {
|
|
611
|
+
const provider = options.provider || (typeof window !== 'undefined'
|
|
612
|
+
? await getProvider({ preferYeYing: true })
|
|
613
|
+
: null);
|
|
614
|
+
if (!provider) {
|
|
615
|
+
throw new Error('No wallet provider for UCAN session');
|
|
616
|
+
}
|
|
617
|
+
return await requestWalletUcanSession(provider, options);
|
|
618
|
+
}
|
|
619
|
+
async function clearUcanSession(id = DEFAULT_SESSION_ID) {
|
|
620
|
+
await deleteSessionRecord(id);
|
|
621
|
+
}
|
|
622
|
+
async function storeUcanRoot(root, id = DEFAULT_SESSION_ID) {
|
|
623
|
+
const record = await readSessionRecord(id);
|
|
624
|
+
const createdAt = record?.createdAt ?? Date.now();
|
|
625
|
+
const expiresAt = record?.expiresAt ?? null;
|
|
626
|
+
const did = record?.did || root.aud;
|
|
627
|
+
const nextRecord = {
|
|
628
|
+
id,
|
|
629
|
+
did,
|
|
630
|
+
createdAt,
|
|
631
|
+
expiresAt,
|
|
632
|
+
root,
|
|
633
|
+
};
|
|
634
|
+
await writeSessionRecord(nextRecord);
|
|
635
|
+
}
|
|
636
|
+
async function getStoredUcanRoot(id = DEFAULT_SESSION_ID) {
|
|
637
|
+
const record = await readSessionRecord(id);
|
|
638
|
+
return record?.root || null;
|
|
639
|
+
}
|
|
640
|
+
function buildUcanStatement(payload) {
|
|
641
|
+
return `UCAN-AUTH ${JSON.stringify(payload)}`;
|
|
642
|
+
}
|
|
643
|
+
function buildSiweMessage(params) {
|
|
644
|
+
const lines = [
|
|
645
|
+
`${params.domain} wants you to sign in with your Ethereum account:`,
|
|
646
|
+
params.address,
|
|
647
|
+
'',
|
|
648
|
+
params.statement,
|
|
649
|
+
'',
|
|
650
|
+
`URI: ${params.uri}`,
|
|
651
|
+
'Version: 1',
|
|
652
|
+
`Chain ID: ${params.chainId}`,
|
|
653
|
+
`Nonce: ${params.nonce}`,
|
|
654
|
+
`Issued At: ${params.issuedAt}`,
|
|
655
|
+
];
|
|
656
|
+
if (params.expirationTime) {
|
|
657
|
+
lines.push(`Expiration Time: ${params.expirationTime}`);
|
|
658
|
+
}
|
|
659
|
+
return lines.join('\n');
|
|
660
|
+
}
|
|
661
|
+
async function resolveAddress(provider, address) {
|
|
662
|
+
if (address)
|
|
663
|
+
return address;
|
|
664
|
+
const accounts = await getAccounts(provider);
|
|
665
|
+
if (!accounts[0])
|
|
666
|
+
throw new Error('No account available');
|
|
667
|
+
return accounts[0];
|
|
668
|
+
}
|
|
669
|
+
async function signWithProvider(provider, address, message) {
|
|
670
|
+
const signature = await provider.request({
|
|
671
|
+
method: 'personal_sign',
|
|
672
|
+
params: [message, address],
|
|
673
|
+
});
|
|
674
|
+
if (typeof signature !== 'string') {
|
|
675
|
+
throw new Error('Invalid signature response');
|
|
676
|
+
}
|
|
677
|
+
return signature;
|
|
678
|
+
}
|
|
679
|
+
async function createRootUcan(options) {
|
|
680
|
+
const provider = options.provider || (await requireProvider());
|
|
681
|
+
const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
|
|
682
|
+
const address = await resolveAddress(provider, options.address);
|
|
683
|
+
const chainId = options.chainId || (await getChainId(provider)) || '1';
|
|
684
|
+
const domain = options.domain || (typeof window !== 'undefined' ? window.location.host : 'localhost');
|
|
685
|
+
const uri = options.uri || (typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
|
|
686
|
+
const nonce = options.nonce || randomNonce(8);
|
|
687
|
+
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_SESSION_TTL);
|
|
688
|
+
const nbf = options.notBeforeMs;
|
|
689
|
+
const statementPayload = {
|
|
690
|
+
aud: session.did,
|
|
691
|
+
cap: options.capabilities,
|
|
692
|
+
exp,
|
|
693
|
+
};
|
|
694
|
+
if (nbf)
|
|
695
|
+
statementPayload.nbf = nbf;
|
|
696
|
+
const statement = options.statement || buildUcanStatement(statementPayload);
|
|
697
|
+
const issuedAt = new Date().toISOString();
|
|
698
|
+
const expirationTime = new Date(exp).toISOString();
|
|
699
|
+
const message = buildSiweMessage({
|
|
700
|
+
domain,
|
|
701
|
+
address,
|
|
702
|
+
statement,
|
|
703
|
+
uri,
|
|
704
|
+
chainId,
|
|
705
|
+
nonce,
|
|
706
|
+
issuedAt,
|
|
707
|
+
expirationTime,
|
|
708
|
+
});
|
|
709
|
+
const signature = await signWithProvider(provider, address, message);
|
|
710
|
+
const root = {
|
|
711
|
+
type: 'siwe',
|
|
712
|
+
iss: `did:pkh:eth:${address.toLowerCase()}`,
|
|
713
|
+
aud: session.did,
|
|
714
|
+
cap: options.capabilities,
|
|
715
|
+
exp,
|
|
716
|
+
nbf,
|
|
717
|
+
siwe: {
|
|
718
|
+
message,
|
|
719
|
+
signature,
|
|
720
|
+
},
|
|
721
|
+
};
|
|
722
|
+
await storeUcanRoot(root, session.id);
|
|
723
|
+
return root;
|
|
724
|
+
}
|
|
725
|
+
async function signUcanPayload(payload, session) {
|
|
726
|
+
const header = { alg: 'EdDSA', typ: 'UCAN' };
|
|
727
|
+
const headerB64 = encodeJson(header);
|
|
728
|
+
const payloadB64 = encodeJson(payload);
|
|
729
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
730
|
+
let signatureB64;
|
|
731
|
+
if (session.signer) {
|
|
732
|
+
signatureB64 = await session.signer(signingInput, payload);
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
if (!session.privateKey) {
|
|
736
|
+
throw new Error('Missing UCAN session key');
|
|
737
|
+
}
|
|
738
|
+
const data = textEncoder.encode(signingInput);
|
|
739
|
+
const signature = await crypto.subtle.sign('Ed25519', session.privateKey, data);
|
|
740
|
+
signatureB64 = toBase64Url(signature);
|
|
741
|
+
}
|
|
742
|
+
return `${headerB64}.${payloadB64}.${signatureB64}`;
|
|
743
|
+
}
|
|
744
|
+
async function resolveProofs(options, issuer) {
|
|
745
|
+
if (options.proofs && options.proofs.length > 0)
|
|
746
|
+
return options.proofs;
|
|
747
|
+
const stored = await getStoredUcanRoot(options.sessionId || DEFAULT_SESSION_ID);
|
|
748
|
+
if (!stored) {
|
|
749
|
+
throw new Error('Missing UCAN proof chain');
|
|
750
|
+
}
|
|
751
|
+
if (issuer?.did && stored.aud && stored.aud !== issuer.did) {
|
|
752
|
+
throw new Error('UCAN root audience mismatch');
|
|
753
|
+
}
|
|
754
|
+
return [stored];
|
|
755
|
+
}
|
|
756
|
+
async function createDelegationUcan(options) {
|
|
757
|
+
const issuer = options.issuer || (await createUcanSession({
|
|
758
|
+
id: options.sessionId,
|
|
759
|
+
provider: options.provider,
|
|
760
|
+
}));
|
|
761
|
+
if (!issuer)
|
|
762
|
+
throw new Error('Missing UCAN session key');
|
|
763
|
+
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_UCAN_TTL);
|
|
764
|
+
const payload = {
|
|
765
|
+
iss: issuer.did,
|
|
766
|
+
aud: options.audience,
|
|
767
|
+
cap: options.capabilities,
|
|
768
|
+
exp,
|
|
769
|
+
nbf: options.notBeforeMs,
|
|
770
|
+
prf: await resolveProofs(options, issuer),
|
|
771
|
+
};
|
|
772
|
+
return await signUcanPayload(payload, issuer);
|
|
773
|
+
}
|
|
774
|
+
async function createInvocationUcan(options) {
|
|
775
|
+
const issuer = options.issuer || (await createUcanSession({
|
|
776
|
+
id: options.sessionId,
|
|
777
|
+
provider: options.provider,
|
|
778
|
+
}));
|
|
779
|
+
if (!issuer)
|
|
780
|
+
throw new Error('Missing UCAN session key');
|
|
781
|
+
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_UCAN_TTL);
|
|
782
|
+
const payload = {
|
|
783
|
+
iss: issuer.did,
|
|
784
|
+
aud: options.audience,
|
|
785
|
+
cap: options.capabilities,
|
|
786
|
+
exp,
|
|
787
|
+
nbf: options.notBeforeMs,
|
|
788
|
+
prf: await resolveProofs(options, issuer),
|
|
789
|
+
};
|
|
790
|
+
return await signUcanPayload(payload, issuer);
|
|
791
|
+
}
|
|
792
|
+
async function authUcanFetch(input, init = {}, options = {}) {
|
|
793
|
+
const fetcher = options.fetcher || fetch;
|
|
794
|
+
let token = options.ucan;
|
|
795
|
+
if (!token) {
|
|
796
|
+
if (!options.audience || !options.capabilities) {
|
|
797
|
+
throw new Error('Missing UCAN audience or capabilities');
|
|
798
|
+
}
|
|
799
|
+
token = await createInvocationUcan({
|
|
800
|
+
issuer: options.issuer,
|
|
801
|
+
sessionId: options.sessionId,
|
|
802
|
+
provider: options.provider,
|
|
803
|
+
audience: options.audience,
|
|
804
|
+
capabilities: options.capabilities,
|
|
805
|
+
expiresInMs: options.expiresInMs,
|
|
806
|
+
notBeforeMs: options.notBeforeMs,
|
|
807
|
+
proofs: options.proofs,
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
const headers = new Headers(init.headers || {});
|
|
811
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
812
|
+
return fetcher(input, {
|
|
813
|
+
...init,
|
|
814
|
+
headers,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function normalizeBaseUrl(baseUrl) {
|
|
819
|
+
return baseUrl.replace(/\/+$/, '');
|
|
820
|
+
}
|
|
821
|
+
function normalizePrefix(prefix) {
|
|
822
|
+
if (!prefix || prefix === '/')
|
|
823
|
+
return '';
|
|
824
|
+
let next = prefix.startsWith('/') ? prefix : `/${prefix}`;
|
|
825
|
+
next = next.replace(/\/+$/, '');
|
|
826
|
+
return next;
|
|
827
|
+
}
|
|
828
|
+
function normalizePath(path) {
|
|
829
|
+
if (!path || path === '/')
|
|
830
|
+
return '/';
|
|
831
|
+
const next = path.startsWith('/') ? path : `/${path}`;
|
|
832
|
+
return encodeURI(next);
|
|
833
|
+
}
|
|
834
|
+
function joinUrl(baseUrl, path) {
|
|
835
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
836
|
+
const suffix = path.startsWith('/') ? path : `/${path}`;
|
|
837
|
+
return `${base}${suffix}`;
|
|
838
|
+
}
|
|
839
|
+
function resolveAuthHeader(auth, token) {
|
|
840
|
+
if (auth?.type === 'bearer') {
|
|
841
|
+
return `Bearer ${auth.token}`;
|
|
842
|
+
}
|
|
843
|
+
if (auth?.type === 'basic') {
|
|
844
|
+
const raw = `${auth.username}:${auth.password}`;
|
|
845
|
+
return `Basic ${btoa(raw)}`;
|
|
846
|
+
}
|
|
847
|
+
if (token) {
|
|
848
|
+
return `Bearer ${token}`;
|
|
849
|
+
}
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
class WebDavClient {
|
|
853
|
+
baseUrl;
|
|
854
|
+
prefix;
|
|
855
|
+
auth;
|
|
856
|
+
token;
|
|
857
|
+
fetcher;
|
|
858
|
+
credentials;
|
|
859
|
+
constructor(options) {
|
|
860
|
+
this.baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
861
|
+
this.prefix = normalizePrefix(options.prefix);
|
|
862
|
+
this.auth = options.auth;
|
|
863
|
+
this.token = options.token;
|
|
864
|
+
this.fetcher = options.fetcher || ((input, init) => fetch(input, init));
|
|
865
|
+
this.credentials = options.credentials;
|
|
866
|
+
}
|
|
867
|
+
setToken(token) {
|
|
868
|
+
this.token = token || undefined;
|
|
869
|
+
}
|
|
870
|
+
setAuth(auth) {
|
|
871
|
+
this.auth = auth;
|
|
872
|
+
}
|
|
873
|
+
buildUrl(path) {
|
|
874
|
+
const webdavPath = `${this.prefix}${normalizePath(path)}`;
|
|
875
|
+
return `${this.baseUrl}${webdavPath}`;
|
|
876
|
+
}
|
|
877
|
+
buildHeaders(options) {
|
|
878
|
+
const headers = new Headers(options?.headers || {});
|
|
879
|
+
const authHeader = resolveAuthHeader(options?.auth || this.auth, options?.token || this.token);
|
|
880
|
+
if (authHeader) {
|
|
881
|
+
headers.set('Authorization', authHeader);
|
|
882
|
+
}
|
|
883
|
+
if (options?.depth !== undefined) {
|
|
884
|
+
headers.set('Depth', String(options.depth));
|
|
885
|
+
}
|
|
886
|
+
if (typeof options?.overwrite === 'boolean') {
|
|
887
|
+
headers.set('Overwrite', options.overwrite ? 'T' : 'F');
|
|
888
|
+
}
|
|
889
|
+
if (options?.contentType) {
|
|
890
|
+
headers.set('Content-Type', options.contentType);
|
|
891
|
+
}
|
|
892
|
+
return headers;
|
|
893
|
+
}
|
|
894
|
+
async request(method, path, body, options = {}) {
|
|
895
|
+
const response = await this.fetcher(this.buildUrl(path), {
|
|
896
|
+
method,
|
|
897
|
+
headers: this.buildHeaders(options),
|
|
898
|
+
body: body ?? undefined,
|
|
899
|
+
credentials: this.credentials,
|
|
900
|
+
signal: options.signal,
|
|
901
|
+
});
|
|
902
|
+
if (!response.ok) {
|
|
903
|
+
throw new Error(`WebDAV ${method} ${path} failed: ${response.status} ${response.statusText}`);
|
|
904
|
+
}
|
|
905
|
+
return response;
|
|
906
|
+
}
|
|
907
|
+
async listDirectory(path = '/', depth = 1) {
|
|
908
|
+
const res = await this.request('PROPFIND', path, null, { depth });
|
|
909
|
+
return await res.text();
|
|
910
|
+
}
|
|
911
|
+
async download(path) {
|
|
912
|
+
return await this.request('GET', path);
|
|
913
|
+
}
|
|
914
|
+
async downloadText(path) {
|
|
915
|
+
const res = await this.download(path);
|
|
916
|
+
return await res.text();
|
|
917
|
+
}
|
|
918
|
+
async downloadArrayBuffer(path) {
|
|
919
|
+
const res = await this.download(path);
|
|
920
|
+
return await res.arrayBuffer();
|
|
921
|
+
}
|
|
922
|
+
async upload(path, content, contentType) {
|
|
923
|
+
return await this.request('PUT', path, content, { contentType });
|
|
924
|
+
}
|
|
925
|
+
async createDirectory(path) {
|
|
926
|
+
return await this.request('MKCOL', path);
|
|
927
|
+
}
|
|
928
|
+
async remove(path) {
|
|
929
|
+
return await this.request('DELETE', path);
|
|
930
|
+
}
|
|
931
|
+
async move(path, destination, overwrite = true) {
|
|
932
|
+
const destinationUrl = destination.startsWith('http')
|
|
933
|
+
? destination
|
|
934
|
+
: this.buildUrl(destination);
|
|
935
|
+
return await this.request('MOVE', path, null, {
|
|
936
|
+
headers: { Destination: destinationUrl },
|
|
937
|
+
overwrite,
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
async copy(path, destination, overwrite = true) {
|
|
941
|
+
const destinationUrl = destination.startsWith('http')
|
|
942
|
+
? destination
|
|
943
|
+
: this.buildUrl(destination);
|
|
944
|
+
return await this.request('COPY', path, null, {
|
|
945
|
+
headers: { Destination: destinationUrl },
|
|
946
|
+
overwrite,
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
async getQuota() {
|
|
950
|
+
const res = await this.fetcher(joinUrl(this.baseUrl, '/api/v1/public/webdav/quota'), {
|
|
951
|
+
method: 'GET',
|
|
952
|
+
headers: this.buildHeaders(),
|
|
953
|
+
credentials: this.credentials,
|
|
954
|
+
});
|
|
955
|
+
if (!res.ok) {
|
|
956
|
+
throw new Error(`WebDAV quota failed: ${res.status} ${res.statusText}`);
|
|
957
|
+
}
|
|
958
|
+
return await res.json();
|
|
959
|
+
}
|
|
960
|
+
async listRecycle() {
|
|
961
|
+
const res = await this.fetcher(joinUrl(this.baseUrl, '/api/v1/public/webdav/recycle/list'), {
|
|
962
|
+
method: 'GET',
|
|
963
|
+
headers: this.buildHeaders(),
|
|
964
|
+
credentials: this.credentials,
|
|
965
|
+
});
|
|
966
|
+
if (!res.ok) {
|
|
967
|
+
throw new Error(`WebDAV recycle list failed: ${res.status} ${res.statusText}`);
|
|
968
|
+
}
|
|
969
|
+
return await res.json();
|
|
970
|
+
}
|
|
971
|
+
async recoverRecycle(hash) {
|
|
972
|
+
const res = await this.fetcher(joinUrl(this.baseUrl, '/api/v1/public/webdav/recycle/recover'), {
|
|
973
|
+
method: 'POST',
|
|
974
|
+
headers: this.buildHeaders({ contentType: 'application/json' }),
|
|
975
|
+
body: JSON.stringify({ hash }),
|
|
976
|
+
credentials: this.credentials,
|
|
977
|
+
});
|
|
978
|
+
if (!res.ok) {
|
|
979
|
+
throw new Error(`WebDAV recycle recover failed: ${res.status} ${res.statusText}`);
|
|
980
|
+
}
|
|
981
|
+
return await res.json();
|
|
982
|
+
}
|
|
983
|
+
async deleteRecycle(hash) {
|
|
984
|
+
const res = await this.fetcher(joinUrl(this.baseUrl, '/api/v1/public/webdav/recycle/permanent'), {
|
|
985
|
+
method: 'DELETE',
|
|
986
|
+
headers: this.buildHeaders({ contentType: 'application/json' }),
|
|
987
|
+
body: JSON.stringify({ hash }),
|
|
988
|
+
credentials: this.credentials,
|
|
989
|
+
});
|
|
990
|
+
if (!res.ok) {
|
|
991
|
+
throw new Error(`WebDAV recycle delete failed: ${res.status} ${res.statusText}`);
|
|
992
|
+
}
|
|
993
|
+
return await res.json();
|
|
994
|
+
}
|
|
995
|
+
async clearRecycle() {
|
|
996
|
+
const res = await this.fetcher(joinUrl(this.baseUrl, '/api/v1/public/webdav/recycle/clear'), {
|
|
997
|
+
method: 'DELETE',
|
|
998
|
+
headers: this.buildHeaders(),
|
|
999
|
+
credentials: this.credentials,
|
|
1000
|
+
});
|
|
1001
|
+
if (!res.ok) {
|
|
1002
|
+
throw new Error(`WebDAV recycle clear failed: ${res.status} ${res.statusText}`);
|
|
1003
|
+
}
|
|
1004
|
+
return await res.json();
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function createWebDavClient(options) {
|
|
1008
|
+
return new WebDavClient(options);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
export { WebDavClient, authFetch, authUcanFetch, clearAccessToken, clearUcanSession, createDelegationUcan, createInvocationUcan, createRootUcan, createUcanSession, createWebDavClient, getAccessToken, getAccounts, getBalance, getChainId, getProvider, getStoredUcanRoot, getUcanSession, isYeYingProvider, loginWithChallenge, logout, onAccountsChanged, onChainChanged, refreshAccessToken, requestAccounts, requireProvider, setAccessToken, signMessage, storeUcanRoot };
|
|
1012
|
+
//# sourceMappingURL=web3-bs.esm.js.map
|