@yeying-community/web3-bs 1.0.2 → 1.0.3

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 CHANGED
@@ -9,6 +9,189 @@
9
9
  npm install @yeying-community/web3-bs
10
10
  ```
11
11
 
12
+ ## Dapp 集成(浏览器)
13
+
14
+ SDK 仅支持浏览器环境。推荐两种接入方式:
15
+
16
+ ### 方式一:模块化(打包器 / Vite / Webpack)
17
+
18
+ ```ts
19
+ import { getProvider, loginWithChallenge, authFetch } from '@yeying-community/web3-bs';
20
+
21
+ const provider = await getProvider({ timeoutMs: 3000 });
22
+ const login = await loginWithChallenge({
23
+ provider,
24
+ baseUrl: 'http://localhost:3203/api/v1/public/auth',
25
+ storeToken: false,
26
+ });
27
+
28
+ const res = await authFetch('http://localhost:3203/api/v1/public/profile', { method: 'GET' }, {
29
+ baseUrl: 'http://localhost:3203/api/v1/public/auth',
30
+ storeToken: false,
31
+ });
32
+ console.log(await res.json());
33
+ ```
34
+
35
+ ### 方式二:UMD(纯 HTML)
36
+
37
+ ```html
38
+ <script src="https://your-host/dist/web3-bs.umd.js"></script>
39
+ <script>
40
+ const { getProvider, loginWithChallenge } = window.YeYingWeb3;
41
+ async function login() {
42
+ const provider = await getProvider({ timeoutMs: 3000 });
43
+ const result = await loginWithChallenge({
44
+ provider,
45
+ baseUrl: 'http://localhost:3203/api/v1/public/auth',
46
+ storeToken: false,
47
+ });
48
+ console.log(result);
49
+ }
50
+ login();
51
+ </script>
52
+ ```
53
+
54
+ 提示:UMD 模式下全局对象为 `window.YeYingWeb3`,需要保证 `dist/web3-bs.umd.js` 可访问。
55
+
56
+ ## SIWE 登录(不使用 UCAN)
57
+
58
+ 如果只需要传统 SIWE + JWT 登录(单后端),可以不启用 UCAN:
59
+
60
+ ```ts
61
+ import { getProvider, loginWithChallenge, authFetch, refreshAccessToken, logout } from '@yeying-community/web3-bs';
62
+
63
+ const provider = await getProvider();
64
+ const login = await loginWithChallenge({
65
+ provider,
66
+ baseUrl: 'http://localhost:3203/api/v1/public/auth',
67
+ storeToken: false,
68
+ });
69
+
70
+ const profile = await authFetch('http://localhost:3203/api/v1/public/profile', { method: 'GET' }, {
71
+ baseUrl: 'http://localhost:3203/api/v1/public/auth',
72
+ storeToken: false,
73
+ });
74
+ console.log(await profile.json());
75
+
76
+ await refreshAccessToken({ baseUrl: 'http://localhost:3203/api/v1/public/auth', storeToken: false });
77
+ await logout({ baseUrl: 'http://localhost:3203/api/v1/public/auth', storeToken: false });
78
+ ```
79
+
80
+ ## SIWE 与 UCAN 完整登录流程
81
+
82
+ ### SIWE vs UCAN 对比(token 来源 / 有效期 / 续期方式)
83
+
84
+ | 维度 | SIWE 登录(JWT) | UCAN 授权 |
85
+ | --- | --- | --- |
86
+ | Token 来源 | 后端签发 access token + refresh cookie | 前端生成 UCAN(Root/Invocation),后端只校验 |
87
+ | 有效期 | access/refresh 由后端配置 | Invocation 默认 5 分钟,Root 默认 24 小时(可配置) |
88
+ | 续期方式 | `POST /auth/refresh`(refresh cookie) | 重新生成 Invocation;Root 过期则重新生成 Root + Invocation |
89
+
90
+ ### SIWE-only(单后端)
91
+ 1. 前端检测钱包 Provider(`getProvider` / `requestAccounts`)。
92
+ 2. 请求后端 challenge:`POST /api/v1/public/auth/challenge`。
93
+ 3. 钱包签名 challenge(`personal_sign`)。
94
+ 4. 提交签名到后端:`POST /api/v1/public/auth/verify`,获取 access token + refresh cookie。
95
+ 5. 访问业务接口:`Authorization: Bearer <access-token>`(`authFetch` 会自动附带)。
96
+ 6. access 过期时刷新:`POST /api/v1/public/auth/refresh`(依赖 httpOnly refresh cookie)。
97
+ 7. 退出登录:`POST /api/v1/public/auth/logout`。
98
+
99
+ Mermaid:
100
+
101
+ ```mermaid
102
+ sequenceDiagram
103
+ actor 用户
104
+ participant FE as Dapp 前端
105
+ participant WP as 钱包 Provider
106
+ participant BE as 认证后端
107
+
108
+ 用户 ->> FE: 打开 Dapp / 点击登录
109
+ FE ->> WP: 获取 Provider / 请求账户
110
+ FE ->> BE: POST /auth/challenge { address }
111
+ BE -->> FE: 返回 challenge, nonce
112
+ FE ->> WP: 签名 challenge
113
+ WP -->> FE: 返回 signature
114
+ FE ->> BE: POST /auth/verify { address, signature }
115
+ BE -->> FE: 返回 access token + refresh cookie
116
+ FE ->> BE: GET /public/profile (Authorization: Bearer access)
117
+ BE -->> FE: 返回业务数据
118
+ FE ->> BE: POST /auth/refresh (refresh cookie)
119
+ BE -->> FE: 返回新 access token
120
+ FE ->> BE: POST /auth/logout
121
+ BE -->> FE: 登出成功
122
+ ```
123
+
124
+ ### UCAN(多后端 / Delegation)
125
+ 1. 前端检测钱包 Provider(`getProvider` / `requestAccounts`)。
126
+ 2. 由钱包生成 UCAN Session Key:`createUcanSession`。
127
+ 3. 用 SIWE 作为桥梁生成 Root UCAN(包含能力 `[{ resource, action }]`):`createRootUcan` 或 `getOrCreateUcanRoot`。
128
+ 4. 针对每个后端生成 Invocation UCAN:`createInvocationUcan({ issuer: session, audience, capabilities, proofs: [root] })`。
129
+ 5. 调用后端业务接口:`Authorization: Bearer <UCAN>`(可用 `authUcanFetch`)。
130
+ 6. 如需可转授权,先生成 Delegation UCAN:`createDelegationUcan`,由被委任方再生成 Invocation UCAN。
131
+ 7. Root/Invocation 过期时重新生成(或由 `initWebDavStorage` 自动处理)。
132
+ 提示:Root 过期后前端应重新授权并重试(重新生成 Root + Invocation)。
133
+
134
+ 推荐重试逻辑示例:
135
+ ```ts
136
+ import { getOrCreateUcanRoot, createInvocationUcan } from '@yeying-community/web3-bs';
137
+
138
+ async function callWithUcanRetry(target, caps, provider, session) {
139
+ const root = await getOrCreateUcanRoot({ provider, session, capabilities: caps });
140
+ let token = await createInvocationUcan({
141
+ issuer: session,
142
+ audience: target.aud,
143
+ capabilities: caps,
144
+ proofs: [root],
145
+ });
146
+
147
+ let res = await fetch(target.url, {
148
+ headers: { Authorization: `Bearer ${token}` },
149
+ });
150
+ if (res.status !== 401) return res;
151
+
152
+ // Root 过期:重新生成 Root 并重试一次
153
+ const newRoot = await getOrCreateUcanRoot({ provider, session, capabilities: caps, expiresInMs: 24 * 60 * 60 * 1000 });
154
+ token = await createInvocationUcan({
155
+ issuer: session,
156
+ audience: target.aud,
157
+ capabilities: caps,
158
+ proofs: [newRoot],
159
+ });
160
+ return await fetch(target.url, {
161
+ headers: { Authorization: `Bearer ${token}` },
162
+ });
163
+ }
164
+ ```
165
+
166
+ Mermaid:
167
+
168
+ ```mermaid
169
+ sequenceDiagram
170
+ actor 用户
171
+ participant FE as Dapp 前端
172
+ participant WP as 钱包 Provider
173
+ participant BA as 后端 A
174
+ participant BB as 后端 B
175
+
176
+ 用户 ->> FE: 打开 Dapp / 初始化 UCAN
177
+ FE ->> WP: createUcanSession
178
+ WP -->> FE: 返回 UCAN session key (did, signer)
179
+ FE ->> WP: createRootUcan (SIWE 桥接)
180
+ WP -->> FE: 返回 Root UCAN (proof)
181
+
182
+ FE ->> WP: createInvocationUcan(audience=后端 A)
183
+ WP -->> FE: 返回 Invocation UCAN (A)
184
+ FE ->> BA: GET /public/profile (Authorization: Bearer UCAN-A)
185
+ BA -->> FE: 返回响应
186
+
187
+ FE ->> WP: createInvocationUcan(audience=后端 B)
188
+ WP -->> FE: 返回 Invocation UCAN (B)
189
+ FE ->> BB: GET /public/profile (Authorization: Bearer UCAN-B)
190
+ BB -->> FE: 返回响应
191
+
192
+ Note over FE,WP: 可选委任链\ncreateDelegationUcan -> 转交 -> createInvocationUcan
193
+ ```
194
+
12
195
  ## 钱包交互 API
13
196
 
14
197
  ### Provider 发现
@@ -179,7 +362,7 @@ const ucan = await createInvocationUcan({
179
362
  proofs: [root],
180
363
  });
181
364
 
182
- const res = await authUcanFetch('http://localhost:3203/api/v1/private/profile', { method: 'GET' }, { ucan });
365
+ const res = await authUcanFetch('http://localhost:3203/api/v1/public/profile', { method: 'GET' }, { ucan });
183
366
  console.log(await res.json());
184
367
  ```
185
368
 
@@ -194,9 +377,13 @@ console.log(await res.json());
194
377
 
195
378
  提供基于 WebDAV 服务的文件操作封装(上传/下载/删除/目录等),适配 `webdav` 项目 API。
196
379
 
380
+ 新增便捷能力:
381
+ - `initWebDavStorage`:自动生成/复用 UCAN Invocation Token,并可在首次使用时创建应用目录
382
+ - `ensureDirectory(path)`:递归创建目录(存在时自动跳过)
383
+
197
384
  示例:
198
385
  ```ts
199
- import { createWebDavClient, loginWithChallenge } from '@yeying-community/web3-bs';
386
+ import { createWebDavClient, initWebDavStorage, loginWithChallenge } from '@yeying-community/web3-bs';
200
387
 
201
388
  const login = await loginWithChallenge({
202
389
  provider,
@@ -213,6 +400,15 @@ const client = createWebDavClient({
213
400
  const listingXml = await client.listDirectory('/');
214
401
  await client.upload('/docs/hello.txt', 'Hello WebDAV');
215
402
  const content = await client.downloadText('/docs/hello.txt');
403
+
404
+ // UCAN 登录 + 自动创建应用目录(推荐)
405
+ const storage = await initWebDavStorage({
406
+ baseUrl: 'http://localhost:6065',
407
+ audience: 'did:web:localhost:6065',
408
+ appId: 'my-dapp',
409
+ capabilities: [{ resource: 'profile', action: 'read' }],
410
+ });
411
+ await storage.client.upload(`${storage.appDir}/hello.txt`, 'Hello WebDAV');
216
412
  ```
217
413
 
218
414
  常用方法:
@@ -220,18 +416,46 @@ const content = await client.downloadText('/docs/hello.txt');
220
416
  - `upload(path, content, contentType?)`
221
417
  - `download(path)` / `downloadText(path)` / `downloadArrayBuffer(path)`
222
418
  - `createDirectory(path)`(MKCOL)
419
+ - `ensureDirectory(path)`(递归 MKCOL,已存在会跳过)
223
420
  - `remove(path)`(DELETE)
224
421
  - `move(path, destination, overwrite?)`
225
422
  - `copy(path, destination, overwrite?)`
226
423
  - `getQuota()` / `listRecycle()` / `recoverRecycle(hash)` / `deleteRecycle(hash)` / `clearRecycle()`
227
424
 
425
+ 提示:`capabilities` 需与 WebDAV 后端 UCAN 策略一致;`appId` 默认映射为 `/apps/<appId>`。
426
+
427
+ ## Dapp 快速接入(推荐)
428
+
429
+ 一次完成 SIWE 登录 + UCAN WebDAV 初始化 + 创建应用目录:
430
+
431
+ ```ts
432
+ import { initDappSession } from '@yeying-community/web3-bs';
433
+
434
+ const session = await initDappSession({
435
+ appAuth: {
436
+ baseUrl: 'http://localhost:3203/api/v1/public/auth',
437
+ storeToken: false,
438
+ },
439
+ webdav: {
440
+ baseUrl: 'http://localhost:6065',
441
+ audience: 'did:web:localhost:6065',
442
+ appId: 'my-dapp',
443
+ capabilities: [{ resource: 'profile', action: 'read' }],
444
+ },
445
+ });
446
+
447
+ const webdav = session.webdavClient;
448
+ await webdav?.upload(`${session.webdavAppDir}/hello.txt`, 'Hello WebDAV');
449
+ ```
450
+
228
451
  ## 本地验证
229
452
 
230
453
  1. 构建 SDK:`npm run build`
231
454
  2. 启动后端:`node examples/backend/node/server.js`
232
- 3. 启动前端:`python3 -m http.server 8001`,然后在浏览器输入访问地址:`http://localhost:8001/examples/frontend/dapp.html`
233
- 4. 确保安装 YeYing 钱包扩展插件
234
- 5. 点击:`Detect Provider` → `Connect Wallet` → `Login`
455
+ 3. 启动前端:`python3 -m http.server 8001`
456
+ 4. 访问:`http://localhost:8001/examples/frontend/dapp.html`
457
+ 5. 确保安装 YeYing 钱包扩展插件
458
+ 6. 点击:`Detect Provider` → `Connect Wallet` → `Login`
235
459
 
236
460
  提示:如果前端来自其他域名,请设置
237
461
  `COOKIE_SAMESITE=none` 且 `COOKIE_SECURE=true` 并使用 HTTPS,
@@ -86,6 +86,7 @@ export declare function createUcanSession(options?: CreateUcanSessionOptions): P
86
86
  export declare function clearUcanSession(id?: string): Promise<void>;
87
87
  export declare function storeUcanRoot(root: UcanRootProof, id?: string): Promise<void>;
88
88
  export declare function getStoredUcanRoot(id?: string): Promise<UcanRootProof | null>;
89
+ export declare function getOrCreateUcanRoot(options: CreateRootUcanOptions): Promise<UcanRootProof>;
89
90
  export declare function createRootUcan(options: CreateRootUcanOptions): Promise<UcanRootProof>;
90
91
  export declare function createDelegationUcan(options: CreateUcanTokenOptions): Promise<string>;
91
92
  export declare function createInvocationUcan(options: CreateUcanTokenOptions): Promise<string>;
package/dist/dapp.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { loginWithChallenge } from './auth/siwe';
2
+ import { UcanCapability, UcanRootProof, UcanSessionKey } from './auth/ucan';
3
+ import { Eip1193Provider, LoginWithChallengeOptions } from './auth/types';
4
+ import { WebDavClient } from './storage/webdav';
5
+ export type InitWebDavStorageOptions = {
6
+ baseUrl: string;
7
+ audience: string;
8
+ prefix?: string;
9
+ appDir?: string;
10
+ appId?: string;
11
+ ensureAppDir?: boolean;
12
+ capabilities?: UcanCapability[];
13
+ invocationCapabilities?: UcanCapability[];
14
+ provider?: Eip1193Provider;
15
+ sessionId?: string;
16
+ session?: UcanSessionKey;
17
+ root?: UcanRootProof;
18
+ rootExpiresInMs?: number;
19
+ invocationExpiresInMs?: number;
20
+ notBeforeMs?: number;
21
+ fetcher?: typeof fetch;
22
+ credentials?: RequestCredentials;
23
+ };
24
+ export type InitWebDavStorageResult = {
25
+ client: WebDavClient;
26
+ token: string;
27
+ appDir?: string;
28
+ session: UcanSessionKey;
29
+ root: UcanRootProof;
30
+ };
31
+ export type InitDappSessionOptions = {
32
+ provider?: Eip1193Provider;
33
+ address?: string;
34
+ appAuth?: LoginWithChallengeOptions;
35
+ webdav?: InitWebDavStorageOptions;
36
+ };
37
+ export type InitDappSessionResult = {
38
+ provider: Eip1193Provider;
39
+ address?: string;
40
+ appLogin?: Awaited<ReturnType<typeof loginWithChallenge>>;
41
+ ucanSession?: UcanSessionKey;
42
+ ucanRoot?: UcanRootProof;
43
+ webdavClient?: WebDavClient;
44
+ webdavToken?: string;
45
+ webdavAppDir?: string;
46
+ };
47
+ export declare function initWebDavStorage(options: InitWebDavStorageOptions): Promise<InitWebDavStorageResult>;
48
+ export declare function initDappSession(options: InitDappSessionOptions): Promise<InitDappSessionResult>;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './auth';
2
2
  export * from './storage';
3
+ export * from './dapp';
@@ -42,6 +42,7 @@ export declare class WebDavClient {
42
42
  downloadArrayBuffer(path: string): Promise<ArrayBuffer>;
43
43
  upload(path: string, content: BodyInit, contentType?: string): Promise<Response>;
44
44
  createDirectory(path: string): Promise<Response>;
45
+ ensureDirectory(path: string): Promise<void>;
45
46
  remove(path: string): Promise<Response>;
46
47
  move(path: string, destination: string, overwrite?: boolean): Promise<Response>;
47
48
  copy(path: string, destination: string, overwrite?: boolean): Promise<Response>;
@@ -637,6 +637,25 @@ async function getStoredUcanRoot(id = DEFAULT_SESSION_ID) {
637
637
  const record = await readSessionRecord(id);
638
638
  return record?.root || null;
639
639
  }
640
+ function capsEqual(a, b) {
641
+ return JSON.stringify(a || []) === JSON.stringify(b || []);
642
+ }
643
+ function isRootExpired(root, nowMs) {
644
+ return Boolean(root.exp && nowMs > root.exp);
645
+ }
646
+ async function getOrCreateUcanRoot(options) {
647
+ const provider = options.provider || (await requireProvider());
648
+ const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
649
+ const nowMs = Date.now();
650
+ const stored = await getStoredUcanRoot(session.id);
651
+ if (stored &&
652
+ (!stored.aud || stored.aud === session.did) &&
653
+ capsEqual(stored.cap, options.capabilities) &&
654
+ !isRootExpired(stored, nowMs)) {
655
+ return stored;
656
+ }
657
+ return await createRootUcan({ ...options, provider, session });
658
+ }
640
659
  function buildUcanStatement(payload) {
641
660
  return `UCAN-AUTH ${JSON.stringify(payload)}`;
642
661
  }
@@ -661,7 +680,15 @@ function buildSiweMessage(params) {
661
680
  async function resolveAddress(provider, address) {
662
681
  if (address)
663
682
  return address;
664
- const accounts = await getAccounts(provider);
683
+ let accounts = await getAccounts(provider);
684
+ if (!accounts[0]) {
685
+ const requested = (await provider.request({
686
+ method: 'eth_requestAccounts',
687
+ }));
688
+ if (Array.isArray(requested)) {
689
+ accounts = requested;
690
+ }
691
+ }
665
692
  if (!accounts[0])
666
693
  throw new Error('No account available');
667
694
  return accounts[0];
@@ -925,6 +952,27 @@ class WebDavClient {
925
952
  async createDirectory(path) {
926
953
  return await this.request('MKCOL', path);
927
954
  }
955
+ async ensureDirectory(path) {
956
+ if (!path || path === '/')
957
+ return;
958
+ const segments = path.split('/').filter(Boolean);
959
+ if (segments.length === 0)
960
+ return;
961
+ let current = '';
962
+ for (const segment of segments) {
963
+ current = `${current}/${segment}`;
964
+ const res = await this.fetcher(this.buildUrl(current), {
965
+ method: 'MKCOL',
966
+ headers: this.buildHeaders(),
967
+ credentials: this.credentials,
968
+ });
969
+ if (res.ok)
970
+ continue;
971
+ if (res.status === 405)
972
+ continue;
973
+ throw new Error(`WebDAV MKCOL ${current} failed: ${res.status} ${res.statusText}`);
974
+ }
975
+ }
928
976
  async remove(path) {
929
977
  return await this.request('DELETE', path);
930
978
  }
@@ -1008,5 +1056,197 @@ function createWebDavClient(options) {
1008
1056
  return new WebDavClient(options);
1009
1057
  }
1010
1058
 
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 };
1059
+ const tokenCache = new Map();
1060
+ const TOKEN_SKEW_MS = 5000;
1061
+ function normalizeAppDir(path) {
1062
+ const trimmed = path.trim();
1063
+ if (!trimmed)
1064
+ return '/';
1065
+ let next = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
1066
+ next = next.replace(/\/+$/, '');
1067
+ return next || '/';
1068
+ }
1069
+ function sanitizeAppId(appId) {
1070
+ return appId.trim().replace(/[^a-zA-Z0-9._-]/g, '-');
1071
+ }
1072
+ function resolveAppDir(options) {
1073
+ if (options.appDir) {
1074
+ return normalizeAppDir(options.appDir);
1075
+ }
1076
+ if (options.appId) {
1077
+ return normalizeAppDir(`/apps/${sanitizeAppId(options.appId)}`);
1078
+ }
1079
+ return undefined;
1080
+ }
1081
+ function buildCapsKey(caps) {
1082
+ return JSON.stringify(caps || []);
1083
+ }
1084
+ function buildTokenCacheKey(issuer, audience, caps) {
1085
+ return `${issuer.did}|${audience}|${buildCapsKey(caps)}`;
1086
+ }
1087
+ function isTokenValid(entry, nowMs) {
1088
+ if (!entry.exp)
1089
+ return false;
1090
+ if (entry.nbf && nowMs < entry.nbf)
1091
+ return false;
1092
+ return entry.exp - TOKEN_SKEW_MS > nowMs;
1093
+ }
1094
+ function decodeBase64Url(input) {
1095
+ if (!input)
1096
+ return null;
1097
+ const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
1098
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
1099
+ try {
1100
+ if (typeof atob === 'function') {
1101
+ return atob(padded);
1102
+ }
1103
+ }
1104
+ catch {
1105
+ // ignore
1106
+ }
1107
+ try {
1108
+ const nodeBuffer = globalThis.Buffer;
1109
+ if (nodeBuffer) {
1110
+ return nodeBuffer.from(padded, 'base64').toString('utf8');
1111
+ }
1112
+ }
1113
+ catch {
1114
+ return null;
1115
+ }
1116
+ return null;
1117
+ }
1118
+ function decodeUcanPayload(token) {
1119
+ const parts = token.split('.');
1120
+ if (parts.length < 2)
1121
+ return null;
1122
+ const decoded = decodeBase64Url(parts[1]);
1123
+ if (!decoded)
1124
+ return null;
1125
+ try {
1126
+ return JSON.parse(decoded);
1127
+ }
1128
+ catch {
1129
+ return null;
1130
+ }
1131
+ }
1132
+ async function getCachedInvocationToken(options) {
1133
+ const cacheKey = buildTokenCacheKey(options.issuer, options.audience, options.capabilities);
1134
+ const cached = tokenCache.get(cacheKey);
1135
+ const nowMs = Date.now();
1136
+ if (cached && isTokenValid(cached, nowMs)) {
1137
+ return cached.token;
1138
+ }
1139
+ const token = await createInvocationUcan({
1140
+ issuer: options.issuer,
1141
+ audience: options.audience,
1142
+ capabilities: options.capabilities,
1143
+ proofs: options.proofs,
1144
+ expiresInMs: options.expiresInMs,
1145
+ notBeforeMs: options.notBeforeMs,
1146
+ });
1147
+ const payload = decodeUcanPayload(token);
1148
+ if (payload && typeof payload.exp === 'number') {
1149
+ tokenCache.set(cacheKey, {
1150
+ token,
1151
+ exp: payload.exp,
1152
+ nbf: payload.nbf,
1153
+ });
1154
+ }
1155
+ return token;
1156
+ }
1157
+ async function initWebDavStorage(options) {
1158
+ const caps = options.capabilities || options.root?.cap;
1159
+ if (!caps || caps.length === 0) {
1160
+ throw new Error('Missing UCAN capabilities for WebDAV');
1161
+ }
1162
+ const needsProvider = !options.session || !options.root;
1163
+ const provider = options.provider || (needsProvider ? await requireProvider() : undefined);
1164
+ const session = options.session ||
1165
+ (await createUcanSession({
1166
+ id: options.sessionId,
1167
+ provider,
1168
+ }));
1169
+ const nowMs = Date.now();
1170
+ let root = options.root;
1171
+ if (root && root.aud && root.aud !== session.did) {
1172
+ root = undefined;
1173
+ }
1174
+ if (root && buildCapsKey(root.cap) !== buildCapsKey(caps)) {
1175
+ root = undefined;
1176
+ }
1177
+ if (root && root.exp && nowMs > root.exp) {
1178
+ root = undefined;
1179
+ }
1180
+ if (!root) {
1181
+ root = await getOrCreateUcanRoot({
1182
+ provider: provider || (await requireProvider()),
1183
+ session,
1184
+ capabilities: caps,
1185
+ expiresInMs: options.rootExpiresInMs,
1186
+ });
1187
+ }
1188
+ const invocationCaps = options.invocationCapabilities || caps;
1189
+ const token = await getCachedInvocationToken({
1190
+ issuer: session,
1191
+ audience: options.audience,
1192
+ capabilities: invocationCaps,
1193
+ proofs: [root],
1194
+ expiresInMs: options.invocationExpiresInMs,
1195
+ notBeforeMs: options.notBeforeMs,
1196
+ });
1197
+ const client = createWebDavClient({
1198
+ baseUrl: options.baseUrl,
1199
+ prefix: options.prefix,
1200
+ token,
1201
+ fetcher: options.fetcher,
1202
+ credentials: options.credentials,
1203
+ });
1204
+ const appDir = resolveAppDir(options);
1205
+ if (appDir && options.ensureAppDir !== false) {
1206
+ await client.ensureDirectory(appDir);
1207
+ }
1208
+ return {
1209
+ client,
1210
+ token,
1211
+ appDir,
1212
+ session,
1213
+ root,
1214
+ };
1215
+ }
1216
+ async function initDappSession(options) {
1217
+ if (!options.appAuth && !options.webdav) {
1218
+ throw new Error('No init options provided');
1219
+ }
1220
+ const provider = options.provider ||
1221
+ options.appAuth?.provider ||
1222
+ options.webdav?.provider ||
1223
+ (await requireProvider());
1224
+ const result = {
1225
+ provider,
1226
+ address: options.address,
1227
+ };
1228
+ if (options.appAuth) {
1229
+ const appLogin = await loginWithChallenge({
1230
+ ...options.appAuth,
1231
+ provider: options.appAuth.provider || provider,
1232
+ address: options.appAuth.address || options.address,
1233
+ });
1234
+ result.appLogin = appLogin;
1235
+ result.address = appLogin.address;
1236
+ }
1237
+ if (options.webdav) {
1238
+ const webdav = await initWebDavStorage({
1239
+ ...options.webdav,
1240
+ provider: options.webdav.provider || provider,
1241
+ });
1242
+ result.ucanSession = webdav.session;
1243
+ result.ucanRoot = webdav.root;
1244
+ result.webdavClient = webdav.client;
1245
+ result.webdavToken = webdav.token;
1246
+ result.webdavAppDir = webdav.appDir;
1247
+ }
1248
+ return result;
1249
+ }
1250
+
1251
+ export { WebDavClient, authFetch, authUcanFetch, clearAccessToken, clearUcanSession, createDelegationUcan, createInvocationUcan, createRootUcan, createUcanSession, createWebDavClient, getAccessToken, getAccounts, getBalance, getChainId, getOrCreateUcanRoot, getProvider, getStoredUcanRoot, getUcanSession, initDappSession, initWebDavStorage, isYeYingProvider, loginWithChallenge, logout, onAccountsChanged, onChainChanged, refreshAccessToken, requestAccounts, requireProvider, setAccessToken, signMessage, storeUcanRoot };
1012
1252
  //# sourceMappingURL=web3-bs.esm.js.map