@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.
@@ -643,6 +643,25 @@
643
643
  const record = await readSessionRecord(id);
644
644
  return record?.root || null;
645
645
  }
646
+ function capsEqual(a, b) {
647
+ return JSON.stringify(a || []) === JSON.stringify(b || []);
648
+ }
649
+ function isRootExpired(root, nowMs) {
650
+ return Boolean(root.exp && nowMs > root.exp);
651
+ }
652
+ async function getOrCreateUcanRoot(options) {
653
+ const provider = options.provider || (await requireProvider());
654
+ const session = options.session || (await createUcanSession({ id: options.sessionId, provider }));
655
+ const nowMs = Date.now();
656
+ const stored = await getStoredUcanRoot(session.id);
657
+ if (stored &&
658
+ (!stored.aud || stored.aud === session.did) &&
659
+ capsEqual(stored.cap, options.capabilities) &&
660
+ !isRootExpired(stored, nowMs)) {
661
+ return stored;
662
+ }
663
+ return await createRootUcan({ ...options, provider, session });
664
+ }
646
665
  function buildUcanStatement(payload) {
647
666
  return `UCAN-AUTH ${JSON.stringify(payload)}`;
648
667
  }
@@ -667,7 +686,15 @@
667
686
  async function resolveAddress(provider, address) {
668
687
  if (address)
669
688
  return address;
670
- const accounts = await getAccounts(provider);
689
+ let accounts = await getAccounts(provider);
690
+ if (!accounts[0]) {
691
+ const requested = (await provider.request({
692
+ method: 'eth_requestAccounts',
693
+ }));
694
+ if (Array.isArray(requested)) {
695
+ accounts = requested;
696
+ }
697
+ }
671
698
  if (!accounts[0])
672
699
  throw new Error('No account available');
673
700
  return accounts[0];
@@ -931,6 +958,27 @@
931
958
  async createDirectory(path) {
932
959
  return await this.request('MKCOL', path);
933
960
  }
961
+ async ensureDirectory(path) {
962
+ if (!path || path === '/')
963
+ return;
964
+ const segments = path.split('/').filter(Boolean);
965
+ if (segments.length === 0)
966
+ return;
967
+ let current = '';
968
+ for (const segment of segments) {
969
+ current = `${current}/${segment}`;
970
+ const res = await this.fetcher(this.buildUrl(current), {
971
+ method: 'MKCOL',
972
+ headers: this.buildHeaders(),
973
+ credentials: this.credentials,
974
+ });
975
+ if (res.ok)
976
+ continue;
977
+ if (res.status === 405)
978
+ continue;
979
+ throw new Error(`WebDAV MKCOL ${current} failed: ${res.status} ${res.statusText}`);
980
+ }
981
+ }
934
982
  async remove(path) {
935
983
  return await this.request('DELETE', path);
936
984
  }
@@ -1014,6 +1062,198 @@
1014
1062
  return new WebDavClient(options);
1015
1063
  }
1016
1064
 
1065
+ const tokenCache = new Map();
1066
+ const TOKEN_SKEW_MS = 5000;
1067
+ function normalizeAppDir(path) {
1068
+ const trimmed = path.trim();
1069
+ if (!trimmed)
1070
+ return '/';
1071
+ let next = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
1072
+ next = next.replace(/\/+$/, '');
1073
+ return next || '/';
1074
+ }
1075
+ function sanitizeAppId(appId) {
1076
+ return appId.trim().replace(/[^a-zA-Z0-9._-]/g, '-');
1077
+ }
1078
+ function resolveAppDir(options) {
1079
+ if (options.appDir) {
1080
+ return normalizeAppDir(options.appDir);
1081
+ }
1082
+ if (options.appId) {
1083
+ return normalizeAppDir(`/apps/${sanitizeAppId(options.appId)}`);
1084
+ }
1085
+ return undefined;
1086
+ }
1087
+ function buildCapsKey(caps) {
1088
+ return JSON.stringify(caps || []);
1089
+ }
1090
+ function buildTokenCacheKey(issuer, audience, caps) {
1091
+ return `${issuer.did}|${audience}|${buildCapsKey(caps)}`;
1092
+ }
1093
+ function isTokenValid(entry, nowMs) {
1094
+ if (!entry.exp)
1095
+ return false;
1096
+ if (entry.nbf && nowMs < entry.nbf)
1097
+ return false;
1098
+ return entry.exp - TOKEN_SKEW_MS > nowMs;
1099
+ }
1100
+ function decodeBase64Url(input) {
1101
+ if (!input)
1102
+ return null;
1103
+ const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
1104
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
1105
+ try {
1106
+ if (typeof atob === 'function') {
1107
+ return atob(padded);
1108
+ }
1109
+ }
1110
+ catch {
1111
+ // ignore
1112
+ }
1113
+ try {
1114
+ const nodeBuffer = globalThis.Buffer;
1115
+ if (nodeBuffer) {
1116
+ return nodeBuffer.from(padded, 'base64').toString('utf8');
1117
+ }
1118
+ }
1119
+ catch {
1120
+ return null;
1121
+ }
1122
+ return null;
1123
+ }
1124
+ function decodeUcanPayload(token) {
1125
+ const parts = token.split('.');
1126
+ if (parts.length < 2)
1127
+ return null;
1128
+ const decoded = decodeBase64Url(parts[1]);
1129
+ if (!decoded)
1130
+ return null;
1131
+ try {
1132
+ return JSON.parse(decoded);
1133
+ }
1134
+ catch {
1135
+ return null;
1136
+ }
1137
+ }
1138
+ async function getCachedInvocationToken(options) {
1139
+ const cacheKey = buildTokenCacheKey(options.issuer, options.audience, options.capabilities);
1140
+ const cached = tokenCache.get(cacheKey);
1141
+ const nowMs = Date.now();
1142
+ if (cached && isTokenValid(cached, nowMs)) {
1143
+ return cached.token;
1144
+ }
1145
+ const token = await createInvocationUcan({
1146
+ issuer: options.issuer,
1147
+ audience: options.audience,
1148
+ capabilities: options.capabilities,
1149
+ proofs: options.proofs,
1150
+ expiresInMs: options.expiresInMs,
1151
+ notBeforeMs: options.notBeforeMs,
1152
+ });
1153
+ const payload = decodeUcanPayload(token);
1154
+ if (payload && typeof payload.exp === 'number') {
1155
+ tokenCache.set(cacheKey, {
1156
+ token,
1157
+ exp: payload.exp,
1158
+ nbf: payload.nbf,
1159
+ });
1160
+ }
1161
+ return token;
1162
+ }
1163
+ async function initWebDavStorage(options) {
1164
+ const caps = options.capabilities || options.root?.cap;
1165
+ if (!caps || caps.length === 0) {
1166
+ throw new Error('Missing UCAN capabilities for WebDAV');
1167
+ }
1168
+ const needsProvider = !options.session || !options.root;
1169
+ const provider = options.provider || (needsProvider ? await requireProvider() : undefined);
1170
+ const session = options.session ||
1171
+ (await createUcanSession({
1172
+ id: options.sessionId,
1173
+ provider,
1174
+ }));
1175
+ const nowMs = Date.now();
1176
+ let root = options.root;
1177
+ if (root && root.aud && root.aud !== session.did) {
1178
+ root = undefined;
1179
+ }
1180
+ if (root && buildCapsKey(root.cap) !== buildCapsKey(caps)) {
1181
+ root = undefined;
1182
+ }
1183
+ if (root && root.exp && nowMs > root.exp) {
1184
+ root = undefined;
1185
+ }
1186
+ if (!root) {
1187
+ root = await getOrCreateUcanRoot({
1188
+ provider: provider || (await requireProvider()),
1189
+ session,
1190
+ capabilities: caps,
1191
+ expiresInMs: options.rootExpiresInMs,
1192
+ });
1193
+ }
1194
+ const invocationCaps = options.invocationCapabilities || caps;
1195
+ const token = await getCachedInvocationToken({
1196
+ issuer: session,
1197
+ audience: options.audience,
1198
+ capabilities: invocationCaps,
1199
+ proofs: [root],
1200
+ expiresInMs: options.invocationExpiresInMs,
1201
+ notBeforeMs: options.notBeforeMs,
1202
+ });
1203
+ const client = createWebDavClient({
1204
+ baseUrl: options.baseUrl,
1205
+ prefix: options.prefix,
1206
+ token,
1207
+ fetcher: options.fetcher,
1208
+ credentials: options.credentials,
1209
+ });
1210
+ const appDir = resolveAppDir(options);
1211
+ if (appDir && options.ensureAppDir !== false) {
1212
+ await client.ensureDirectory(appDir);
1213
+ }
1214
+ return {
1215
+ client,
1216
+ token,
1217
+ appDir,
1218
+ session,
1219
+ root,
1220
+ };
1221
+ }
1222
+ async function initDappSession(options) {
1223
+ if (!options.appAuth && !options.webdav) {
1224
+ throw new Error('No init options provided');
1225
+ }
1226
+ const provider = options.provider ||
1227
+ options.appAuth?.provider ||
1228
+ options.webdav?.provider ||
1229
+ (await requireProvider());
1230
+ const result = {
1231
+ provider,
1232
+ address: options.address,
1233
+ };
1234
+ if (options.appAuth) {
1235
+ const appLogin = await loginWithChallenge({
1236
+ ...options.appAuth,
1237
+ provider: options.appAuth.provider || provider,
1238
+ address: options.appAuth.address || options.address,
1239
+ });
1240
+ result.appLogin = appLogin;
1241
+ result.address = appLogin.address;
1242
+ }
1243
+ if (options.webdav) {
1244
+ const webdav = await initWebDavStorage({
1245
+ ...options.webdav,
1246
+ provider: options.webdav.provider || provider,
1247
+ });
1248
+ result.ucanSession = webdav.session;
1249
+ result.ucanRoot = webdav.root;
1250
+ result.webdavClient = webdav.client;
1251
+ result.webdavToken = webdav.token;
1252
+ result.webdavAppDir = webdav.appDir;
1253
+ }
1254
+ return result;
1255
+ }
1256
+
1017
1257
  exports.WebDavClient = WebDavClient;
1018
1258
  exports.authFetch = authFetch;
1019
1259
  exports.authUcanFetch = authUcanFetch;
@@ -1028,9 +1268,12 @@
1028
1268
  exports.getAccounts = getAccounts;
1029
1269
  exports.getBalance = getBalance;
1030
1270
  exports.getChainId = getChainId;
1271
+ exports.getOrCreateUcanRoot = getOrCreateUcanRoot;
1031
1272
  exports.getProvider = getProvider;
1032
1273
  exports.getStoredUcanRoot = getStoredUcanRoot;
1033
1274
  exports.getUcanSession = getUcanSession;
1275
+ exports.initDappSession = initDappSession;
1276
+ exports.initWebDavStorage = initWebDavStorage;
1034
1277
  exports.isYeYingProvider = isYeYingProvider;
1035
1278
  exports.loginWithChallenge = loginWithChallenge;
1036
1279
  exports.logout = logout;