@bandeira-tech/b3nd-web 0.2.6 → 0.3.0

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.
@@ -446,27 +446,42 @@ async function proxyWrite(proxyClient, credentialClient, serverPublicKey, userna
446
446
  };
447
447
  }
448
448
  }
449
- async function proxyRead(proxyClient, uri, serverEncryptionPrivateKeyPem) {
449
+ async function proxyRead(proxyClient, credentialClient, serverPublicKey, username, serverEncryptionPrivateKeyPem, uri) {
450
450
  try {
451
- const result = await proxyClient.read(uri);
451
+ const accountKey = await loadUserAccountKey(
452
+ credentialClient,
453
+ serverPublicKey,
454
+ username,
455
+ serverEncryptionPrivateKeyPem
456
+ );
457
+ const resolvedUri = uri.replace(/:key/g, accountKey.publicKeyHex);
458
+ const result = await proxyClient.read(resolvedUri);
452
459
  if (!result.success) {
453
460
  return {
454
461
  success: false,
462
+ uri: resolvedUri,
455
463
  error: result.error || "Read failed"
456
464
  };
457
465
  }
458
466
  const response = {
459
467
  success: true,
468
+ uri: resolvedUri,
460
469
  record: result.record
461
470
  };
462
- if (serverEncryptionPrivateKeyPem && result.record?.data) {
471
+ if (result.record?.data) {
463
472
  try {
464
473
  const data = result.record.data;
465
474
  if (typeof data === "object" && data.payload && typeof data.payload === "object") {
466
475
  const payload = data.payload;
467
476
  if (payload.data && payload.nonce && payload.ephemeralPublicKey) {
477
+ const userEncryptionKey = await loadUserEncryptionKey(
478
+ credentialClient,
479
+ serverPublicKey,
480
+ username,
481
+ serverEncryptionPrivateKeyPem
482
+ );
468
483
  const privateKey = await pemToCryptoKey(
469
- serverEncryptionPrivateKeyPem,
484
+ userEncryptionKey.privateKeyPem,
470
485
  "X25519"
471
486
  );
472
487
  const encryptedPayload = {
@@ -491,6 +506,7 @@ async function proxyRead(proxyClient, uri, serverEncryptionPrivateKeyPem) {
491
506
  } catch (error) {
492
507
  return {
493
508
  success: false,
509
+ uri,
494
510
  error: `Proxy read failed: ${error instanceof Error ? error.message : String(error)}`
495
511
  };
496
512
  }
@@ -1704,22 +1720,25 @@ var WalletServerCore = class {
1704
1720
  return c.json({ success: false, error: "Authorization required" }, 401);
1705
1721
  }
1706
1722
  const token = authHeader.substring(7);
1707
- await verifyJwt(token, this.config.jwtSecret);
1723
+ const payload = await verifyJwt(token, this.config.jwtSecret);
1708
1724
  const uri = c.req.query("uri");
1709
1725
  if (!uri) {
1710
1726
  return c.json({ success: false, error: "uri query parameter is required" }, 400);
1711
1727
  }
1712
1728
  const result = await proxyRead(
1713
1729
  this.proxyClient,
1714
- uri,
1715
- serverEncryptionPrivateKeyPem
1730
+ this.credentialClient,
1731
+ serverPublicKey,
1732
+ payload.username,
1733
+ serverEncryptionPrivateKeyPem,
1734
+ uri
1716
1735
  );
1717
1736
  if (!result.success) {
1718
1737
  return c.json({ success: false, error: result.error }, 400);
1719
1738
  }
1720
1739
  return c.json({
1721
1740
  success: true,
1722
- uri,
1741
+ uri: result.uri,
1723
1742
  record: result.record,
1724
1743
  decrypted: result.decrypted
1725
1744
  });
@@ -1,14 +1,15 @@
1
+ import {
2
+ MemoryClient,
3
+ createTestSchema
4
+ } from "./chunk-OXHGNAQJ.js";
1
5
  import {
2
6
  WalletServerCore
3
- } from "./chunk-F3W5GZU6.js";
7
+ } from "./chunk-4C4YY2X7.js";
4
8
  import {
5
9
  exportPrivateKeyPem,
6
10
  generateEncryptionKeyPair,
7
11
  generateSigningKeyPair
8
12
  } from "./chunk-JN75UL5C.js";
9
- import {
10
- MemoryClient
11
- } from "./chunk-GIMIWVPX.js";
12
13
 
13
14
  // wallet/client.ts
14
15
  var WalletClient = class {
@@ -254,6 +255,8 @@ var WalletClient = class {
254
255
  * Proxy a write request through the wallet server
255
256
  * The server signs the write with its identity key
256
257
  * Requires active authentication session
258
+ *
259
+ * @returns ProxyWriteResponse - check `success` field for result
257
260
  */
258
261
  async proxyWrite(request) {
259
262
  if (!this.currentSession) {
@@ -272,17 +275,14 @@ var WalletClient = class {
272
275
  })
273
276
  });
274
277
  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
278
  return data;
281
279
  }
282
280
  /**
283
281
  * Proxy a read request through the wallet server
284
282
  * The server decrypts encrypted data using user's encryption key
285
283
  * Requires active authentication session
284
+ *
285
+ * @returns ProxyReadResponse - check `success` field for result, `decrypted` for decrypted data
286
286
  */
287
287
  async proxyRead(request) {
288
288
  if (!this.currentSession) {
@@ -297,11 +297,6 @@ var WalletClient = class {
297
297
  }
298
298
  });
299
299
  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
300
  return data;
306
301
  }
307
302
  /**
@@ -442,14 +437,14 @@ var MemoryWalletClient = class _MemoryWalletClient {
442
437
  */
443
438
  static async create(config = {}) {
444
439
  const serverKeys = config.serverKeys || await generateTestServerKeys();
445
- const storageClient = config.storageClient || createWalletMemoryClient();
440
+ const backend = config.backend || createWalletMemoryClient();
446
441
  const serverConfig = {
447
442
  serverKeys,
448
443
  jwtSecret: config.jwtSecret || "test-jwt-secret-for-memory-wallet-client!!",
449
444
  jwtExpirationSeconds: config.jwtExpirationSeconds || 3600,
450
445
  deps: {
451
- credentialClient: storageClient,
452
- proxyClient: storageClient,
446
+ credentialClient: backend,
447
+ proxyClient: backend,
453
448
  logger: {
454
449
  log: () => {
455
450
  },
@@ -672,18 +667,12 @@ var MemoryWalletClient = class _MemoryWalletClient {
672
667
  encrypt: request.encrypt
673
668
  });
674
669
  const data = await response.json();
675
- if (!response.ok || !data.success) {
676
- throw new Error(data.error || `Proxy write failed: ${response.statusText}`);
677
- }
678
670
  return data;
679
671
  }
680
672
  async proxyRead(request) {
681
673
  const url = `/proxy/read?uri=${encodeURIComponent(request.uri)}`;
682
674
  const response = await this.authRequest("GET", url);
683
675
  const data = await response.json();
684
- if (!response.ok || !data.success) {
685
- throw new Error(data.error || `Proxy read failed: ${response.statusText}`);
686
- }
687
676
  return data;
688
677
  }
689
678
  // ============================================================
@@ -750,8 +739,43 @@ var MemoryWalletClient = class _MemoryWalletClient {
750
739
  }
751
740
  };
752
741
 
742
+ // wallet/testing.ts
743
+ async function createTestEnvironment(config = {}) {
744
+ const schema = config.schema || createTestSchema();
745
+ const serverKeys = config.serverKeys || await generateTestServerKeys();
746
+ const backend = new MemoryClient({ schema });
747
+ const walletConfig = {
748
+ serverKeys,
749
+ jwtSecret: config.jwtSecret,
750
+ backend
751
+ };
752
+ const wallet = await MemoryWalletClient.create(walletConfig);
753
+ return {
754
+ backend,
755
+ wallet,
756
+ serverKeys,
757
+ async signupTestUser(appKey, username, password) {
758
+ const session = await wallet.signupWithToken(appKey, { username, password });
759
+ wallet.setSession(session);
760
+ const keys = await wallet.getPublicKeys(appKey);
761
+ return { session, keys };
762
+ },
763
+ async loginTestUser(appKey, sessionKey, username, password) {
764
+ const session = await wallet.loginWithTokenSession(appKey, sessionKey, { username, password });
765
+ wallet.setSession(session);
766
+ const keys = await wallet.getPublicKeys(appKey);
767
+ return { session, keys };
768
+ },
769
+ async cleanup() {
770
+ await backend.cleanup();
771
+ wallet.logout();
772
+ }
773
+ };
774
+ }
775
+
753
776
  export {
754
777
  WalletClient,
755
778
  generateTestServerKeys,
756
- MemoryWalletClient
779
+ MemoryWalletClient,
780
+ createTestEnvironment
757
781
  };
@@ -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();
@@ -196,7 +211,19 @@ var MemoryClient = class {
196
211
  });
197
212
  }
198
213
  };
214
+ function createTestSchema() {
215
+ const acceptAll = async () => ({ valid: true });
216
+ return {
217
+ "mutable://accounts": acceptAll,
218
+ "mutable://open": acceptAll,
219
+ "mutable://data": acceptAll,
220
+ "immutable://accounts": acceptAll,
221
+ "immutable://open": acceptAll,
222
+ "immutable://data": acceptAll
223
+ };
224
+ }
199
225
 
200
226
  export {
201
- MemoryClient
227
+ MemoryClient,
228
+ createTestSchema
202
229
  };
@@ -3,6 +3,50 @@
3
3
  *
4
4
  * Type definitions for the B3nd Wallet Client that interacts with wallet servers.
5
5
  */
6
+ /**
7
+ * Common interface for wallet clients (WalletClient and MemoryWalletClient)
8
+ *
9
+ * Implement this interface to create custom wallet clients or use
10
+ * for dependency injection in tests.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * function setupApp(wallet: WalletClientInterface) {
15
+ * // Works with both WalletClient and MemoryWalletClient
16
+ * await wallet.proxyWrite({ uri: "...", data: {...} });
17
+ * }
18
+ *
19
+ * // Production
20
+ * setupApp(new WalletClient({ walletServerUrl: "...", apiBasePath: "/api/v1" }));
21
+ *
22
+ * // Tests
23
+ * setupApp(await MemoryWalletClient.create());
24
+ * ```
25
+ */
26
+ interface WalletClientInterface {
27
+ getSession(): AuthSession | null;
28
+ setSession(session: AuthSession | null): void;
29
+ isAuthenticated(): boolean;
30
+ getUsername(): string | null;
31
+ getToken(): string | null;
32
+ logout(): void;
33
+ health(): Promise<HealthResponse>;
34
+ getServerKeys(): Promise<{
35
+ identityPublicKeyHex: string;
36
+ encryptionPublicKeyHex: string;
37
+ }>;
38
+ signupWithToken(appKey: string, tokenOrCredentials: string | UserCredentials, maybeCredentials?: UserCredentials): Promise<AuthSession>;
39
+ loginWithTokenSession(appKey: string, tokenOrSession: string, sessionOrCredentials: string | UserCredentials, maybeCredentials?: UserCredentials): Promise<AuthSession>;
40
+ changePassword(appKey: string, oldPassword: string, newPassword: string): Promise<void>;
41
+ requestPasswordResetWithToken(appKey: string, tokenOrUsername: string, maybeUsername?: string): Promise<PasswordResetToken>;
42
+ resetPasswordWithToken(appKey: string, tokenOrUsername: string, usernameOrReset: string, resetToken?: string, newPassword?: string): Promise<AuthSession>;
43
+ getPublicKeys(appKey: string): Promise<UserPublicKeys>;
44
+ getMyPublicKeys(appKey: string): Promise<UserPublicKeys>;
45
+ proxyWrite(request: ProxyWriteRequest): Promise<ProxyWriteResponse>;
46
+ proxyRead(request: ProxyReadRequest): Promise<ProxyReadResponse>;
47
+ signupWithGoogle(appKey: string, token: string, googleIdToken: string): Promise<GoogleAuthSession>;
48
+ loginWithGoogle(appKey: string, token: string, session: string, googleIdToken: string): Promise<GoogleAuthSession>;
49
+ }
6
50
  /**
7
51
  * Configuration for wallet client
8
52
  */
@@ -307,12 +351,16 @@ declare class WalletClient {
307
351
  * Proxy a write request through the wallet server
308
352
  * The server signs the write with its identity key
309
353
  * Requires active authentication session
354
+ *
355
+ * @returns ProxyWriteResponse - check `success` field for result
310
356
  */
311
357
  proxyWrite(request: ProxyWriteRequest): Promise<ProxyWriteResponse>;
312
358
  /**
313
359
  * Proxy a read request through the wallet server
314
360
  * The server decrypts encrypted data using user's encryption key
315
361
  * Requires active authentication session
362
+ *
363
+ * @returns ProxyReadResponse - check `success` field for result, `decrypted` for decrypted data
316
364
  */
317
365
  proxyRead(request: ProxyReadRequest): Promise<ProxyReadResponse>;
318
366
  /**
@@ -351,4 +399,4 @@ declare class WalletClient {
351
399
  loginWithGoogle(appKey: string, token: string, session: string, googleIdToken: string): Promise<GoogleAuthSession>;
352
400
  }
353
401
 
354
- export { type AuthSession as A, type ChangePasswordResponse as C, type GoogleAuthSession as G, type HealthResponse as H, type LoginResponse as L, type PasswordResetToken as P, type RequestPasswordResetResponse as R, type SignupResponse as S, type UserCredentials as U, WalletClient as W, type UserPublicKeys as a, type ProxyWriteRequest as b, type ProxyWriteResponse as c, type ProxyReadRequest as d, type ProxyReadResponse as e, type WalletClientConfig as f, type ApiResponse as g, type PublicKeysResponse as h, type ResetPasswordResponse as i, type GoogleSignupResponse as j, type GoogleLoginResponse as k };
402
+ export { type AuthSession as A, type ChangePasswordResponse as C, type GoogleAuthSession as G, type HealthResponse as H, type LoginResponse as L, type PasswordResetToken as P, type RequestPasswordResetResponse as R, type SignupResponse as S, type UserCredentials as U, WalletClient as W, type UserPublicKeys as a, type ProxyWriteRequest as b, type ProxyWriteResponse as c, type ProxyReadRequest as d, type ProxyReadResponse as e, type WalletClientInterface as f, type WalletClientConfig as g, type ApiResponse as h, type PublicKeysResponse as i, type ResetPasswordResponse as j, type GoogleSignupResponse as k, type GoogleLoginResponse as l };
@@ -6,12 +6,33 @@ type MemoryClientStorageNode<T> = {
6
6
  };
7
7
  type MemoryClientStorage = Map<string, MemoryClientStorageNode<unknown>>;
8
8
  interface MemoryClientConfig {
9
+ /**
10
+ * Schema mapping protocol://hostname to validators.
11
+ *
12
+ * Keys MUST be in format: "protocol://hostname"
13
+ * Examples: "mutable://accounts", "immutable://data", "mutable://open"
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const schema = {
18
+ * "mutable://accounts": async () => ({ valid: true }),
19
+ * "immutable://accounts": async () => ({ valid: true }),
20
+ * };
21
+ * const client = new MemoryClient({ schema });
22
+ * ```
23
+ */
9
24
  schema: Schema;
10
25
  storage?: MemoryClientStorage;
11
26
  }
12
27
  declare class MemoryClient implements NodeProtocolInterface {
13
28
  protected storage: MemoryClientStorage;
14
29
  protected schema: Schema;
30
+ /**
31
+ * Create a new MemoryClient
32
+ *
33
+ * @param config - Configuration with schema mapping
34
+ * @throws Error if schema keys are not in "protocol://hostname" format
35
+ */
15
36
  constructor(config: MemoryClientConfig);
16
37
  write<T = unknown>(uri: string, payload: T): Promise<WriteResult<T>>;
17
38
  read<T>(uri: string): Promise<ReadResult<T>>;
@@ -21,5 +42,17 @@ declare class MemoryClient implements NodeProtocolInterface {
21
42
  cleanup(): Promise<void>;
22
43
  delete(uri: string): Promise<DeleteResult>;
23
44
  }
45
+ /**
46
+ * Default schema for testing that accepts all writes.
47
+ * Includes common b3nd protocol prefixes.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * import { MemoryClient, createTestSchema } from "@bandeira-tech/b3nd-web/clients/memory";
52
+ *
53
+ * const backend = new MemoryClient({ schema: createTestSchema() });
54
+ * ```
55
+ */
56
+ declare function createTestSchema(): Schema;
24
57
 
25
- export { MemoryClient, type MemoryClientConfig };
58
+ export { MemoryClient, type MemoryClientConfig, createTestSchema };
@@ -1,7 +1,9 @@
1
1
  import {
2
- MemoryClient
3
- } from "../../chunk-GIMIWVPX.js";
2
+ MemoryClient,
3
+ createTestSchema
4
+ } from "../../chunk-OXHGNAQJ.js";
4
5
  import "../../chunk-MLKGABMK.js";
5
6
  export {
6
- MemoryClient
7
+ MemoryClient,
8
+ createTestSchema
7
9
  };
@@ -169,6 +169,7 @@ interface ProxyWriteResponse {
169
169
  */
170
170
  interface ProxyReadResponse {
171
171
  success: boolean;
172
+ uri?: string;
172
173
  error?: string;
173
174
  record?: {
174
175
  data: unknown;
@@ -2,6 +2,6 @@ export { C as ClientError, D as DeleteResult, H as HealthStatus, a as HttpClient
2
2
  export { HttpClient } from '../clients/http/mod.js';
3
3
  export { WebSocketClient } from '../clients/websocket/mod.js';
4
4
  export { LocalStorageClient } from '../clients/local-storage/mod.js';
5
- export { W as WalletClient } from '../client-B0Ekm99R.js';
5
+ export { W as WalletClient } from '../client-fsiY-9VW.js';
6
6
  export { AppsClient } from '../apps/mod.js';
7
7
  export { m as encrypt } from '../mod-CII9wqu2.js';
@@ -1,13 +1,11 @@
1
- import {
2
- WebSocketClient
3
- } from "../chunk-T43IWAQK.js";
4
1
  import {
5
2
  AppsClient
6
3
  } from "../chunk-VAZUCGED.js";
7
4
  import {
8
5
  WalletClient
9
- } from "../chunk-FVLXYKYS.js";
10
- import "../chunk-F3W5GZU6.js";
6
+ } from "../chunk-IILPY4TK.js";
7
+ import "../chunk-OXHGNAQJ.js";
8
+ import "../chunk-4C4YY2X7.js";
11
9
  import {
12
10
  mod_exports
13
11
  } from "../chunk-JN75UL5C.js";
@@ -17,7 +15,9 @@ import {
17
15
  import {
18
16
  LocalStorageClient
19
17
  } from "../chunk-7U5JDFQW.js";
20
- import "../chunk-GIMIWVPX.js";
18
+ import {
19
+ WebSocketClient
20
+ } from "../chunk-T43IWAQK.js";
21
21
  import "../chunk-MLKGABMK.js";
22
22
  export {
23
23
  AppsClient,
@@ -1,9 +1,9 @@
1
- import { A as AuthSession, H as HealthResponse, U as UserCredentials, P as PasswordResetToken, a as UserPublicKeys, b as ProxyWriteRequest, c as ProxyWriteResponse, d as ProxyReadRequest, e as ProxyReadResponse, G as GoogleAuthSession } from '../client-B0Ekm99R.js';
2
- export { g as ApiResponse, C as ChangePasswordResponse, k as GoogleLoginResponse, j as GoogleSignupResponse, L as LoginResponse, h as PublicKeysResponse, R as RequestPasswordResetResponse, i as ResetPasswordResponse, S as SignupResponse, W as WalletClient, f as WalletClientConfig } from '../client-B0Ekm99R.js';
3
- import { S as ServerKeys, W as WalletServerCore } from '../core-CgxQpSVM.js';
1
+ import { A as AuthSession, H as HealthResponse, U as UserCredentials, P as PasswordResetToken, a as UserPublicKeys, b as ProxyWriteRequest, c as ProxyWriteResponse, d as ProxyReadRequest, e as ProxyReadResponse, G as GoogleAuthSession } from '../client-fsiY-9VW.js';
2
+ export { h as ApiResponse, C as ChangePasswordResponse, l as GoogleLoginResponse, k as GoogleSignupResponse, L as LoginResponse, i as PublicKeysResponse, R as RequestPasswordResetResponse, j as ResetPasswordResponse, S as SignupResponse, W as WalletClient, g as WalletClientConfig, f as WalletClientInterface } from '../client-fsiY-9VW.js';
3
+ import { N as NodeProtocolInterface, S as Schema } from '../types-oQCx3U-_.js';
4
+ import { S as ServerKeys, W as WalletServerCore } from '../core-BLTm0eu6.js';
4
5
  import { MemoryClient } from '../clients/memory/mod.js';
5
6
  import 'hono';
6
- import '../types-oQCx3U-_.js';
7
7
 
8
8
  /**
9
9
  * Memory Wallet Client
@@ -16,16 +16,22 @@ import '../types-oQCx3U-_.js';
16
16
  * @example
17
17
  * ```typescript
18
18
  * // Production
19
+ * const backend = new HttpClient({ baseUrl: "http://localhost:8842" });
19
20
  * const wallet = new WalletClient({
20
21
  * walletServerUrl: "http://localhost:8843",
21
22
  * apiBasePath: "/api/v1"
22
23
  * });
23
24
  *
24
- * // Tests
25
- * const wallet = await MemoryWalletClient.create();
25
+ * // Tests - share same backend between wallet and direct operations
26
+ * const backend = new MemoryClient({ schema: { "mutable://accounts": ... } });
27
+ * const wallet = await MemoryWalletClient.create({ backend });
26
28
  *
27
29
  * // Same API works for both
28
30
  * await wallet.signupWithToken(appKey, { username: "alice", password: "secret" });
31
+ * await wallet.proxyWrite({ uri: "mutable://data/test", data: { foo: "bar" } });
32
+ *
33
+ * // Direct backend access in tests
34
+ * const result = await backend.read("mutable://accounts/...");
29
35
  * ```
30
36
  */
31
37
 
@@ -43,10 +49,21 @@ interface MemoryWalletClientConfig {
43
49
  */
44
50
  jwtExpirationSeconds?: number;
45
51
  /**
46
- * Optional existing MemoryClient to use as storage backend.
47
- * If not provided, a new one is created.
52
+ * Shared backend storage (e.g., MemoryClient).
53
+ * Use this to share the same storage between wallet and direct operations.
54
+ * If not provided, a new MemoryClient is created.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const backend = new MemoryClient({ schema: { ... } });
59
+ * const wallet = await MemoryWalletClient.create({ backend });
60
+ *
61
+ * // Both use the same storage
62
+ * await backend.write("mutable://accounts/...", data);
63
+ * await wallet.proxyWrite({ uri: "mutable://...", data });
64
+ * ```
48
65
  */
49
- storageClient?: MemoryClient;
66
+ backend?: NodeProtocolInterface;
50
67
  }
51
68
  /**
52
69
  * Generate server keys for testing
@@ -116,4 +133,138 @@ declare class MemoryWalletClient {
116
133
  };
117
134
  }
118
135
 
119
- export { AuthSession, GoogleAuthSession, HealthResponse, MemoryWalletClient, type MemoryWalletClientConfig, PasswordResetToken, ProxyReadRequest, ProxyReadResponse, ProxyWriteRequest, ProxyWriteResponse, UserCredentials, UserPublicKeys, generateTestServerKeys };
136
+ /**
137
+ * Test Utilities for B3nd Wallet
138
+ *
139
+ * Provides helpers for setting up test environments with shared in-memory storage.
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * import { createTestEnvironment } from "@bandeira-tech/b3nd-web/wallet";
144
+ *
145
+ * const { backend, wallet, signupTestUser, cleanup } = await createTestEnvironment();
146
+ *
147
+ * // Sign up a test user
148
+ * const { session, keys } = await signupTestUser("app-key", "testuser", "pass123");
149
+ *
150
+ * // Now wallet.proxyRead/proxyWrite work with the shared backend
151
+ * const writeResult = await wallet.proxyWrite({ uri: "mutable://data/test", data: { foo: "bar" }, encrypt: true });
152
+ * const readResult = await wallet.proxyRead({ uri: "mutable://data/test" });
153
+ * console.log(readResult.decrypted); // { foo: "bar" }
154
+ *
155
+ * // Direct backend access also works
156
+ * const raw = await backend.read("mutable://data/test");
157
+ * ```
158
+ */
159
+
160
+ /**
161
+ * Test environment configuration
162
+ */
163
+ interface TestEnvironmentConfig {
164
+ /**
165
+ * Custom schema for the backend.
166
+ * If not provided, uses createTestSchema() which accepts all writes.
167
+ */
168
+ schema?: Schema;
169
+ /**
170
+ * Custom server keys.
171
+ * If not provided, keys are auto-generated.
172
+ */
173
+ serverKeys?: ServerKeys;
174
+ /**
175
+ * Custom JWT secret.
176
+ * Defaults to a test secret.
177
+ */
178
+ jwtSecret?: string;
179
+ }
180
+ /**
181
+ * Test environment with shared backend
182
+ */
183
+ interface TestEnvironment {
184
+ /**
185
+ * The shared in-memory backend storage.
186
+ * Use this for direct reads/writes in tests.
187
+ */
188
+ backend: MemoryClient;
189
+ /**
190
+ * The wallet client connected to the shared backend.
191
+ * Same API as WalletClient but runs in-memory.
192
+ */
193
+ wallet: MemoryWalletClient;
194
+ /**
195
+ * Server keys (for advanced testing)
196
+ */
197
+ serverKeys: ServerKeys;
198
+ /**
199
+ * Sign up a test user and set the session.
200
+ *
201
+ * @param appKey - App key for the signup
202
+ * @param username - Username
203
+ * @param password - Password
204
+ * @returns Session and user public keys
205
+ */
206
+ signupTestUser(appKey: string, username: string, password: string): Promise<{
207
+ session: AuthSession;
208
+ keys: UserPublicKeys;
209
+ }>;
210
+ /**
211
+ * Login a test user and set the session.
212
+ *
213
+ * @param appKey - App key for the login
214
+ * @param sessionKey - Session key (can be any string for tests)
215
+ * @param username - Username
216
+ * @param password - Password
217
+ * @returns Session and user public keys
218
+ */
219
+ loginTestUser(appKey: string, sessionKey: string, username: string, password: string): Promise<{
220
+ session: AuthSession;
221
+ keys: UserPublicKeys;
222
+ }>;
223
+ /**
224
+ * Clean up the test environment.
225
+ * Clears all data from the backend.
226
+ */
227
+ cleanup(): Promise<void>;
228
+ }
229
+ /**
230
+ * Create a test environment with shared in-memory backend.
231
+ *
232
+ * This sets up a MemoryClient and MemoryWalletClient that share the same storage,
233
+ * enabling you to test wallet operations alongside direct backend access.
234
+ *
235
+ * @param config - Optional configuration
236
+ * @returns Test environment with backend, wallet, and helper functions
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * // Basic usage
241
+ * const { backend, wallet, signupTestUser } = await createTestEnvironment();
242
+ * const { keys } = await signupTestUser("my-app", "alice", "password123");
243
+ *
244
+ * // Write encrypted data
245
+ * await wallet.proxyWrite({
246
+ * uri: "mutable://data/:key/profile",
247
+ * data: { name: "Alice" },
248
+ * encrypt: true
249
+ * });
250
+ *
251
+ * // Read and decrypt
252
+ * const result = await wallet.proxyRead({ uri: "mutable://data/:key/profile" });
253
+ * console.log(result.decrypted); // { name: "Alice" }
254
+ * ```
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * // With custom schema
259
+ * const { backend, wallet } = await createTestEnvironment({
260
+ * schema: {
261
+ * "mutable://myapp": async () => ({ valid: true }),
262
+ * "mutable://accounts": async () => ({ valid: true }),
263
+ * "immutable://accounts": async () => ({ valid: true }),
264
+ * }
265
+ * });
266
+ * ```
267
+ */
268
+ declare function createTestEnvironment(config?: TestEnvironmentConfig): Promise<TestEnvironment>;
269
+
270
+ export { AuthSession, GoogleAuthSession, HealthResponse, MemoryWalletClient, type MemoryWalletClientConfig, PasswordResetToken, ProxyReadRequest, ProxyReadResponse, ProxyWriteRequest, ProxyWriteResponse, type TestEnvironment, type TestEnvironmentConfig, UserCredentials, UserPublicKeys, createTestEnvironment, generateTestServerKeys };
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  MemoryWalletClient,
3
3
  WalletClient,
4
+ createTestEnvironment,
4
5
  generateTestServerKeys
5
- } from "../chunk-FVLXYKYS.js";
6
- import "../chunk-F3W5GZU6.js";
6
+ } from "../chunk-IILPY4TK.js";
7
+ import "../chunk-OXHGNAQJ.js";
8
+ import "../chunk-4C4YY2X7.js";
7
9
  import "../chunk-JN75UL5C.js";
8
10
  import "../chunk-LFUC4ETD.js";
9
- import "../chunk-GIMIWVPX.js";
10
11
  import "../chunk-MLKGABMK.js";
11
12
  export {
12
13
  MemoryWalletClient,
13
14
  WalletClient,
15
+ createTestEnvironment,
14
16
  generateTestServerKeys
15
17
  };
@@ -1,5 +1,5 @@
1
- import { F as FileStorage, S as ServerKeys, W as WalletServerCore } from '../../core-CgxQpSVM.js';
2
- export { C as BrowserEnvironment, M as BrowserMemoryStorage } from '../../core-CgxQpSVM.js';
1
+ import { F as FileStorage, S as ServerKeys, W as WalletServerCore } from '../../core-BLTm0eu6.js';
2
+ export { C as BrowserEnvironment, M as BrowserMemoryStorage } from '../../core-BLTm0eu6.js';
3
3
  import 'hono';
4
4
  import '../../types-oQCx3U-_.js';
5
5
 
@@ -2,7 +2,7 @@ import {
2
2
  ConfigEnvironment,
3
3
  MemoryFileStorage,
4
4
  WalletServerCore
5
- } from "../../chunk-F3W5GZU6.js";
5
+ } from "../../chunk-4C4YY2X7.js";
6
6
  import "../../chunk-JN75UL5C.js";
7
7
  import "../../chunk-LFUC4ETD.js";
8
8
  import {
@@ -1,5 +1,5 @@
1
- import { P as ProxyWriteRequest, a as ProxyWriteResponse, b as ProxyReadResponse, U as UserKeys, L as Logger, H as HttpFetch } from '../core-CgxQpSVM.js';
2
- export { A as AppBackendConfig, f as AuthSessionResponse, C as ConfigEnvironment, E as Environment, F as FileStorage, h as HealthResponse, M as MemoryFileStorage, g as PublicKeysResponse, R as ResolvedWalletServerConfig, e as ServerKeyPair, S as ServerKeys, i as ServerKeysResponse, c as WalletServerConfig, W as WalletServerCore, d as WalletServerDeps, j as defaultLogger } from '../core-CgxQpSVM.js';
1
+ import { P as ProxyWriteRequest, a as ProxyWriteResponse, b as ProxyReadResponse, U as UserKeys, L as Logger, H as HttpFetch } from '../core-BLTm0eu6.js';
2
+ export { A as AppBackendConfig, f as AuthSessionResponse, C as ConfigEnvironment, E as Environment, F as FileStorage, h as HealthResponse, M as MemoryFileStorage, g as PublicKeysResponse, R as ResolvedWalletServerConfig, e as ServerKeyPair, S as ServerKeys, i as ServerKeysResponse, c as WalletServerConfig, W as WalletServerCore, d as WalletServerDeps, j as defaultLogger } from '../core-BLTm0eu6.js';
3
3
  import { N as NodeProtocolInterface } from '../types-oQCx3U-_.js';
4
4
  import { S as SignedEncryptedMessage, E as EncryptedPayload } from '../mod-CII9wqu2.js';
5
5
  import 'hono';
@@ -43,8 +43,11 @@ declare function extractUsernameFromJwt(token: string): string | null;
43
43
  declare function proxyWrite(proxyClient: NodeProtocolInterface, credentialClient: NodeProtocolInterface, serverPublicKey: string, username: string, serverEncryptionPrivateKeyPem: string, request: ProxyWriteRequest): Promise<ProxyWriteResponse>;
44
44
  /**
45
45
  * Proxy a read request (with optional decryption)
46
+ *
47
+ * Data encrypted with proxyWrite is encrypted with the user's encryption key,
48
+ * so we need to load the user's encryption key to decrypt it.
46
49
  */
47
- declare function proxyRead(proxyClient: NodeProtocolInterface, uri: string, serverEncryptionPrivateKeyPem?: string): Promise<ProxyReadResponse>;
50
+ declare function proxyRead(proxyClient: NodeProtocolInterface, credentialClient: NodeProtocolInterface, serverPublicKey: string, username: string, serverEncryptionPrivateKeyPem: string, uri: string): Promise<ProxyReadResponse>;
48
51
 
49
52
  /**
50
53
  * User Key Management
@@ -34,7 +34,7 @@ import {
34
34
  userExists,
35
35
  verifyGoogleIdToken,
36
36
  verifyJwt
37
- } from "../chunk-F3W5GZU6.js";
37
+ } from "../chunk-4C4YY2X7.js";
38
38
  import "../chunk-JN75UL5C.js";
39
39
  import "../chunk-LFUC4ETD.js";
40
40
  import "../chunk-MLKGABMK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bandeira-tech/b3nd-web",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "description": "Browser-focused B3nd SDK bundle",
5
5
  "type": "module",
6
6
  "main": "./dist/src/mod.web.js",