@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.umd.js
CHANGED
|
@@ -6,11 +6,43 @@
|
|
|
6
6
|
|
|
7
7
|
const YEYING_RDNS = 'io.github.yeying';
|
|
8
8
|
const DEFAULT_TIMEOUT = 1000;
|
|
9
|
+
const DEFAULT_ACCOUNT_STORAGE_KEY = 'yeying:last_account';
|
|
9
10
|
function getWindowEthereum() {
|
|
10
11
|
if (typeof window === 'undefined')
|
|
11
12
|
return null;
|
|
12
13
|
return window.ethereum || null;
|
|
13
14
|
}
|
|
15
|
+
function readStoredAccount(storageKey) {
|
|
16
|
+
if (typeof localStorage === 'undefined')
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
return localStorage.getItem(storageKey);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function writeStoredAccount(storageKey, account) {
|
|
26
|
+
if (typeof localStorage === 'undefined')
|
|
27
|
+
return;
|
|
28
|
+
try {
|
|
29
|
+
if (account) {
|
|
30
|
+
localStorage.setItem(storageKey, account);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
localStorage.removeItem(storageKey);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// ignore storage errors
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function selectPreferredAccount(accounts, stored, preferStored) {
|
|
41
|
+
if (preferStored && stored && accounts.includes(stored)) {
|
|
42
|
+
return stored;
|
|
43
|
+
}
|
|
44
|
+
return accounts[0] || null;
|
|
45
|
+
}
|
|
14
46
|
function isYeYingProvider(provider, info) {
|
|
15
47
|
if (!provider)
|
|
16
48
|
return false;
|
|
@@ -116,6 +148,29 @@
|
|
|
116
148
|
const chainId = (await p.request({ method: 'eth_chainId' }));
|
|
117
149
|
return typeof chainId === 'string' ? chainId : null;
|
|
118
150
|
}
|
|
151
|
+
async function getPreferredAccount(options = {}) {
|
|
152
|
+
const provider = options.provider || (await requireProvider());
|
|
153
|
+
const storageKey = options.storageKey || DEFAULT_ACCOUNT_STORAGE_KEY;
|
|
154
|
+
const preferStored = options.preferStored !== false;
|
|
155
|
+
let accounts = await getAccounts(provider);
|
|
156
|
+
if (accounts.length === 0 && options.autoConnect) {
|
|
157
|
+
accounts = await requestAccounts({ provider });
|
|
158
|
+
}
|
|
159
|
+
const stored = readStoredAccount(storageKey);
|
|
160
|
+
const account = selectPreferredAccount(accounts, stored, preferStored);
|
|
161
|
+
writeStoredAccount(storageKey, account);
|
|
162
|
+
return { account, accounts };
|
|
163
|
+
}
|
|
164
|
+
function watchAccounts(provider, handler, options = {}) {
|
|
165
|
+
const storageKey = options.storageKey || DEFAULT_ACCOUNT_STORAGE_KEY;
|
|
166
|
+
const preferStored = options.preferStored !== false;
|
|
167
|
+
return onAccountsChanged(provider, (accounts) => {
|
|
168
|
+
const stored = readStoredAccount(storageKey);
|
|
169
|
+
const account = selectPreferredAccount(accounts, stored, preferStored);
|
|
170
|
+
writeStoredAccount(storageKey, account);
|
|
171
|
+
handler({ account, accounts });
|
|
172
|
+
});
|
|
173
|
+
}
|
|
119
174
|
async function getBalance(provider, address, blockTag = 'latest') {
|
|
120
175
|
const p = provider || (await requireProvider());
|
|
121
176
|
let target = address;
|
|
@@ -643,6 +698,25 @@
|
|
|
643
698
|
const record = await readSessionRecord(id);
|
|
644
699
|
return record?.root || null;
|
|
645
700
|
}
|
|
701
|
+
function capsEqual(a, b) {
|
|
702
|
+
return JSON.stringify(a || []) === JSON.stringify(b || []);
|
|
703
|
+
}
|
|
704
|
+
function isRootExpired(root, nowMs) {
|
|
705
|
+
return Boolean(root.exp && nowMs > root.exp);
|
|
706
|
+
}
|
|
707
|
+
async function getOrCreateUcanRoot(options) {
|
|
708
|
+
const provider = options.provider || (await requireProvider());
|
|
709
|
+
const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
|
|
710
|
+
const nowMs = Date.now();
|
|
711
|
+
const stored = await getStoredUcanRoot(session.id);
|
|
712
|
+
if (stored &&
|
|
713
|
+
(!stored.aud || stored.aud === session.did) &&
|
|
714
|
+
capsEqual(stored.cap, options.capabilities) &&
|
|
715
|
+
!isRootExpired(stored, nowMs)) {
|
|
716
|
+
return stored;
|
|
717
|
+
}
|
|
718
|
+
return await createRootUcan({ ...options, provider, session });
|
|
719
|
+
}
|
|
646
720
|
function buildUcanStatement(payload) {
|
|
647
721
|
return `UCAN-AUTH ${JSON.stringify(payload)}`;
|
|
648
722
|
}
|
|
@@ -667,7 +741,15 @@
|
|
|
667
741
|
async function resolveAddress(provider, address) {
|
|
668
742
|
if (address)
|
|
669
743
|
return address;
|
|
670
|
-
|
|
744
|
+
let accounts = await getAccounts(provider);
|
|
745
|
+
if (!accounts[0]) {
|
|
746
|
+
const requested = (await provider.request({
|
|
747
|
+
method: 'eth_requestAccounts',
|
|
748
|
+
}));
|
|
749
|
+
if (Array.isArray(requested)) {
|
|
750
|
+
accounts = requested;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
671
753
|
if (!accounts[0])
|
|
672
754
|
throw new Error('No account available');
|
|
673
755
|
return accounts[0];
|
|
@@ -687,8 +769,8 @@
|
|
|
687
769
|
const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
|
|
688
770
|
const address = await resolveAddress(provider, options.address);
|
|
689
771
|
const chainId = options.chainId || (await getChainId(provider)) || '1';
|
|
690
|
-
const domain = options.domain || (typeof window !== 'undefined' ? window.location.host : '
|
|
691
|
-
const uri = options.uri || (typeof window !== 'undefined' ? window.location.origin : 'http://
|
|
772
|
+
const domain = options.domain || (typeof window !== 'undefined' ? window.location.host : '127.0.0.1');
|
|
773
|
+
const uri = options.uri || (typeof window !== 'undefined' ? window.location.origin : 'http://127.0.0.1');
|
|
692
774
|
const nonce = options.nonce || randomNonce(8);
|
|
693
775
|
const exp = normalizeExpiry(undefined, options.expiresInMs ?? DEFAULT_SESSION_TTL);
|
|
694
776
|
const nbf = options.notBeforeMs;
|
|
@@ -931,6 +1013,27 @@
|
|
|
931
1013
|
async createDirectory(path) {
|
|
932
1014
|
return await this.request('MKCOL', path);
|
|
933
1015
|
}
|
|
1016
|
+
async ensureDirectory(path) {
|
|
1017
|
+
if (!path || path === '/')
|
|
1018
|
+
return;
|
|
1019
|
+
const segments = path.split('/').filter(Boolean);
|
|
1020
|
+
if (segments.length === 0)
|
|
1021
|
+
return;
|
|
1022
|
+
let current = '';
|
|
1023
|
+
for (const segment of segments) {
|
|
1024
|
+
current = `${current}/${segment}`;
|
|
1025
|
+
const res = await this.fetcher(this.buildUrl(current), {
|
|
1026
|
+
method: 'MKCOL',
|
|
1027
|
+
headers: this.buildHeaders(),
|
|
1028
|
+
credentials: this.credentials,
|
|
1029
|
+
});
|
|
1030
|
+
if (res.ok)
|
|
1031
|
+
continue;
|
|
1032
|
+
if (res.status === 405)
|
|
1033
|
+
continue;
|
|
1034
|
+
throw new Error(`WebDAV MKCOL ${current} failed: ${res.status} ${res.statusText}`);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
934
1037
|
async remove(path) {
|
|
935
1038
|
return await this.request('DELETE', path);
|
|
936
1039
|
}
|
|
@@ -1014,6 +1117,198 @@
|
|
|
1014
1117
|
return new WebDavClient(options);
|
|
1015
1118
|
}
|
|
1016
1119
|
|
|
1120
|
+
const tokenCache = new Map();
|
|
1121
|
+
const TOKEN_SKEW_MS = 5000;
|
|
1122
|
+
function normalizeAppDir(path) {
|
|
1123
|
+
const trimmed = path.trim();
|
|
1124
|
+
if (!trimmed)
|
|
1125
|
+
return '/';
|
|
1126
|
+
let next = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
1127
|
+
next = next.replace(/\/+$/, '');
|
|
1128
|
+
return next || '/';
|
|
1129
|
+
}
|
|
1130
|
+
function sanitizeAppId(appId) {
|
|
1131
|
+
return appId.trim().replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
1132
|
+
}
|
|
1133
|
+
function resolveAppDir(options) {
|
|
1134
|
+
if (options.appDir) {
|
|
1135
|
+
return normalizeAppDir(options.appDir);
|
|
1136
|
+
}
|
|
1137
|
+
if (options.appId) {
|
|
1138
|
+
return normalizeAppDir(`/apps/${sanitizeAppId(options.appId)}`);
|
|
1139
|
+
}
|
|
1140
|
+
return undefined;
|
|
1141
|
+
}
|
|
1142
|
+
function buildCapsKey(caps) {
|
|
1143
|
+
return JSON.stringify(caps || []);
|
|
1144
|
+
}
|
|
1145
|
+
function buildTokenCacheKey(issuer, audience, caps) {
|
|
1146
|
+
return `${issuer.did}|${audience}|${buildCapsKey(caps)}`;
|
|
1147
|
+
}
|
|
1148
|
+
function isTokenValid(entry, nowMs) {
|
|
1149
|
+
if (!entry.exp)
|
|
1150
|
+
return false;
|
|
1151
|
+
if (entry.nbf && nowMs < entry.nbf)
|
|
1152
|
+
return false;
|
|
1153
|
+
return entry.exp - TOKEN_SKEW_MS > nowMs;
|
|
1154
|
+
}
|
|
1155
|
+
function decodeBase64Url(input) {
|
|
1156
|
+
if (!input)
|
|
1157
|
+
return null;
|
|
1158
|
+
const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
1159
|
+
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
|
|
1160
|
+
try {
|
|
1161
|
+
if (typeof atob === 'function') {
|
|
1162
|
+
return atob(padded);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
catch {
|
|
1166
|
+
// ignore
|
|
1167
|
+
}
|
|
1168
|
+
try {
|
|
1169
|
+
const nodeBuffer = globalThis.Buffer;
|
|
1170
|
+
if (nodeBuffer) {
|
|
1171
|
+
return nodeBuffer.from(padded, 'base64').toString('utf8');
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
catch {
|
|
1175
|
+
return null;
|
|
1176
|
+
}
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
1179
|
+
function decodeUcanPayload(token) {
|
|
1180
|
+
const parts = token.split('.');
|
|
1181
|
+
if (parts.length < 2)
|
|
1182
|
+
return null;
|
|
1183
|
+
const decoded = decodeBase64Url(parts[1]);
|
|
1184
|
+
if (!decoded)
|
|
1185
|
+
return null;
|
|
1186
|
+
try {
|
|
1187
|
+
return JSON.parse(decoded);
|
|
1188
|
+
}
|
|
1189
|
+
catch {
|
|
1190
|
+
return null;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
async function getCachedInvocationToken(options) {
|
|
1194
|
+
const cacheKey = buildTokenCacheKey(options.issuer, options.audience, options.capabilities);
|
|
1195
|
+
const cached = tokenCache.get(cacheKey);
|
|
1196
|
+
const nowMs = Date.now();
|
|
1197
|
+
if (cached && isTokenValid(cached, nowMs)) {
|
|
1198
|
+
return cached.token;
|
|
1199
|
+
}
|
|
1200
|
+
const token = await createInvocationUcan({
|
|
1201
|
+
issuer: options.issuer,
|
|
1202
|
+
audience: options.audience,
|
|
1203
|
+
capabilities: options.capabilities,
|
|
1204
|
+
proofs: options.proofs,
|
|
1205
|
+
expiresInMs: options.expiresInMs,
|
|
1206
|
+
notBeforeMs: options.notBeforeMs,
|
|
1207
|
+
});
|
|
1208
|
+
const payload = decodeUcanPayload(token);
|
|
1209
|
+
if (payload && typeof payload.exp === 'number') {
|
|
1210
|
+
tokenCache.set(cacheKey, {
|
|
1211
|
+
token,
|
|
1212
|
+
exp: payload.exp,
|
|
1213
|
+
nbf: payload.nbf,
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
return token;
|
|
1217
|
+
}
|
|
1218
|
+
async function initWebDavStorage(options) {
|
|
1219
|
+
const caps = options.capabilities || options.root?.cap;
|
|
1220
|
+
if (!caps || caps.length === 0) {
|
|
1221
|
+
throw new Error('Missing UCAN capabilities for WebDAV');
|
|
1222
|
+
}
|
|
1223
|
+
const needsProvider = !options.session || !options.root;
|
|
1224
|
+
const provider = options.provider || (needsProvider ? await requireProvider() : undefined);
|
|
1225
|
+
const session = options.session ||
|
|
1226
|
+
(await createUcanSession({
|
|
1227
|
+
id: options.sessionId,
|
|
1228
|
+
provider,
|
|
1229
|
+
}));
|
|
1230
|
+
const nowMs = Date.now();
|
|
1231
|
+
let root = options.root;
|
|
1232
|
+
if (root && root.aud && root.aud !== session.did) {
|
|
1233
|
+
root = undefined;
|
|
1234
|
+
}
|
|
1235
|
+
if (root && buildCapsKey(root.cap) !== buildCapsKey(caps)) {
|
|
1236
|
+
root = undefined;
|
|
1237
|
+
}
|
|
1238
|
+
if (root && root.exp && nowMs > root.exp) {
|
|
1239
|
+
root = undefined;
|
|
1240
|
+
}
|
|
1241
|
+
if (!root) {
|
|
1242
|
+
root = await getOrCreateUcanRoot({
|
|
1243
|
+
provider: provider || (await requireProvider()),
|
|
1244
|
+
session,
|
|
1245
|
+
capabilities: caps,
|
|
1246
|
+
expiresInMs: options.rootExpiresInMs,
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
const invocationCaps = options.invocationCapabilities || caps;
|
|
1250
|
+
const token = await getCachedInvocationToken({
|
|
1251
|
+
issuer: session,
|
|
1252
|
+
audience: options.audience,
|
|
1253
|
+
capabilities: invocationCaps,
|
|
1254
|
+
proofs: [root],
|
|
1255
|
+
expiresInMs: options.invocationExpiresInMs,
|
|
1256
|
+
notBeforeMs: options.notBeforeMs,
|
|
1257
|
+
});
|
|
1258
|
+
const client = createWebDavClient({
|
|
1259
|
+
baseUrl: options.baseUrl,
|
|
1260
|
+
prefix: options.prefix,
|
|
1261
|
+
token,
|
|
1262
|
+
fetcher: options.fetcher,
|
|
1263
|
+
credentials: options.credentials,
|
|
1264
|
+
});
|
|
1265
|
+
const appDir = resolveAppDir(options);
|
|
1266
|
+
if (appDir && options.ensureAppDir !== false) {
|
|
1267
|
+
await client.ensureDirectory(appDir);
|
|
1268
|
+
}
|
|
1269
|
+
return {
|
|
1270
|
+
client,
|
|
1271
|
+
token,
|
|
1272
|
+
appDir,
|
|
1273
|
+
session,
|
|
1274
|
+
root,
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
async function initDappSession(options) {
|
|
1278
|
+
if (!options.appAuth && !options.webdav) {
|
|
1279
|
+
throw new Error('No init options provided');
|
|
1280
|
+
}
|
|
1281
|
+
const provider = options.provider ||
|
|
1282
|
+
options.appAuth?.provider ||
|
|
1283
|
+
options.webdav?.provider ||
|
|
1284
|
+
(await requireProvider());
|
|
1285
|
+
const result = {
|
|
1286
|
+
provider,
|
|
1287
|
+
address: options.address,
|
|
1288
|
+
};
|
|
1289
|
+
if (options.appAuth) {
|
|
1290
|
+
const appLogin = await loginWithChallenge({
|
|
1291
|
+
...options.appAuth,
|
|
1292
|
+
provider: options.appAuth.provider || provider,
|
|
1293
|
+
address: options.appAuth.address || options.address,
|
|
1294
|
+
});
|
|
1295
|
+
result.appLogin = appLogin;
|
|
1296
|
+
result.address = appLogin.address;
|
|
1297
|
+
}
|
|
1298
|
+
if (options.webdav) {
|
|
1299
|
+
const webdav = await initWebDavStorage({
|
|
1300
|
+
...options.webdav,
|
|
1301
|
+
provider: options.webdav.provider || provider,
|
|
1302
|
+
});
|
|
1303
|
+
result.ucanSession = webdav.session;
|
|
1304
|
+
result.ucanRoot = webdav.root;
|
|
1305
|
+
result.webdavClient = webdav.client;
|
|
1306
|
+
result.webdavToken = webdav.token;
|
|
1307
|
+
result.webdavAppDir = webdav.appDir;
|
|
1308
|
+
}
|
|
1309
|
+
return result;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1017
1312
|
exports.WebDavClient = WebDavClient;
|
|
1018
1313
|
exports.authFetch = authFetch;
|
|
1019
1314
|
exports.authUcanFetch = authUcanFetch;
|
|
@@ -1028,9 +1323,13 @@
|
|
|
1028
1323
|
exports.getAccounts = getAccounts;
|
|
1029
1324
|
exports.getBalance = getBalance;
|
|
1030
1325
|
exports.getChainId = getChainId;
|
|
1326
|
+
exports.getOrCreateUcanRoot = getOrCreateUcanRoot;
|
|
1327
|
+
exports.getPreferredAccount = getPreferredAccount;
|
|
1031
1328
|
exports.getProvider = getProvider;
|
|
1032
1329
|
exports.getStoredUcanRoot = getStoredUcanRoot;
|
|
1033
1330
|
exports.getUcanSession = getUcanSession;
|
|
1331
|
+
exports.initDappSession = initDappSession;
|
|
1332
|
+
exports.initWebDavStorage = initWebDavStorage;
|
|
1034
1333
|
exports.isYeYingProvider = isYeYingProvider;
|
|
1035
1334
|
exports.loginWithChallenge = loginWithChallenge;
|
|
1036
1335
|
exports.logout = logout;
|
|
@@ -1042,6 +1341,7 @@
|
|
|
1042
1341
|
exports.setAccessToken = setAccessToken;
|
|
1043
1342
|
exports.signMessage = signMessage;
|
|
1044
1343
|
exports.storeUcanRoot = storeUcanRoot;
|
|
1344
|
+
exports.watchAccounts = watchAccounts;
|
|
1045
1345
|
|
|
1046
1346
|
}));
|
|
1047
1347
|
//# sourceMappingURL=web3-bs.umd.js.map
|