@yeying-community/web3-bs 1.0.2 → 1.0.4
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 +65 -209
- package/dist/auth/provider.d.ts +3 -1
- package/dist/auth/types.d.ts +14 -0
- package/dist/auth/ucan.d.ts +1 -0
- package/dist/dapp.d.ts +48 -0
- package/dist/index.d.ts +1 -0
- package/dist/storage/webdav.d.ts +1 -0
- package/dist/web3-bs.esm.js +299 -4
- package/dist/web3-bs.esm.js.map +1 -1
- package/dist/web3-bs.umd.js +303 -3
- package/dist/web3-bs.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/web3-bs.esm.js
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
const YEYING_RDNS = 'io.github.yeying';
|
|
2
2
|
const DEFAULT_TIMEOUT = 1000;
|
|
3
|
+
const DEFAULT_ACCOUNT_STORAGE_KEY = 'yeying:last_account';
|
|
3
4
|
function getWindowEthereum() {
|
|
4
5
|
if (typeof window === 'undefined')
|
|
5
6
|
return null;
|
|
6
7
|
return window.ethereum || null;
|
|
7
8
|
}
|
|
9
|
+
function readStoredAccount(storageKey) {
|
|
10
|
+
if (typeof localStorage === 'undefined')
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
return localStorage.getItem(storageKey);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function writeStoredAccount(storageKey, account) {
|
|
20
|
+
if (typeof localStorage === 'undefined')
|
|
21
|
+
return;
|
|
22
|
+
try {
|
|
23
|
+
if (account) {
|
|
24
|
+
localStorage.setItem(storageKey, account);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
localStorage.removeItem(storageKey);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// ignore storage errors
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function selectPreferredAccount(accounts, stored, preferStored) {
|
|
35
|
+
if (preferStored && stored && accounts.includes(stored)) {
|
|
36
|
+
return stored;
|
|
37
|
+
}
|
|
38
|
+
return accounts[0] || null;
|
|
39
|
+
}
|
|
8
40
|
function isYeYingProvider(provider, info) {
|
|
9
41
|
if (!provider)
|
|
10
42
|
return false;
|
|
@@ -110,6 +142,29 @@ async function getChainId(provider) {
|
|
|
110
142
|
const chainId = (await p.request({ method: 'eth_chainId' }));
|
|
111
143
|
return typeof chainId === 'string' ? chainId : null;
|
|
112
144
|
}
|
|
145
|
+
async function getPreferredAccount(options = {}) {
|
|
146
|
+
const provider = options.provider || (await requireProvider());
|
|
147
|
+
const storageKey = options.storageKey || DEFAULT_ACCOUNT_STORAGE_KEY;
|
|
148
|
+
const preferStored = options.preferStored !== false;
|
|
149
|
+
let accounts = await getAccounts(provider);
|
|
150
|
+
if (accounts.length === 0 && options.autoConnect) {
|
|
151
|
+
accounts = await requestAccounts({ provider });
|
|
152
|
+
}
|
|
153
|
+
const stored = readStoredAccount(storageKey);
|
|
154
|
+
const account = selectPreferredAccount(accounts, stored, preferStored);
|
|
155
|
+
writeStoredAccount(storageKey, account);
|
|
156
|
+
return { account, accounts };
|
|
157
|
+
}
|
|
158
|
+
function watchAccounts(provider, handler, options = {}) {
|
|
159
|
+
const storageKey = options.storageKey || DEFAULT_ACCOUNT_STORAGE_KEY;
|
|
160
|
+
const preferStored = options.preferStored !== false;
|
|
161
|
+
return onAccountsChanged(provider, (accounts) => {
|
|
162
|
+
const stored = readStoredAccount(storageKey);
|
|
163
|
+
const account = selectPreferredAccount(accounts, stored, preferStored);
|
|
164
|
+
writeStoredAccount(storageKey, account);
|
|
165
|
+
handler({ account, accounts });
|
|
166
|
+
});
|
|
167
|
+
}
|
|
113
168
|
async function getBalance(provider, address, blockTag = 'latest') {
|
|
114
169
|
const p = provider || (await requireProvider());
|
|
115
170
|
let target = address;
|
|
@@ -637,6 +692,25 @@ async function getStoredUcanRoot(id = DEFAULT_SESSION_ID) {
|
|
|
637
692
|
const record = await readSessionRecord(id);
|
|
638
693
|
return record?.root || null;
|
|
639
694
|
}
|
|
695
|
+
function capsEqual(a, b) {
|
|
696
|
+
return JSON.stringify(a || []) === JSON.stringify(b || []);
|
|
697
|
+
}
|
|
698
|
+
function isRootExpired(root, nowMs) {
|
|
699
|
+
return Boolean(root.exp && nowMs > root.exp);
|
|
700
|
+
}
|
|
701
|
+
async function getOrCreateUcanRoot(options) {
|
|
702
|
+
const provider = options.provider || (await requireProvider());
|
|
703
|
+
const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
|
|
704
|
+
const nowMs = Date.now();
|
|
705
|
+
const stored = await getStoredUcanRoot(session.id);
|
|
706
|
+
if (stored &&
|
|
707
|
+
(!stored.aud || stored.aud === session.did) &&
|
|
708
|
+
capsEqual(stored.cap, options.capabilities) &&
|
|
709
|
+
!isRootExpired(stored, nowMs)) {
|
|
710
|
+
return stored;
|
|
711
|
+
}
|
|
712
|
+
return await createRootUcan({ ...options, provider, session });
|
|
713
|
+
}
|
|
640
714
|
function buildUcanStatement(payload) {
|
|
641
715
|
return `UCAN-AUTH ${JSON.stringify(payload)}`;
|
|
642
716
|
}
|
|
@@ -661,7 +735,15 @@ function buildSiweMessage(params) {
|
|
|
661
735
|
async function resolveAddress(provider, address) {
|
|
662
736
|
if (address)
|
|
663
737
|
return address;
|
|
664
|
-
|
|
738
|
+
let accounts = await getAccounts(provider);
|
|
739
|
+
if (!accounts[0]) {
|
|
740
|
+
const requested = (await provider.request({
|
|
741
|
+
method: 'eth_requestAccounts',
|
|
742
|
+
}));
|
|
743
|
+
if (Array.isArray(requested)) {
|
|
744
|
+
accounts = requested;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
665
747
|
if (!accounts[0])
|
|
666
748
|
throw new Error('No account available');
|
|
667
749
|
return accounts[0];
|
|
@@ -681,8 +763,8 @@ async function createRootUcan(options) {
|
|
|
681
763
|
const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
|
|
682
764
|
const address = await resolveAddress(provider, options.address);
|
|
683
765
|
const chainId = options.chainId || (await getChainId(provider)) || '1';
|
|
684
|
-
const domain = options.domain || (typeof window !== 'undefined' ? window.location.host : '
|
|
685
|
-
const uri = options.uri || (typeof window !== 'undefined' ? window.location.origin : 'http://
|
|
766
|
+
const domain = options.domain || (typeof window !== 'undefined' ? window.location.host : '127.0.0.1');
|
|
767
|
+
const uri = options.uri || (typeof window !== 'undefined' ? window.location.origin : 'http://127.0.0.1');
|
|
686
768
|
const nonce = options.nonce || randomNonce(8);
|
|
687
769
|
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_SESSION_TTL);
|
|
688
770
|
const nbf = options.notBeforeMs;
|
|
@@ -925,6 +1007,27 @@ class WebDavClient {
|
|
|
925
1007
|
async createDirectory(path) {
|
|
926
1008
|
return await this.request('MKCOL', path);
|
|
927
1009
|
}
|
|
1010
|
+
async ensureDirectory(path) {
|
|
1011
|
+
if (!path || path === '/')
|
|
1012
|
+
return;
|
|
1013
|
+
const segments = path.split('/').filter(Boolean);
|
|
1014
|
+
if (segments.length === 0)
|
|
1015
|
+
return;
|
|
1016
|
+
let current = '';
|
|
1017
|
+
for (const segment of segments) {
|
|
1018
|
+
current = `${current}/${segment}`;
|
|
1019
|
+
const res = await this.fetcher(this.buildUrl(current), {
|
|
1020
|
+
method: 'MKCOL',
|
|
1021
|
+
headers: this.buildHeaders(),
|
|
1022
|
+
credentials: this.credentials,
|
|
1023
|
+
});
|
|
1024
|
+
if (res.ok)
|
|
1025
|
+
continue;
|
|
1026
|
+
if (res.status === 405)
|
|
1027
|
+
continue;
|
|
1028
|
+
throw new Error(`WebDAV MKCOL ${current} failed: ${res.status} ${res.statusText}`);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
928
1031
|
async remove(path) {
|
|
929
1032
|
return await this.request('DELETE', path);
|
|
930
1033
|
}
|
|
@@ -1008,5 +1111,197 @@ function createWebDavClient(options) {
|
|
|
1008
1111
|
return new WebDavClient(options);
|
|
1009
1112
|
}
|
|
1010
1113
|
|
|
1011
|
-
|
|
1114
|
+
const tokenCache = new Map();
|
|
1115
|
+
const TOKEN_SKEW_MS = 5000;
|
|
1116
|
+
function normalizeAppDir(path) {
|
|
1117
|
+
const trimmed = path.trim();
|
|
1118
|
+
if (!trimmed)
|
|
1119
|
+
return '/';
|
|
1120
|
+
let next = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
1121
|
+
next = next.replace(/\/+$/, '');
|
|
1122
|
+
return next || '/';
|
|
1123
|
+
}
|
|
1124
|
+
function sanitizeAppId(appId) {
|
|
1125
|
+
return appId.trim().replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
1126
|
+
}
|
|
1127
|
+
function resolveAppDir(options) {
|
|
1128
|
+
if (options.appDir) {
|
|
1129
|
+
return normalizeAppDir(options.appDir);
|
|
1130
|
+
}
|
|
1131
|
+
if (options.appId) {
|
|
1132
|
+
return normalizeAppDir(`/apps/${sanitizeAppId(options.appId)}`);
|
|
1133
|
+
}
|
|
1134
|
+
return undefined;
|
|
1135
|
+
}
|
|
1136
|
+
function buildCapsKey(caps) {
|
|
1137
|
+
return JSON.stringify(caps || []);
|
|
1138
|
+
}
|
|
1139
|
+
function buildTokenCacheKey(issuer, audience, caps) {
|
|
1140
|
+
return `${issuer.did}|${audience}|${buildCapsKey(caps)}`;
|
|
1141
|
+
}
|
|
1142
|
+
function isTokenValid(entry, nowMs) {
|
|
1143
|
+
if (!entry.exp)
|
|
1144
|
+
return false;
|
|
1145
|
+
if (entry.nbf && nowMs < entry.nbf)
|
|
1146
|
+
return false;
|
|
1147
|
+
return entry.exp - TOKEN_SKEW_MS > nowMs;
|
|
1148
|
+
}
|
|
1149
|
+
function decodeBase64Url(input) {
|
|
1150
|
+
if (!input)
|
|
1151
|
+
return null;
|
|
1152
|
+
const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
1153
|
+
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
|
|
1154
|
+
try {
|
|
1155
|
+
if (typeof atob === 'function') {
|
|
1156
|
+
return atob(padded);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
catch {
|
|
1160
|
+
// ignore
|
|
1161
|
+
}
|
|
1162
|
+
try {
|
|
1163
|
+
const nodeBuffer = globalThis.Buffer;
|
|
1164
|
+
if (nodeBuffer) {
|
|
1165
|
+
return nodeBuffer.from(padded, 'base64').toString('utf8');
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
catch {
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
return null;
|
|
1172
|
+
}
|
|
1173
|
+
function decodeUcanPayload(token) {
|
|
1174
|
+
const parts = token.split('.');
|
|
1175
|
+
if (parts.length < 2)
|
|
1176
|
+
return null;
|
|
1177
|
+
const decoded = decodeBase64Url(parts[1]);
|
|
1178
|
+
if (!decoded)
|
|
1179
|
+
return null;
|
|
1180
|
+
try {
|
|
1181
|
+
return JSON.parse(decoded);
|
|
1182
|
+
}
|
|
1183
|
+
catch {
|
|
1184
|
+
return null;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
async function getCachedInvocationToken(options) {
|
|
1188
|
+
const cacheKey = buildTokenCacheKey(options.issuer, options.audience, options.capabilities);
|
|
1189
|
+
const cached = tokenCache.get(cacheKey);
|
|
1190
|
+
const nowMs = Date.now();
|
|
1191
|
+
if (cached && isTokenValid(cached, nowMs)) {
|
|
1192
|
+
return cached.token;
|
|
1193
|
+
}
|
|
1194
|
+
const token = await createInvocationUcan({
|
|
1195
|
+
issuer: options.issuer,
|
|
1196
|
+
audience: options.audience,
|
|
1197
|
+
capabilities: options.capabilities,
|
|
1198
|
+
proofs: options.proofs,
|
|
1199
|
+
expiresInMs: options.expiresInMs,
|
|
1200
|
+
notBeforeMs: options.notBeforeMs,
|
|
1201
|
+
});
|
|
1202
|
+
const payload = decodeUcanPayload(token);
|
|
1203
|
+
if (payload && typeof payload.exp === 'number') {
|
|
1204
|
+
tokenCache.set(cacheKey, {
|
|
1205
|
+
token,
|
|
1206
|
+
exp: payload.exp,
|
|
1207
|
+
nbf: payload.nbf,
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
return token;
|
|
1211
|
+
}
|
|
1212
|
+
async function initWebDavStorage(options) {
|
|
1213
|
+
const caps = options.capabilities || options.root?.cap;
|
|
1214
|
+
if (!caps || caps.length === 0) {
|
|
1215
|
+
throw new Error('Missing UCAN capabilities for WebDAV');
|
|
1216
|
+
}
|
|
1217
|
+
const needsProvider = !options.session || !options.root;
|
|
1218
|
+
const provider = options.provider || (needsProvider ? await requireProvider() : undefined);
|
|
1219
|
+
const session = options.session ||
|
|
1220
|
+
(await createUcanSession({
|
|
1221
|
+
id: options.sessionId,
|
|
1222
|
+
provider,
|
|
1223
|
+
}));
|
|
1224
|
+
const nowMs = Date.now();
|
|
1225
|
+
let root = options.root;
|
|
1226
|
+
if (root && root.aud && root.aud !== session.did) {
|
|
1227
|
+
root = undefined;
|
|
1228
|
+
}
|
|
1229
|
+
if (root && buildCapsKey(root.cap) !== buildCapsKey(caps)) {
|
|
1230
|
+
root = undefined;
|
|
1231
|
+
}
|
|
1232
|
+
if (root && root.exp && nowMs > root.exp) {
|
|
1233
|
+
root = undefined;
|
|
1234
|
+
}
|
|
1235
|
+
if (!root) {
|
|
1236
|
+
root = await getOrCreateUcanRoot({
|
|
1237
|
+
provider: provider || (await requireProvider()),
|
|
1238
|
+
session,
|
|
1239
|
+
capabilities: caps,
|
|
1240
|
+
expiresInMs: options.rootExpiresInMs,
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
const invocationCaps = options.invocationCapabilities || caps;
|
|
1244
|
+
const token = await getCachedInvocationToken({
|
|
1245
|
+
issuer: session,
|
|
1246
|
+
audience: options.audience,
|
|
1247
|
+
capabilities: invocationCaps,
|
|
1248
|
+
proofs: [root],
|
|
1249
|
+
expiresInMs: options.invocationExpiresInMs,
|
|
1250
|
+
notBeforeMs: options.notBeforeMs,
|
|
1251
|
+
});
|
|
1252
|
+
const client = createWebDavClient({
|
|
1253
|
+
baseUrl: options.baseUrl,
|
|
1254
|
+
prefix: options.prefix,
|
|
1255
|
+
token,
|
|
1256
|
+
fetcher: options.fetcher,
|
|
1257
|
+
credentials: options.credentials,
|
|
1258
|
+
});
|
|
1259
|
+
const appDir = resolveAppDir(options);
|
|
1260
|
+
if (appDir && options.ensureAppDir !== false) {
|
|
1261
|
+
await client.ensureDirectory(appDir);
|
|
1262
|
+
}
|
|
1263
|
+
return {
|
|
1264
|
+
client,
|
|
1265
|
+
token,
|
|
1266
|
+
appDir,
|
|
1267
|
+
session,
|
|
1268
|
+
root,
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
async function initDappSession(options) {
|
|
1272
|
+
if (!options.appAuth && !options.webdav) {
|
|
1273
|
+
throw new Error('No init options provided');
|
|
1274
|
+
}
|
|
1275
|
+
const provider = options.provider ||
|
|
1276
|
+
options.appAuth?.provider ||
|
|
1277
|
+
options.webdav?.provider ||
|
|
1278
|
+
(await requireProvider());
|
|
1279
|
+
const result = {
|
|
1280
|
+
provider,
|
|
1281
|
+
address: options.address,
|
|
1282
|
+
};
|
|
1283
|
+
if (options.appAuth) {
|
|
1284
|
+
const appLogin = await loginWithChallenge({
|
|
1285
|
+
...options.appAuth,
|
|
1286
|
+
provider: options.appAuth.provider || provider,
|
|
1287
|
+
address: options.appAuth.address || options.address,
|
|
1288
|
+
});
|
|
1289
|
+
result.appLogin = appLogin;
|
|
1290
|
+
result.address = appLogin.address;
|
|
1291
|
+
}
|
|
1292
|
+
if (options.webdav) {
|
|
1293
|
+
const webdav = await initWebDavStorage({
|
|
1294
|
+
...options.webdav,
|
|
1295
|
+
provider: options.webdav.provider || provider,
|
|
1296
|
+
});
|
|
1297
|
+
result.ucanSession = webdav.session;
|
|
1298
|
+
result.ucanRoot = webdav.root;
|
|
1299
|
+
result.webdavClient = webdav.client;
|
|
1300
|
+
result.webdavToken = webdav.token;
|
|
1301
|
+
result.webdavAppDir = webdav.appDir;
|
|
1302
|
+
}
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
export { WebDavClient, authFetch, authUcanFetch, clearAccessToken, clearUcanSession, createDelegationUcan, createInvocationUcan, createRootUcan, createUcanSession, createWebDavClient, getAccessToken, getAccounts, getBalance, getChainId, getOrCreateUcanRoot, getPreferredAccount, getProvider, getStoredUcanRoot, getUcanSession, initDappSession, initWebDavStorage, isYeYingProvider, loginWithChallenge, logout, onAccountsChanged, onChainChanged, refreshAccessToken, requestAccounts, requireProvider, setAccessToken, signMessage, storeUcanRoot, watchAccounts };
|
|
1012
1307
|
//# sourceMappingURL=web3-bs.esm.js.map
|