wolfronix-sdk 2.4.1 → 2.4.2
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 +568 -287
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,117 +1,296 @@
|
|
|
1
1
|
# wolfronix-sdk
|
|
2
2
|
|
|
3
|
-
Official JavaScript/TypeScript SDK for Wolfronix
|
|
3
|
+
Official JavaScript/TypeScript SDK for **Wolfronix** — Zero-knowledge encryption made simple.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/wolfronix-sdk)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
- 🔐 **Zero-Knowledge Encryption**
|
|
11
|
-
- 🏢 **Enterprise Ready**
|
|
12
|
-
- 🚀 **Simple API**
|
|
13
|
-
- 📦 **TypeScript Native**
|
|
14
|
-
- 🌐 **Universal**
|
|
15
|
-
- 🔄 **Auto Retry**
|
|
10
|
+
- 🔐 **Zero-Knowledge Encryption** — Keys generated client-side, never leave your device
|
|
11
|
+
- 🏢 **Enterprise Ready** — Seamless integration with your existing storage (Supabase, MongoDB, MySQL, Firebase, PostgreSQL)
|
|
12
|
+
- 🚀 **Simple API** — Encrypt files in 2 lines of code
|
|
13
|
+
- 📦 **TypeScript Native** — Full type definitions included
|
|
14
|
+
- 🌐 **Universal** — Works in Node.js 18+ and modern browsers
|
|
15
|
+
- 🔄 **Auto Retry** — Built-in retry logic with exponential backoff
|
|
16
|
+
- 💬 **E2E Chat** — Hybrid RSA+AES message encryption out of the box
|
|
17
|
+
- 📡 **Real-Time Streaming** — WebSocket-based streaming encryption/decryption
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
---
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
## Installation
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
2. **GET** `/wolfronix/files/{id}` - Retrieve metadata
|
|
23
|
-
3. **GET** `/wolfronix/files/{id}/data` - Retrieve encrypted file blob
|
|
23
|
+
### npm / yarn / pnpm
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
```bash
|
|
26
|
+
npm install wolfronix-sdk
|
|
27
|
+
```
|
|
26
28
|
|
|
29
|
+
### Browser (Script Tag)
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
For plain HTML/JS apps, use the pre-built browser bundle:
|
|
29
32
|
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
```html
|
|
34
|
+
<script src="https://unpkg.com/wolfronix-sdk/dist/index.global.js"></script>
|
|
35
|
+
<script>
|
|
36
|
+
// All exports are available on the global `Wolfronix` object
|
|
37
|
+
const wfx = new Wolfronix.default({
|
|
38
|
+
baseUrl: 'https://your-server:9443',
|
|
39
|
+
clientId: 'your-client-id',
|
|
40
|
+
wolfronixKey: 'your-api-key'
|
|
41
|
+
});
|
|
42
|
+
</script>
|
|
36
43
|
```
|
|
37
44
|
|
|
45
|
+
Or host the bundle yourself — copy `node_modules/wolfronix-sdk/dist/index.global.js` to your project.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
38
49
|
## Quick Start
|
|
39
50
|
|
|
40
|
-
|
|
51
|
+
### 1. Connect to Wolfronix Server
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
41
54
|
import Wolfronix from 'wolfronix-sdk';
|
|
42
55
|
|
|
43
|
-
// Initialize client
|
|
44
56
|
const wfx = new Wolfronix({
|
|
45
|
-
baseUrl: 'https://your-wolfronix-server:
|
|
46
|
-
clientId: 'your-
|
|
47
|
-
wolfronixKey: 'your-api-key'
|
|
57
|
+
baseUrl: 'https://your-wolfronix-server:9443',
|
|
58
|
+
clientId: 'your-client-id', // From enterprise registration
|
|
59
|
+
wolfronixKey: 'your-api-key', // From enterprise registration
|
|
48
60
|
});
|
|
61
|
+
```
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
63
|
+
### 2. Register a User (First Time Only)
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
await wfx.register('user@example.com', 'securePassword');
|
|
67
|
+
// Generates RSA key pair client-side, wraps private key with password,
|
|
68
|
+
// sends only the ENCRYPTED key to the server (zero-knowledge)
|
|
69
|
+
```
|
|
52
70
|
|
|
53
|
-
|
|
54
|
-
await wfx.login('user@example.com', 'password123');
|
|
71
|
+
### 3. Login (Subsequent Visits)
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
```javascript
|
|
74
|
+
await wfx.login('user@example.com', 'securePassword');
|
|
75
|
+
// Fetches encrypted private key from server, decrypts it locally
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 4. Encrypt a File
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
const result = await wfx.encrypt(file); // File or Blob
|
|
58
82
|
console.log('Encrypted! File ID:', result.file_id);
|
|
83
|
+
console.log('Time:', result.enc_time_ms, 'ms');
|
|
84
|
+
```
|
|
59
85
|
|
|
60
|
-
|
|
61
|
-
|
|
86
|
+
### 5. Decrypt a File
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
const blob = await wfx.decrypt(result.file_id);
|
|
90
|
+
// blob is a standard Blob — display, download, or process it
|
|
62
91
|
```
|
|
63
92
|
|
|
64
|
-
|
|
93
|
+
### 6. List & Delete Files
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const { files } = await wfx.listFiles();
|
|
97
|
+
await wfx.deleteFile(files[0].file_id);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Step-by-Step Integration Guide
|
|
103
|
+
|
|
104
|
+
### Plain HTML/JS Web App
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<!DOCTYPE html>
|
|
108
|
+
<html>
|
|
109
|
+
<head>
|
|
110
|
+
<title>My Secure App</title>
|
|
111
|
+
<script src="https://unpkg.com/wolfronix-sdk/dist/index.global.js"></script>
|
|
112
|
+
</head>
|
|
113
|
+
<body>
|
|
114
|
+
<input type="email" id="email" placeholder="Email">
|
|
115
|
+
<input type="password" id="password" placeholder="Password">
|
|
116
|
+
<button onclick="doLogin()">Login</button>
|
|
117
|
+
<button onclick="doRegister()">Register</button>
|
|
118
|
+
|
|
119
|
+
<hr>
|
|
120
|
+
|
|
121
|
+
<input type="file" id="fileInput">
|
|
122
|
+
<button onclick="doEncrypt()">Encrypt & Upload</button>
|
|
123
|
+
<button onclick="doList()">List Files</button>
|
|
124
|
+
|
|
125
|
+
<div id="output"></div>
|
|
126
|
+
|
|
127
|
+
<script>
|
|
128
|
+
const wfx = new Wolfronix.default({
|
|
129
|
+
baseUrl: 'https://your-server:9443',
|
|
130
|
+
clientId: 'your-client-id',
|
|
131
|
+
wolfronixKey: 'your-api-key'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
async function doRegister() {
|
|
135
|
+
const email = document.getElementById('email').value;
|
|
136
|
+
const pass = document.getElementById('password').value;
|
|
137
|
+
await wfx.register(email, pass);
|
|
138
|
+
alert('Registered! Keys generated.');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function doLogin() {
|
|
142
|
+
const email = document.getElementById('email').value;
|
|
143
|
+
const pass = document.getElementById('password').value;
|
|
144
|
+
await wfx.login(email, pass);
|
|
145
|
+
alert('Logged in! User: ' + wfx.getUserId());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function doEncrypt() {
|
|
149
|
+
const file = document.getElementById('fileInput').files[0];
|
|
150
|
+
const result = await wfx.encrypt(file);
|
|
151
|
+
document.getElementById('output').textContent =
|
|
152
|
+
'Encrypted! ID: ' + result.file_id + ' (' + result.enc_time_ms + 'ms)';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function doList() {
|
|
156
|
+
const { files } = await wfx.listFiles();
|
|
157
|
+
document.getElementById('output').textContent =
|
|
158
|
+
files.map(f => f.original_name + ' (ID: ' + f.file_id + ')').join('\n');
|
|
159
|
+
}
|
|
160
|
+
</script>
|
|
161
|
+
</body>
|
|
162
|
+
</html>
|
|
163
|
+
```
|
|
65
164
|
|
|
66
|
-
###
|
|
165
|
+
### React / Next.js
|
|
67
166
|
|
|
68
167
|
```typescript
|
|
69
168
|
import Wolfronix from 'wolfronix-sdk';
|
|
70
169
|
|
|
71
|
-
|
|
170
|
+
// Create a singleton instance (e.g., in a context or module)
|
|
171
|
+
const wfx = new Wolfronix({
|
|
172
|
+
baseUrl: process.env.NEXT_PUBLIC_WOLFRONIX_URL!,
|
|
173
|
+
clientId: process.env.NEXT_PUBLIC_WOLFRONIX_CLIENT_ID!,
|
|
174
|
+
wolfronixKey: process.env.NEXT_PUBLIC_WOLFRONIX_KEY!,
|
|
175
|
+
});
|
|
72
176
|
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const file = event.target.files?.[0];
|
|
76
|
-
if (!file) return;
|
|
177
|
+
export default function FileVault() {
|
|
178
|
+
const [files, setFiles] = useState([]);
|
|
77
179
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
180
|
+
const handleLogin = async (email: string, password: string) => {
|
|
181
|
+
await wfx.login(email, password);
|
|
182
|
+
const { files } = await wfx.listFiles();
|
|
183
|
+
setFiles(files);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const handleUpload = async (file: File) => {
|
|
187
|
+
const result = await wfx.encrypt(file);
|
|
188
|
+
console.log('Encrypted:', result.file_id);
|
|
189
|
+
// Refresh file list
|
|
190
|
+
const { files } = await wfx.listFiles();
|
|
191
|
+
setFiles(files);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const handleDownload = async (fileId: string, filename: string) => {
|
|
195
|
+
const blob = await wfx.decrypt(fileId);
|
|
196
|
+
const url = URL.createObjectURL(blob);
|
|
197
|
+
const a = document.createElement('a');
|
|
198
|
+
a.href = url;
|
|
199
|
+
a.download = filename;
|
|
200
|
+
a.click();
|
|
201
|
+
URL.revokeObjectURL(url);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<div>
|
|
206
|
+
<input type="file" onChange={(e) => handleUpload(e.target.files![0])} />
|
|
207
|
+
{files.map(f => (
|
|
208
|
+
<div key={f.file_id}>
|
|
209
|
+
{f.original_name}
|
|
210
|
+
<button onClick={() => handleDownload(f.file_id, f.original_name)}>
|
|
211
|
+
Download
|
|
212
|
+
</button>
|
|
213
|
+
</div>
|
|
214
|
+
))}
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
98
218
|
```
|
|
99
219
|
|
|
100
|
-
###
|
|
220
|
+
### React Hook
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// hooks/useWolfronix.ts
|
|
224
|
+
import { useState, useCallback, useMemo } from 'react';
|
|
225
|
+
import Wolfronix, { FileInfo } from 'wolfronix-sdk';
|
|
226
|
+
|
|
227
|
+
export function useWolfronix(baseUrl: string, clientId?: string, wolfronixKey?: string) {
|
|
228
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
229
|
+
const [error, setError] = useState<Error | null>(null);
|
|
230
|
+
const [files, setFiles] = useState<FileInfo[]>([]);
|
|
231
|
+
|
|
232
|
+
const client = useMemo(
|
|
233
|
+
() => new Wolfronix({ baseUrl, clientId, wolfronixKey }),
|
|
234
|
+
[baseUrl, clientId, wolfronixKey]
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const login = useCallback(async (email: string, password: string) => {
|
|
238
|
+
setIsLoading(true);
|
|
239
|
+
try {
|
|
240
|
+
return await client.login(email, password);
|
|
241
|
+
} catch (e) {
|
|
242
|
+
setError(e as Error);
|
|
243
|
+
throw e;
|
|
244
|
+
} finally {
|
|
245
|
+
setIsLoading(false);
|
|
246
|
+
}
|
|
247
|
+
}, [client]);
|
|
248
|
+
|
|
249
|
+
const encrypt = useCallback(async (file: File) => {
|
|
250
|
+
setIsLoading(true);
|
|
251
|
+
try {
|
|
252
|
+
const result = await client.encrypt(file);
|
|
253
|
+
const { files } = await client.listFiles();
|
|
254
|
+
setFiles(files);
|
|
255
|
+
return result;
|
|
256
|
+
} catch (e) {
|
|
257
|
+
setError(e as Error);
|
|
258
|
+
throw e;
|
|
259
|
+
} finally {
|
|
260
|
+
setIsLoading(false);
|
|
261
|
+
}
|
|
262
|
+
}, [client]);
|
|
263
|
+
|
|
264
|
+
const decrypt = useCallback(async (fileId: string) => {
|
|
265
|
+
setIsLoading(true);
|
|
266
|
+
try {
|
|
267
|
+
return await client.decrypt(fileId);
|
|
268
|
+
} catch (e) {
|
|
269
|
+
setError(e as Error);
|
|
270
|
+
throw e;
|
|
271
|
+
} finally {
|
|
272
|
+
setIsLoading(false);
|
|
273
|
+
}
|
|
274
|
+
}, [client]);
|
|
275
|
+
|
|
276
|
+
return { client, isLoading, error, files, login, encrypt, decrypt };
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Node.js (Server-Side)
|
|
101
281
|
|
|
102
282
|
```typescript
|
|
103
283
|
import Wolfronix from 'wolfronix-sdk';
|
|
104
284
|
import * as fs from 'fs';
|
|
105
285
|
|
|
106
286
|
const wfx = new Wolfronix({
|
|
107
|
-
baseUrl: 'https://wolfronix-server:
|
|
287
|
+
baseUrl: 'https://wolfronix-server:9443',
|
|
108
288
|
clientId: 'your-client-id',
|
|
109
289
|
wolfronixKey: 'your-api-key',
|
|
110
|
-
insecure: true
|
|
290
|
+
insecure: true // For self-signed certs in development
|
|
111
291
|
});
|
|
112
292
|
|
|
113
293
|
async function main() {
|
|
114
|
-
// Login
|
|
115
294
|
await wfx.login('user@example.com', 'password123');
|
|
116
295
|
|
|
117
296
|
// Encrypt a file
|
|
@@ -131,291 +310,364 @@ async function main() {
|
|
|
131
310
|
main();
|
|
132
311
|
```
|
|
133
312
|
|
|
134
|
-
|
|
313
|
+
---
|
|
135
314
|
|
|
136
|
-
|
|
315
|
+
## API Reference
|
|
316
|
+
|
|
317
|
+
### Constructor
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
new Wolfronix(config: WolfronixConfig | string)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
| Option | Type | Default | Description |
|
|
324
|
+
|--------|------|---------|-------------|
|
|
325
|
+
| `baseUrl` | `string` | **required** | Wolfronix server URL |
|
|
326
|
+
| `clientId` | `string` | `''` | Enterprise client ID |
|
|
327
|
+
| `wolfronixKey` | `string` | `''` | API key (sent as `X-Wolfronix-Key` header) |
|
|
328
|
+
| `timeout` | `number` | `30000` | Request timeout in ms (file uploads bypass this) |
|
|
329
|
+
| `retries` | `number` | `3` | Max retry attempts with exponential backoff |
|
|
330
|
+
| `insecure` | `boolean` | `false` | Skip SSL verification (Node.js only) |
|
|
331
|
+
|
|
332
|
+
You can also pass just a URL string: `new Wolfronix('https://server:9443')`
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### Authentication
|
|
337
|
+
|
|
338
|
+
| Method | Returns | Description |
|
|
339
|
+
|--------|---------|-------------|
|
|
340
|
+
| `register(email, password)` | `Promise<AuthResponse>` | Generate RSA keys + register (first time) |
|
|
341
|
+
| `login(email, password)` | `Promise<AuthResponse>` | Fetch & unwrap keys (subsequent logins) |
|
|
342
|
+
| `setToken(token, userId?)` | `void` | Set auth token directly (for custom auth) |
|
|
343
|
+
| `logout()` | `void` | Clear keys and session from memory |
|
|
344
|
+
| `isAuthenticated()` | `boolean` | Check if user is logged in |
|
|
345
|
+
| `getUserId()` | `string \| null` | Get current user ID |
|
|
346
|
+
| `hasPrivateKey()` | `boolean` | Check if RSA private key is loaded in memory |
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
### File Operations
|
|
351
|
+
|
|
352
|
+
| Method | Returns | Description |
|
|
353
|
+
|--------|---------|-------------|
|
|
354
|
+
| `encrypt(file, filename?)` | `Promise<EncryptResponse>` | Encrypt and store a file |
|
|
355
|
+
| `decrypt(fileId, role?)` | `Promise<Blob>` | Decrypt file → Blob (for browser display/download) |
|
|
356
|
+
| `decryptToBuffer(fileId, role?)` | `Promise<ArrayBuffer>` | Decrypt file → ArrayBuffer (for Node.js) |
|
|
357
|
+
| `getFileKey(fileId)` | `Promise<KeyPartResponse>` | Get encrypted key_part_a (advanced use) |
|
|
358
|
+
| `listFiles()` | `Promise<ListFilesResponse>` | List user's encrypted files |
|
|
359
|
+
| `deleteFile(fileId)` | `Promise<DeleteResponse>` | Delete an encrypted file |
|
|
360
|
+
|
|
361
|
+
**`EncryptResponse` fields:**
|
|
362
|
+
```typescript
|
|
363
|
+
{
|
|
364
|
+
status: string;
|
|
365
|
+
file_id: string;
|
|
366
|
+
file_size: number;
|
|
367
|
+
enc_time_ms: number;
|
|
368
|
+
// Detailed timing breakdown:
|
|
369
|
+
upload_ms?: number; // Network upload time
|
|
370
|
+
read_ms?: number; // Server file read time
|
|
371
|
+
encrypt_ms?: number; // AES-256-GCM encryption time
|
|
372
|
+
store_ms?: number; // Storage write time
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
### E2E Chat Encryption
|
|
379
|
+
|
|
380
|
+
Turn any chat app into a secure, end-to-end encrypted messenger.
|
|
381
|
+
|
|
382
|
+
| Method | Returns | Description |
|
|
383
|
+
|--------|---------|-------------|
|
|
384
|
+
| `getPublicKey(userId, clientId?)` | `Promise<CryptoKey>` | Fetch a user's RSA public key |
|
|
385
|
+
| `encryptMessage(text, recipientId)` | `Promise<string>` | Encrypt text for a recipient (returns JSON packet) |
|
|
386
|
+
| `decryptMessage(packetString)` | `Promise<string>` | Decrypt a received message packet |
|
|
137
387
|
|
|
138
388
|
**Sender (Alice):**
|
|
139
389
|
```typescript
|
|
140
|
-
|
|
141
|
-
const securePacket = await wfx.encryptMessage("Secret details at 5 PM", "bob_user_id");
|
|
390
|
+
const securePacket = await wfx.encryptMessage("Secret meeting at 5 PM", "bob_user_id");
|
|
142
391
|
|
|
143
|
-
//
|
|
144
|
-
chatSocket.emit('message', {
|
|
145
|
-
to: 'bob',
|
|
146
|
-
text: securePacket // Valid JSON string
|
|
147
|
-
});
|
|
392
|
+
// Send via your regular chat backend (Socket.io, Firebase, etc.)
|
|
393
|
+
chatSocket.emit('message', { to: 'bob', text: securePacket });
|
|
148
394
|
```
|
|
149
395
|
|
|
150
396
|
**Recipient (Bob):**
|
|
151
397
|
```typescript
|
|
152
|
-
// 1. Receive message from chat server
|
|
153
398
|
chatSocket.on('message', async (msg) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const plainText = await wfx.decryptMessage(msg.text);
|
|
157
|
-
console.log("Decrypted:", plainText);
|
|
158
|
-
} catch (err) {
|
|
159
|
-
console.error("Could not decrypt message");
|
|
160
|
-
}
|
|
399
|
+
const plainText = await wfx.decryptMessage(msg.text);
|
|
400
|
+
console.log("Decrypted:", plainText);
|
|
161
401
|
});
|
|
162
402
|
```
|
|
163
403
|
|
|
164
|
-
|
|
165
|
-
- **Hybrid Encryption:** Uses AES-256 for messages + RSA-2048 for key exchange (Fast & Secure).
|
|
166
|
-
- **Zero-Knowledge:** Your chat server only sees encrypted packets.
|
|
167
|
-
- **Universal:** Works with any backend (Socket.io, Firebase, PostgreSQL, etc).
|
|
404
|
+
---
|
|
168
405
|
|
|
169
|
-
|
|
406
|
+
### Server-Side Message Encryption
|
|
170
407
|
|
|
171
|
-
|
|
408
|
+
For messages that need **server-managed** encryption (Layer 3/4 dual-key split):
|
|
409
|
+
|
|
410
|
+
| Method | Returns | Description |
|
|
411
|
+
|--------|---------|-------------|
|
|
412
|
+
| `serverEncrypt(message, options?)` | `Promise<ServerEncryptResult>` | Encrypt a message server-side |
|
|
413
|
+
| `serverDecrypt(params)` | `Promise<string>` | Decrypt a server-encrypted message |
|
|
414
|
+
| `serverEncryptBatch(messages, options?)` | `Promise<ServerBatchEncryptResult>` | Batch encrypt multiple messages |
|
|
415
|
+
| `serverDecryptBatchItem(item)` | `Promise<string>` | Decrypt a single batch item |
|
|
172
416
|
|
|
173
417
|
```typescript
|
|
174
|
-
|
|
418
|
+
// Encrypt
|
|
419
|
+
const encrypted = await wfx.serverEncrypt("Confidential data", { layer: 4 });
|
|
420
|
+
|
|
421
|
+
// Decrypt
|
|
422
|
+
const original = await wfx.serverDecrypt({
|
|
423
|
+
encryptedMessage: encrypted.encrypted_message,
|
|
424
|
+
nonce: encrypted.nonce,
|
|
425
|
+
keyPartA: encrypted.key_part_a,
|
|
426
|
+
messageTag: encrypted.message_tag
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Batch encrypt
|
|
430
|
+
const batch = await wfx.serverEncryptBatch([
|
|
431
|
+
{ id: 'msg1', message: 'Hello' },
|
|
432
|
+
{ id: 'msg2', message: 'World' }
|
|
433
|
+
], { layer: 4 });
|
|
175
434
|
```
|
|
176
435
|
|
|
177
|
-
|
|
178
|
-
|--------|------|---------|-------------|
|
|
179
|
-
| `baseUrl` | string | required | Wolfronix server URL |
|
|
180
|
-
| `clientId` | string | `''` | Enterprise client ID |
|
|
181
|
-
| `wolfronixKey` | string | `''` | API key for X-Wolfronix-Key auth |
|
|
182
|
-
| `timeout` | number | `30000` | Request timeout (ms) |
|
|
183
|
-
| `retries` | number | `3` | Max retry attempts |
|
|
184
|
-
| `insecure` | boolean | `false` | Skip SSL verification (Node.js: uses undici Agent, or set `NODE_TLS_REJECT_UNAUTHORIZED=0`) |
|
|
436
|
+
---
|
|
185
437
|
|
|
186
|
-
###
|
|
438
|
+
### Real-Time Streaming Encryption
|
|
187
439
|
|
|
188
|
-
|
|
189
|
-
|--------|-------------|
|
|
190
|
-
| `register(email, password)` | Register new user |
|
|
191
|
-
| `login(email, password)` | Login existing user |
|
|
192
|
-
| `setToken(token, userId?)` | Set auth token directly |
|
|
193
|
-
| `logout()` | Clear authentication |
|
|
194
|
-
| `isAuthenticated()` | Check auth status |
|
|
195
|
-
| `getUserId()` | Get current user ID |
|
|
440
|
+
For encrypting/decrypting live data streams (audio, video, real-time feeds) via WebSocket:
|
|
196
441
|
|
|
197
|
-
|
|
442
|
+
| Method | Returns | Description |
|
|
443
|
+
|--------|---------|-------------|
|
|
444
|
+
| `createStream(direction, streamKey?)` | `Promise<WolfronixStream>` | Open a streaming encryption session |
|
|
198
445
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
| `getFileKey(fileId)` | Get encrypted key_part_a for client-side decryption |
|
|
205
|
-
| `listFiles()` | List user's encrypted files |
|
|
206
|
-
| `deleteFile(fileId)` | Delete encrypted file |
|
|
446
|
+
```typescript
|
|
447
|
+
// Encrypt stream
|
|
448
|
+
const stream = await wfx.createStream('encrypt');
|
|
449
|
+
stream.onData((chunk, seq) => sendToRecipient(chunk));
|
|
450
|
+
stream.onError((err) => console.error(err));
|
|
207
451
|
|
|
208
|
-
|
|
452
|
+
const processed = await stream.send('data to encrypt'); // Text
|
|
453
|
+
const processed2 = await stream.sendBinary(audioChunk); // Binary
|
|
209
454
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
| `getPublicKey(userId, clientId?)` | Fetch a user's RSA public key |
|
|
213
|
-
| `encryptMessage(text, recipientId)` | Encrypt text for a recipient (returns packet string) |
|
|
214
|
-
| `decryptMessage(packetString)` | Decrypt a received message packet |
|
|
455
|
+
// Save these for the recipient to decrypt:
|
|
456
|
+
console.log('Key:', stream.keyPartA, 'Tag:', stream.streamTag);
|
|
215
457
|
|
|
216
|
-
|
|
458
|
+
const summary = await stream.end();
|
|
459
|
+
console.log('Chunks processed:', summary.chunksProcessed);
|
|
217
460
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
461
|
+
// Decrypt stream (recipient)
|
|
462
|
+
const decStream = await wfx.createStream('decrypt', {
|
|
463
|
+
keyPartA: senderKeyPartA,
|
|
464
|
+
streamTag: senderStreamTag
|
|
465
|
+
});
|
|
466
|
+
decStream.onData((chunk, seq) => playAudio(chunk));
|
|
467
|
+
```
|
|
222
468
|
|
|
223
|
-
|
|
469
|
+
---
|
|
224
470
|
|
|
225
|
-
###
|
|
471
|
+
### Admin API (Enterprise Client Management)
|
|
226
472
|
|
|
227
|
-
|
|
473
|
+
For managing enterprise clients programmatically:
|
|
228
474
|
|
|
475
|
+
```typescript
|
|
476
|
+
import { WolfronixAdmin } from 'wolfronix-sdk';
|
|
477
|
+
|
|
478
|
+
const admin = new WolfronixAdmin({
|
|
479
|
+
baseUrl: 'https://your-server:9443',
|
|
480
|
+
adminKey: 'your-admin-api-key'
|
|
481
|
+
});
|
|
229
482
|
```
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
483
|
+
|
|
484
|
+
| Method | Returns | Description |
|
|
485
|
+
|--------|---------|-------------|
|
|
486
|
+
| `registerClient(params)` | `Promise<RegisterClientResponse>` | Register a new enterprise client |
|
|
487
|
+
| `listClients()` | `Promise<ListClientsResponse>` | List all registered clients |
|
|
488
|
+
| `getClient(clientId)` | `Promise<EnterpriseClient>` | Get details for a specific client |
|
|
489
|
+
| `updateClient(clientId, params)` | `Promise<UpdateClientResponse>` | Update client configuration |
|
|
490
|
+
| `deactivateClient(clientId)` | `Promise<DeactivateClientResponse>` | Deactivate (revoke) a client |
|
|
491
|
+
| `healthCheck()` | `Promise<boolean>` | Check server health |
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
// Register a new client with Supabase connector
|
|
495
|
+
const result = await admin.registerClient({
|
|
496
|
+
client_id: 'acme_corp',
|
|
497
|
+
client_name: 'Acme Corporation',
|
|
498
|
+
db_type: 'supabase', // or: mongodb, mysql, firebase, postgresql, custom_api
|
|
499
|
+
db_config: JSON.stringify({
|
|
500
|
+
supabase_url: 'https://xxx.supabase.co',
|
|
501
|
+
supabase_service_key: 'eyJ...'
|
|
502
|
+
})
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
console.log('Wolfronix Key:', result.wolfronix_key); // Give this to the client
|
|
249
506
|
```
|
|
250
507
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
### Utility Methods
|
|
511
|
+
|
|
512
|
+
| Method | Returns | Description |
|
|
513
|
+
|--------|---------|-------------|
|
|
514
|
+
| `getMetrics()` | `Promise<MetricsResponse>` | Get encryption/decryption stats |
|
|
515
|
+
| `healthCheck()` | `Promise<boolean>` | Check if server is reachable |
|
|
516
|
+
|
|
517
|
+
---
|
|
258
518
|
|
|
259
519
|
## Error Handling
|
|
260
520
|
|
|
261
|
-
The SDK provides
|
|
521
|
+
The SDK provides typed error classes for different failure scenarios:
|
|
262
522
|
|
|
263
523
|
```typescript
|
|
264
|
-
import Wolfronix, {
|
|
265
|
-
WolfronixError,
|
|
266
|
-
AuthenticationError,
|
|
267
|
-
FileNotFoundError,
|
|
268
|
-
PermissionDeniedError,
|
|
269
|
-
NetworkError,
|
|
270
|
-
ValidationError
|
|
524
|
+
import Wolfronix, {
|
|
525
|
+
WolfronixError, // Base error class
|
|
526
|
+
AuthenticationError, // Invalid credentials or expired session
|
|
527
|
+
FileNotFoundError, // File doesn't exist
|
|
528
|
+
PermissionDeniedError, // Not authorized for this file
|
|
529
|
+
NetworkError, // Server unreachable
|
|
530
|
+
ValidationError // Invalid input parameters
|
|
271
531
|
} from 'wolfronix-sdk';
|
|
272
532
|
|
|
273
533
|
try {
|
|
274
534
|
await wfx.encrypt(file);
|
|
275
535
|
} catch (error) {
|
|
276
536
|
if (error instanceof AuthenticationError) {
|
|
277
|
-
//
|
|
278
|
-
router.push('/login');
|
|
537
|
+
// Redirect to login
|
|
279
538
|
} else if (error instanceof FileNotFoundError) {
|
|
280
|
-
// File
|
|
281
|
-
showToast('File not found');
|
|
282
|
-
} else if (error instanceof PermissionDeniedError) {
|
|
283
|
-
// Not your file
|
|
284
|
-
showToast('Access denied');
|
|
539
|
+
// File was deleted
|
|
285
540
|
} else if (error instanceof NetworkError) {
|
|
286
|
-
// Server
|
|
287
|
-
showToast('Connection failed. Retrying...');
|
|
541
|
+
// Server down — SDK already retried 3 times
|
|
288
542
|
} else if (error instanceof ValidationError) {
|
|
289
|
-
//
|
|
290
|
-
showToast(error.message);
|
|
291
|
-
} else {
|
|
292
|
-
// Unknown error
|
|
293
|
-
console.error('Unexpected error:', error);
|
|
543
|
+
// Bad input (e.g., missing file, empty message)
|
|
294
544
|
}
|
|
295
545
|
}
|
|
296
546
|
```
|
|
297
547
|
|
|
298
|
-
|
|
548
|
+
All errors include:
|
|
549
|
+
- `error.message` — Human-readable description
|
|
550
|
+
- `error.code` — Machine-readable error code
|
|
551
|
+
- `error.statusCode` — HTTP status code (if applicable)
|
|
552
|
+
- `error.details` — Server error details (if available)
|
|
299
553
|
|
|
300
|
-
|
|
554
|
+
---
|
|
301
555
|
|
|
302
|
-
|
|
303
|
-
import Wolfronix, {
|
|
304
|
-
WolfronixConfig,
|
|
305
|
-
AuthResponse,
|
|
306
|
-
EncryptResponse,
|
|
307
|
-
FileInfo,
|
|
308
|
-
ListFilesResponse,
|
|
309
|
-
MetricsResponse
|
|
310
|
-
} from 'wolfronix-sdk';
|
|
556
|
+
## Security Architecture
|
|
311
557
|
|
|
312
|
-
|
|
313
|
-
const config: WolfronixConfig = {
|
|
314
|
-
baseUrl: 'https://server:5002',
|
|
315
|
-
clientId: 'my-client',
|
|
316
|
-
wolfronixKey: 'my-api-key'
|
|
317
|
-
};
|
|
558
|
+
### How It Works
|
|
318
559
|
|
|
319
|
-
|
|
320
|
-
|
|
560
|
+
```
|
|
561
|
+
Your App Wolfronix Engine
|
|
562
|
+
┌──────────────┐ ┌────────────────────────┐
|
|
563
|
+
User ──────▶ │ SDK (Browser)│ ──────────▶ │ AES-256-GCM Encrypt │
|
|
564
|
+
Password │ │ HTTPS │ RSA Dual-Key Split │
|
|
565
|
+
│ RSA Keys │ │ RBAC Masking │
|
|
566
|
+
│ (client- │ ◀────────── │ │
|
|
567
|
+
│ side only) │ Encrypted │ Stores NOTHING │
|
|
568
|
+
└──────────────┘ Blob Only │ decryptable alone │
|
|
569
|
+
└────────────────────────┘
|
|
570
|
+
│
|
|
571
|
+
▼
|
|
572
|
+
┌────────────────────────┐
|
|
573
|
+
│ Your Database │
|
|
574
|
+
│ (Supabase, MongoDB, │
|
|
575
|
+
│ PostgreSQL, etc.) │
|
|
576
|
+
│ Stores encrypted │
|
|
577
|
+
│ blobs only │
|
|
578
|
+
└────────────────────────┘
|
|
321
579
|
```
|
|
322
580
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
```typescript
|
|
326
|
-
// useWolfronix.ts
|
|
327
|
-
import { useState, useCallback, useMemo } from 'react';
|
|
328
|
-
import Wolfronix, { FileInfo as WolfronixFile } from 'wolfronix-sdk';
|
|
329
|
-
|
|
330
|
-
export function useWolfronix(baseUrl: string, clientId?: string, wolfronixKey?: string) {
|
|
331
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
332
|
-
const [error, setError] = useState<Error | null>(null);
|
|
333
|
-
const [files, setFiles] = useState<WolfronixFile[]>([]);
|
|
581
|
+
### Key Security Properties
|
|
334
582
|
|
|
335
|
-
|
|
583
|
+
| Property | Implementation |
|
|
584
|
+
|----------|---------------|
|
|
585
|
+
| **Encryption** | AES-256-GCM (authenticated, tamper-proof) |
|
|
586
|
+
| **Key Transport** | RSA-OAEP with SHA-256 |
|
|
587
|
+
| **Key Wrapping** | PBKDF2 (100,000 iterations) + AES-GCM |
|
|
588
|
+
| **Dual-Key Split** | AES key split in half; each half encrypted with different RSA key |
|
|
589
|
+
| **Zero-Knowledge** | Private keys wrapped client-side; server never sees raw keys |
|
|
590
|
+
| **Auth** | API key (`X-Wolfronix-Key`) + zero-knowledge login |
|
|
336
591
|
|
|
337
|
-
|
|
338
|
-
setIsLoading(true);
|
|
339
|
-
setError(null);
|
|
340
|
-
try {
|
|
341
|
-
return await client.login(email, password);
|
|
342
|
-
} catch (e) {
|
|
343
|
-
setError(e as Error);
|
|
344
|
-
throw e;
|
|
345
|
-
} finally {
|
|
346
|
-
setIsLoading(false);
|
|
347
|
-
}
|
|
348
|
-
}, [client]);
|
|
592
|
+
### Zero-Knowledge Decryption Flow
|
|
349
593
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
594
|
+
```
|
|
595
|
+
Client Wolfronix Server
|
|
596
|
+
│ │
|
|
597
|
+
│ GET /files/{id}/key │
|
|
598
|
+
│──────────────────────────────────────►│
|
|
599
|
+
│ { key_part_a: "<RSA-OAEP encrypted>"}│
|
|
600
|
+
│◄──────────────────────────────────────│
|
|
601
|
+
│ │
|
|
602
|
+
│ [Decrypt key_part_a locally │
|
|
603
|
+
│ with private key (RSA-OAEP)] │
|
|
604
|
+
│ │
|
|
605
|
+
│ POST /files/{id}/decrypt │
|
|
606
|
+
│ { decrypted_key_a: "<base64>" } │
|
|
607
|
+
│──────────────────────────────────────►│
|
|
608
|
+
│ │
|
|
609
|
+
│ [Server combines key_a + key_b, │
|
|
610
|
+
│ decrypts with AES-256-GCM] │
|
|
611
|
+
│ │
|
|
612
|
+
│ <decrypted file bytes> │
|
|
613
|
+
│◄──────────────────────────────────────│
|
|
614
|
+
```
|
|
364
615
|
|
|
365
|
-
|
|
366
|
-
setIsLoading(true);
|
|
367
|
-
setError(null);
|
|
368
|
-
try {
|
|
369
|
-
return await client.decrypt(fileId);
|
|
370
|
-
} catch (e) {
|
|
371
|
-
setError(e as Error);
|
|
372
|
-
throw e;
|
|
373
|
-
} finally {
|
|
374
|
-
setIsLoading(false);
|
|
375
|
-
}
|
|
376
|
-
}, [client]);
|
|
616
|
+
---
|
|
377
617
|
|
|
378
|
-
|
|
379
|
-
const { files } = await client.listFiles();
|
|
380
|
-
setFiles(files);
|
|
381
|
-
}, [client]);
|
|
618
|
+
## TypeScript Types
|
|
382
619
|
|
|
383
|
-
|
|
384
|
-
client,
|
|
385
|
-
isLoading,
|
|
386
|
-
error,
|
|
387
|
-
files,
|
|
388
|
-
login,
|
|
389
|
-
encrypt,
|
|
390
|
-
decrypt,
|
|
391
|
-
refreshFiles
|
|
392
|
-
};
|
|
393
|
-
}
|
|
620
|
+
All interfaces are exported for full type safety:
|
|
394
621
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
);
|
|
622
|
+
```typescript
|
|
623
|
+
import Wolfronix, {
|
|
624
|
+
// Config
|
|
625
|
+
WolfronixConfig,
|
|
626
|
+
WolfronixAdminConfig,
|
|
401
627
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
628
|
+
// Responses
|
|
629
|
+
AuthResponse,
|
|
630
|
+
EncryptResponse,
|
|
631
|
+
FileInfo,
|
|
632
|
+
ListFilesResponse,
|
|
633
|
+
DeleteResponse,
|
|
634
|
+
KeyPartResponse,
|
|
635
|
+
MetricsResponse,
|
|
636
|
+
|
|
637
|
+
// Message Encryption
|
|
638
|
+
EncryptMessagePacket,
|
|
639
|
+
ServerEncryptResult,
|
|
640
|
+
ServerDecryptParams,
|
|
641
|
+
ServerBatchEncryptResult,
|
|
642
|
+
|
|
643
|
+
// Streaming
|
|
644
|
+
WolfronixStream,
|
|
645
|
+
StreamSession,
|
|
646
|
+
StreamChunk,
|
|
647
|
+
|
|
648
|
+
// Enterprise Admin
|
|
649
|
+
WolfronixAdmin,
|
|
650
|
+
RegisterClientRequest,
|
|
651
|
+
RegisterClientResponse,
|
|
652
|
+
EnterpriseClient,
|
|
653
|
+
ListClientsResponse,
|
|
654
|
+
UpdateClientRequest,
|
|
655
|
+
UpdateClientResponse,
|
|
656
|
+
DeactivateClientResponse,
|
|
657
|
+
|
|
658
|
+
// Error Classes
|
|
659
|
+
WolfronixError,
|
|
660
|
+
AuthenticationError,
|
|
661
|
+
FileNotFoundError,
|
|
662
|
+
PermissionDeniedError,
|
|
663
|
+
NetworkError,
|
|
664
|
+
ValidationError
|
|
665
|
+
} from 'wolfronix-sdk';
|
|
414
666
|
```
|
|
415
667
|
|
|
416
|
-
|
|
668
|
+
---
|
|
417
669
|
|
|
418
|
-
|
|
670
|
+
## Real-World Use Cases
|
|
419
671
|
|
|
420
672
|
| Industry | Application | How Wolfronix Helps |
|
|
421
673
|
|----------|------------|---------------------|
|
|
@@ -424,22 +676,51 @@ Wolfronix can be integrated into **any application** that handles sensitive data
|
|
|
424
676
|
| ⚖️ **Legal** | Contracts, case files | Zero-knowledge confidential storage |
|
|
425
677
|
| ☁️ **Cloud Storage** | Drive/Dropbox alternatives | Encrypted file vault with user-owned keys |
|
|
426
678
|
| 🏢 **Enterprise** | HR records, internal docs | Per-employee encryption isolation |
|
|
679
|
+
| 💬 **Messaging** | Chat attachments | Encrypted file sharing + E2E messages |
|
|
427
680
|
| 🎓 **Education** | Exam papers, student data | Tamper-proof academic records |
|
|
428
|
-
|
|
429
|
-
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## Backend Integration
|
|
685
|
+
|
|
686
|
+
Your backend only needs to store/retrieve encrypted blobs. Wolfronix handles all crypto.
|
|
687
|
+
|
|
688
|
+
### Supported Connectors (Managed)
|
|
689
|
+
|
|
690
|
+
| Connector | `db_type` | What You Provide |
|
|
691
|
+
|-----------|-----------|-----------------|
|
|
692
|
+
| Supabase | `supabase` | `supabase_url`, `supabase_service_key` |
|
|
693
|
+
| MongoDB | `mongodb` | `connection_string`, `database` |
|
|
694
|
+
| MySQL | `mysql` | `host`, `port`, `user`, `password`, `database` |
|
|
695
|
+
| Firebase | `firebase` | `project_id`, `service_account_key` |
|
|
696
|
+
| PostgreSQL | `postgresql` | `host`, `port`, `user`, `password`, `database` |
|
|
697
|
+
| Custom API | `custom_api` | Your own REST endpoint |
|
|
698
|
+
|
|
699
|
+
### Custom API Mode
|
|
700
|
+
|
|
701
|
+
If using `custom_api`, your backend must implement these endpoints:
|
|
702
|
+
|
|
703
|
+
| Method | Endpoint | Purpose |
|
|
704
|
+
|--------|----------|---------|
|
|
705
|
+
| `POST` | `/wolfronix/files/upload` | Store encrypted file + metadata |
|
|
706
|
+
| `GET` | `/wolfronix/files/{id}` | Retrieve file metadata |
|
|
707
|
+
| `GET` | `/wolfronix/files/{id}/data` | Retrieve encrypted blob |
|
|
708
|
+
| `DELETE` | `/wolfronix/files/{id}` | Delete file |
|
|
709
|
+
|
|
710
|
+
---
|
|
430
711
|
|
|
431
712
|
## Requirements
|
|
432
713
|
|
|
433
|
-
- Node.js 18+ (for
|
|
434
|
-
-
|
|
714
|
+
- **Node.js:** 18+ (for server-side usage)
|
|
715
|
+
- **Browser:** Any modern browser with Web Crypto API (Chrome, Firefox, Safari, Edge)
|
|
716
|
+
- **Wolfronix Engine:** v2.4.1+
|
|
435
717
|
|
|
436
718
|
## License
|
|
437
719
|
|
|
438
|
-
MIT License
|
|
720
|
+
MIT License — see [LICENSE](./LICENSE) for details.
|
|
439
721
|
|
|
440
722
|
## Links
|
|
441
723
|
|
|
442
|
-
- [
|
|
443
|
-
- [API Reference](https://wolfronix.com/docs/api)
|
|
724
|
+
- [npm Package](https://www.npmjs.com/package/wolfronix-sdk)
|
|
444
725
|
- [GitHub](https://github.com/wolfronix/sdk-javascript)
|
|
445
726
|
- [Report Issues](https://github.com/wolfronix/sdk-javascript/issues)
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED