wolfronix-sdk 1.1.0 → 1.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.
package/README.md CHANGED
@@ -1,35 +1,44 @@
1
- # @wolfronix/sdk
1
+ # wolfronix-sdk
2
2
 
3
3
  Official JavaScript/TypeScript SDK for Wolfronix - Zero-knowledge encryption made simple.
4
4
 
5
- [![npm version](https://badge.fury.io/js/@wolfronix%2Fsdk.svg)](https://www.npmjs.com/package/@wolfronix/sdk)
5
+ [![npm version](https://badge.fury.io/js/wolfronix-sdk.svg)](https://www.npmjs.com/package/wolfronix-sdk)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
8
  ## Features
9
9
 
10
10
  - 🔐 **Zero-Knowledge Encryption** - Keys generated client-side, never leave your device
11
11
  - 🏢 **Enterprise Ready** - Seamless integration with your existing storage
12
-
13
12
  - 🚀 **Simple API** - Encrypt files in 2 lines of code
14
13
  - 📦 **TypeScript Native** - Full type definitions included
15
- - 🌐 **Universal** - Works in Node.js and browsers
16
- - ⚡ **Streaming** - Handle large files with progress tracking
14
+ - 🌐 **Universal** - Works in Node.js 16+ and modern browsers
17
15
  - 🔄 **Auto Retry** - Built-in retry logic with exponential backoff
18
16
 
17
+ ## Backend Integration (Enterprise Mode)
18
+
19
+ To use this SDK, your backend API must implement 3 storage endpoints that Wolfronix will call:
20
+
21
+ 1. **POST** `/wolfronix/files/upload` - Store encrypted file + metadata
22
+ 2. **GET** `/wolfronix/files/{id}` - Retrieve metadata
23
+ 3. **GET** `/wolfronix/files/{id}/data` - Retrieve encrypted file blob
24
+
25
+ Wolfronix handles all encryption/decryption keys and logic; you only handle the encrypted blobs.
26
+
27
+
19
28
  ## Installation
20
29
 
21
30
  ```bash
22
- npm install @wolfronix/sdk
31
+ npm install wolfronix-sdk
23
32
  # or
24
- yarn add @wolfronix/sdk
33
+ yarn add wolfronix-sdk
25
34
  # or
26
- pnpm add @wolfronix/sdk
35
+ pnpm add wolfronix-sdk
27
36
  ```
28
37
 
29
38
  ## Quick Start
30
39
 
31
40
  ```typescript
32
- import Wolfronix from '@wolfronix/sdk';
41
+ import Wolfronix from 'wolfronix-sdk';
33
42
 
34
43
  // Initialize client
35
44
  const wfx = new Wolfronix({
@@ -37,7 +46,10 @@ const wfx = new Wolfronix({
37
46
  clientId: 'your-enterprise-client-id'
38
47
  });
39
48
 
40
- // Login
49
+ // Register (First time only) - Generates keys client-side
50
+ await wfx.register('user@example.com', 'password123');
51
+
52
+ // Login (Subsequent visits)
41
53
  await wfx.login('user@example.com', 'password123');
42
54
 
43
55
  // Encrypt a file
@@ -53,7 +65,7 @@ const decrypted = await wfx.decrypt(result.file_id);
53
65
  ### Browser (React, Vue, Angular, etc.)
54
66
 
55
67
  ```typescript
56
- import Wolfronix from '@wolfronix/sdk';
68
+ import Wolfronix from 'wolfronix-sdk';
57
69
 
58
70
  const wfx = new Wolfronix('https://wolfronix-server:5002');
59
71
 
@@ -63,7 +75,6 @@ const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
63
75
  if (!file) return;
64
76
 
65
77
  try {
66
- // Keys are automatically handled by the SDK
67
78
  const { file_id } = await wfx.encrypt(file);
68
79
  console.log('File encrypted with your private key:', file_id);
69
80
  } catch (error) {
@@ -88,7 +99,7 @@ const handleDownload = async (fileId: string, filename: string) => {
88
99
  ### Node.js
89
100
 
90
101
  ```typescript
91
- import Wolfronix from '@wolfronix/sdk';
102
+ import Wolfronix from 'wolfronix-sdk';
92
103
  import * as fs from 'fs';
93
104
 
94
105
  const wfx = new Wolfronix({
@@ -118,23 +129,6 @@ async function main() {
118
129
  main();
119
130
  ```
120
131
 
121
- ### Large File Streaming with Progress
122
-
123
- ```typescript
124
- const wfx = new Wolfronix('https://wolfronix-server:5002');
125
-
126
- // Encrypt with progress
127
- const result = await wfx.encryptStream(largeFile, (percent) => {
128
- console.log(`Uploading: ${percent}%`);
129
- progressBar.value = percent;
130
- });
131
-
132
- // Decrypt with progress
133
- const blob = await wfx.decryptStream(fileId, (percent) => {
134
- console.log(`Downloading: ${percent}%`);
135
- });
136
- ```
137
-
138
132
  ## API Reference
139
133
 
140
134
  ### Constructor
@@ -167,10 +161,8 @@ new Wolfronix(config: WolfronixConfig | string)
167
161
  | Method | Description |
168
162
  |--------|-------------|
169
163
  | `encrypt(file, filename?)` | Encrypt and store file |
170
- | `encryptStream(file, onProgress?)` | Encrypt large file with progress |
171
164
  | `decrypt(fileId)` | Decrypt file (returns Blob) |
172
165
  | `decryptToBuffer(fileId)` | Decrypt file (returns ArrayBuffer) |
173
- | `decryptStream(fileId, onProgress?)` | Decrypt large file with progress |
174
166
  | `listFiles()` | List user's encrypted files |
175
167
  | `deleteFile(fileId)` | Delete encrypted file |
176
168
 
@@ -193,7 +185,7 @@ import Wolfronix, {
193
185
  PermissionDeniedError,
194
186
  NetworkError,
195
187
  ValidationError
196
- } from '@wolfronix/sdk';
188
+ } from 'wolfronix-sdk';
197
189
 
198
190
  try {
199
191
  await wfx.encrypt(file);
@@ -232,7 +224,7 @@ import Wolfronix, {
232
224
  FileInfo,
233
225
  ListFilesResponse,
234
226
  MetricsResponse
235
- } from '@wolfronix/sdk';
227
+ } from 'wolfronix-sdk';
236
228
 
237
229
  // All methods are fully typed
238
230
  const config: WolfronixConfig = {
@@ -249,7 +241,7 @@ const response: EncryptResponse = await wfx.encrypt(file);
249
241
  ```typescript
250
242
  // useWolfronix.ts
251
243
  import { useState, useCallback, useMemo } from 'react';
252
- import Wolfronix, { FileInfo as WolfronixFile } from '@wolfronix/sdk';
244
+ import Wolfronix, { FileInfo as WolfronixFile } from 'wolfronix-sdk';
253
245
 
254
246
  export function useWolfronix(baseUrl: string, clientId?: string) {
255
247
  const [isLoading, setIsLoading] = useState(false);
@@ -337,10 +329,25 @@ function FileManager() {
337
329
  }
338
330
  ```
339
331
 
332
+ ## Real-World Use Cases
333
+
334
+ Wolfronix can be integrated into **any application** that handles sensitive data:
335
+
336
+ | Industry | Application | How Wolfronix Helps |
337
+ |----------|------------|---------------------|
338
+ | 🏥 **Healthcare** | Patient records, lab reports | HIPAA-compliant encryption at rest |
339
+ | 🏦 **Finance** | Invoices, tax docs, receipts | End-to-end encrypted banking documents |
340
+ | ⚖️ **Legal** | Contracts, case files | Zero-knowledge confidential storage |
341
+ | ☁️ **Cloud Storage** | Drive/Dropbox alternatives | Encrypted file vault with user-owned keys |
342
+ | 🏢 **Enterprise** | HR records, internal docs | Per-employee encryption isolation |
343
+ | 🎓 **Education** | Exam papers, student data | Tamper-proof academic records |
344
+ | 💬 **Messaging** | File attachments | Encrypted file sharing in chat apps |
345
+ | 🛒 **E-commerce** | Order docs, payment receipts | PCI-compliant document storage |
346
+
340
347
  ## Requirements
341
348
 
342
349
  - Node.js 16+ (for Node.js usage)
343
- - Modern browser with Fetch API support
350
+ - Modern browser with Web Crypto API support
344
351
 
345
352
  ## License
346
353
 
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Zero-knowledge encryption made simple
4
4
  *
5
5
  * @package @wolfronix/sdk
6
- * @version 1.1.0
6
+ * @version 1.3.0
7
7
  */
8
8
  interface WolfronixConfig {
9
9
  /** Wolfronix server base URL */
@@ -24,11 +24,10 @@ interface AuthResponse {
24
24
  message: string;
25
25
  }
26
26
  interface EncryptResponse {
27
- success: boolean;
27
+ status: string;
28
28
  file_id: string;
29
- original_name: string;
30
- encrypted_size: number;
31
- message: string;
29
+ file_size: number;
30
+ enc_time_ms: number;
32
31
  }
33
32
  interface FileInfo {
34
33
  file_id: string;
@@ -52,10 +51,6 @@ interface MetricsResponse {
52
51
  total_bytes_encrypted: number;
53
52
  total_bytes_decrypted: number;
54
53
  }
55
- interface StreamToken {
56
- token: string;
57
- expires_at: string;
58
- }
59
54
  declare class WolfronixError extends Error {
60
55
  readonly code: string;
61
56
  readonly statusCode?: number;
@@ -155,17 +150,6 @@ declare class Wolfronix {
155
150
  * ```
156
151
  */
157
152
  encrypt(file: File | Blob | ArrayBuffer | Uint8Array, filename?: string): Promise<EncryptResponse>;
158
- /**
159
- * Encrypt a file using streaming (for large files)
160
- *
161
- * @example
162
- * ```typescript
163
- * const result = await wfx.encryptStream(largeFile, (progress) => {
164
- * console.log(`Progress: ${progress}%`);
165
- * });
166
- * ```
167
- */
168
- encryptStream(file: File | Blob, onProgress?: (percent: number) => void): Promise<EncryptResponse>;
169
153
  /**
170
154
  * Decrypt and retrieve a file
171
155
  *
@@ -185,10 +169,6 @@ declare class Wolfronix {
185
169
  * Decrypt and return as ArrayBuffer
186
170
  */
187
171
  decryptToBuffer(fileId: string): Promise<ArrayBuffer>;
188
- /**
189
- * Decrypt using streaming (for large files)
190
- */
191
- decryptStream(fileId: string, onProgress?: (percent: number) => void): Promise<Blob>;
192
172
  /**
193
173
  * List all encrypted files for current user
194
174
  *
@@ -238,4 +218,4 @@ declare class Wolfronix {
238
218
  */
239
219
  declare function createClient(config: WolfronixConfig | string): Wolfronix;
240
220
 
241
- export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, type StreamToken, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
221
+ export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Zero-knowledge encryption made simple
4
4
  *
5
5
  * @package @wolfronix/sdk
6
- * @version 1.1.0
6
+ * @version 1.3.0
7
7
  */
8
8
  interface WolfronixConfig {
9
9
  /** Wolfronix server base URL */
@@ -24,11 +24,10 @@ interface AuthResponse {
24
24
  message: string;
25
25
  }
26
26
  interface EncryptResponse {
27
- success: boolean;
27
+ status: string;
28
28
  file_id: string;
29
- original_name: string;
30
- encrypted_size: number;
31
- message: string;
29
+ file_size: number;
30
+ enc_time_ms: number;
32
31
  }
33
32
  interface FileInfo {
34
33
  file_id: string;
@@ -52,10 +51,6 @@ interface MetricsResponse {
52
51
  total_bytes_encrypted: number;
53
52
  total_bytes_decrypted: number;
54
53
  }
55
- interface StreamToken {
56
- token: string;
57
- expires_at: string;
58
- }
59
54
  declare class WolfronixError extends Error {
60
55
  readonly code: string;
61
56
  readonly statusCode?: number;
@@ -155,17 +150,6 @@ declare class Wolfronix {
155
150
  * ```
156
151
  */
157
152
  encrypt(file: File | Blob | ArrayBuffer | Uint8Array, filename?: string): Promise<EncryptResponse>;
158
- /**
159
- * Encrypt a file using streaming (for large files)
160
- *
161
- * @example
162
- * ```typescript
163
- * const result = await wfx.encryptStream(largeFile, (progress) => {
164
- * console.log(`Progress: ${progress}%`);
165
- * });
166
- * ```
167
- */
168
- encryptStream(file: File | Blob, onProgress?: (percent: number) => void): Promise<EncryptResponse>;
169
153
  /**
170
154
  * Decrypt and retrieve a file
171
155
  *
@@ -185,10 +169,6 @@ declare class Wolfronix {
185
169
  * Decrypt and return as ArrayBuffer
186
170
  */
187
171
  decryptToBuffer(fileId: string): Promise<ArrayBuffer>;
188
- /**
189
- * Decrypt using streaming (for large files)
190
- */
191
- decryptStream(fileId: string, onProgress?: (percent: number) => void): Promise<Blob>;
192
172
  /**
193
173
  * List all encrypted files for current user
194
174
  *
@@ -238,4 +218,4 @@ declare class Wolfronix {
238
218
  */
239
219
  declare function createClient(config: WolfronixConfig | string): Wolfronix;
240
220
 
241
- export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, type StreamToken, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
221
+ export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
package/dist/index.js CHANGED
@@ -33,6 +33,14 @@ __export(index_exports, {
33
33
  module.exports = __toCommonJS(index_exports);
34
34
 
35
35
  // src/crypto.ts
36
+ var getCrypto = () => {
37
+ if (typeof globalThis.crypto !== "undefined") {
38
+ return globalThis.crypto;
39
+ }
40
+ throw new Error(
41
+ "Web Crypto API not available. Requires a modern browser or Node.js 16+."
42
+ );
43
+ };
36
44
  var RSA_ALG = {
37
45
  name: "RSA-OAEP",
38
46
  modulusLength: 2048,
@@ -42,7 +50,7 @@ var RSA_ALG = {
42
50
  var WRAP_ALG = "AES-GCM";
43
51
  var PBKDF2_ITERATIONS = 1e5;
44
52
  async function generateKeyPair() {
45
- return await window.crypto.subtle.generateKey(
53
+ return await getCrypto().subtle.generateKey(
46
54
  RSA_ALG,
47
55
  true,
48
56
  // extractable
@@ -51,7 +59,7 @@ async function generateKeyPair() {
51
59
  }
52
60
  async function exportKeyToPEM(key, type) {
53
61
  const format = type === "public" ? "spki" : "pkcs8";
54
- const exported = await window.crypto.subtle.exportKey(format, key);
62
+ const exported = await getCrypto().subtle.exportKey(format, key);
55
63
  const exportedAsBase64 = arrayBufferToBase64(exported);
56
64
  const pemHeader = type === "public" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
57
65
  const pemFooter = type === "public" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
@@ -66,7 +74,7 @@ async function importKeyFromPEM(pem, type) {
66
74
  const binaryDer = base64ToArrayBuffer(pemContents);
67
75
  const format = type === "public" ? "spki" : "pkcs8";
68
76
  const usage = type === "public" ? ["encrypt", "wrapKey"] : ["decrypt", "unwrapKey"];
69
- return await window.crypto.subtle.importKey(
77
+ return await getCrypto().subtle.importKey(
70
78
  format,
71
79
  binaryDer,
72
80
  RSA_ALG,
@@ -76,7 +84,7 @@ async function importKeyFromPEM(pem, type) {
76
84
  }
77
85
  async function deriveWrappingKey(password, saltHex) {
78
86
  const enc = new TextEncoder();
79
- const passwordKey = await window.crypto.subtle.importKey(
87
+ const passwordKey = await getCrypto().subtle.importKey(
80
88
  "raw",
81
89
  enc.encode(password),
82
90
  "PBKDF2",
@@ -84,7 +92,7 @@ async function deriveWrappingKey(password, saltHex) {
84
92
  ["deriveKey"]
85
93
  );
86
94
  const salt = hexToArrayBuffer(saltHex);
87
- return await window.crypto.subtle.deriveKey(
95
+ return await getCrypto().subtle.deriveKey(
88
96
  {
89
97
  name: "PBKDF2",
90
98
  salt,
@@ -98,12 +106,13 @@ async function deriveWrappingKey(password, saltHex) {
98
106
  );
99
107
  }
100
108
  async function wrapPrivateKey(privateKey, password) {
101
- const salt = window.crypto.getRandomValues(new Uint8Array(16));
109
+ const crypto = getCrypto();
110
+ const salt = crypto.getRandomValues(new Uint8Array(16));
102
111
  const saltHex = arrayBufferToHex(salt.buffer);
103
112
  const wrappingKey = await deriveWrappingKey(password, saltHex);
104
- const exportedKey = await window.crypto.subtle.exportKey("pkcs8", privateKey);
105
- const iv = window.crypto.getRandomValues(new Uint8Array(12));
106
- const encryptedContent = await window.crypto.subtle.encrypt(
113
+ const exportedKey = await crypto.subtle.exportKey("pkcs8", privateKey);
114
+ const iv = crypto.getRandomValues(new Uint8Array(12));
115
+ const encryptedContent = await crypto.subtle.encrypt(
107
116
  {
108
117
  name: WRAP_ALG,
109
118
  iv
@@ -125,7 +134,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
125
134
  const iv = combinedArray.slice(0, 12);
126
135
  const data = combinedArray.slice(12);
127
136
  const wrappingKey = await deriveWrappingKey(password, saltHex);
128
- const decryptedKeyData = await window.crypto.subtle.decrypt(
137
+ const decryptedKeyData = await getCrypto().subtle.decrypt(
129
138
  {
130
139
  name: WRAP_ALG,
131
140
  iv
@@ -133,7 +142,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
133
142
  wrappingKey,
134
143
  data
135
144
  );
136
- return await window.crypto.subtle.importKey(
145
+ return await getCrypto().subtle.importKey(
137
146
  "pkcs8",
138
147
  decryptedKeyData,
139
148
  RSA_ALG,
@@ -142,16 +151,23 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
142
151
  );
143
152
  }
144
153
  function arrayBufferToBase64(buffer) {
145
- let binary = "";
146
154
  const bytes = new Uint8Array(buffer);
155
+ if (typeof Buffer !== "undefined") {
156
+ return Buffer.from(bytes).toString("base64");
157
+ }
158
+ let binary = "";
147
159
  const len = bytes.byteLength;
148
160
  for (let i = 0; i < len; i++) {
149
161
  binary += String.fromCharCode(bytes[i]);
150
162
  }
151
- return window.btoa(binary);
163
+ return btoa(binary);
152
164
  }
153
165
  function base64ToArrayBuffer(base64) {
154
- const binary_string = window.atob(base64);
166
+ if (typeof Buffer !== "undefined") {
167
+ const buf = Buffer.from(base64, "base64");
168
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
169
+ }
170
+ const binary_string = atob(base64);
155
171
  const len = binary_string.length;
156
172
  const bytes = new Uint8Array(len);
157
173
  for (let i = 0; i < len; i++) {
@@ -261,6 +277,9 @@ var Wolfronix = class {
261
277
  }
262
278
  if (includeAuth && this.token) {
263
279
  headers["Authorization"] = `Bearer ${this.token}`;
280
+ if (this.userId) {
281
+ headers["X-User-ID"] = this.userId;
282
+ }
264
283
  }
265
284
  return headers;
266
285
  }
@@ -489,60 +508,15 @@ var Wolfronix = class {
489
508
  throw new Error("Public key not available. Is user logged in?");
490
509
  }
491
510
  formData.append("client_public_key", this.publicKeyPEM);
492
- return this.request("POST", "/api/v1/encrypt", {
493
- formData
494
- });
495
- }
496
- /**
497
- * Encrypt a file using streaming (for large files)
498
- *
499
- * @example
500
- * ```typescript
501
- * const result = await wfx.encryptStream(largeFile, (progress) => {
502
- * console.log(`Progress: ${progress}%`);
503
- * });
504
- * ```
505
- */
506
- async encryptStream(file, onProgress) {
507
- this.ensureAuthenticated();
508
- const tokenResponse = await this.request("POST", "/api/v1/stream/token", {
509
- body: {
510
- user_id: this.userId,
511
- client_id: this.config.clientId
512
- }
513
- });
514
- const formData = new FormData();
515
- formData.append("file", file);
516
- formData.append("user_id", this.userId || "");
517
- formData.append("stream_token", tokenResponse.token);
518
- if (onProgress && typeof XMLHttpRequest !== "undefined") {
519
- return new Promise((resolve, reject) => {
520
- const xhr = new XMLHttpRequest();
521
- xhr.upload.onprogress = (event) => {
522
- if (event.lengthComputable) {
523
- onProgress(Math.round(event.loaded / event.total * 100));
524
- }
525
- };
526
- xhr.onload = () => {
527
- if (xhr.status >= 200 && xhr.status < 300) {
528
- resolve(JSON.parse(xhr.responseText));
529
- } else {
530
- reject(new WolfronixError("Upload failed", "UPLOAD_ERROR", xhr.status));
531
- }
532
- };
533
- xhr.onerror = () => reject(new NetworkError("Upload failed"));
534
- xhr.open("POST", `${this.config.baseUrl}/api/v1/stream/encrypt`);
535
- const headers = this.getHeaders();
536
- Object.entries(headers).forEach(([key, value]) => {
537
- xhr.setRequestHeader(key, value);
538
- });
539
- xhr.send(formData);
540
- });
541
- }
542
- return this.request("POST", "/api/v1/stream/encrypt", {
511
+ const response = await this.request("POST", "/api/v1/encrypt", {
543
512
  formData
544
- // Stream endpoint might need update to accept public key too if re-implemented
545
513
  });
514
+ return {
515
+ status: response.status,
516
+ file_id: String(response.file_id),
517
+ file_size: response.file_size,
518
+ enc_time_ms: response.enc_time_ms
519
+ };
546
520
  }
547
521
  /**
548
522
  * Decrypt and retrieve a file
@@ -567,9 +541,6 @@ var Wolfronix = class {
567
541
  throw new Error("Private key not available. Is user logged in?");
568
542
  }
569
543
  const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
570
- const headers = this.getHeaders();
571
- headers["X-Private-Key"] = privateKeyPEM;
572
- headers["X-User-Role"] = "owner";
573
544
  return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
574
545
  responseType: "blob",
575
546
  headers: {
@@ -598,51 +569,6 @@ var Wolfronix = class {
598
569
  }
599
570
  });
600
571
  }
601
- /**
602
- * Decrypt using streaming (for large files)
603
- */
604
- async decryptStream(fileId, onProgress) {
605
- this.ensureAuthenticated();
606
- if (!fileId) {
607
- throw new ValidationError("File ID is required");
608
- }
609
- if (!this.privateKey) {
610
- throw new Error("Private key not available. Is user logged in?");
611
- }
612
- const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
613
- if (onProgress && typeof XMLHttpRequest !== "undefined") {
614
- return new Promise((resolve, reject) => {
615
- const xhr = new XMLHttpRequest();
616
- xhr.responseType = "blob";
617
- xhr.onprogress = (event) => {
618
- if (event.lengthComputable) {
619
- onProgress(Math.round(event.loaded / event.total * 100));
620
- }
621
- };
622
- xhr.onload = () => {
623
- if (xhr.status >= 200 && xhr.status < 300) {
624
- resolve(xhr.response);
625
- } else {
626
- reject(new WolfronixError("Download failed", "DOWNLOAD_ERROR", xhr.status));
627
- }
628
- };
629
- xhr.onerror = () => reject(new NetworkError("Download failed"));
630
- xhr.open("POST", `${this.config.baseUrl}/api/v1/files/${fileId}/decrypt`);
631
- const headers = { ...this.getHeaders(), "X-Private-Key": privateKeyPEM, "X-User-Role": "owner" };
632
- Object.entries(headers).forEach(([key, value]) => {
633
- xhr.setRequestHeader(key, value);
634
- });
635
- xhr.send();
636
- });
637
- }
638
- return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
639
- responseType: "blob",
640
- headers: {
641
- "X-Private-Key": privateKeyPEM,
642
- "X-User-Role": "owner"
643
- }
644
- });
645
- }
646
572
  /**
647
573
  * List all encrypted files for current user
648
574
  *
@@ -654,7 +580,17 @@ var Wolfronix = class {
654
580
  */
655
581
  async listFiles() {
656
582
  this.ensureAuthenticated();
657
- return this.request("GET", "/api/v1/files");
583
+ const files = await this.request("GET", "/api/v1/files");
584
+ return {
585
+ success: true,
586
+ files: (files || []).map((f) => ({
587
+ file_id: f.id,
588
+ original_name: f.name,
589
+ encrypted_size: f.size_bytes,
590
+ created_at: f.date
591
+ })),
592
+ total: (files || []).length
593
+ };
658
594
  }
659
595
  /**
660
596
  * Delete an encrypted file
@@ -685,7 +621,7 @@ var Wolfronix = class {
685
621
  */
686
622
  async getMetrics() {
687
623
  this.ensureAuthenticated();
688
- return this.request("GET", "/api/v1/metrics");
624
+ return this.request("GET", "/api/v1/metrics/summary");
689
625
  }
690
626
  /**
691
627
  * Check if server is healthy
package/dist/index.mjs CHANGED
@@ -1,4 +1,12 @@
1
1
  // src/crypto.ts
2
+ var getCrypto = () => {
3
+ if (typeof globalThis.crypto !== "undefined") {
4
+ return globalThis.crypto;
5
+ }
6
+ throw new Error(
7
+ "Web Crypto API not available. Requires a modern browser or Node.js 16+."
8
+ );
9
+ };
2
10
  var RSA_ALG = {
3
11
  name: "RSA-OAEP",
4
12
  modulusLength: 2048,
@@ -8,7 +16,7 @@ var RSA_ALG = {
8
16
  var WRAP_ALG = "AES-GCM";
9
17
  var PBKDF2_ITERATIONS = 1e5;
10
18
  async function generateKeyPair() {
11
- return await window.crypto.subtle.generateKey(
19
+ return await getCrypto().subtle.generateKey(
12
20
  RSA_ALG,
13
21
  true,
14
22
  // extractable
@@ -17,7 +25,7 @@ async function generateKeyPair() {
17
25
  }
18
26
  async function exportKeyToPEM(key, type) {
19
27
  const format = type === "public" ? "spki" : "pkcs8";
20
- const exported = await window.crypto.subtle.exportKey(format, key);
28
+ const exported = await getCrypto().subtle.exportKey(format, key);
21
29
  const exportedAsBase64 = arrayBufferToBase64(exported);
22
30
  const pemHeader = type === "public" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
23
31
  const pemFooter = type === "public" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
@@ -32,7 +40,7 @@ async function importKeyFromPEM(pem, type) {
32
40
  const binaryDer = base64ToArrayBuffer(pemContents);
33
41
  const format = type === "public" ? "spki" : "pkcs8";
34
42
  const usage = type === "public" ? ["encrypt", "wrapKey"] : ["decrypt", "unwrapKey"];
35
- return await window.crypto.subtle.importKey(
43
+ return await getCrypto().subtle.importKey(
36
44
  format,
37
45
  binaryDer,
38
46
  RSA_ALG,
@@ -42,7 +50,7 @@ async function importKeyFromPEM(pem, type) {
42
50
  }
43
51
  async function deriveWrappingKey(password, saltHex) {
44
52
  const enc = new TextEncoder();
45
- const passwordKey = await window.crypto.subtle.importKey(
53
+ const passwordKey = await getCrypto().subtle.importKey(
46
54
  "raw",
47
55
  enc.encode(password),
48
56
  "PBKDF2",
@@ -50,7 +58,7 @@ async function deriveWrappingKey(password, saltHex) {
50
58
  ["deriveKey"]
51
59
  );
52
60
  const salt = hexToArrayBuffer(saltHex);
53
- return await window.crypto.subtle.deriveKey(
61
+ return await getCrypto().subtle.deriveKey(
54
62
  {
55
63
  name: "PBKDF2",
56
64
  salt,
@@ -64,12 +72,13 @@ async function deriveWrappingKey(password, saltHex) {
64
72
  );
65
73
  }
66
74
  async function wrapPrivateKey(privateKey, password) {
67
- const salt = window.crypto.getRandomValues(new Uint8Array(16));
75
+ const crypto = getCrypto();
76
+ const salt = crypto.getRandomValues(new Uint8Array(16));
68
77
  const saltHex = arrayBufferToHex(salt.buffer);
69
78
  const wrappingKey = await deriveWrappingKey(password, saltHex);
70
- const exportedKey = await window.crypto.subtle.exportKey("pkcs8", privateKey);
71
- const iv = window.crypto.getRandomValues(new Uint8Array(12));
72
- const encryptedContent = await window.crypto.subtle.encrypt(
79
+ const exportedKey = await crypto.subtle.exportKey("pkcs8", privateKey);
80
+ const iv = crypto.getRandomValues(new Uint8Array(12));
81
+ const encryptedContent = await crypto.subtle.encrypt(
73
82
  {
74
83
  name: WRAP_ALG,
75
84
  iv
@@ -91,7 +100,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
91
100
  const iv = combinedArray.slice(0, 12);
92
101
  const data = combinedArray.slice(12);
93
102
  const wrappingKey = await deriveWrappingKey(password, saltHex);
94
- const decryptedKeyData = await window.crypto.subtle.decrypt(
103
+ const decryptedKeyData = await getCrypto().subtle.decrypt(
95
104
  {
96
105
  name: WRAP_ALG,
97
106
  iv
@@ -99,7 +108,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
99
108
  wrappingKey,
100
109
  data
101
110
  );
102
- return await window.crypto.subtle.importKey(
111
+ return await getCrypto().subtle.importKey(
103
112
  "pkcs8",
104
113
  decryptedKeyData,
105
114
  RSA_ALG,
@@ -108,16 +117,23 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
108
117
  );
109
118
  }
110
119
  function arrayBufferToBase64(buffer) {
111
- let binary = "";
112
120
  const bytes = new Uint8Array(buffer);
121
+ if (typeof Buffer !== "undefined") {
122
+ return Buffer.from(bytes).toString("base64");
123
+ }
124
+ let binary = "";
113
125
  const len = bytes.byteLength;
114
126
  for (let i = 0; i < len; i++) {
115
127
  binary += String.fromCharCode(bytes[i]);
116
128
  }
117
- return window.btoa(binary);
129
+ return btoa(binary);
118
130
  }
119
131
  function base64ToArrayBuffer(base64) {
120
- const binary_string = window.atob(base64);
132
+ if (typeof Buffer !== "undefined") {
133
+ const buf = Buffer.from(base64, "base64");
134
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
135
+ }
136
+ const binary_string = atob(base64);
121
137
  const len = binary_string.length;
122
138
  const bytes = new Uint8Array(len);
123
139
  for (let i = 0; i < len; i++) {
@@ -227,6 +243,9 @@ var Wolfronix = class {
227
243
  }
228
244
  if (includeAuth && this.token) {
229
245
  headers["Authorization"] = `Bearer ${this.token}`;
246
+ if (this.userId) {
247
+ headers["X-User-ID"] = this.userId;
248
+ }
230
249
  }
231
250
  return headers;
232
251
  }
@@ -455,60 +474,15 @@ var Wolfronix = class {
455
474
  throw new Error("Public key not available. Is user logged in?");
456
475
  }
457
476
  formData.append("client_public_key", this.publicKeyPEM);
458
- return this.request("POST", "/api/v1/encrypt", {
459
- formData
460
- });
461
- }
462
- /**
463
- * Encrypt a file using streaming (for large files)
464
- *
465
- * @example
466
- * ```typescript
467
- * const result = await wfx.encryptStream(largeFile, (progress) => {
468
- * console.log(`Progress: ${progress}%`);
469
- * });
470
- * ```
471
- */
472
- async encryptStream(file, onProgress) {
473
- this.ensureAuthenticated();
474
- const tokenResponse = await this.request("POST", "/api/v1/stream/token", {
475
- body: {
476
- user_id: this.userId,
477
- client_id: this.config.clientId
478
- }
479
- });
480
- const formData = new FormData();
481
- formData.append("file", file);
482
- formData.append("user_id", this.userId || "");
483
- formData.append("stream_token", tokenResponse.token);
484
- if (onProgress && typeof XMLHttpRequest !== "undefined") {
485
- return new Promise((resolve, reject) => {
486
- const xhr = new XMLHttpRequest();
487
- xhr.upload.onprogress = (event) => {
488
- if (event.lengthComputable) {
489
- onProgress(Math.round(event.loaded / event.total * 100));
490
- }
491
- };
492
- xhr.onload = () => {
493
- if (xhr.status >= 200 && xhr.status < 300) {
494
- resolve(JSON.parse(xhr.responseText));
495
- } else {
496
- reject(new WolfronixError("Upload failed", "UPLOAD_ERROR", xhr.status));
497
- }
498
- };
499
- xhr.onerror = () => reject(new NetworkError("Upload failed"));
500
- xhr.open("POST", `${this.config.baseUrl}/api/v1/stream/encrypt`);
501
- const headers = this.getHeaders();
502
- Object.entries(headers).forEach(([key, value]) => {
503
- xhr.setRequestHeader(key, value);
504
- });
505
- xhr.send(formData);
506
- });
507
- }
508
- return this.request("POST", "/api/v1/stream/encrypt", {
477
+ const response = await this.request("POST", "/api/v1/encrypt", {
509
478
  formData
510
- // Stream endpoint might need update to accept public key too if re-implemented
511
479
  });
480
+ return {
481
+ status: response.status,
482
+ file_id: String(response.file_id),
483
+ file_size: response.file_size,
484
+ enc_time_ms: response.enc_time_ms
485
+ };
512
486
  }
513
487
  /**
514
488
  * Decrypt and retrieve a file
@@ -533,9 +507,6 @@ var Wolfronix = class {
533
507
  throw new Error("Private key not available. Is user logged in?");
534
508
  }
535
509
  const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
536
- const headers = this.getHeaders();
537
- headers["X-Private-Key"] = privateKeyPEM;
538
- headers["X-User-Role"] = "owner";
539
510
  return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
540
511
  responseType: "blob",
541
512
  headers: {
@@ -564,51 +535,6 @@ var Wolfronix = class {
564
535
  }
565
536
  });
566
537
  }
567
- /**
568
- * Decrypt using streaming (for large files)
569
- */
570
- async decryptStream(fileId, onProgress) {
571
- this.ensureAuthenticated();
572
- if (!fileId) {
573
- throw new ValidationError("File ID is required");
574
- }
575
- if (!this.privateKey) {
576
- throw new Error("Private key not available. Is user logged in?");
577
- }
578
- const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
579
- if (onProgress && typeof XMLHttpRequest !== "undefined") {
580
- return new Promise((resolve, reject) => {
581
- const xhr = new XMLHttpRequest();
582
- xhr.responseType = "blob";
583
- xhr.onprogress = (event) => {
584
- if (event.lengthComputable) {
585
- onProgress(Math.round(event.loaded / event.total * 100));
586
- }
587
- };
588
- xhr.onload = () => {
589
- if (xhr.status >= 200 && xhr.status < 300) {
590
- resolve(xhr.response);
591
- } else {
592
- reject(new WolfronixError("Download failed", "DOWNLOAD_ERROR", xhr.status));
593
- }
594
- };
595
- xhr.onerror = () => reject(new NetworkError("Download failed"));
596
- xhr.open("POST", `${this.config.baseUrl}/api/v1/files/${fileId}/decrypt`);
597
- const headers = { ...this.getHeaders(), "X-Private-Key": privateKeyPEM, "X-User-Role": "owner" };
598
- Object.entries(headers).forEach(([key, value]) => {
599
- xhr.setRequestHeader(key, value);
600
- });
601
- xhr.send();
602
- });
603
- }
604
- return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
605
- responseType: "blob",
606
- headers: {
607
- "X-Private-Key": privateKeyPEM,
608
- "X-User-Role": "owner"
609
- }
610
- });
611
- }
612
538
  /**
613
539
  * List all encrypted files for current user
614
540
  *
@@ -620,7 +546,17 @@ var Wolfronix = class {
620
546
  */
621
547
  async listFiles() {
622
548
  this.ensureAuthenticated();
623
- return this.request("GET", "/api/v1/files");
549
+ const files = await this.request("GET", "/api/v1/files");
550
+ return {
551
+ success: true,
552
+ files: (files || []).map((f) => ({
553
+ file_id: f.id,
554
+ original_name: f.name,
555
+ encrypted_size: f.size_bytes,
556
+ created_at: f.date
557
+ })),
558
+ total: (files || []).length
559
+ };
624
560
  }
625
561
  /**
626
562
  * Delete an encrypted file
@@ -651,7 +587,7 @@ var Wolfronix = class {
651
587
  */
652
588
  async getMetrics() {
653
589
  this.ensureAuthenticated();
654
- return this.request("GET", "/api/v1/metrics");
590
+ return this.request("GET", "/api/v1/metrics/summary");
655
591
  }
656
592
  /**
657
593
  * Check if server is healthy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfronix-sdk",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Official Wolfronix SDK for JavaScript/TypeScript - Zero-knowledge encryption made simple",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",