@bandeira-tech/b3nd-web 0.2.7 → 0.3.1
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/dist/{chunk-F3W5GZU6.js → chunk-B4VAPGAO.js} +218 -22
- package/dist/{chunk-7O4D7SF6.js → chunk-GWPCZVXV.js} +233 -59
- package/dist/{chunk-GIMIWVPX.js → chunk-O53KW746.js} +53 -2
- package/dist/{chunk-LFUC4ETD.js → chunk-OY4CDOHY.js} +45 -0
- package/dist/{chunk-7U5JDFQW.js → chunk-PZFEKQ7F.js} +24 -0
- package/dist/{chunk-T43IWAQK.js → chunk-UUHVOWVI.js} +29 -0
- package/dist/{client-B0Ekm99R.d.ts → client-C4oQxiDu.d.ts} +147 -8
- package/dist/clients/http/mod.d.ts +3 -1
- package/dist/clients/http/mod.js +1 -1
- package/dist/clients/local-storage/mod.d.ts +2 -1
- package/dist/clients/local-storage/mod.js +1 -1
- package/dist/clients/memory/mod.d.ts +36 -2
- package/dist/clients/memory/mod.js +5 -3
- package/dist/clients/websocket/mod.d.ts +2 -1
- package/dist/clients/websocket/mod.js +1 -1
- package/dist/{core-CgxQpSVM.d.ts → core-ClnuubZw.d.ts} +17 -1
- package/dist/src/mod.web.d.ts +2 -2
- package/dist/src/mod.web.js +10 -10
- package/dist/{types-oQCx3U-_.d.ts → types-uuvn4oKw.d.ts} +36 -2
- package/dist/wallet/mod.d.ts +183 -8
- package/dist/wallet/mod.js +8 -4
- package/dist/wallet-server/adapters/browser.d.ts +3 -3
- package/dist/wallet-server/adapters/browser.js +5 -5
- package/dist/wallet-server/mod.d.ts +12 -6
- package/dist/wallet-server/mod.js +2 -2
- package/package.json +1 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MemoryClient
|
|
3
|
-
} from "./chunk-GIMIWVPX.js";
|
|
4
1
|
import {
|
|
5
2
|
WalletServerCore
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-B4VAPGAO.js";
|
|
7
4
|
import {
|
|
8
5
|
exportPrivateKeyPem,
|
|
9
6
|
generateEncryptionKeyPair,
|
|
10
|
-
generateSigningKeyPair
|
|
7
|
+
generateSigningKeyPair,
|
|
8
|
+
signWithHex
|
|
11
9
|
} from "./chunk-JN75UL5C.js";
|
|
10
|
+
import {
|
|
11
|
+
MemoryClient,
|
|
12
|
+
createTestSchema
|
|
13
|
+
} from "./chunk-O53KW746.js";
|
|
12
14
|
|
|
13
15
|
// wallet/client.ts
|
|
14
16
|
var WalletClient = class {
|
|
@@ -142,19 +144,34 @@ var WalletClient = class {
|
|
|
142
144
|
throw new Error("Use resetPasswordWithToken(appKey, username, resetToken, newPassword)");
|
|
143
145
|
}
|
|
144
146
|
/**
|
|
145
|
-
* Sign up with
|
|
147
|
+
* Sign up with session keypair (scoped to an app)
|
|
148
|
+
*
|
|
149
|
+
* The session must be approved by the app beforehand:
|
|
150
|
+
* 1. Client writes request to: immutable://inbox/{appKey}/sessions/{sessionPubkey} = 1
|
|
151
|
+
* 2. App approves by writing: mutable://accounts/{appKey}/sessions/{sessionPubkey} = 1
|
|
152
|
+
* 3. Client calls this method with the session keypair
|
|
153
|
+
*
|
|
154
|
+
* @param appKey - The app's public key
|
|
155
|
+
* @param session - Session keypair (generated via generateSessionKeypair)
|
|
156
|
+
* @param credentials - User credentials (username/password)
|
|
146
157
|
*/
|
|
147
|
-
async signupWithToken(appKey,
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
async signupWithToken(appKey, session, credentials) {
|
|
159
|
+
if (!session?.publicKeyHex || !session?.privateKeyHex) {
|
|
160
|
+
throw new Error("session keypair is required");
|
|
161
|
+
}
|
|
162
|
+
const payloadToSign = {
|
|
163
|
+
sessionPubkey: session.publicKeyHex,
|
|
164
|
+
type: "password",
|
|
165
|
+
username: credentials.username,
|
|
166
|
+
password: credentials.password
|
|
167
|
+
};
|
|
168
|
+
const sessionSignature = await signWithHex(session.privateKeyHex, payloadToSign);
|
|
150
169
|
const response = await this.fetchImpl(this.buildAppKeyUrl("/auth/signup", appKey), {
|
|
151
170
|
method: "POST",
|
|
152
171
|
headers: { "Content-Type": "application/json" },
|
|
153
172
|
body: JSON.stringify({
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
username: credentials.username,
|
|
157
|
-
password: credentials.password
|
|
173
|
+
...payloadToSign,
|
|
174
|
+
sessionSignature
|
|
158
175
|
})
|
|
159
176
|
});
|
|
160
177
|
const data = await response.json();
|
|
@@ -164,21 +181,34 @@ var WalletClient = class {
|
|
|
164
181
|
return { username: data.username, token: data.token, expiresIn: data.expiresIn };
|
|
165
182
|
}
|
|
166
183
|
/**
|
|
167
|
-
* Login with
|
|
184
|
+
* Login with session keypair (scoped to an app)
|
|
185
|
+
*
|
|
186
|
+
* The session must be approved by the app beforehand:
|
|
187
|
+
* 1. Client writes request to: immutable://inbox/{appKey}/sessions/{sessionPubkey} = 1
|
|
188
|
+
* 2. App approves by writing: mutable://accounts/{appKey}/sessions/{sessionPubkey} = 1
|
|
189
|
+
* 3. Client calls this method with the session keypair
|
|
190
|
+
*
|
|
191
|
+
* @param appKey - The app's public key
|
|
192
|
+
* @param session - Session keypair (generated via generateSessionKeypair)
|
|
193
|
+
* @param credentials - User credentials (username/password)
|
|
168
194
|
*/
|
|
169
|
-
async loginWithTokenSession(appKey,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
195
|
+
async loginWithTokenSession(appKey, session, credentials) {
|
|
196
|
+
if (!session?.publicKeyHex || !session?.privateKeyHex) {
|
|
197
|
+
throw new Error("session keypair is required");
|
|
198
|
+
}
|
|
199
|
+
const payloadToSign = {
|
|
200
|
+
sessionPubkey: session.publicKeyHex,
|
|
201
|
+
type: "password",
|
|
202
|
+
username: credentials.username,
|
|
203
|
+
password: credentials.password
|
|
204
|
+
};
|
|
205
|
+
const sessionSignature = await signWithHex(session.privateKeyHex, payloadToSign);
|
|
173
206
|
const response = await this.fetchImpl(this.buildAppKeyUrl("/auth/login", appKey), {
|
|
174
207
|
method: "POST",
|
|
175
208
|
headers: { "Content-Type": "application/json" },
|
|
176
209
|
body: JSON.stringify({
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
type: "password",
|
|
180
|
-
username: credentials.username,
|
|
181
|
-
password: credentials.password
|
|
210
|
+
...payloadToSign,
|
|
211
|
+
sessionSignature
|
|
182
212
|
})
|
|
183
213
|
});
|
|
184
214
|
const data = await response.json();
|
|
@@ -254,6 +284,8 @@ var WalletClient = class {
|
|
|
254
284
|
* Proxy a write request through the wallet server
|
|
255
285
|
* The server signs the write with its identity key
|
|
256
286
|
* Requires active authentication session
|
|
287
|
+
*
|
|
288
|
+
* @returns ProxyWriteResponse - check `success` field for result
|
|
257
289
|
*/
|
|
258
290
|
async proxyWrite(request) {
|
|
259
291
|
if (!this.currentSession) {
|
|
@@ -272,17 +304,14 @@ var WalletClient = class {
|
|
|
272
304
|
})
|
|
273
305
|
});
|
|
274
306
|
const data = await response.json();
|
|
275
|
-
if (!response.ok || !data.success) {
|
|
276
|
-
throw new Error(
|
|
277
|
-
data.error || `Proxy write failed: ${response.statusText}`
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
307
|
return data;
|
|
281
308
|
}
|
|
282
309
|
/**
|
|
283
310
|
* Proxy a read request through the wallet server
|
|
284
311
|
* The server decrypts encrypted data using user's encryption key
|
|
285
312
|
* Requires active authentication session
|
|
313
|
+
*
|
|
314
|
+
* @returns ProxyReadResponse - check `success` field for result, `decrypted` for decrypted data
|
|
286
315
|
*/
|
|
287
316
|
async proxyRead(request) {
|
|
288
317
|
if (!this.currentSession) {
|
|
@@ -297,13 +326,33 @@ var WalletClient = class {
|
|
|
297
326
|
}
|
|
298
327
|
});
|
|
299
328
|
const data = await response.json();
|
|
300
|
-
if (!response.ok || !data.success) {
|
|
301
|
-
throw new Error(
|
|
302
|
-
data.error || `Proxy read failed: ${response.statusText}`
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
329
|
return data;
|
|
306
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* Proxy multiple read requests through the wallet server
|
|
333
|
+
* Reads multiple URIs in a single request (max 50 URIs)
|
|
334
|
+
* The server decrypts encrypted data using user's encryption key
|
|
335
|
+
* Requires active authentication session
|
|
336
|
+
*
|
|
337
|
+
* @returns ProxyReadMultiResponse - check `success` for overall result, `results` for per-URI results
|
|
338
|
+
*/
|
|
339
|
+
async proxyReadMulti(request) {
|
|
340
|
+
if (!this.currentSession) {
|
|
341
|
+
throw new Error("Not authenticated. Please login first.");
|
|
342
|
+
}
|
|
343
|
+
const response = await this.fetchImpl(
|
|
344
|
+
`${this.walletServerUrl}${this.apiBasePath}/proxy/read-multi`,
|
|
345
|
+
{
|
|
346
|
+
method: "POST",
|
|
347
|
+
headers: {
|
|
348
|
+
"Content-Type": "application/json",
|
|
349
|
+
Authorization: `Bearer ${this.currentSession.token}`
|
|
350
|
+
},
|
|
351
|
+
body: JSON.stringify({ uris: request.uris })
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
return await response.json();
|
|
355
|
+
}
|
|
307
356
|
/**
|
|
308
357
|
* Convenience method: Get current user's public keys
|
|
309
358
|
* Requires active authentication session
|
|
@@ -361,22 +410,37 @@ var WalletClient = class {
|
|
|
361
410
|
};
|
|
362
411
|
}
|
|
363
412
|
/**
|
|
364
|
-
* Login with Google OAuth (scoped to app token and session)
|
|
413
|
+
* Login with Google OAuth (scoped to app token and session keypair)
|
|
365
414
|
* Returns session data with Google profile info - call setSession() to activate it
|
|
366
415
|
*
|
|
416
|
+
* The session must be approved by the app beforehand.
|
|
417
|
+
*
|
|
418
|
+
* @param appKey - The app's public key
|
|
367
419
|
* @param token - App token from app server
|
|
368
|
-
* @param session - Session
|
|
420
|
+
* @param session - Session keypair (generated via generateSessionKeypair)
|
|
369
421
|
* @param googleIdToken - Google ID token from Google Sign-In
|
|
370
422
|
* @returns GoogleAuthSession with username, JWT token, and Google profile info
|
|
371
423
|
*/
|
|
372
424
|
async loginWithGoogle(appKey, token, session, googleIdToken) {
|
|
373
425
|
if (!token) throw new Error("token is required");
|
|
374
|
-
if (!session
|
|
426
|
+
if (!session?.publicKeyHex || !session?.privateKeyHex) {
|
|
427
|
+
throw new Error("session keypair is required");
|
|
428
|
+
}
|
|
375
429
|
if (!googleIdToken) throw new Error("googleIdToken is required");
|
|
430
|
+
const payloadToSign = {
|
|
431
|
+
token,
|
|
432
|
+
sessionPubkey: session.publicKeyHex,
|
|
433
|
+
type: "google",
|
|
434
|
+
googleIdToken
|
|
435
|
+
};
|
|
436
|
+
const sessionSignature = await signWithHex(session.privateKeyHex, payloadToSign);
|
|
376
437
|
const response = await this.fetchImpl(this.buildAppKeyUrl("/auth/login", appKey), {
|
|
377
438
|
method: "POST",
|
|
378
439
|
headers: { "Content-Type": "application/json" },
|
|
379
|
-
body: JSON.stringify({
|
|
440
|
+
body: JSON.stringify({
|
|
441
|
+
...payloadToSign,
|
|
442
|
+
sessionSignature
|
|
443
|
+
})
|
|
380
444
|
});
|
|
381
445
|
const data = await response.json();
|
|
382
446
|
if (!response.ok || !data.success) {
|
|
@@ -392,6 +456,13 @@ var WalletClient = class {
|
|
|
392
456
|
};
|
|
393
457
|
}
|
|
394
458
|
};
|
|
459
|
+
async function generateSessionKeypair() {
|
|
460
|
+
const keyPair = await generateSigningKeyPair();
|
|
461
|
+
return {
|
|
462
|
+
publicKeyHex: keyPair.publicKeyHex,
|
|
463
|
+
privateKeyHex: keyPair.privateKeyHex
|
|
464
|
+
};
|
|
465
|
+
}
|
|
395
466
|
|
|
396
467
|
// wallet/memory-client.ts
|
|
397
468
|
async function generateTestServerKeys() {
|
|
@@ -546,14 +617,32 @@ var MemoryWalletClient = class _MemoryWalletClient {
|
|
|
546
617
|
async login(_credentials) {
|
|
547
618
|
throw new Error("Use loginWithTokenSession(appKey, session, credentials) \u2014 app token + session required");
|
|
548
619
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
620
|
+
/**
|
|
621
|
+
* Sign up with session keypair (scoped to an app)
|
|
622
|
+
*
|
|
623
|
+
* The session must be approved by the app beforehand:
|
|
624
|
+
* 1. Client writes request to: immutable://inbox/{appKey}/sessions/{sessionPubkey} = 1
|
|
625
|
+
* 2. App approves by writing: mutable://accounts/{appKey}/sessions/{sessionPubkey} = 1
|
|
626
|
+
* 3. Client calls this method with the session keypair
|
|
627
|
+
*
|
|
628
|
+
* @param appKey - The app's public key
|
|
629
|
+
* @param session - Session keypair (generated via generateSessionKeypair)
|
|
630
|
+
* @param credentials - User credentials (username/password)
|
|
631
|
+
*/
|
|
632
|
+
async signupWithToken(appKey, session, credentials) {
|
|
633
|
+
if (!session?.publicKeyHex || !session?.privateKeyHex) {
|
|
634
|
+
throw new Error("session keypair is required");
|
|
635
|
+
}
|
|
636
|
+
const payloadToSign = {
|
|
637
|
+
sessionPubkey: session.publicKeyHex,
|
|
554
638
|
type: "password",
|
|
555
639
|
username: credentials.username,
|
|
556
640
|
password: credentials.password
|
|
641
|
+
};
|
|
642
|
+
const sessionSignature = await signWithHex(session.privateKeyHex, payloadToSign);
|
|
643
|
+
const response = await this.request("POST", `/auth/signup/${appKey}`, {
|
|
644
|
+
...payloadToSign,
|
|
645
|
+
sessionSignature
|
|
557
646
|
});
|
|
558
647
|
const data = await response.json();
|
|
559
648
|
if (!response.ok || !data.success) {
|
|
@@ -565,16 +654,32 @@ var MemoryWalletClient = class _MemoryWalletClient {
|
|
|
565
654
|
expiresIn: data.expiresIn
|
|
566
655
|
};
|
|
567
656
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
657
|
+
/**
|
|
658
|
+
* Login with session keypair (scoped to an app)
|
|
659
|
+
*
|
|
660
|
+
* The session must be approved by the app beforehand:
|
|
661
|
+
* 1. Client writes request to: immutable://inbox/{appKey}/sessions/{sessionPubkey} = 1
|
|
662
|
+
* 2. App approves by writing: mutable://accounts/{appKey}/sessions/{sessionPubkey} = 1
|
|
663
|
+
* 3. Client calls this method with the session keypair
|
|
664
|
+
*
|
|
665
|
+
* @param appKey - The app's public key
|
|
666
|
+
* @param session - Session keypair (generated via generateSessionKeypair)
|
|
667
|
+
* @param credentials - User credentials (username/password)
|
|
668
|
+
*/
|
|
669
|
+
async loginWithTokenSession(appKey, session, credentials) {
|
|
670
|
+
if (!session?.publicKeyHex || !session?.privateKeyHex) {
|
|
671
|
+
throw new Error("session keypair is required");
|
|
672
|
+
}
|
|
673
|
+
const payloadToSign = {
|
|
674
|
+
sessionPubkey: session.publicKeyHex,
|
|
575
675
|
type: "password",
|
|
576
676
|
username: credentials.username,
|
|
577
677
|
password: credentials.password
|
|
678
|
+
};
|
|
679
|
+
const sessionSignature = await signWithHex(session.privateKeyHex, payloadToSign);
|
|
680
|
+
const response = await this.request("POST", `/auth/login/${appKey}`, {
|
|
681
|
+
...payloadToSign,
|
|
682
|
+
sessionSignature
|
|
578
683
|
});
|
|
579
684
|
const data = await response.json();
|
|
580
685
|
if (!response.ok || !data.success) {
|
|
@@ -672,20 +777,20 @@ var MemoryWalletClient = class _MemoryWalletClient {
|
|
|
672
777
|
encrypt: request.encrypt
|
|
673
778
|
});
|
|
674
779
|
const data = await response.json();
|
|
675
|
-
if (!response.ok || !data.success) {
|
|
676
|
-
throw new Error(data.error || `Proxy write failed: ${response.statusText}`);
|
|
677
|
-
}
|
|
678
780
|
return data;
|
|
679
781
|
}
|
|
680
782
|
async proxyRead(request) {
|
|
681
783
|
const url = `/proxy/read?uri=${encodeURIComponent(request.uri)}`;
|
|
682
784
|
const response = await this.authRequest("GET", url);
|
|
683
785
|
const data = await response.json();
|
|
684
|
-
if (!response.ok || !data.success) {
|
|
685
|
-
throw new Error(data.error || `Proxy read failed: ${response.statusText}`);
|
|
686
|
-
}
|
|
687
786
|
return data;
|
|
688
787
|
}
|
|
788
|
+
async proxyReadMulti(request) {
|
|
789
|
+
const response = await this.authRequest("POST", "/proxy/read-multi", {
|
|
790
|
+
uris: request.uris
|
|
791
|
+
});
|
|
792
|
+
return await response.json();
|
|
793
|
+
}
|
|
689
794
|
// ============================================================
|
|
690
795
|
// Google OAuth (for completeness - may not work without real Google)
|
|
691
796
|
// ============================================================
|
|
@@ -710,15 +815,34 @@ var MemoryWalletClient = class _MemoryWalletClient {
|
|
|
710
815
|
picture: data.picture
|
|
711
816
|
};
|
|
712
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
* Login with Google OAuth (scoped to app token and session keypair)
|
|
820
|
+
* Returns session data with Google profile info - call setSession() to activate it
|
|
821
|
+
*
|
|
822
|
+
* The session must be approved by the app beforehand.
|
|
823
|
+
*
|
|
824
|
+
* @param appKey - The app's public key
|
|
825
|
+
* @param token - App token from app server
|
|
826
|
+
* @param session - Session keypair (generated via generateSessionKeypair)
|
|
827
|
+
* @param googleIdToken - Google ID token from Google Sign-In
|
|
828
|
+
* @returns GoogleAuthSession with username, JWT token, and Google profile info
|
|
829
|
+
*/
|
|
713
830
|
async loginWithGoogle(appKey, token, session, googleIdToken) {
|
|
714
831
|
if (!token) throw new Error("token is required");
|
|
715
|
-
if (!session
|
|
832
|
+
if (!session?.publicKeyHex || !session?.privateKeyHex) {
|
|
833
|
+
throw new Error("session keypair is required");
|
|
834
|
+
}
|
|
716
835
|
if (!googleIdToken) throw new Error("googleIdToken is required");
|
|
717
|
-
const
|
|
836
|
+
const payloadToSign = {
|
|
718
837
|
token,
|
|
719
|
-
session,
|
|
838
|
+
sessionPubkey: session.publicKeyHex,
|
|
720
839
|
type: "google",
|
|
721
840
|
googleIdToken
|
|
841
|
+
};
|
|
842
|
+
const sessionSignature = await signWithHex(session.privateKeyHex, payloadToSign);
|
|
843
|
+
const response = await this.request("POST", `/auth/login/${appKey}`, {
|
|
844
|
+
...payloadToSign,
|
|
845
|
+
sessionSignature
|
|
722
846
|
});
|
|
723
847
|
const data = await response.json();
|
|
724
848
|
if (!response.ok || !data.success) {
|
|
@@ -750,8 +874,58 @@ var MemoryWalletClient = class _MemoryWalletClient {
|
|
|
750
874
|
}
|
|
751
875
|
};
|
|
752
876
|
|
|
877
|
+
// wallet/testing.ts
|
|
878
|
+
async function createTestEnvironment(config = {}) {
|
|
879
|
+
const schema = config.schema || createTestSchema();
|
|
880
|
+
const serverKeys = config.serverKeys || await generateTestServerKeys();
|
|
881
|
+
const backend = new MemoryClient({ schema });
|
|
882
|
+
const walletConfig = {
|
|
883
|
+
serverKeys,
|
|
884
|
+
jwtSecret: config.jwtSecret,
|
|
885
|
+
backend
|
|
886
|
+
};
|
|
887
|
+
const wallet = await MemoryWalletClient.create(walletConfig);
|
|
888
|
+
return {
|
|
889
|
+
backend,
|
|
890
|
+
wallet,
|
|
891
|
+
serverKeys,
|
|
892
|
+
async signupTestUser(appKey, username, password) {
|
|
893
|
+
const keypair = await generateSigningKeyPair();
|
|
894
|
+
const sessionKeypair = {
|
|
895
|
+
publicKeyHex: keypair.publicKeyHex,
|
|
896
|
+
privateKeyHex: keypair.privateKeyHex
|
|
897
|
+
};
|
|
898
|
+
const sessionUri = `mutable://accounts/${appKey}/sessions/${sessionKeypair.publicKeyHex}`;
|
|
899
|
+
await backend.write(sessionUri, 1);
|
|
900
|
+
const session = await wallet.signupWithToken(appKey, sessionKeypair, { username, password });
|
|
901
|
+
wallet.setSession(session);
|
|
902
|
+
const keys = await wallet.getPublicKeys(appKey);
|
|
903
|
+
return { session, keys, sessionKeypair };
|
|
904
|
+
},
|
|
905
|
+
async loginTestUser(appKey, username, password) {
|
|
906
|
+
const keypair = await generateSigningKeyPair();
|
|
907
|
+
const sessionKeypair = {
|
|
908
|
+
publicKeyHex: keypair.publicKeyHex,
|
|
909
|
+
privateKeyHex: keypair.privateKeyHex
|
|
910
|
+
};
|
|
911
|
+
const sessionUri = `mutable://accounts/${appKey}/sessions/${sessionKeypair.publicKeyHex}`;
|
|
912
|
+
await backend.write(sessionUri, 1);
|
|
913
|
+
const session = await wallet.loginWithTokenSession(appKey, sessionKeypair, { username, password });
|
|
914
|
+
wallet.setSession(session);
|
|
915
|
+
const keys = await wallet.getPublicKeys(appKey);
|
|
916
|
+
return { session, keys, sessionKeypair };
|
|
917
|
+
},
|
|
918
|
+
async cleanup() {
|
|
919
|
+
await backend.cleanup();
|
|
920
|
+
wallet.logout();
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
|
|
753
925
|
export {
|
|
754
926
|
WalletClient,
|
|
927
|
+
generateSessionKeypair,
|
|
755
928
|
generateTestServerKeys,
|
|
756
|
-
MemoryWalletClient
|
|
929
|
+
MemoryWalletClient,
|
|
930
|
+
createTestEnvironment
|
|
757
931
|
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// clients/memory/mod.ts
|
|
2
|
+
function validateSchemaKey(key) {
|
|
3
|
+
return /^[a-z]+:\/\/[a-z0-9-]+$/.test(key);
|
|
4
|
+
}
|
|
2
5
|
function target(uri, schema, storage) {
|
|
3
6
|
const url = URL.parse(uri);
|
|
4
7
|
const program = `${url.protocol}//${url.hostname}`;
|
|
@@ -19,7 +22,19 @@ function target(uri, schema, storage) {
|
|
|
19
22
|
};
|
|
20
23
|
}
|
|
21
24
|
var MemoryClient = class {
|
|
25
|
+
/**
|
|
26
|
+
* Create a new MemoryClient
|
|
27
|
+
*
|
|
28
|
+
* @param config - Configuration with schema mapping
|
|
29
|
+
* @throws Error if schema keys are not in "protocol://hostname" format
|
|
30
|
+
*/
|
|
22
31
|
constructor(config) {
|
|
32
|
+
const invalidKeys = Object.keys(config.schema).filter((key) => !validateSchemaKey(key));
|
|
33
|
+
if (invalidKeys.length > 0) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Invalid schema key format: ${invalidKeys.map((k) => `"${k}"`).join(", ")}. Keys must be in "protocol://hostname" format (e.g., "mutable://accounts", "immutable://data").`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
23
38
|
this.schema = config.schema;
|
|
24
39
|
this.storage = config.storage || /* @__PURE__ */ new Map();
|
|
25
40
|
this.cleanup();
|
|
@@ -86,6 +101,30 @@ var MemoryClient = class {
|
|
|
86
101
|
record: current.value
|
|
87
102
|
});
|
|
88
103
|
}
|
|
104
|
+
async readMulti(uris) {
|
|
105
|
+
if (uris.length > 50) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
results: [],
|
|
109
|
+
summary: { total: uris.length, succeeded: 0, failed: uris.length }
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const results = await Promise.all(
|
|
113
|
+
uris.map(async (uri) => {
|
|
114
|
+
const result = await this.read(uri);
|
|
115
|
+
if (result.success && result.record) {
|
|
116
|
+
return { uri, success: true, record: result.record };
|
|
117
|
+
}
|
|
118
|
+
return { uri, success: false, error: result.error || "Read failed" };
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
122
|
+
return {
|
|
123
|
+
success: succeeded > 0,
|
|
124
|
+
results,
|
|
125
|
+
summary: { total: uris.length, succeeded, failed: uris.length - succeeded }
|
|
126
|
+
};
|
|
127
|
+
}
|
|
89
128
|
list(uri, options) {
|
|
90
129
|
const result = target(uri, this.schema, this.storage);
|
|
91
130
|
if (!result.success) {
|
|
@@ -115,7 +154,7 @@ var MemoryClient = class {
|
|
|
115
154
|
});
|
|
116
155
|
}
|
|
117
156
|
let items = Array.from(current.children.entries()).map(([key, child]) => ({
|
|
118
|
-
uri: path.endsWith("/") ? `${program}
|
|
157
|
+
uri: path.endsWith("/") ? `${program}${path}${key}` : `${program}${path}/${key}`,
|
|
119
158
|
type: child.value ? "file" : "directory"
|
|
120
159
|
}));
|
|
121
160
|
if (options?.pattern) {
|
|
@@ -196,7 +235,19 @@ var MemoryClient = class {
|
|
|
196
235
|
});
|
|
197
236
|
}
|
|
198
237
|
};
|
|
238
|
+
function createTestSchema() {
|
|
239
|
+
const acceptAll = async () => ({ valid: true });
|
|
240
|
+
return {
|
|
241
|
+
"mutable://accounts": acceptAll,
|
|
242
|
+
"mutable://open": acceptAll,
|
|
243
|
+
"mutable://data": acceptAll,
|
|
244
|
+
"immutable://accounts": acceptAll,
|
|
245
|
+
"immutable://open": acceptAll,
|
|
246
|
+
"immutable://data": acceptAll
|
|
247
|
+
};
|
|
248
|
+
}
|
|
199
249
|
|
|
200
250
|
export {
|
|
201
|
-
MemoryClient
|
|
251
|
+
MemoryClient,
|
|
252
|
+
createTestSchema
|
|
202
253
|
};
|
|
@@ -102,6 +102,51 @@ var HttpClient = class {
|
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
+
async readMulti(uris) {
|
|
106
|
+
if (uris.length > 50) {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
results: [],
|
|
110
|
+
summary: { total: uris.length, succeeded: 0, failed: uris.length }
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const response = await this.request("/api/v1/read-multi", {
|
|
115
|
+
method: "POST",
|
|
116
|
+
body: JSON.stringify({ uris })
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
if (response.status === 404) {
|
|
120
|
+
return this.readMultiFallback(uris);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
results: uris.map((uri) => ({ uri, success: false, error: response.statusText })),
|
|
125
|
+
summary: { total: uris.length, succeeded: 0, failed: uris.length }
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return await response.json();
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return this.readMultiFallback(uris);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async readMultiFallback(uris) {
|
|
134
|
+
const results = await Promise.all(
|
|
135
|
+
uris.map(async (uri) => {
|
|
136
|
+
const result = await this.read(uri);
|
|
137
|
+
if (result.success && result.record) {
|
|
138
|
+
return { uri, success: true, record: result.record };
|
|
139
|
+
}
|
|
140
|
+
return { uri, success: false, error: result.error || "Read failed" };
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
144
|
+
return {
|
|
145
|
+
success: succeeded > 0,
|
|
146
|
+
results,
|
|
147
|
+
summary: { total: uris.length, succeeded, failed: uris.length - succeeded }
|
|
148
|
+
};
|
|
149
|
+
}
|
|
105
150
|
async list(uri, options) {
|
|
106
151
|
try {
|
|
107
152
|
const { protocol, domain, path } = this.parseUri(uri);
|
|
@@ -104,6 +104,30 @@ var LocalStorageClient = class {
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
async readMulti(uris) {
|
|
108
|
+
if (uris.length > 50) {
|
|
109
|
+
return {
|
|
110
|
+
success: false,
|
|
111
|
+
results: [],
|
|
112
|
+
summary: { total: uris.length, succeeded: 0, failed: uris.length }
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const results = await Promise.all(
|
|
116
|
+
uris.map(async (uri) => {
|
|
117
|
+
const result = await this.read(uri);
|
|
118
|
+
if (result.success && result.record) {
|
|
119
|
+
return { uri, success: true, record: result.record };
|
|
120
|
+
}
|
|
121
|
+
return { uri, success: false, error: result.error || "Read failed" };
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
125
|
+
return {
|
|
126
|
+
success: succeeded > 0,
|
|
127
|
+
results,
|
|
128
|
+
summary: { total: uris.length, succeeded, failed: uris.length - succeeded }
|
|
129
|
+
};
|
|
130
|
+
}
|
|
107
131
|
async list(uri, options) {
|
|
108
132
|
try {
|
|
109
133
|
const { page = 1, limit = 50, pattern, sortBy = "name", sortOrder = "asc" } = options || {};
|
|
@@ -195,6 +195,35 @@ var WebSocketClient = class {
|
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
+
async readMulti(uris) {
|
|
199
|
+
if (uris.length > 50) {
|
|
200
|
+
return {
|
|
201
|
+
success: false,
|
|
202
|
+
results: [],
|
|
203
|
+
summary: { total: uris.length, succeeded: 0, failed: uris.length }
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const result = await this.sendRequest("readMulti", { uris });
|
|
208
|
+
return result;
|
|
209
|
+
} catch {
|
|
210
|
+
const results = await Promise.all(
|
|
211
|
+
uris.map(async (uri) => {
|
|
212
|
+
const result = await this.read(uri);
|
|
213
|
+
if (result.success && result.record) {
|
|
214
|
+
return { uri, success: true, record: result.record };
|
|
215
|
+
}
|
|
216
|
+
return { uri, success: false, error: result.error || "Read failed" };
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
220
|
+
return {
|
|
221
|
+
success: succeeded > 0,
|
|
222
|
+
results,
|
|
223
|
+
summary: { total: uris.length, succeeded, failed: uris.length - succeeded }
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
198
227
|
async list(uri, options) {
|
|
199
228
|
try {
|
|
200
229
|
const result = await this.sendRequest("list", { uri, options });
|