@p47h/vault-js 0.9.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.
@@ -0,0 +1,598 @@
1
+ /**
2
+ * Puerto para la persistencia de datos (Storage Port).
3
+ * Implementado por adaptadores de infraestructura (IndexedDB, InMemory, etc).
4
+ */
5
+ interface IStorage {
6
+ /**
7
+ * Guarda un blob cifrado asociado a un ID (DID).
8
+ */
9
+ save(key: string, data: EncryptedVaultBlob): Promise<void>;
10
+ /**
11
+ * Recupera un blob cifrado por su ID.
12
+ * Retorna null si no existe.
13
+ */
14
+ get(key: string): Promise<EncryptedVaultBlob | null>;
15
+ /**
16
+ * Elimina un blob del almacenamiento.
17
+ */
18
+ remove(key: string): Promise<void>;
19
+ /**
20
+ * Lista todos los IDs (DIDs) almacenados.
21
+ */
22
+ listKeys(): Promise<string[]>;
23
+ /**
24
+ * Limpia todo el almacenamiento (Cuidado: destructivo).
25
+ */
26
+ clear(): Promise<void>;
27
+ }
28
+
29
+ /**
30
+ * @fileoverview P47H Vault Domain Types
31
+ *
32
+ * Core entities and value objects for the vault system.
33
+ *
34
+ * @module domain/types
35
+ * @license AGPL-3.0 OR Commercial
36
+ */
37
+
38
+ /**
39
+ * Configuration options for initializing the P47H Vault.
40
+ */
41
+ interface VaultConfig {
42
+ /**
43
+ * Path to the WASM binary file.
44
+ * @default '/wasm/p47h_wasm_core_bg.wasm'
45
+ */
46
+ wasmPath?: string;
47
+ /**
48
+ * Custom storage adapter implementation.
49
+ * If not provided, uses IndexedDB by default.
50
+ */
51
+ storage?: IStorage;
52
+ /**
53
+ * Commercial license key (format: PAYLOAD_B64.SIGNATURE_B64).
54
+ * If not provided or invalid, the SDK operates in AGPLv3 "nagware" mode.
55
+ * Get your commercial license at: https://p47h.com/licensing
56
+ */
57
+ licenseKey?: string;
58
+ }
59
+ /**
60
+ * Public information about a loaded cryptographic identity.
61
+ */
62
+ interface IdentityInfo {
63
+ /** Decentralized Identifier (DID) for this identity */
64
+ did: string;
65
+ /** Raw Ed25519 public key bytes (32 bytes) */
66
+ publicKey: Uint8Array;
67
+ }
68
+ /**
69
+ * Result of a successful registration including the recovery code.
70
+ */
71
+ interface RegistrationResult {
72
+ /** The newly created Decentralized Identifier */
73
+ did: string;
74
+ /**
75
+ * Emergency recovery code - MUST be stored securely by the user.
76
+ * Format: RK-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX
77
+ * This is the ONLY way to recover the vault if the password is lost.
78
+ */
79
+ recoveryCode: string;
80
+ }
81
+ /**
82
+ * Encrypted vault blob as persisted to storage.
83
+ * Contains both password-encrypted and recovery-encrypted copies.
84
+ */
85
+ interface EncryptedVaultBlob {
86
+ /** Schema version for future migrations */
87
+ version: number;
88
+ /** DID associated with this vault */
89
+ did: string;
90
+ /** Salt used for password key derivation (Base64) */
91
+ salt: string;
92
+ /** Vault data encrypted with user password (Base64) */
93
+ wrappedData: string;
94
+ /**
95
+ * Vault data encrypted with recovery code (Base64).
96
+ * Used for password recovery without server involvement.
97
+ * @since v1.1
98
+ */
99
+ recoveryBlob?: string;
100
+ /** Timestamp of last update (Unix ms) */
101
+ updatedAt: number;
102
+ }
103
+ /**
104
+ * Options for account recovery using the emergency recovery code.
105
+ */
106
+ interface RecoveryOptions {
107
+ /** The recovery code provided during registration */
108
+ recoveryCode: string;
109
+ /** The new password to set */
110
+ newPassword: string;
111
+ /** Optional specific DID to recover (uses first found if omitted) */
112
+ did?: string;
113
+ /**
114
+ * If true, generates a new recovery code after successful recovery.
115
+ * Recommended for security.
116
+ * @default false
117
+ */
118
+ rotateRecoveryCode?: boolean;
119
+ }
120
+ /**
121
+ * Result of a successful account recovery.
122
+ */
123
+ interface RecoveryResult {
124
+ /** The recovered identity's DID */
125
+ did: string;
126
+ /**
127
+ * New recovery code (only present if rotateRecoveryCode was true).
128
+ * If present, the old recovery code is invalidated.
129
+ */
130
+ newRecoveryCode?: string;
131
+ }
132
+
133
+ /**
134
+ * @fileoverview P47H Vault Interface - Core Contract
135
+ *
136
+ * This interface defines the public API for the P47H Vault.
137
+ * All vault implementations must conform to this contract.
138
+ *
139
+ * @module domain/IVault
140
+ * @license AGPL-3.0 OR Commercial
141
+ */
142
+
143
+ /**
144
+ * Core interface for the P47H Vault.
145
+ *
146
+ * Defines all cryptographic and identity management operations
147
+ * that a vault implementation must provide.
148
+ */
149
+ interface IVault {
150
+ /**
151
+ * Initializes the cryptographic environment (WASM module).
152
+ * Must be called before any other operation.
153
+ *
154
+ * @param config - Optional configuration object
155
+ * @throws {InitializationError} If WASM loading fails
156
+ */
157
+ init(config?: VaultConfig): Promise<void>;
158
+ /**
159
+ * Creates a new cryptographic identity (DID) and persists it encrypted.
160
+ * Generates Ed25519 keys and encrypts them with the provided password.
161
+ *
162
+ * **IMPORTANT**: The returned `recoveryCode` is the ONLY way to recover
163
+ * the vault if the password is lost. It must be stored securely by the user.
164
+ *
165
+ * @param password - Master password for key derivation
166
+ * @returns The generated DID and emergency recovery code
167
+ * @throws {InitializationError} If vault not initialized
168
+ * @throws {CryptoError} If key generation fails
169
+ */
170
+ register(password: string): Promise<RegistrationResult>;
171
+ /**
172
+ * Unlocks an existing identity with the master password.
173
+ * Decrypts the private keys and establishes an authenticated session.
174
+ *
175
+ * @param password - Master password
176
+ * @param did - Optional specific DID to unlock (uses first found if omitted)
177
+ * @throws {AuthenticationError} If password is wrong or identity not found
178
+ * @throws {VaultError} If vault data is corrupted
179
+ */
180
+ login(password: string, did?: string): Promise<IdentityInfo>;
181
+ /**
182
+ * Recovers account access using the emergency recovery code.
183
+ *
184
+ * Use this when the user has forgotten their password but has their
185
+ * recovery code. This will:
186
+ * 1. Decrypt the vault using the recovery code
187
+ * 2. Re-encrypt with the new password
188
+ * 3. Optionally generate a new recovery code
189
+ *
190
+ * @param options - Recovery options including recovery code and new password
191
+ * @returns Recovery result with optional new recovery code
192
+ * @throws {AuthenticationError} If recovery code is invalid
193
+ * @throws {VaultError} If recovery is not available for this identity
194
+ */
195
+ recoverAccount(options: RecoveryOptions): Promise<RecoveryResult>;
196
+ /**
197
+ * Locks the vault and clears all sensitive data from memory.
198
+ * Frees WASM memory, session keys, and cached secrets.
199
+ */
200
+ lock(): void;
201
+ /**
202
+ * Checks if the vault is currently unlocked.
203
+ *
204
+ * @returns True if an identity is loaded and session is active
205
+ */
206
+ isAuthenticated(): boolean;
207
+ /**
208
+ * Gets the DID of the currently authenticated identity.
209
+ *
210
+ * @returns The current Decentralized Identifier
211
+ * @throws {NotAuthenticatedError} If vault is locked
212
+ */
213
+ getDid(): string;
214
+ /**
215
+ * Returns a list of DIDs (identities) that exist in local storage.
216
+ * Useful for UI to decide whether to show Login or Register.
217
+ *
218
+ * @returns Array of DID strings stored locally
219
+ * @throws {InitializationError} If vault not initialized
220
+ */
221
+ getStoredIdentities(): Promise<string[]>;
222
+ /**
223
+ * Signs data with the current identity's private key (Ed25519).
224
+ *
225
+ * @param data - Data to sign
226
+ * @returns 64-byte Ed25519 signature
227
+ * @throws {NotAuthenticatedError} If vault is locked
228
+ */
229
+ sign(data: Uint8Array): Promise<Uint8Array>;
230
+ /**
231
+ * Saves an encrypted secret to the vault.
232
+ *
233
+ * @param key - Unique identifier for the secret
234
+ * @param secret - The secret value to store
235
+ * @throws {NotAuthenticatedError} If vault is locked
236
+ * @throws {VaultError} If storage operation fails
237
+ */
238
+ saveSecret(key: string, secret: string): Promise<void>;
239
+ /**
240
+ * Retrieves a decrypted secret from the vault.
241
+ *
242
+ * @param key - The secret identifier
243
+ * @returns The decrypted value, or null if not found
244
+ * @throws {NotAuthenticatedError} If vault is locked
245
+ */
246
+ getSecret(key: string): Promise<string | null>;
247
+ /**
248
+ * Checks if running with a valid commercial license.
249
+ * When false, the SDK operates in AGPLv3 "nagware" mode.
250
+ *
251
+ * @returns True if commercial license is active
252
+ */
253
+ isCommercialLicense(): boolean;
254
+ /**
255
+ * Gets the licensee name if commercial license is active.
256
+ *
257
+ * @returns The licensed entity name, or null if AGPLv3 mode
258
+ */
259
+ getLicensee(): string | null;
260
+ /**
261
+ * Disposes of the vault and releases all resources.
262
+ * After calling dispose(), the vault cannot be used again.
263
+ */
264
+ dispose(): void;
265
+ }
266
+
267
+ /**
268
+ * @fileoverview P47H Vault Service - Core Identity and Secret Management
269
+ *
270
+ * This is the main facade for the P47H Vault SDK. It provides a high-level,
271
+ * type-safe API for:
272
+ * - Creating and managing cryptographic identities (DIDs)
273
+ * - Securely storing and retrieving encrypted secrets
274
+ * - Emergency recovery using Recovery Codes (PUK)
275
+ * - Session management with automatic memory cleanup
276
+ *
277
+ * @module logic/VaultService
278
+ * @license AGPL-3.0 OR Commercial
279
+ */
280
+
281
+ /**
282
+ * P47H Vault Service - Secure local-first identity and secret management.
283
+ *
284
+ * This service provides:
285
+ * - **Identity Management**: Create and restore Ed25519 cryptographic identities
286
+ * - **Secret Storage**: Encrypt and persist arbitrary secrets locally
287
+ * - **Emergency Recovery**: Recover access using Recovery Codes (like 1Password)
288
+ * - **Memory Safety**: Automatic cleanup of sensitive data from RAM
289
+ * - **Commercial Licensing**: Optional license key for proprietary use
290
+ *
291
+ * ## Security Model
292
+ *
293
+ * - All cryptographic operations are performed in WASM (Rust)
294
+ * - Private keys never leave WASM memory as plaintext
295
+ * - Session keys are derived using Argon2id (OWASP recommended)
296
+ * - Data is encrypted using XChaCha20-Poly1305 (AEAD)
297
+ * - Dual encryption: password + recovery code for emergency access
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * import { P47hVault } from '@p47h/vault-js';
302
+ *
303
+ * const vault = new P47hVault();
304
+ * await vault.init({ wasmPath: '/wasm/p47h_wasm_core_bg.wasm' });
305
+ *
306
+ * // Create new identity - SAVE THE RECOVERY CODE!
307
+ * const { did, recoveryCode } = await vault.register('my-secure-password');
308
+ * console.log('Created:', did);
309
+ * console.log('⚠️ Save this recovery code:', recoveryCode);
310
+ *
311
+ * // If password is forgotten:
312
+ * await vault.recoverAccount({
313
+ * recoveryCode: 'RK-A1B2C3D4...',
314
+ * newPassword: 'new-secure-password'
315
+ * });
316
+ * ```
317
+ *
318
+ * @implements {IVault}
319
+ */
320
+ declare class VaultService implements IVault {
321
+ private readonly _storage;
322
+ private _crypto;
323
+ private _client;
324
+ private _sessionKey;
325
+ private _currentDid;
326
+ private _secretsCache;
327
+ private _passwordCache;
328
+ private _isInitialized;
329
+ private _isDisposed;
330
+ /**
331
+ * Creates a new VaultService instance with default adapters.
332
+ *
333
+ * @param storage - Optional custom storage adapter (defaults to IndexedDB)
334
+ */
335
+ constructor(storage?: IStorage);
336
+ /**
337
+ * Initializes the vault with WASM module and optional configuration.
338
+ *
339
+ * This method must be called before any other operations. It:
340
+ * 1. Loads the WASM cryptographic core
341
+ * 2. Verifies the commercial license (if provided)
342
+ * 3. Prepares the storage adapter
343
+ *
344
+ * @param config - Optional configuration object
345
+ * @returns Promise that resolves when initialization is complete
346
+ * @throws {InitializationError} If WASM loading fails
347
+ */
348
+ init(config?: VaultConfig): Promise<void>;
349
+ /**
350
+ * Checks if the vault is running with a valid commercial license.
351
+ *
352
+ * @returns True if commercial license is active, false if AGPLv3 mode
353
+ */
354
+ isCommercialLicense(): boolean;
355
+ /**
356
+ * Gets the name of the licensed entity.
357
+ *
358
+ * @returns The licensee name, or null if running in AGPLv3 mode
359
+ */
360
+ getLicensee(): string | null;
361
+ /**
362
+ * Creates a new cryptographic identity and persists it encrypted.
363
+ *
364
+ * This method:
365
+ * 1. Generates a new Ed25519 keypair in WASM
366
+ * 2. Derives a session key from the password using Argon2id
367
+ * 3. Generates a high-entropy Recovery Code
368
+ * 4. Encrypts the vault twice: once with password, once with recovery code
369
+ * 5. Persists both encrypted copies to storage
370
+ * 6. Establishes an authenticated session
371
+ *
372
+ * **CRITICAL**: The returned `recoveryCode` is the ONLY way to recover
373
+ * the vault if the password is lost. It must be stored securely by the user
374
+ * (e.g., printed and stored in a safe, password manager, etc.).
375
+ *
376
+ * @param password - Master password for key derivation (min 8 chars recommended)
377
+ * @returns Promise resolving to the DID and recovery code
378
+ * @throws {InitializationError} If vault not initialized
379
+ * @throws {CryptoError} If key generation fails
380
+ * @throws {StorageError} If persistence fails
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * const { did, recoveryCode } = await vault.register('my-secure-password');
385
+ * console.log('New identity:', did);
386
+ * console.log('⚠️ SAVE THIS CODE:', recoveryCode);
387
+ * // recoveryCode format: RK-A1B2C3D4-E5F6G7H8-I9J0K1L2-M3N4O5P6
388
+ * ```
389
+ */
390
+ register(password: string): Promise<RegistrationResult>;
391
+ /**
392
+ * Unlocks an existing identity with the master password.
393
+ *
394
+ * This method:
395
+ * 1. Retrieves the encrypted identity from storage
396
+ * 2. Decrypts and validates the vault data
397
+ * 3. Restores the Ed25519 keypair in WASM
398
+ * 4. Establishes an authenticated session
399
+ *
400
+ * @param password - Master password used during registration
401
+ * @param did - Optional specific DID to unlock (uses first found if omitted)
402
+ * @returns Promise resolving to the identity info
403
+ * @throws {InitializationError} If vault not initialized
404
+ * @throws {AuthenticationError} If password is wrong or identity not found
405
+ * @throws {VaultError} If vault data is corrupted
406
+ *
407
+ * @example
408
+ * ```typescript
409
+ * try {
410
+ * const identity = await vault.login('my-password');
411
+ * console.log('Unlocked:', identity.did);
412
+ * } catch (e) {
413
+ * if (e instanceof AuthenticationError) {
414
+ * console.error('Wrong password - use recovery code?');
415
+ * }
416
+ * }
417
+ * ```
418
+ */
419
+ login(password: string, did?: string): Promise<IdentityInfo>;
420
+ /**
421
+ * Recovers account access using the emergency recovery code.
422
+ *
423
+ * Use this when the user has forgotten their password but has their
424
+ * Recovery Code (Emergency Kit). This will:
425
+ * 1. Decrypt the vault using the recovery code
426
+ * 2. Re-encrypt with the new password
427
+ * 3. Optionally generate a new recovery code (recommended)
428
+ * 4. Auto-login with the new credentials
429
+ *
430
+ * @param options - Recovery options including recovery code and new password
431
+ * @returns Recovery result with optional new recovery code
432
+ * @throws {AuthenticationError} If recovery code is invalid
433
+ * @throws {VaultError} If recovery is not available for this identity
434
+ *
435
+ * @example
436
+ * ```typescript
437
+ * // Basic recovery
438
+ * const result = await vault.recoverAccount({
439
+ * recoveryCode: 'RK-A1B2C3D4-E5F6G7H8-I9J0K1L2-M3N4O5P6',
440
+ * newPassword: 'my-new-secure-password'
441
+ * });
442
+ *
443
+ * // Recovery with code rotation (recommended)
444
+ * const result = await vault.recoverAccount({
445
+ * recoveryCode: 'RK-A1B2C3D4-...',
446
+ * newPassword: 'my-new-secure-password',
447
+ * rotateRecoveryCode: true
448
+ * });
449
+ * console.log('⚠️ NEW recovery code:', result.newRecoveryCode);
450
+ * ```
451
+ */
452
+ recoverAccount(options: RecoveryOptions): Promise<RecoveryResult>;
453
+ /**
454
+ * Locks the vault and clears all sensitive data from memory.
455
+ *
456
+ * This method:
457
+ * 1. Frees WASM memory holding the private key
458
+ * 2. Clears the session key from JavaScript heap
459
+ * 3. Clears the password cache
460
+ * 4. Clears the secrets cache
461
+ *
462
+ * Always call this when the user logs out or the app is backgrounded.
463
+ */
464
+ lock(): void;
465
+ /**
466
+ * Checks if the vault is currently unlocked with an active session.
467
+ *
468
+ * @returns True if an identity is loaded and session is active
469
+ */
470
+ isAuthenticated(): boolean;
471
+ /**
472
+ * Gets the DID of the currently authenticated identity.
473
+ *
474
+ * @returns The current Decentralized Identifier
475
+ * @throws {NotAuthenticatedError} If vault is locked
476
+ */
477
+ getDid(): string;
478
+ /**
479
+ * Returns a list of DIDs (identities) that exist in local storage.
480
+ * Useful for UI to decide whether to show Login or Register.
481
+ *
482
+ * @returns Promise resolving to array of DID strings
483
+ * @throws {InitializationError} If vault not initialized
484
+ */
485
+ getStoredIdentities(): Promise<string[]>;
486
+ /**
487
+ * Signs arbitrary data with the current identity's private key.
488
+ *
489
+ * Uses Ed25519 signatures. The private key never leaves WASM memory.
490
+ *
491
+ * @param data - Data to sign
492
+ * @returns Promise resolving to the 64-byte Ed25519 signature
493
+ * @throws {NotAuthenticatedError} If vault is locked
494
+ */
495
+ sign(data: Uint8Array): Promise<Uint8Array>;
496
+ /**
497
+ * Saves an encrypted secret to the vault.
498
+ *
499
+ * The secret is:
500
+ * 1. Stored in the in-memory cache
501
+ * 2. Re-encrypted with the vault's master key
502
+ * 3. Persisted to storage (both password and recovery copies)
503
+ *
504
+ * @param key - Unique identifier for the secret
505
+ * @param secret - The secret value to store
506
+ * @throws {NotAuthenticatedError} If vault is locked
507
+ * @throws {VaultError} If storage operation fails
508
+ */
509
+ saveSecret(key: string, secret: string): Promise<void>;
510
+ /**
511
+ * Retrieves a decrypted secret from the vault.
512
+ *
513
+ * @param key - The secret identifier
514
+ * @returns The decrypted secret value, or null if not found
515
+ * @throws {NotAuthenticatedError} If vault is locked
516
+ */
517
+ getSecret(key: string): Promise<string | null>;
518
+ /**
519
+ * Disposes of the vault and releases all resources.
520
+ *
521
+ * After calling dispose():
522
+ * - All sensitive data is cleared from memory
523
+ * - The vault cannot be used again
524
+ * - A new VaultService instance must be created
525
+ */
526
+ dispose(): void;
527
+ /**
528
+ * Generates a high-entropy recovery code.
529
+ * Format: RK-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX (32 hex chars)
530
+ */
531
+ private generateRecoveryCode;
532
+ private ensureNotDisposed;
533
+ private ensureInitialized;
534
+ private ensureAuthenticated;
535
+ private setSession;
536
+ private toBase64;
537
+ private fromBase64;
538
+ }
539
+
540
+ /**
541
+ * License verification status returned after WASM initialization.
542
+ */
543
+ interface LicenseStatus {
544
+ /** True if a valid commercial license was verified */
545
+ readonly isCommercial: boolean;
546
+ /** The licensed entity name, or null if AGPLv3 mode */
547
+ readonly licensee: string | null;
548
+ }
549
+
550
+ /**
551
+ * Domain error definitions for P47H Vault JS.
552
+ * Basis for handling typed exceptions.
553
+ */
554
+ declare class VaultError extends Error {
555
+ code: string;
556
+ cause?: unknown | undefined;
557
+ constructor(message: string, code: string, cause?: unknown | undefined);
558
+ }
559
+ declare class InitializationError extends VaultError {
560
+ constructor(message: string, cause?: unknown);
561
+ }
562
+ declare class AuthenticationError extends VaultError {
563
+ constructor(message?: string);
564
+ }
565
+ declare class NotAuthenticatedError extends VaultError {
566
+ constructor();
567
+ }
568
+ declare class StorageError extends VaultError {
569
+ constructor(message: string, cause?: unknown);
570
+ }
571
+ declare class CryptoError extends VaultError {
572
+ constructor(message: string, cause?: unknown);
573
+ }
574
+
575
+ declare class BrowserStorage implements IStorage {
576
+ private readonly dbName;
577
+ private readonly storeName;
578
+ private readonly version;
579
+ /**
580
+ * Promisify IndexedDB request helper
581
+ */
582
+ private request;
583
+ /**
584
+ * Open database connection with migration logic
585
+ */
586
+ private getDB;
587
+ /**
588
+ * Execute a transaction
589
+ */
590
+ private withStore;
591
+ save(key: string, data: EncryptedVaultBlob): Promise<void>;
592
+ get(key: string): Promise<EncryptedVaultBlob | null>;
593
+ remove(key: string): Promise<void>;
594
+ listKeys(): Promise<string[]>;
595
+ clear(): Promise<void>;
596
+ }
597
+
598
+ export { AuthenticationError, BrowserStorage, CryptoError, type EncryptedVaultBlob, type IStorage, type IVault, type IdentityInfo, InitializationError, type LicenseStatus, NotAuthenticatedError, VaultService as P47hVault, type RecoveryOptions, type RecoveryResult, type RegistrationResult, StorageError, type VaultConfig, VaultError };
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * P47H Vault JS - Secure local-first cryptographic storage
3
+ * @license AGPL-3.0 OR Commercial (https://p47h.com/licensing)
4
+ */
5
+ var a=class extends Error{constructor(r,e,i){super(r);this.code=e;this.cause=i;this.name="VaultError";}},m=class extends a{constructor(t,r){super(t,"VAULT_INIT_ERROR",r),this.name="InitializationError";}},u=class extends a{constructor(t="Invalid password or corrupted vault"){super(t,"AUTH_FAILED"),this.name="AuthenticationError";}},g=class extends a{constructor(){super("Vault is locked. Unlock it first.","VAULT_LOCKED"),this.name="NotAuthenticatedError";}},d=class extends a{constructor(t,r){super(t,"STORAGE_ERROR",r),this.name="StorageError";}},h=class extends a{constructor(t,r){super(t,"CRYPTO_ERROR",r),this.name="CryptoError";}};var v=class{constructor(t="/wasm/p47h_wasm_core_bg.wasm"){this.module=null;this._licenseStatus={isCommercial:false,licensee:null};this._isInitialized=false;this._wasmPath=t;}async init(t){if(this._isInitialized&&this.module)return this._licenseStatus;try{let r=await this.loadWasmModule();return await this.initializeWasmInstance(r),this.initializeLicenseSession(r,t),this.module=r,this._isInitialized=!0,this._licenseStatus}catch(r){throw new m("Failed to load P47H WASM core",r)}}async loadWasmModule(){let t=await fetch("/wasm/p47h_wasm_core.js");if(!t.ok)throw new Error(`Failed to load WASM module: HTTP ${t.status}. Ensure p47h_wasm_core.js is available at /wasm/p47h_wasm_core.js`);let r=await t.text(),e=new Blob([r],{type:"application/javascript"}),i=URL.createObjectURL(e);try{return await import(i)}finally{URL.revokeObjectURL(i);}}async initializeWasmInstance(t){try{await t.default({module_or_path:this._wasmPath});}catch{await t.default(this._wasmPath);}typeof t.init=="function"&&t.init();}initializeLicenseSession(t,r){if(typeof t.init_session!="function"){this._licenseStatus={isCommercial:false,licensee:null};return}let e=t.init_session(r||null),i=null;e&&typeof t.get_licensee=="function"&&(i=t.get_licensee()??null),this._licenseStatus=Object.freeze({isCommercial:e,licensee:i});}getModule(){if(!this.module||!this._isInitialized)throw new m("WasmCryptoAdapter not initialized. Call init() first.");return this.module}deriveSessionKey(t,r){try{return this.getModule().VaultCrypto.derive_session_key(t,r)}catch(e){throw new h("Failed to derive session key",e)}}encryptVault(t,r){try{return this.getModule().VaultCrypto.encrypt_vault(t,r)}catch(e){throw new h("Vault encryption failed",e)}}decryptVault(t,r){try{return this.getModule().VaultCrypto.decrypt_vault(t,r)}catch(e){throw new h("Vault decryption failed: Invalid password or corrupted data",e)}}createIdentity(){try{let t=this.getModule();return new t.P47hClient}catch(t){throw new h("Failed to generate new identity",t)}}restoreIdentity(t,r){try{return this.getModule().P47hClient.from_wrapped_secret(t,r)}catch(e){throw new h("Failed to restore identity from wrapped secret",e)}}getRandomValues(t){return crypto.getRandomValues(new Uint8Array(t))}getLicenseStatus(){return this._licenseStatus}isCommercial(){return this._licenseStatus.isCommercial}isInitialized(){return this._isInitialized}};var I=class{constructor(){this.dbName="p47h-vault";this.storeName="keystores";this.version=2;}request(t){return new Promise((r,e)=>{t.onsuccess=()=>r(t.result),t.onerror=()=>e(t.error);})}async getDB(){return new Promise((t,r)=>{if(typeof indexedDB>"u")return r(new d("IndexedDB not supported in this environment"));let e=indexedDB.open(this.dbName,this.version);e.onerror=()=>r(new d("Failed to open database",e.error)),e.onsuccess=()=>t(e.result),e.onupgradeneeded=i=>{let n=e.result;i.oldVersion<2&&(n.objectStoreNames.contains(this.storeName)&&n.deleteObjectStore(this.storeName),n.createObjectStore(this.storeName,{keyPath:"did"}));};})}async withStore(t,r){try{let i=(await this.getDB()).transaction(this.storeName,t),n=i.objectStore(this.storeName),s=r(n);return new Promise((o,c)=>{i.oncomplete=()=>{s instanceof IDBRequest?o(s.result):o(void 0);},i.onerror=()=>c(new d("Transaction failed",i.error)),s instanceof IDBRequest&&(s.onerror=()=>c(new d("Request failed",s.error)));})}catch(e){throw e instanceof d?e:new d("Database operation failed",e)}}async save(t,r){if(r.did!==t)throw new d(`Integrity check: Key ${t} does not match DID ${r.did}`);await this.withStore("readwrite",e=>e.put(r));}async get(t){return await this.withStore("readonly",e=>e.get(t))||null}async remove(t){await this.withStore("readwrite",r=>r.delete(t));}async listKeys(){return this.withStore("readonly",t=>t.getAllKeys())}async clear(){await this.withStore("readwrite",t=>t.clear());}};var S="RK",A=16,R=class{constructor(t){this._client=null;this._sessionKey=null;this._currentDid=null;this._secretsCache=null;this._passwordCache=null;this._isInitialized=false;this._isDisposed=false;this._crypto=new v,this._storage=t??new I;}async init(t){this.ensureNotDisposed(),!this._isInitialized&&(t?.wasmPath&&(this._crypto=new v(t.wasmPath)),await this._crypto.init(t?.licenseKey),this._isInitialized=true);}isCommercialLicense(){return this._crypto.isCommercial()}getLicensee(){return this._crypto.getLicenseStatus().licensee}async register(t){this.ensureInitialized();let r=this._crypto.createIdentity(),e=r.get_did(),i=this._crypto.getRandomValues(16),n=this._crypto.deriveSessionKey(t,i),s=r.export_wrapped_secret(n),o={did:e,wrappedSecret:this.toBase64(s),salt:this.toBase64(i),secrets:{},createdAt:Date.now()},c=JSON.stringify(o),w=new TextEncoder().encode(c),_=this._crypto.encryptVault(w,t),l=this.generateRecoveryCode(),f=this._crypto.encryptVault(w,l),y={version:1,did:e,salt:this.toBase64(i),wrappedData:this.toBase64(_),recoveryBlob:this.toBase64(f),updatedAt:Date.now()};return await this._storage.save(e,y),this.setSession(r,n,e,t,{}),{did:e,recoveryCode:l}}async login(t,r){this.ensureInitialized();let e=r;if(!e){let l=await this._storage.listKeys();if(l.length===0)throw new u("No identities found in storage");e=l[0];}let i=await this._storage.get(e);if(!i)throw new u(`Identity ${e} not found`);let n;try{n=this._crypto.decryptVault(this.fromBase64(i.wrappedData),t);}catch{throw new u("Invalid password or corrupted vault")}let s;try{let l=new TextDecoder().decode(n);s=JSON.parse(l);}catch{throw new a("Vault data corruption: Invalid JSON","CORRUPT_DATA")}if(s.did!==e)throw new a("Integrity error: DID mismatch inside vault","INTEGRITY_ERROR");let o=this.fromBase64(s.salt),c=this._crypto.deriveSessionKey(t,o),w=this.fromBase64(s.wrappedSecret),_=this._crypto.restoreIdentity(w,c);return this.setSession(_,c,e,t,s.secrets),{did:e,publicKey:_.get_public_key()}}async recoverAccount(t){this.ensureInitialized();let{recoveryCode:r,newPassword:e,did:i,rotateRecoveryCode:n=false}=t,s=i;if(!s){let y=await this._storage.listKeys();if(y.length===0)throw new u("No vaults found");s=y[0];}let o=await this._storage.get(s);if(!o)throw new u(`Identity ${s} not found`);if(!o.recoveryBlob)throw new a("Recovery not available for this identity. It may have been created with an older SDK version.","RECOVERY_UNAVAILABLE");let c;try{c=this._crypto.decryptVault(this.fromBase64(o.recoveryBlob),r);}catch{throw new u("Invalid Recovery Code")}let w;try{let y=new TextDecoder().decode(c);w=JSON.parse(y);}catch{throw new a("Vault data corruption during recovery","CORRUPT_DATA")}let _=this._crypto.encryptVault(c,e),l,f;if(n){l=this.generateRecoveryCode();let y=this._crypto.encryptVault(c,l);f=this.toBase64(y);}else f=o.recoveryBlob;return o.wrappedData=this.toBase64(_),o.recoveryBlob=f,o.updatedAt=Date.now(),await this._storage.save(s,o),await this.login(e,s),{did:s,newRecoveryCode:l}}lock(){if(this._client){try{this._client.free();}catch{}this._client=null;}this._sessionKey=null,this._currentDid=null,this._secretsCache=null,this._passwordCache=null;}isAuthenticated(){return this._client!==null&&!this._isDisposed}getDid(){return this.ensureAuthenticated(),this._currentDid}async getStoredIdentities(){return this.ensureInitialized(),this._storage.listKeys()}async sign(t){return this.ensureAuthenticated(),this._client.sign_data(t)}async saveSecret(t,r){if(this.ensureAuthenticated(),!this._secretsCache||!this._passwordCache||!this._currentDid)throw new g;this._secretsCache[t]=r;let e=await this._storage.get(this._currentDid);if(!e)throw new a("Storage corruption during save","STORAGE_ERROR");let i=this._crypto.decryptVault(this.fromBase64(e.wrappedData),this._passwordCache),n=JSON.parse(new TextDecoder().decode(i));n.secrets={...this._secretsCache},n.createdAt=Date.now();let s=new TextEncoder().encode(JSON.stringify(n)),o=this._crypto.encryptVault(s,this._passwordCache);e.wrappedData=this.toBase64(o),e.recoveryBlob,e.updatedAt=Date.now(),await this._storage.save(this._currentDid,e);}async getSecret(t){return this.ensureAuthenticated(),this._secretsCache?.[t]??null}dispose(){this._isDisposed||(this.lock(),this._isInitialized=false,this._isDisposed=true);}generateRecoveryCode(){let t=this._crypto.getRandomValues(A),r=Array.from(t).map(e=>e.toString(16).padStart(2,"0").toUpperCase()).join("");return `${S}-${r.slice(0,8)}-${r.slice(8,16)}-${r.slice(16,24)}-${r.slice(24,32)}`}ensureNotDisposed(){if(this._isDisposed)throw new a("VaultService has been disposed","DISPOSED")}ensureInitialized(){if(this.ensureNotDisposed(),!this._isInitialized)throw new m("VaultService not initialized. Call init() first.")}ensureAuthenticated(){if(this.ensureInitialized(),!this._client)throw new g}setSession(t,r,e,i,n){this._client=t,this._sessionKey=r,this._currentDid=e,this._passwordCache=i,this._secretsCache={...n};}toBase64(t){let r=Array.from(t,e=>String.fromCharCode(e)).join("");return btoa(r)}fromBase64(t){let r=atob(t);return Uint8Array.from(r,e=>e.codePointAt(0))}};/**
6
+ * @fileoverview WASM Crypto Adapter for P47H Vault
7
+ *
8
+ * This module provides the bridge between TypeScript and the Rust/WASM
9
+ * cryptographic core. It handles module loading, initialization, and
10
+ * provides type-safe wrappers for all WASM operations.
11
+ *
12
+ * @module adapters/WasmCryptoAdapter
13
+ * @license AGPL-3.0 OR Commercial
14
+ */
15
+ /**
16
+ * @fileoverview P47H Vault Service - Core Identity and Secret Management
17
+ *
18
+ * This is the main facade for the P47H Vault SDK. It provides a high-level,
19
+ * type-safe API for:
20
+ * - Creating and managing cryptographic identities (DIDs)
21
+ * - Securely storing and retrieving encrypted secrets
22
+ * - Emergency recovery using Recovery Codes (PUK)
23
+ * - Session management with automatic memory cleanup
24
+ *
25
+ * @module logic/VaultService
26
+ * @license AGPL-3.0 OR Commercial
27
+ */
28
+ /**
29
+ * @fileoverview P47H Vault JS SDK - Entry Point
30
+ *
31
+ * Secure local-first cryptographic vault for the browser.
32
+ *
33
+ * @module @p47h/vault-js
34
+ * @license AGPL-3.0 OR Commercial (https://p47h.com/licensing)
35
+ */export{u as AuthenticationError,I as BrowserStorage,h as CryptoError,m as InitializationError,g as NotAuthenticatedError,R as P47hVault,d as StorageError,a as VaultError};//# sourceMappingURL=index.js.map
36
+ //# sourceMappingURL=index.js.map