@vaultsandbox/client 0.5.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.
- package/LICENSE +21 -0
- package/README.md +612 -0
- package/dist/client.d.ts +231 -0
- package/dist/client.js +432 -0
- package/dist/client.js.map +1 -0
- package/dist/crypto/constants.d.ts +13 -0
- package/dist/crypto/constants.js +14 -0
- package/dist/crypto/constants.js.map +1 -0
- package/dist/crypto/decrypt.d.ts +41 -0
- package/dist/crypto/decrypt.js +112 -0
- package/dist/crypto/decrypt.js.map +1 -0
- package/dist/crypto/keypair.d.ts +39 -0
- package/dist/crypto/keypair.js +109 -0
- package/dist/crypto/keypair.js.map +1 -0
- package/dist/crypto/signature.d.ts +28 -0
- package/dist/crypto/signature.js +89 -0
- package/dist/crypto/signature.js.map +1 -0
- package/dist/crypto/utils.d.ts +28 -0
- package/dist/crypto/utils.js +60 -0
- package/dist/crypto/utils.js.map +1 -0
- package/dist/email.d.ts +63 -0
- package/dist/email.js +186 -0
- package/dist/email.js.map +1 -0
- package/dist/http/api-client.d.ts +145 -0
- package/dist/http/api-client.js +242 -0
- package/dist/http/api-client.js.map +1 -0
- package/dist/inbox.d.ts +120 -0
- package/dist/inbox.js +243 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/strategies/delivery-strategy.d.ts +29 -0
- package/dist/strategies/delivery-strategy.js +2 -0
- package/dist/strategies/delivery-strategy.js.map +1 -0
- package/dist/strategies/polling-strategy.d.ts +36 -0
- package/dist/strategies/polling-strategy.js +146 -0
- package/dist/strategies/polling-strategy.js.map +1 -0
- package/dist/strategies/sse-strategy.d.ts +49 -0
- package/dist/strategies/sse-strategy.js +266 -0
- package/dist/strategies/sse-strategy.js.map +1 -0
- package/dist/types/index.d.ts +434 -0
- package/dist/types/index.js +127 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/email-utils.d.ts +19 -0
- package/dist/utils/email-utils.js +92 -0
- package/dist/utils/email-utils.js.map +1 -0
- package/dist/utils/sleep.d.ts +6 -0
- package/dist/utils/sleep.js +9 -0
- package/dist/utils/sleep.js.map +1 -0
- package/package.json +85 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VaultSandboxClient - Main entry point for the SDK
|
|
3
|
+
*/
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import { Inbox } from './inbox.js';
|
|
6
|
+
import type { ClientConfig, CreateInboxOptions, ServerInfo, Subscription, IEmail, ExportedInboxData } from './types/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* An event emitter for monitoring multiple inboxes simultaneously.
|
|
9
|
+
* @emits email - When a new email arrives in any of the monitored inboxes.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const monitor = client.monitorInboxes([inbox1, inbox2]);
|
|
13
|
+
* monitor.on('email', (inbox, email) => {
|
|
14
|
+
* console.log(`New email in ${inbox.emailAddress}: ${email.subject}`);
|
|
15
|
+
* });
|
|
16
|
+
* // To stop monitoring:
|
|
17
|
+
* monitor.unsubscribe();
|
|
18
|
+
*/
|
|
19
|
+
export declare class InboxMonitor extends EventEmitter {
|
|
20
|
+
private subscriptions;
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
* Adds a subscription to the monitor.
|
|
24
|
+
* @param subscription - The subscription to add.
|
|
25
|
+
*/
|
|
26
|
+
addSubscription(subscription: Subscription): void;
|
|
27
|
+
/**
|
|
28
|
+
* Unsubscribes from all monitored inboxes and cleans up resources.
|
|
29
|
+
*/
|
|
30
|
+
unsubscribe(): void;
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
* Emits an 'email' event for a specific inbox.
|
|
34
|
+
* @param inbox - The inbox that received the email.
|
|
35
|
+
* @param email - The email that was received.
|
|
36
|
+
*/
|
|
37
|
+
emitEmail(inbox: Inbox, email: IEmail): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The main client for interacting with the VaultSandbox API.
|
|
41
|
+
*
|
|
42
|
+
* This class provides methods for creating and managing inboxes,
|
|
43
|
+
* as well as for monitoring them for new emails.
|
|
44
|
+
*/
|
|
45
|
+
export declare class VaultSandboxClient {
|
|
46
|
+
private apiClient;
|
|
47
|
+
private config;
|
|
48
|
+
private serverPublicKey;
|
|
49
|
+
private inboxes;
|
|
50
|
+
private strategy;
|
|
51
|
+
/**
|
|
52
|
+
* Creates a new VaultSandboxClient instance.
|
|
53
|
+
* @param config - The client configuration.
|
|
54
|
+
*/
|
|
55
|
+
constructor(config: ClientConfig);
|
|
56
|
+
/**
|
|
57
|
+
* Initializes the client by fetching server info and creating a delivery strategy.
|
|
58
|
+
* This method is called automatically when needed and should not be called directly.
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
private ensureInitialized;
|
|
62
|
+
/**
|
|
63
|
+
* Creates the appropriate delivery strategy based on configuration.
|
|
64
|
+
* SSE for email events (/api/events) is always available on the server.
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
private createStrategy;
|
|
68
|
+
/**
|
|
69
|
+
* Creates a new, temporary email inbox.
|
|
70
|
+
*
|
|
71
|
+
* This method generates a new quantum-safe keypair, registers the inbox
|
|
72
|
+
* with the VaultSandbox server, and returns an `Inbox` instance.
|
|
73
|
+
*
|
|
74
|
+
* @param options - Optional parameters for inbox creation.
|
|
75
|
+
* @returns A promise that resolves to a new `Inbox` instance.
|
|
76
|
+
* @example
|
|
77
|
+
* const inbox = await client.createInbox({ ttl: 3600 }); // Inbox expires in 1 hour
|
|
78
|
+
*/
|
|
79
|
+
createInbox(options?: CreateInboxOptions): Promise<Inbox>;
|
|
80
|
+
/**
|
|
81
|
+
* Deletes all inboxes associated with the current API key.
|
|
82
|
+
*
|
|
83
|
+
* @returns A promise that resolves to the number of inboxes deleted.
|
|
84
|
+
*/
|
|
85
|
+
deleteAllInboxes(): Promise<number>;
|
|
86
|
+
/**
|
|
87
|
+
* Retrieves information about the VaultSandbox server.
|
|
88
|
+
*
|
|
89
|
+
* @returns A promise that resolves to the server information.
|
|
90
|
+
*/
|
|
91
|
+
getServerInfo(): Promise<ServerInfo>;
|
|
92
|
+
/**
|
|
93
|
+
* Checks if the configured API key is valid.
|
|
94
|
+
*
|
|
95
|
+
* @returns A promise that resolves to `true` if the API key is valid, `false` otherwise.
|
|
96
|
+
*/
|
|
97
|
+
checkKey(): Promise<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Monitors multiple inboxes simultaneously for new emails.
|
|
100
|
+
*
|
|
101
|
+
* @param inboxes - An array of `Inbox` instances to monitor.
|
|
102
|
+
* @returns An `InboxMonitor` instance that emits 'email' events.
|
|
103
|
+
*/
|
|
104
|
+
monitorInboxes(inboxes: Inbox[]): InboxMonitor;
|
|
105
|
+
/**
|
|
106
|
+
* Exports an inbox's data for backup or sharing purposes.
|
|
107
|
+
*
|
|
108
|
+
* @param inboxOrEmail - Either an Inbox instance or an email address string
|
|
109
|
+
* @returns The exported inbox data containing all necessary information to import the inbox
|
|
110
|
+
* @throws {InboxNotFoundError} If the inbox is not found in the client
|
|
111
|
+
* @example
|
|
112
|
+
* const exportedData = client.exportInbox(inbox);
|
|
113
|
+
* // or
|
|
114
|
+
* const exportedData = client.exportInbox('test@example.com');
|
|
115
|
+
*/
|
|
116
|
+
exportInbox(inboxOrEmail: Inbox | string): ExportedInboxData;
|
|
117
|
+
/**
|
|
118
|
+
* Imports an inbox from exported data.
|
|
119
|
+
*
|
|
120
|
+
* @param data - The exported inbox data
|
|
121
|
+
* @returns A promise that resolves to the imported Inbox instance
|
|
122
|
+
* @throws {InvalidImportDataError} If the data is invalid or malformed
|
|
123
|
+
* @throws {InboxAlreadyExistsError} If an inbox with this email already exists
|
|
124
|
+
* @example
|
|
125
|
+
* const importedInbox = await client.importInbox(exportedData);
|
|
126
|
+
*/
|
|
127
|
+
importInbox(data: ExportedInboxData): Promise<Inbox>;
|
|
128
|
+
/**
|
|
129
|
+
* Validates that all required fields are present and non-empty in the exported inbox data.
|
|
130
|
+
* @private
|
|
131
|
+
* @param data - The exported inbox data to validate
|
|
132
|
+
* @throws {InvalidImportDataError} If any required field is missing, not a string, or empty
|
|
133
|
+
*/
|
|
134
|
+
private validateRequiredFields;
|
|
135
|
+
/**
|
|
136
|
+
* Ensures the public key is present in the exported data, deriving it from the secret key if necessary.
|
|
137
|
+
* In ML-KEM (Kyber), the public key is embedded in the secret key, so we can extract it if missing.
|
|
138
|
+
* @private
|
|
139
|
+
* @param data - The exported inbox data (will be mutated to add publicKeyB64 if missing)
|
|
140
|
+
* @throws {InvalidImportDataError} If the secret key is invalid or derivation fails
|
|
141
|
+
*/
|
|
142
|
+
private ensurePublicKeyPresent;
|
|
143
|
+
/**
|
|
144
|
+
* Validates that the timestamp fields contain valid ISO 8601 date strings.
|
|
145
|
+
* @private
|
|
146
|
+
* @param data - The exported inbox data containing timestamps to validate
|
|
147
|
+
* @throws {InvalidImportDataError} If either timestamp is not a valid date format
|
|
148
|
+
*/
|
|
149
|
+
private validateTimestamps;
|
|
150
|
+
/**
|
|
151
|
+
* Checks that an inbox with the given email address is not already tracked by this client.
|
|
152
|
+
* @private
|
|
153
|
+
* @param emailAddress - The email address to check
|
|
154
|
+
* @throws {InboxAlreadyExistsError} If an inbox with this email address already exists
|
|
155
|
+
*/
|
|
156
|
+
private checkInboxDoesNotExist;
|
|
157
|
+
/**
|
|
158
|
+
* Validates that the server public key in the exported data matches the current server's key.
|
|
159
|
+
* This prevents importing inboxes that were created for a different VaultSandbox server.
|
|
160
|
+
* @private
|
|
161
|
+
* @param serverSigPk - The server public key from the exported data
|
|
162
|
+
* @throws {InvalidImportDataError} If the server public keys don't match
|
|
163
|
+
*/
|
|
164
|
+
private validateServerPublicKey;
|
|
165
|
+
/**
|
|
166
|
+
* Decodes the cryptographic keys from base64 and validates their lengths.
|
|
167
|
+
* @private
|
|
168
|
+
* @param data - The exported inbox data containing base64-encoded keys
|
|
169
|
+
* @returns A keypair object with decoded keys and base64url-encoded public key
|
|
170
|
+
* @throws {InvalidImportDataError} If keys cannot be decoded or have invalid lengths
|
|
171
|
+
*/
|
|
172
|
+
private decodeAndValidateKeys;
|
|
173
|
+
/**
|
|
174
|
+
* Decodes a base64-encoded cryptographic key to a byte array.
|
|
175
|
+
* @private
|
|
176
|
+
* @param keyB64 - The base64-encoded key string
|
|
177
|
+
* @param keyType - The type of key (e.g., 'public', 'secret') for error messages
|
|
178
|
+
* @returns The decoded key as a Uint8Array
|
|
179
|
+
* @throws {InvalidImportDataError} If the base64 string is malformed
|
|
180
|
+
*/
|
|
181
|
+
private decodeBase64Key;
|
|
182
|
+
/**
|
|
183
|
+
* Validates that a cryptographic key has the expected byte length.
|
|
184
|
+
* @private
|
|
185
|
+
* @param key - The decoded key to validate
|
|
186
|
+
* @param expectedLength - The expected length in bytes
|
|
187
|
+
* @param keyType - The type of key (e.g., 'public', 'secret') for error messages
|
|
188
|
+
* @throws {InvalidImportDataError} If the key length doesn't match the expected length
|
|
189
|
+
*/
|
|
190
|
+
private validateKeyLength;
|
|
191
|
+
/**
|
|
192
|
+
* Constructs an InboxData object from exported data.
|
|
193
|
+
* @private
|
|
194
|
+
* @param data - The exported inbox data
|
|
195
|
+
* @returns An InboxData object ready for creating an Inbox instance
|
|
196
|
+
*/
|
|
197
|
+
private buildInboxData;
|
|
198
|
+
/**
|
|
199
|
+
* Creates a new Inbox instance, configures it with the delivery strategy, and adds it to tracking.
|
|
200
|
+
* @private
|
|
201
|
+
* @param inboxData - The inbox metadata
|
|
202
|
+
* @param keypair - The cryptographic keypair for the inbox
|
|
203
|
+
* @returns The newly created and tracked Inbox instance
|
|
204
|
+
*/
|
|
205
|
+
private createAndTrackInbox;
|
|
206
|
+
/**
|
|
207
|
+
* Exports an inbox to a JSON file.
|
|
208
|
+
*
|
|
209
|
+
* @param inboxOrEmail - Either an Inbox instance or an email address string
|
|
210
|
+
* @param filePath - The path where the file should be written
|
|
211
|
+
* @throws {InboxNotFoundError} If the inbox is not found in the client
|
|
212
|
+
* @example
|
|
213
|
+
* await client.exportInboxToFile(inbox, './inbox-backup.json');
|
|
214
|
+
*/
|
|
215
|
+
exportInboxToFile(inboxOrEmail: Inbox | string, filePath: string): Promise<void>;
|
|
216
|
+
/**
|
|
217
|
+
* Imports an inbox from a JSON file.
|
|
218
|
+
*
|
|
219
|
+
* @param filePath - The path to the exported inbox JSON file
|
|
220
|
+
* @returns A promise that resolves to the imported Inbox instance
|
|
221
|
+
* @throws {InvalidImportDataError} If the file cannot be read or parsed
|
|
222
|
+
* @throws {InboxAlreadyExistsError} If an inbox with this email already exists
|
|
223
|
+
* @example
|
|
224
|
+
* const importedInbox = await client.importInboxFromFile('./inbox-backup.json');
|
|
225
|
+
*/
|
|
226
|
+
importInboxFromFile(filePath: string): Promise<Inbox>;
|
|
227
|
+
/**
|
|
228
|
+
* Closes the client, terminates any active connections, and cleans up resources.
|
|
229
|
+
*/
|
|
230
|
+
close(): Promise<void>;
|
|
231
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VaultSandboxClient - Main entry point for the SDK
|
|
3
|
+
*/
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
6
|
+
import createDebug from 'debug';
|
|
7
|
+
import { ApiClient } from './http/api-client.js';
|
|
8
|
+
import { Inbox } from './inbox.js';
|
|
9
|
+
import { generateKeypair, PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, derivePublicKeyFromSecret } from './crypto/keypair.js';
|
|
10
|
+
import { toBase64Url, toBase64, fromBase64 } from './crypto/utils.js';
|
|
11
|
+
import { SSEStrategy } from './strategies/sse-strategy.js';
|
|
12
|
+
import { PollingStrategy } from './strategies/polling-strategy.js';
|
|
13
|
+
import { InboxNotFoundError, InboxAlreadyExistsError, InvalidImportDataError, StrategyError } from './types/index.js';
|
|
14
|
+
const debug = createDebug('vaultsandbox:client');
|
|
15
|
+
/**
|
|
16
|
+
* An event emitter for monitoring multiple inboxes simultaneously.
|
|
17
|
+
* @emits email - When a new email arrives in any of the monitored inboxes.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const monitor = client.monitorInboxes([inbox1, inbox2]);
|
|
21
|
+
* monitor.on('email', (inbox, email) => {
|
|
22
|
+
* console.log(`New email in ${inbox.emailAddress}: ${email.subject}`);
|
|
23
|
+
* });
|
|
24
|
+
* // To stop monitoring:
|
|
25
|
+
* monitor.unsubscribe();
|
|
26
|
+
*/
|
|
27
|
+
export class InboxMonitor extends EventEmitter {
|
|
28
|
+
subscriptions = [];
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
* Adds a subscription to the monitor.
|
|
32
|
+
* @param subscription - The subscription to add.
|
|
33
|
+
*/
|
|
34
|
+
addSubscription(subscription) {
|
|
35
|
+
this.subscriptions.push(subscription);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Unsubscribes from all monitored inboxes and cleans up resources.
|
|
39
|
+
*/
|
|
40
|
+
unsubscribe() {
|
|
41
|
+
this.subscriptions.forEach((sub) => sub.unsubscribe());
|
|
42
|
+
this.subscriptions = [];
|
|
43
|
+
this.removeAllListeners();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @internal
|
|
47
|
+
* Emits an 'email' event for a specific inbox.
|
|
48
|
+
* @param inbox - The inbox that received the email.
|
|
49
|
+
* @param email - The email that was received.
|
|
50
|
+
*/
|
|
51
|
+
emitEmail(inbox, email) {
|
|
52
|
+
this.emit('email', inbox, email);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The main client for interacting with the VaultSandbox API.
|
|
57
|
+
*
|
|
58
|
+
* This class provides methods for creating and managing inboxes,
|
|
59
|
+
* as well as for monitoring them for new emails.
|
|
60
|
+
*/
|
|
61
|
+
export class VaultSandboxClient {
|
|
62
|
+
apiClient;
|
|
63
|
+
config;
|
|
64
|
+
serverPublicKey = null;
|
|
65
|
+
inboxes = new Map();
|
|
66
|
+
strategy = null;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a new VaultSandboxClient instance.
|
|
69
|
+
* @param config - The client configuration.
|
|
70
|
+
*/
|
|
71
|
+
constructor(config) {
|
|
72
|
+
this.config = config;
|
|
73
|
+
this.apiClient = new ApiClient(config);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Initializes the client by fetching server info and creating a delivery strategy.
|
|
77
|
+
* This method is called automatically when needed and should not be called directly.
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
async ensureInitialized() {
|
|
81
|
+
if (this.serverPublicKey) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const serverInfo = await this.apiClient.getServerInfo();
|
|
85
|
+
this.serverPublicKey = serverInfo.serverSigPk;
|
|
86
|
+
// Create delivery strategy based on config
|
|
87
|
+
// Note: SSE for email events (/api/events) is always available
|
|
88
|
+
this.strategy = this.createStrategy();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates the appropriate delivery strategy based on configuration.
|
|
92
|
+
* SSE for email events (/api/events) is always available on the server.
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
createStrategy() {
|
|
96
|
+
const strategyType = this.config.strategy ?? 'auto';
|
|
97
|
+
// SSE strategy (default for 'auto' and 'sse')
|
|
98
|
+
if (strategyType === 'sse' || strategyType === 'auto') {
|
|
99
|
+
debug('Using SSE strategy for real-time delivery');
|
|
100
|
+
return new SSEStrategy(this.apiClient, {
|
|
101
|
+
url: this.config.url,
|
|
102
|
+
apiKey: this.config.apiKey,
|
|
103
|
+
reconnectInterval: this.config.sseReconnectInterval ?? 5000,
|
|
104
|
+
maxReconnectAttempts: this.config.sseMaxReconnectAttempts ?? 10,
|
|
105
|
+
backoffMultiplier: 2,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Polling strategy (explicit only)
|
|
109
|
+
debug('Using polling strategy');
|
|
110
|
+
return new PollingStrategy(this.apiClient, {
|
|
111
|
+
initialInterval: this.config.pollingInterval ?? 2000,
|
|
112
|
+
maxBackoff: 30000,
|
|
113
|
+
backoffMultiplier: 1.5,
|
|
114
|
+
jitterFactor: 0.3,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Creates a new, temporary email inbox.
|
|
119
|
+
*
|
|
120
|
+
* This method generates a new quantum-safe keypair, registers the inbox
|
|
121
|
+
* with the VaultSandbox server, and returns an `Inbox` instance.
|
|
122
|
+
*
|
|
123
|
+
* @param options - Optional parameters for inbox creation.
|
|
124
|
+
* @returns A promise that resolves to a new `Inbox` instance.
|
|
125
|
+
* @example
|
|
126
|
+
* const inbox = await client.createInbox({ ttl: 3600 }); // Inbox expires in 1 hour
|
|
127
|
+
*/
|
|
128
|
+
async createInbox(options = {}) {
|
|
129
|
+
await this.ensureInitialized();
|
|
130
|
+
// Generate keypair
|
|
131
|
+
const keypair = generateKeypair();
|
|
132
|
+
// Create inbox on server
|
|
133
|
+
const inboxData = await this.apiClient.createInbox(keypair.publicKeyB64, options.ttl, options.emailAddress);
|
|
134
|
+
// Create Inbox instance (use serverSigPk from response)
|
|
135
|
+
const inbox = new Inbox(inboxData, keypair, this.apiClient, inboxData.serverSigPk);
|
|
136
|
+
// Set delivery strategy
|
|
137
|
+
if (this.strategy) {
|
|
138
|
+
inbox.setStrategy(this.strategy);
|
|
139
|
+
}
|
|
140
|
+
// Track inbox
|
|
141
|
+
this.inboxes.set(inbox.emailAddress, inbox);
|
|
142
|
+
return inbox;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Deletes all inboxes associated with the current API key.
|
|
146
|
+
*
|
|
147
|
+
* @returns A promise that resolves to the number of inboxes deleted.
|
|
148
|
+
*/
|
|
149
|
+
async deleteAllInboxes() {
|
|
150
|
+
const result = await this.apiClient.deleteAllInboxes();
|
|
151
|
+
this.inboxes.clear();
|
|
152
|
+
return result.deleted;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Retrieves information about the VaultSandbox server.
|
|
156
|
+
*
|
|
157
|
+
* @returns A promise that resolves to the server information.
|
|
158
|
+
*/
|
|
159
|
+
async getServerInfo() {
|
|
160
|
+
return this.apiClient.getServerInfo();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Checks if the configured API key is valid.
|
|
164
|
+
*
|
|
165
|
+
* @returns A promise that resolves to `true` if the API key is valid, `false` otherwise.
|
|
166
|
+
*/
|
|
167
|
+
async checkKey() {
|
|
168
|
+
return this.apiClient.checkKey();
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Monitors multiple inboxes simultaneously for new emails.
|
|
172
|
+
*
|
|
173
|
+
* @param inboxes - An array of `Inbox` instances to monitor.
|
|
174
|
+
* @returns An `InboxMonitor` instance that emits 'email' events.
|
|
175
|
+
*/
|
|
176
|
+
monitorInboxes(inboxes) {
|
|
177
|
+
if (!this.strategy) {
|
|
178
|
+
throw new StrategyError('No delivery strategy available. Client not initialized.');
|
|
179
|
+
}
|
|
180
|
+
const monitor = new InboxMonitor();
|
|
181
|
+
// Subscribe to each inbox
|
|
182
|
+
for (const inbox of inboxes) {
|
|
183
|
+
const subscription = inbox.onNewEmail((email) => {
|
|
184
|
+
monitor.emitEmail(inbox, email);
|
|
185
|
+
});
|
|
186
|
+
monitor.addSubscription(subscription);
|
|
187
|
+
}
|
|
188
|
+
return monitor;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Exports an inbox's data for backup or sharing purposes.
|
|
192
|
+
*
|
|
193
|
+
* @param inboxOrEmail - Either an Inbox instance or an email address string
|
|
194
|
+
* @returns The exported inbox data containing all necessary information to import the inbox
|
|
195
|
+
* @throws {InboxNotFoundError} If the inbox is not found in the client
|
|
196
|
+
* @example
|
|
197
|
+
* const exportedData = client.exportInbox(inbox);
|
|
198
|
+
* // or
|
|
199
|
+
* const exportedData = client.exportInbox('test@example.com');
|
|
200
|
+
*/
|
|
201
|
+
exportInbox(inboxOrEmail) {
|
|
202
|
+
// Get the inbox instance
|
|
203
|
+
const emailAddress = typeof inboxOrEmail === 'string' ? inboxOrEmail : inboxOrEmail.emailAddress;
|
|
204
|
+
const inbox = this.inboxes.get(emailAddress);
|
|
205
|
+
if (!inbox) {
|
|
206
|
+
throw new InboxNotFoundError(`Inbox not found: ${emailAddress}`);
|
|
207
|
+
}
|
|
208
|
+
return inbox.export();
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Imports an inbox from exported data.
|
|
212
|
+
*
|
|
213
|
+
* @param data - The exported inbox data
|
|
214
|
+
* @returns A promise that resolves to the imported Inbox instance
|
|
215
|
+
* @throws {InvalidImportDataError} If the data is invalid or malformed
|
|
216
|
+
* @throws {InboxAlreadyExistsError} If an inbox with this email already exists
|
|
217
|
+
* @example
|
|
218
|
+
* const importedInbox = await client.importInbox(exportedData);
|
|
219
|
+
*/
|
|
220
|
+
async importInbox(data) {
|
|
221
|
+
this.validateRequiredFields(data);
|
|
222
|
+
this.ensurePublicKeyPresent(data);
|
|
223
|
+
this.validateTimestamps(data);
|
|
224
|
+
this.checkInboxDoesNotExist(data.emailAddress);
|
|
225
|
+
await this.ensureInitialized();
|
|
226
|
+
this.validateServerPublicKey(data.serverSigPk);
|
|
227
|
+
const keypair = this.decodeAndValidateKeys(data);
|
|
228
|
+
const inboxData = this.buildInboxData(data);
|
|
229
|
+
return this.createAndTrackInbox(inboxData, keypair);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Validates that all required fields are present and non-empty in the exported inbox data.
|
|
233
|
+
* @private
|
|
234
|
+
* @param data - The exported inbox data to validate
|
|
235
|
+
* @throws {InvalidImportDataError} If any required field is missing, not a string, or empty
|
|
236
|
+
*/
|
|
237
|
+
validateRequiredFields(data) {
|
|
238
|
+
const requiredFields = [
|
|
239
|
+
'emailAddress',
|
|
240
|
+
'expiresAt',
|
|
241
|
+
'inboxHash',
|
|
242
|
+
'serverSigPk',
|
|
243
|
+
'secretKeyB64',
|
|
244
|
+
'exportedAt',
|
|
245
|
+
];
|
|
246
|
+
for (const field of requiredFields) {
|
|
247
|
+
if (!data[field] || typeof data[field] !== 'string' || data[field].trim() === '') {
|
|
248
|
+
throw new InvalidImportDataError(`Missing or invalid field: ${field}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Ensures the public key is present in the exported data, deriving it from the secret key if necessary.
|
|
254
|
+
* In ML-KEM (Kyber), the public key is embedded in the secret key, so we can extract it if missing.
|
|
255
|
+
* @private
|
|
256
|
+
* @param data - The exported inbox data (will be mutated to add publicKeyB64 if missing)
|
|
257
|
+
* @throws {InvalidImportDataError} If the secret key is invalid or derivation fails
|
|
258
|
+
*/
|
|
259
|
+
ensurePublicKeyPresent(data) {
|
|
260
|
+
if (data.publicKeyB64) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const secretKeyBytes = fromBase64(data.secretKeyB64);
|
|
265
|
+
const publicKeyBytes = derivePublicKeyFromSecret(secretKeyBytes);
|
|
266
|
+
data.publicKeyB64 = toBase64(publicKeyBytes);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
270
|
+
throw new InvalidImportDataError(`Failed to derive public key from secret key: ${message}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Validates that the timestamp fields contain valid ISO 8601 date strings.
|
|
275
|
+
* @private
|
|
276
|
+
* @param data - The exported inbox data containing timestamps to validate
|
|
277
|
+
* @throws {InvalidImportDataError} If either timestamp is not a valid date format
|
|
278
|
+
*/
|
|
279
|
+
validateTimestamps(data) {
|
|
280
|
+
try {
|
|
281
|
+
new Date(data.expiresAt).toISOString();
|
|
282
|
+
new Date(data.exportedAt).toISOString();
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
throw new InvalidImportDataError('Invalid timestamp format');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Checks that an inbox with the given email address is not already tracked by this client.
|
|
290
|
+
* @private
|
|
291
|
+
* @param emailAddress - The email address to check
|
|
292
|
+
* @throws {InboxAlreadyExistsError} If an inbox with this email address already exists
|
|
293
|
+
*/
|
|
294
|
+
checkInboxDoesNotExist(emailAddress) {
|
|
295
|
+
if (this.inboxes.has(emailAddress)) {
|
|
296
|
+
throw new InboxAlreadyExistsError(`Inbox already exists: ${emailAddress}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Validates that the server public key in the exported data matches the current server's key.
|
|
301
|
+
* This prevents importing inboxes that were created for a different VaultSandbox server.
|
|
302
|
+
* @private
|
|
303
|
+
* @param serverSigPk - The server public key from the exported data
|
|
304
|
+
* @throws {InvalidImportDataError} If the server public keys don't match
|
|
305
|
+
*/
|
|
306
|
+
validateServerPublicKey(serverSigPk) {
|
|
307
|
+
if (serverSigPk !== this.serverPublicKey) {
|
|
308
|
+
throw new InvalidImportDataError('Server public key mismatch. This inbox was created for a different server.');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Decodes the cryptographic keys from base64 and validates their lengths.
|
|
313
|
+
* @private
|
|
314
|
+
* @param data - The exported inbox data containing base64-encoded keys
|
|
315
|
+
* @returns A keypair object with decoded keys and base64url-encoded public key
|
|
316
|
+
* @throws {InvalidImportDataError} If keys cannot be decoded or have invalid lengths
|
|
317
|
+
*/
|
|
318
|
+
decodeAndValidateKeys(data) {
|
|
319
|
+
const publicKey = this.decodeBase64Key(data.publicKeyB64, 'public');
|
|
320
|
+
const secretKey = this.decodeBase64Key(data.secretKeyB64, 'secret');
|
|
321
|
+
this.validateKeyLength(publicKey, PUBLIC_KEY_SIZE, 'public');
|
|
322
|
+
this.validateKeyLength(secretKey, SECRET_KEY_SIZE, 'secret');
|
|
323
|
+
return {
|
|
324
|
+
publicKey,
|
|
325
|
+
secretKey,
|
|
326
|
+
publicKeyB64: toBase64Url(publicKey),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Decodes a base64-encoded cryptographic key to a byte array.
|
|
331
|
+
* @private
|
|
332
|
+
* @param keyB64 - The base64-encoded key string
|
|
333
|
+
* @param keyType - The type of key (e.g., 'public', 'secret') for error messages
|
|
334
|
+
* @returns The decoded key as a Uint8Array
|
|
335
|
+
* @throws {InvalidImportDataError} If the base64 string is malformed
|
|
336
|
+
*/
|
|
337
|
+
decodeBase64Key(keyB64, keyType) {
|
|
338
|
+
try {
|
|
339
|
+
return fromBase64(keyB64);
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
throw new InvalidImportDataError(`Invalid base64 encoding in ${keyType} key`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Validates that a cryptographic key has the expected byte length.
|
|
347
|
+
* @private
|
|
348
|
+
* @param key - The decoded key to validate
|
|
349
|
+
* @param expectedLength - The expected length in bytes
|
|
350
|
+
* @param keyType - The type of key (e.g., 'public', 'secret') for error messages
|
|
351
|
+
* @throws {InvalidImportDataError} If the key length doesn't match the expected length
|
|
352
|
+
*/
|
|
353
|
+
validateKeyLength(key, expectedLength, keyType) {
|
|
354
|
+
if (key.length !== expectedLength) {
|
|
355
|
+
throw new InvalidImportDataError(`Invalid ${keyType} key length: expected ${expectedLength}, got ${key.length}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Constructs an InboxData object from exported data.
|
|
360
|
+
* @private
|
|
361
|
+
* @param data - The exported inbox data
|
|
362
|
+
* @returns An InboxData object ready for creating an Inbox instance
|
|
363
|
+
*/
|
|
364
|
+
buildInboxData(data) {
|
|
365
|
+
return {
|
|
366
|
+
emailAddress: data.emailAddress,
|
|
367
|
+
expiresAt: data.expiresAt,
|
|
368
|
+
inboxHash: data.inboxHash,
|
|
369
|
+
serverSigPk: data.serverSigPk,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Creates a new Inbox instance, configures it with the delivery strategy, and adds it to tracking.
|
|
374
|
+
* @private
|
|
375
|
+
* @param inboxData - The inbox metadata
|
|
376
|
+
* @param keypair - The cryptographic keypair for the inbox
|
|
377
|
+
* @returns The newly created and tracked Inbox instance
|
|
378
|
+
*/
|
|
379
|
+
createAndTrackInbox(inboxData, keypair) {
|
|
380
|
+
const inbox = new Inbox(inboxData, keypair, this.apiClient, inboxData.serverSigPk);
|
|
381
|
+
if (this.strategy) {
|
|
382
|
+
inbox.setStrategy(this.strategy);
|
|
383
|
+
}
|
|
384
|
+
this.inboxes.set(inbox.emailAddress, inbox);
|
|
385
|
+
return inbox;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Exports an inbox to a JSON file.
|
|
389
|
+
*
|
|
390
|
+
* @param inboxOrEmail - Either an Inbox instance or an email address string
|
|
391
|
+
* @param filePath - The path where the file should be written
|
|
392
|
+
* @throws {InboxNotFoundError} If the inbox is not found in the client
|
|
393
|
+
* @example
|
|
394
|
+
* await client.exportInboxToFile(inbox, './inbox-backup.json');
|
|
395
|
+
*/
|
|
396
|
+
async exportInboxToFile(inboxOrEmail, filePath) {
|
|
397
|
+
const data = this.exportInbox(inboxOrEmail);
|
|
398
|
+
const json = JSON.stringify(data, null, 2);
|
|
399
|
+
await writeFile(filePath, json, 'utf-8');
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Imports an inbox from a JSON file.
|
|
403
|
+
*
|
|
404
|
+
* @param filePath - The path to the exported inbox JSON file
|
|
405
|
+
* @returns A promise that resolves to the imported Inbox instance
|
|
406
|
+
* @throws {InvalidImportDataError} If the file cannot be read or parsed
|
|
407
|
+
* @throws {InboxAlreadyExistsError} If an inbox with this email already exists
|
|
408
|
+
* @example
|
|
409
|
+
* const importedInbox = await client.importInboxFromFile('./inbox-backup.json');
|
|
410
|
+
*/
|
|
411
|
+
async importInboxFromFile(filePath) {
|
|
412
|
+
let data;
|
|
413
|
+
try {
|
|
414
|
+
const fileContents = await readFile(filePath, 'utf-8');
|
|
415
|
+
data = JSON.parse(fileContents);
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
throw new InvalidImportDataError(`Failed to read or parse file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
419
|
+
}
|
|
420
|
+
return await this.importInbox(data);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Closes the client, terminates any active connections, and cleans up resources.
|
|
424
|
+
*/
|
|
425
|
+
async close() {
|
|
426
|
+
if (this.strategy) {
|
|
427
|
+
this.strategy.close();
|
|
428
|
+
}
|
|
429
|
+
this.inboxes.clear();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|