@qidcloud/sdk 1.2.2 → 1.2.4

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,142 +1,1660 @@
1
1
  # @qidcloud/sdk
2
2
 
3
- The official JavaScript/TypeScript SDK for QidCloud. Build PQC-secured, enclave-backed applications with ease.
3
+ The official JavaScript/TypeScript SDK for **QidCloud** build PQC-secured, enclave-backed applications with ease.
4
4
 
5
- ## 🚀 Installation
5
+ ---
6
+
7
+ ## Installation
6
8
 
7
9
  ```bash
8
10
  npm install @qidcloud/sdk
9
11
  ```
10
12
 
11
- ## 🛠️ Initialization
13
+ ---
14
+
15
+ ## Quick Start
12
16
 
13
17
  ```typescript
14
18
  import { QidCloud } from '@qidcloud/sdk';
15
19
 
16
20
  const qid = new QidCloud({
17
- apiKey: 'your_api_key', // Found in QidCloud Console -> Settings
18
- tenantId: 'your_project_id' // Optional, for project-scoped operations
21
+ apiKey: 'your-api-key', // From QidCloud Console Project → Settings
22
+ tenantId: 'your-tenant-id', // From QidCloud Console → Project → Overview
23
+ baseUrl: 'https://api.qidcloud.com' // Optional, defaults to production
24
+ });
25
+ ```
26
+
27
+ ### Constructor — `QidConfig`
28
+
29
+ | Param | Type | Required | Default | Description |
30
+ |-------|------|----------|---------|-------------|
31
+ | `apiKey` | `string` | ✅ | — | Your project's API key. Found in QidCloud Console → Project → Settings. |
32
+ | `tenantId` | `string` | ❌ | — | Your project's Tenant ID. Required for all data operations (DB, Edge, Vault). Found in Console → Project → Overview. |
33
+ | `baseUrl` | `string` | ❌ | `https://api.qidcloud.com` | API server URL. Use `http://localhost:5000` for local development. |
34
+
35
+ ### Environment Variables (Vite / React)
36
+
37
+ ```env
38
+ VITE_QID_API_KEY=your-api-key
39
+ VITE_QID_TENANT_ID=your-tenant-id
40
+ VITE_QID_BASE_URL=http://localhost:5000
41
+ ```
42
+
43
+ ```typescript
44
+ const qid = new QidCloud({
45
+ apiKey: import.meta.env.VITE_QID_API_KEY,
46
+ tenantId: import.meta.env.VITE_QID_TENANT_ID,
47
+ baseUrl: import.meta.env.VITE_QID_BASE_URL,
19
48
  });
20
49
  ```
21
50
 
22
51
  ---
23
52
 
24
- ## 🛡️ Identity & Auth (`qid.auth`)
53
+ ## Modules
54
+
55
+ After initialization, the SDK exposes the following modules on the `qid` instance:
56
+
57
+ | Module | Accessor | Purpose |
58
+ |--------|----------|---------|
59
+ | [Authentication](#-authentication-qidauth) | `qid.auth` | Login, sessions, MFA, account recovery |
60
+ | [Database](#-database-qiddb) | `qid.db` | SQL queries, schema setup, migrations |
61
+ | [Edge Computing](#-edge-computing-qidedge) | `qid.edge` | Deploy & invoke serverless functions |
62
+ | [Secure Vault](#-secure-vault-qidvault) | `qid.vault` | Encrypted file storage with recycle bin |
63
+ | [Billing](#-billing--analytics-qidbilling) | `qid.billing` | Plans, transactions, usage analytics |
64
+ | [Projects](#-project-management-qidprojects) | `qid.projects` | Create projects, manage members, service keys |
65
+ | [Resources](#-resource-provisioning-qidresources) | `qid.resources` | Provision databases, storage, edge runtimes |
66
+ | [Logs](#-observability-qidlogs) | `qid.logs` | Write & retrieve application logs |
67
+ | [SDK Config](#-sdk-configuration-qidsdk) | `qid.sdk` | Manage allowed origins for CORS |
68
+
69
+ ---
70
+
71
+ ## 🔐 Authentication (`qid.auth`)
72
+
73
+ QidCloud supports **Post-Quantum Cryptography (PQC)** authentication via QR scanning, as well as traditional username/password login.
25
74
 
26
75
  ### `createSession()`
27
- Initializes a new PQC handshake session for QR login.
28
- - **Returns**: `Promise<{ sessionId: string, qrData: string, expiresAt: number }>`
76
+
77
+ Create a new handshake session. Returns a QR code payload for the mobile app to scan.
78
+
79
+ ```typescript
80
+ const session = await qid.auth.createSession();
81
+ // session.sessionId → unique session identifier
82
+ // session.qrData → stringified QR code data for the mobile app
83
+ // session.expiresAt → expiry timestamp (ms since epoch)
84
+ ```
85
+
86
+ **Returns:** [`QidAuthSession`](#qidauthsession)
87
+
88
+ ---
89
+
90
+ ### `listen(sessionId, onAuthorized, onDenied?)`
91
+
92
+ Listen for mobile app authorization on a specific session via WebSocket.
93
+
94
+ | Param | Type | Required | Description |
95
+ |-------|------|----------|-------------|
96
+ | `sessionId` | `string` | ✅ | The session ID from `createSession()` |
97
+ | `onAuthorized` | `(token: string) => void` | ✅ | Called when the user authorizes. Receives a JWT session token. |
98
+ | `onDenied` | `(msg: string) => void` | ❌ | Called if the user denies or the session expires. |
99
+
29
100
  ```typescript
30
- const { sessionId, qrData } = await qid.auth.createSession();
101
+ qid.auth.listen(
102
+ session.sessionId,
103
+ (token) => console.log('Logged in! Token:', token),
104
+ (err) => console.error('Denied:', err)
105
+ );
106
+ ```
107
+
108
+ ---
109
+
110
+ ### `login(credentials)`
111
+
112
+ Traditional login with username/email and password.
113
+
114
+ | Param | Type | Required | Description |
115
+ |-------|------|----------|-------------|
116
+ | `credentials.username` | `string` | ❌ | Login username (either `username` or `email` required) |
117
+ | `credentials.email` | `string` | ❌ | Login email |
118
+ | `credentials.password` | `string` | ✅ | Account password |
119
+
120
+ ```typescript
121
+ const { token, user } = await qid.auth.login({
122
+ username: 'jdoe',
123
+ password: 'my-secure-password'
124
+ });
125
+ ```
126
+
127
+ **Returns:** `{ token: string; user: QidUser }`
128
+
129
+ ---
130
+
131
+ ### `register(data)`
132
+
133
+ Register a new user identity.
134
+
135
+ | Param | Type | Required | Description |
136
+ |-------|------|----------|-------------|
137
+ | `data.username` | `string` | ✅ | Desired username |
138
+ | `data.email` | `string` | ✅ | User email address |
139
+ | `data.password` | `string` | ✅ | Password |
140
+ | `data.fullName` | `string` | ❌ | Full display name |
141
+
142
+ ```typescript
143
+ const result = await qid.auth.register({
144
+ username: 'jdoe',
145
+ email: 'john@example.com',
146
+ password: 'secure-password',
147
+ fullName: 'John Doe'
148
+ });
149
+ // result.success → true
150
+ // result.message → 'Registration successful'
31
151
  ```
32
152
 
153
+ **Returns:** `{ success: boolean; message: string }`
154
+
155
+ ---
156
+
157
+ ### `initiateRegistration(email)`
158
+
159
+ Initiate an OTP-based registration flow.
160
+
161
+ | Param | Type | Required | Description |
162
+ |-------|------|----------|-------------|
163
+ | `email` | `string` | ✅ | Email to send OTP to |
164
+
165
+ **Returns:** `{ success: boolean; message: string }`
166
+
167
+ ---
168
+
169
+ ### `verify(data)`
170
+
171
+ Verify an OTP or mobile verification code.
172
+
173
+ | Param | Type | Required | Description |
174
+ |-------|------|----------|-------------|
175
+ | `data.email` | `string` | ❌ | Email for OTP verification |
176
+ | `data.otp` | `string` | ❌ | The OTP code |
177
+ | `data.token` | `string` | ❌ | Verification token (for mobile flows) |
178
+
179
+ **Returns:** `{ success: boolean; token?: string }`
180
+
181
+ ---
182
+
33
183
  ### `getProfile(token)`
34
- Verifies a session token and retrieves the user profile.
35
- - **Returns**: `Promise<QidUser>`
184
+
185
+ Fetch the current user's profile using their session token.
186
+
187
+ | Param | Type | Required | Description |
188
+ |-------|------|----------|-------------|
189
+ | `token` | `string` | ✅ | JWT session token from login or `listen()` |
190
+
36
191
  ```typescript
37
- const user = await qid.auth.getProfile(sessionToken);
192
+ const user = await qid.auth.getProfile(token);
193
+ // user.userId, user.username, user.email, user.role, etc.
38
194
  ```
39
195
 
40
- ### `listen(sessionId, onAuthorized, onDenied?)`
41
- Listen for real-time authorization events via WebSocket.
196
+ **Returns:** [`QidUser`](#qiduser)
197
+
198
+ ---
199
+
200
+ ### `logout(token)`
201
+
202
+ Terminate the active session on the server and disconnect WebSocket.
203
+
204
+ | Param | Type | Required | Description |
205
+ |-------|------|----------|-------------|
206
+ | `token` | `string` | ✅ | JWT session token |
207
+
42
208
  ```typescript
43
- qid.auth.listen(sessionId,
44
- (token) => console.log('Authenticated:', token),
45
- (error) => console.error('Denied:', error)
46
- );
209
+ await qid.auth.logout(token);
47
210
  ```
48
211
 
49
212
  ---
50
213
 
51
- ## 🗄️ Secure Vault (`qid.vault`)
214
+ ### `disconnect()`
215
+
216
+ Stop listening on the WebSocket and disconnect. Does not invalidate the session server-side.
52
217
 
53
- ### `upload(file, name, metadata?, userToken?)`
54
- Encrypt and upload a file to the secure enclave.
55
- - **Returns**: `Promise<QidUploadResponse>`
56
218
  ```typescript
57
- await qid.vault.upload(fileBlob, 'secret.pdf', { tag: 'confidential' }, userToken);
219
+ qid.auth.disconnect();
58
220
  ```
59
221
 
60
- ### `list(userToken?)`
61
- List all encrypted files.
62
- - **Returns**: `Promise<QidFile[]>`
222
+ ---
223
+
224
+ ### `refreshSession(token)`
225
+
226
+ Refresh/extend the current JWT session.
227
+
228
+ | Param | Type | Required | Description |
229
+ |-------|------|----------|-------------|
230
+ | `token` | `string` | ✅ | Current JWT session token |
231
+
63
232
  ```typescript
64
- const files = await qid.vault.list(userToken);
233
+ const { token: newToken } = await qid.auth.refreshSession(token);
65
234
  ```
66
235
 
67
- ### `download(fileId, userToken?)`
68
- Download a file as an ArrayBuffer.
236
+ **Returns:** `{ token: string }`
237
+
238
+ ---
239
+
240
+ ### `getSessions(token)`
241
+
242
+ List all active sessions (devices) for the current user.
243
+
244
+ | Param | Type | Required | Description |
245
+ |-------|------|----------|-------------|
246
+ | `token` | `string` | ✅ | JWT session token |
247
+
69
248
  ```typescript
70
- const buffer = await qid.vault.download('file_123', userToken);
249
+ const sessions = await qid.auth.getSessions(token);
250
+ // sessions[0].token, sessions[0].lastActive, sessions[0].ip, sessions[0].userAgent
71
251
  ```
72
252
 
73
- ### `delete(fileId, userToken?)`
74
- Soft delete a file (move to recycle bin).
253
+ **Returns:** [`QidSession[]`](#qidsession)
254
+
255
+ ---
256
+
257
+ ### `revokeSession(token, sessionToken)`
258
+
259
+ Revoke (force-logout) a specific session.
260
+
261
+ | Param | Type | Required | Description |
262
+ |-------|------|----------|-------------|
263
+ | `token` | `string` | ✅ | Your current JWT session token |
264
+ | `sessionToken` | `string` | ✅ | The session token to revoke (from `getSessions()`) |
265
+
75
266
  ```typescript
76
- await qid.vault.delete('file_123', userToken);
267
+ await qid.auth.revokeSession(myToken, 'target-session-token');
77
268
  ```
78
269
 
270
+ **Returns:** `{ success: boolean }`
271
+
79
272
  ---
80
273
 
81
- ## ⚡ Edge Computing (`qid.edge`)
274
+ ### `requestPasswordReset(email)`
275
+
276
+ Send a password reset OTP to the user's email.
277
+
278
+ | Param | Type | Required | Description |
279
+ |-------|------|----------|-------------|
280
+ | `email` | `string` | ✅ | Registered email address |
281
+
282
+ **Returns:** `{ success: boolean; message: string }`
283
+
284
+ ---
285
+
286
+ ### `confirmPasswordReset(data)`
287
+
288
+ Confirm a password reset using the OTP.
289
+
290
+ | Param | Type | Required | Description |
291
+ |-------|------|----------|-------------|
292
+ | `data.email` | `string` | ✅ | Registered email |
293
+ | `data.otp` | `string` | ✅ | OTP from email |
294
+ | `data.newPassword` | `string` | ✅ | New password |
295
+
296
+ **Returns:** `{ success: boolean; message: string }`
297
+
298
+ ---
299
+
300
+ ### `changePassword(token, data)`
301
+
302
+ Change password for the currently logged-in user.
303
+
304
+ | Param | Type | Required | Description |
305
+ |-------|------|----------|-------------|
306
+ | `token` | `string` | ✅ | JWT session token |
307
+ | `data.currentPassword` | `string` | ✅ | Current password |
308
+ | `data.newPassword` | `string` | ✅ | New password |
309
+
310
+ **Returns:** `{ success: boolean }`
311
+
312
+ ---
313
+
314
+ ### `deleteAccount(token)`
315
+
316
+ Permanently delete the user's account.
317
+
318
+ | Param | Type | Required | Description |
319
+ |-------|------|----------|-------------|
320
+ | `token` | `string` | ✅ | JWT session token |
321
+
322
+ **Returns:** `{ success: boolean }`
323
+
324
+ ---
325
+
326
+ ### `toggleMFA(token, data)`
327
+
328
+ Enable or disable Multi-Factor Authentication.
329
+
330
+ | Param | Type | Required | Description |
331
+ |-------|------|----------|-------------|
332
+ | `token` | `string` | ✅ | JWT session token |
333
+ | `data.enabled` | `boolean` | ✅ | `true` to enable, `false` to disable |
334
+ | `data.type` | `'email' \| 'totp' \| 'mobile'` | ✅ | MFA method |
82
335
 
83
- ### `invoke(name, params?, userToken?)`
84
- Invoke a serverless edge function.
85
- - **Returns**: `Promise<QidEdgeResponse>`
86
336
  ```typescript
87
- const result = await qid.edge.invoke('secure-calc', { input: 42 }, userToken);
337
+ await qid.auth.toggleMFA(token, { enabled: true, type: 'email' });
88
338
  ```
89
339
 
90
- ### `list()`
91
- List all provisioned functions.
340
+ **Returns:** `{ success: boolean; message: string }`
341
+
342
+ ---
343
+
344
+ ### `verifyMFA(token, otp)`
345
+
346
+ Verify an MFA toggle request with the OTP sent to the user.
347
+
348
+ | Param | Type | Required | Description |
349
+ |-------|------|----------|-------------|
350
+ | `token` | `string` | ✅ | JWT session token |
351
+ | `otp` | `string` | ✅ | OTP code |
352
+
353
+ **Returns:** `{ success: boolean }`
354
+
355
+ ---
356
+
357
+ ### `initiateRecovery(email)`
358
+
359
+ Initiate account recovery (password reset).
360
+
361
+ | Param | Type | Required | Description |
362
+ |-------|------|----------|-------------|
363
+ | `email` | `string` | ✅ | Registered email |
364
+
365
+ **Returns:** `{ success: boolean; message: string }`
366
+
367
+ ---
368
+
369
+ ### `verifyRecovery(data)`
370
+
371
+ Complete recovery with OTP and set a new password.
372
+
373
+ | Param | Type | Required | Description |
374
+ |-------|------|----------|-------------|
375
+ | `data.email` | `string` | ✅ | Registered email |
376
+ | `data.otp` | `string` | ✅ | OTP from recovery email |
377
+ | `data.newPassword` | `string` | ✅ | New password |
378
+
379
+ **Returns:** `{ success: boolean; token: string }`
380
+
381
+ ---
382
+
383
+ ### `exportUserData(token)`
384
+
385
+ Export all user data (GDPR compliance).
386
+
387
+ | Param | Type | Required | Description |
388
+ |-------|------|----------|-------------|
389
+ | `token` | `string` | ✅ | JWT session token |
390
+
92
391
  ```typescript
93
- const functions = await qid.edge.list();
392
+ const data = await qid.auth.exportUserData(token);
94
393
  ```
95
394
 
96
- ### `getLogs(name, userToken?)`
97
- Get logs for a specific function.
395
+ **Returns:** Full user data export object.
396
+
397
+ ---
398
+
399
+ ## 🗄️ Database (`qid.db`)
400
+
401
+ Execute SQL queries against your project's isolated PostgreSQL enclave.
402
+
403
+ ### `query(sql, params?, userToken?)`
404
+
405
+ Execute a parameterized SQL query.
406
+
407
+ | Param | Type | Required | Default | Description |
408
+ |-------|------|----------|---------|-------------|
409
+ | `sql` | `string` | ✅ | — | SQL query string. Supports `$1`, `$2` placeholders. |
410
+ | `params` | `any[]` | ❌ | `[]` | Parameterized values (prevents SQL injection) |
411
+ | `userToken` | `string` | ❌ | — | JWT session token for user-scoped access |
412
+
98
413
  ```typescript
99
- const logs = await qid.edge.getLogs('my-function', userToken);
414
+ // SELECT
415
+ const posts = await qid.db.query('SELECT * FROM posts WHERE user_id = $1', [userId]);
416
+ // posts.success → true
417
+ // posts.data → [{ id: '...', title: '...' }, ...]
418
+ // posts.count → number of rows
419
+
420
+ // INSERT
421
+ await qid.db.query(
422
+ 'INSERT INTO posts (title, body) VALUES ($1, $2)',
423
+ ['Hello World', 'My first post']
424
+ );
425
+
426
+ // CREATE TABLE
427
+ await qid.db.query(`
428
+ CREATE TABLE IF NOT EXISTS users (
429
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
430
+ email VARCHAR(255) UNIQUE NOT NULL,
431
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
432
+ )
433
+ `);
100
434
  ```
101
435
 
102
- ### `deploy(data, userToken?)`
103
- Deploy or update a function.
436
+ **Returns:** [`QidDbResponse<T>`](#qiddbresponset--any)
437
+
438
+ ---
439
+
440
+ ### `setup(userToken?)`
441
+
442
+ Initialize the project's enclave database schema. Useful for first-time project setup — creates the isolated schema for your tenant.
443
+
444
+ | Param | Type | Required | Description |
445
+ |-------|------|----------|-------------|
446
+ | `userToken` | `string` | ❌ | JWT session token |
447
+
448
+ ```typescript
449
+ const { success, schemaName } = await qid.db.setup();
450
+ ```
451
+
452
+ **Returns:** `{ success: boolean; schemaName: string }`
453
+
454
+ ---
455
+
456
+ ### `migrate(migrations, userToken?)`
457
+
458
+ Run versioned migrations (Django-inspired). Only applies migrations that haven't been run yet. Tracks applied migrations in a `_qid_migrations` table that is automatically created.
459
+
460
+ | Param | Type | Required | Description |
461
+ |-------|------|----------|-------------|
462
+ | `migrations` | [`QidMigration[]`](#qidmigration) | ✅ | Ordered array of versioned migrations |
463
+ | `userToken` | `string` | ❌ | JWT session token |
464
+
465
+ **How it works:**
466
+ 1. Creates `_qid_migrations` tracking table if it doesn't exist
467
+ 2. Queries already-applied migrations
468
+ 3. Runs **only new** migrations in order
469
+ 4. Records each successful migration in the tracking table
470
+ 5. **Stops on first failure** (like Django)
471
+
472
+ ```typescript
473
+ import { QidMigration } from '@qidcloud/sdk';
474
+
475
+ const migrations: QidMigration[] = [
476
+ {
477
+ version: '001',
478
+ name: 'create_users_table',
479
+ up: async () => {
480
+ await qid.db.query(`
481
+ CREATE TABLE IF NOT EXISTS users (
482
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
483
+ email VARCHAR(255) UNIQUE NOT NULL,
484
+ name VARCHAR(255),
485
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
486
+ )
487
+ `);
488
+ }
489
+ },
490
+ {
491
+ version: '002',
492
+ name: 'add_avatar_column',
493
+ up: async () => {
494
+ await qid.db.query(`ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar_url TEXT`);
495
+ }
496
+ },
497
+ {
498
+ version: '003',
499
+ name: 'create_posts_table',
500
+ up: async () => {
501
+ await qid.db.query(`
502
+ CREATE TABLE IF NOT EXISTS posts (
503
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
504
+ user_id UUID REFERENCES users(id),
505
+ title TEXT NOT NULL,
506
+ body TEXT,
507
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
508
+ )
509
+ `);
510
+ }
511
+ }
512
+ ];
513
+
514
+ // Run on app startup — safe to call every time
515
+ const result = await qid.db.migrate(migrations);
516
+
517
+ if (result.success) {
518
+ console.log(`Applied: ${result.applied.length}, Skipped: ${result.skipped.length}`);
519
+ } else {
520
+ console.error(`Migration ${result.failed?.version} failed:`, result.failed?.error);
521
+ }
522
+ ```
523
+
524
+ **Returns:** [`QidMigrateResult`](#qidmigrateresult)
525
+
526
+ ---
527
+
528
+ ### `migrateStatus(migrations, userToken?)`
529
+
530
+ Check which migrations have been applied and which are pending — without running anything.
531
+
532
+ | Param | Type | Required | Description |
533
+ |-------|------|----------|-------------|
534
+ | `migrations` | [`QidMigration[]`](#qidmigration) | ✅ | Your full migration list |
535
+ | `userToken` | `string` | ❌ | JWT session token |
536
+
537
+ ```typescript
538
+ const status = await qid.db.migrateStatus(migrations);
539
+ // status.applied → ['001', '002']
540
+ // status.pending → ['003']
541
+ // status.total → 3
542
+ ```
543
+
544
+ **Returns:** `{ applied: string[]; pending: string[]; total: number }`
545
+
546
+ ---
547
+
548
+ ## ⚡ Edge Computing (`qid.edge`)
549
+
550
+ Deploy and invoke serverless functions inside your project's enclave.
551
+
552
+ ### `invoke(name, params?, userToken?)`
553
+
554
+ Invoke a deployed edge function by name.
555
+
556
+ | Param | Type | Required | Default | Description |
557
+ |-------|------|----------|---------|-------------|
558
+ | `name` | `string` | ✅ | — | Function name (as deployed) |
559
+ | `params` | `any` | ❌ | `{}` | Arguments passed to the function's `event.params` |
560
+ | `userToken` | `string` | ❌ | — | JWT session token |
561
+
104
562
  ```typescript
105
- await qid.edge.deploy({
106
- name: 'new-func',
107
- code: '...',
108
- runtime: 'node18'
563
+ const result = await qid.edge.invoke('calculate-tax', {
564
+ amount: 1000,
565
+ region: 'US'
109
566
  }, userToken);
567
+ // result.success → true
568
+ // result.result → { tax: 80, total: 1080 }
569
+ // result.computeTime → '12ms'
570
+ // result.logs → ['Tax calculated for region US']
110
571
  ```
111
572
 
573
+ **Returns:** [`QidEdgeResponse`](#qidedgeresponse)
574
+
112
575
  ---
113
576
 
114
- ## 📊 Observability (`qid.logs`)
577
+ ### `deploy(data, userToken?)`
578
+
579
+ Deploy a new edge function or update an existing one.
580
+
581
+ | Param | Type | Required | Default | Description |
582
+ |-------|------|----------|---------|-------------|
583
+ | `data.name` | `string` | ✅ | — | Function name (lowercase, hyphens allowed) |
584
+ | `data.code` | `string` | ✅ | — | JavaScript source code with `exports.handler` |
585
+ | `data.runtime` | `string` | ❌ | — | Runtime (e.g. `'nodejs20.x'`). See `getRuntimes()` |
586
+ | `data.envVars` | `object` | ❌ | — | Environment variables for the function |
587
+ | `data.overwrite` | `boolean` | ❌ | `false` | If `true`, replaces existing function with same name |
588
+ | `userToken` | `string` | ❌ | — | JWT session token |
115
589
 
116
- ### `write(message, source?, level?, meta?)`
117
- Ingest custom telemetry into the enclave.
118
590
  ```typescript
119
- await qid.logs.write('User action detected', 'frontend', 'info', { userId: '123' });
591
+ await qid.edge.deploy({
592
+ name: 'send-welcome-email',
593
+ code: `
594
+ exports.handler = async (event) => {
595
+ const { email, name } = event.params;
596
+ // Your logic here
597
+ return { success: true, message: 'Email sent to ' + email };
598
+ };
599
+ `,
600
+ runtime: 'nodejs20.x',
601
+ overwrite: true
602
+ }, userToken);
120
603
  ```
121
604
 
122
- ### `fetch(query?)`
123
- Retrieve project logs.
605
+ **Returns:** `{ success: boolean; message: string }`
606
+
607
+ ---
608
+
609
+ ### `list()`
610
+
611
+ List all deployed edge functions in the project.
612
+
124
613
  ```typescript
125
- const logs = await qid.logs.fetch({ limit: 50, level: 'error' });
614
+ const functions = await qid.edge.list();
126
615
  ```
127
616
 
617
+ **Returns:** `any[]` — Array of function metadata objects.
618
+
128
619
  ---
129
620
 
130
- ## ⚛️ React Helpers
621
+ ### `delete(name, userToken?)`
131
622
 
132
- ### `useQidAuth(sdk)`
133
- Hook for managing authentication state.
134
- ```tsx
135
- const { user, login, logout } = useQidAuth(qid);
623
+ Delete a deployed function.
624
+
625
+ | Param | Type | Required | Description |
626
+ |-------|------|----------|-------------|
627
+ | `name` | `string` | ✅ | Function name to delete |
628
+ | `userToken` | `string` | ❌ | JWT session token |
629
+
630
+ ```typescript
631
+ await qid.edge.delete('old-function', userToken);
136
632
  ```
137
633
 
138
- ### `QidSignInButton`
139
- Pre-built component for PQC login.
140
- ```tsx
141
- <QidSignInButton sdk={qid} onSuccess={(user) => console.log(user)} />
634
+ **Returns:** `{ success: boolean }`
635
+
636
+ ---
637
+
638
+ ### `getLogs(name, userToken?)`
639
+
640
+ Get execution logs for a specific function.
641
+
642
+ | Param | Type | Required | Description |
643
+ |-------|------|----------|-------------|
644
+ | `name` | `string` | ✅ | Function name |
645
+ | `userToken` | `string` | ❌ | JWT session token |
646
+
647
+ **Returns:** `any[]`
648
+
649
+ ---
650
+
651
+ ### `getProjectLogs(userToken?)`
652
+
653
+ Get all edge function logs across the entire project.
654
+
655
+ **Returns:** `any[]`
656
+
657
+ ---
658
+
659
+ ### `getRuntimes()`
660
+
661
+ List available serverless runtimes.
662
+
663
+ ```typescript
664
+ const runtimes = await qid.edge.getRuntimes();
665
+ // ['nodejs18.x', 'nodejs20.x', ...]
142
666
  ```
667
+
668
+ **Returns:** `string[]`
669
+
670
+ ---
671
+
672
+ ### `toggleLogging(enabled, userToken?)`
673
+
674
+ Enable or disable centralized logging for all edge functions in the project.
675
+
676
+ | Param | Type | Required | Description |
677
+ |-------|------|----------|-------------|
678
+ | `enabled` | `boolean` | ✅ | `true` to enable, `false` to disable |
679
+ | `userToken` | `string` | ❌ | JWT session token |
680
+
681
+ **Returns:** `{ success: boolean; loggingEnabled: boolean }`
682
+
683
+ ---
684
+
685
+ ## 🔒 Secure Vault (`qid.vault`)
686
+
687
+ Encrypted file storage within your project's enclave. Supports upload, download, soft delete with recycle bin, restore, and permanent purge.
688
+
689
+ ### `upload(file, fileName, metadata?, userToken?, options?)`
690
+
691
+ Upload a file to the project's secure storage. Automatically uses **chunked upload** for files ≥ 10MB to support large files (up to 2GB) and progress tracking.
692
+
693
+ | Param | Type | Required | Default | Description |
694
+ |-------|------|----------|---------|-------------|
695
+ | `file` | `Blob \| Buffer` | ✅ | — | File data |
696
+ | `fileName` | `string` | ✅ | — | File name (e.g. `'photo.jpg'`) |
697
+ | `metadata` | `object` | ❌ | `{}` | Custom metadata or E2EE tags |
698
+ | `userToken` | `string` | ❌ | — | JWT session token |
699
+ | `options` | [`QidUploadOptions`](#qiduploadoptions) | ❌ | — | Upload options (progress tracking, chunk size) |
700
+
701
+ ```typescript
702
+ // Single-shot upload (< 10MB)
703
+ const result = await qid.vault.upload(fileBlob, 'secret.pdf');
704
+
705
+ // Chunked upload (Automatic for >= 10MB)
706
+ const result = await qid.vault.upload(largeVideo, 'lecture.mp4', {}, token, {
707
+ onProgress: (p) => {
708
+ console.log(`Progress: ${p.percent}%`);
709
+ console.log(`Uploaded ${p.uploadedChunks} of ${p.totalChunks} chunks`);
710
+ }
711
+ });
712
+ ```
713
+
714
+ **Returns:** [`QidUploadResponse`](#qiduploadresponse)
715
+
716
+ ---
717
+
718
+ ### `getUploadStatus(uploadId, userToken?)`
719
+
720
+ Query the status of an ongoing or interrupted chunked upload. Useful for implementing resumable uploads.
721
+
722
+ | Param | Type | Required | Description |
723
+ |-------|------|----------|-------------|
724
+ | `uploadId` | `string` | ✅ | The ID returned by the upload init phase |
725
+ | `userToken` | `string` | ❌ | JWT session token |
726
+
727
+ **Returns:** `any` — Object containing `status`, `receivedChunks`, `missingChunks`, and `progress`.
728
+
729
+ ---
730
+
731
+ ### `resumeUpload(uploadId, file, userToken?, options?)`
732
+
733
+ Resume a previously interrupted chunked upload session.
734
+
735
+ | Param | Type | Required | Description |
736
+ |-------|------|----------|-------------|
737
+ | `uploadId` | `string` | ✅ | Session ID to resume |
738
+ | `file` | `Blob \| Buffer` | ✅ | The SAME file data used in the initial upload |
739
+ | `userToken` | `string` | ❌ | JWT session token |
740
+ | `options` | [`QidUploadOptions`](#qiduploadoptions) | ❌ | Upload options |
741
+
742
+ ```typescript
743
+ await qid.vault.resumeUpload(uploadId, file, token, {
744
+ onProgress: (p) => console.log(`Resuming: ${p.percent}%`)
745
+ });
746
+ ```
747
+
748
+ **Returns:** [`QidUploadResponse`](#qiduploadresponse)
749
+
750
+ ---
751
+
752
+ ### `list(userToken?)`
753
+
754
+ List all files in the project enclave.
755
+
756
+ ```typescript
757
+ const files = await qid.vault.list(userToken);
758
+ // files[0].fileId, files[0].originalName, files[0].mimeType, files[0].size
759
+ ```
760
+
761
+ **Returns:** [`QidFile[]`](#qidfile)
762
+
763
+ ---
764
+
765
+ ### `download(fileId, userToken?)`
766
+
767
+ Download a file by its ID. Returns raw binary data.
768
+
769
+ | Param | Type | Required | Description |
770
+ |-------|------|----------|-------------|
771
+ | `fileId` | `string` | ✅ | File ID from `upload()` or `list()` |
772
+ | `userToken` | `string` | ❌ | JWT session token |
773
+
774
+ ```typescript
775
+ const data = await qid.vault.download('abc-123', userToken);
776
+ const blob = new Blob([data]);
777
+ const url = URL.createObjectURL(blob);
778
+ ```
779
+
780
+ **Returns:** `ArrayBuffer`
781
+
782
+ ---
783
+
784
+ ### `delete(fileId, userToken?)`
785
+
786
+ Soft delete a file. Moves it to the recycle bin — can be restored later.
787
+
788
+ | Param | Type | Required | Description |
789
+ |-------|------|----------|-------------|
790
+ | `fileId` | `string` | ✅ | File ID |
791
+ | `userToken` | `string` | ❌ | JWT session token |
792
+
793
+ **Returns:** `{ success: boolean; message: string }`
794
+
795
+ ---
796
+
797
+ ### `listDeleted(userToken?)`
798
+
799
+ List files in the recycle bin.
800
+
801
+ ```typescript
802
+ const deletedFiles = await qid.vault.listDeleted(userToken);
803
+ ```
804
+
805
+ **Returns:** [`QidFile[]`](#qidfile)
806
+
807
+ ---
808
+
809
+ ### `restore(fileId, userToken?)`
810
+
811
+ Restore a file from the recycle bin.
812
+
813
+ | Param | Type | Required | Description |
814
+ |-------|------|----------|-------------|
815
+ | `fileId` | `string` | ✅ | File ID of the deleted file |
816
+ | `userToken` | `string` | ❌ | JWT session token |
817
+
818
+ **Returns:** `{ success: boolean; message: string }`
819
+
820
+ ---
821
+
822
+ ### `purge(fileId, userToken?)`
823
+
824
+ **Permanently** delete a file from the recycle bin. This action is irreversible.
825
+
826
+ | Param | Type | Required | Description |
827
+ |-------|------|----------|-------------|
828
+ | `fileId` | `string` | ✅ | File ID of the deleted file |
829
+ | `userToken` | `string` | ❌ | JWT session token |
830
+
831
+ **Returns:** `{ success: boolean; message: string }`
832
+
833
+ ---
834
+
835
+ ## 💳 Billing & Analytics (`qid.billing`)
836
+
837
+ Manage plans, transactions, usage analytics, and billing alerts.
838
+
839
+ ### `getPlans()`
840
+
841
+ Retrieve all available plans and their pricing tiers.
842
+
843
+ ```typescript
844
+ const plans = await qid.billing.getPlans();
845
+ ```
846
+
847
+ **Returns:** Plan objects with tier details, pricing, and feature limits.
848
+
849
+ ---
850
+
851
+ ### `getProjectBillingInfo(userToken, tenantId)`
852
+
853
+ Get detailed billing and resource usage info for a specific project.
854
+
855
+ | Param | Type | Required | Description |
856
+ |-------|------|----------|-------------|
857
+ | `userToken` | `string` | ✅ | JWT session token |
858
+ | `tenantId` | `string` | ✅ | Project tenant ID |
859
+
860
+ ```typescript
861
+ const billing = await qid.billing.getProjectBillingInfo(token, tenantId);
862
+ // billing.plan → 'pro'
863
+ // billing.estimatedMonthly → 29.99
864
+ // billing.activeEnclaves → ['database', 'storage', 'edge']
865
+ ```
866
+
867
+ **Returns:** [`QidBillingInfo`](#qidbillinginfo)
868
+
869
+ ---
870
+
871
+ ### `syncPaymentStatus(userToken, paymentId)`
872
+
873
+ Sync payment status after a successful Razorpay transaction.
874
+
875
+ | Param | Type | Required | Description |
876
+ |-------|------|----------|-------------|
877
+ | `userToken` | `string` | ✅ | JWT session token |
878
+ | `paymentId` | `string` | ✅ | Razorpay payment ID |
879
+
880
+ **Returns:** `{ success: boolean; message: string }`
881
+
882
+ ---
883
+
884
+ ### `getTransactions(userToken)`
885
+
886
+ Get transaction history for the current user.
887
+
888
+ ```typescript
889
+ const transactions = await qid.billing.getTransactions(token);
890
+ // transactions[0].transactionId, .amount, .date, .status, .planId
891
+ ```
892
+
893
+ **Returns:** [`QidTransaction[]`](#qidtransaction)
894
+
895
+ ---
896
+
897
+ ### `getUsageAnalytics(userToken)`
898
+
899
+ Get usage analytics and resource consumption history.
900
+
901
+ **Returns:** Usage analytics data.
902
+
903
+ ---
904
+
905
+ ### `getBillingAlerts(userToken, tenantId)`
906
+
907
+ Get all billing alerts for a project.
908
+
909
+ **Returns:** `any[]`
910
+
911
+ ---
912
+
913
+ ### `createBillingAlert(userToken, tenantId, data)`
914
+
915
+ Create a billing threshold alert.
916
+
917
+ | Param | Type | Required | Description |
918
+ |-------|------|----------|-------------|
919
+ | `userToken` | `string` | ✅ | JWT session token |
920
+ | `tenantId` | `string` | ✅ | Project tenant ID |
921
+ | `data.resource` | `string` | ✅ | Resource type (e.g. `'egress'`, `'storage'`, `'compute'`) |
922
+ | `data.threshold` | `number` | ✅ | Alert threshold in USD |
923
+
924
+ ```typescript
925
+ await qid.billing.createBillingAlert(token, tenantId, {
926
+ resource: 'egress',
927
+ threshold: 50 // Alert at $50
928
+ });
929
+ ```
930
+
931
+ **Returns:** `{ success: boolean }`
932
+
933
+ ---
934
+
935
+ ### `deleteBillingAlert(userToken, alertId)`
936
+
937
+ Delete a billing alert.
938
+
939
+ | Param | Type | Required | Description |
940
+ |-------|------|----------|-------------|
941
+ | `userToken` | `string` | ✅ | JWT session token |
942
+ | `alertId` | `string` | ✅ | Alert ID from `getBillingAlerts()` |
943
+
944
+ **Returns:** `{ success: boolean }`
945
+
946
+ ---
947
+
948
+ ### `getConsolidatedReport(userToken, query?)`
949
+
950
+ Generate a consolidated financial report.
951
+
952
+ | Param | Type | Required | Description |
953
+ |-------|------|----------|-------------|
954
+ | `userToken` | `string` | ✅ | JWT session token |
955
+ | `query.year` | `number` | ❌ | Filter by year (e.g. `2026`) |
956
+ | `query.startDate` | `string` | ❌ | Start date (ISO format) |
957
+ | `query.endDate` | `string` | ❌ | End date (ISO format) |
958
+
959
+ ```typescript
960
+ const report = await qid.billing.getConsolidatedReport(token, { year: 2026 });
961
+ ```
962
+
963
+ ---
964
+
965
+ ## 🏗️ Project Management (`qid.projects`)
966
+
967
+ Create and manage projects, invite team members, rotate API keys, and manage service keys.
968
+
969
+ ### `getMyProjects(userToken)`
970
+
971
+ List all projects owned by the currently authenticated user.
972
+
973
+ ```typescript
974
+ const projects = await qid.projects.getMyProjects(token);
975
+ // projects[0].name, .tenantId, .apiKey, .tier, .status
976
+ ```
977
+
978
+ **Returns:** [`QidProject[]`](#qidproject)
979
+
980
+ ---
981
+
982
+ ### `createProject(userToken, data)`
983
+
984
+ Create a new project.
985
+
986
+ | Param | Type | Required | Default | Description |
987
+ |-------|------|----------|---------|-------------|
988
+ | `userToken` | `string` | ✅ | — | JWT session token |
989
+ | `data.name` | `string` | ✅ | — | Project name |
990
+ | `data.tier` | `string` | ❌ | `'free'` | Plan tier: `'free'`, `'pro'`, `'elite'`, `'enterprise'` |
991
+
992
+ ```typescript
993
+ const project = await qid.projects.createProject(token, {
994
+ name: 'My Social App',
995
+ tier: 'pro'
996
+ });
997
+ // project.tenantId → 'abc-123'
998
+ // project.apiKey → 'qid_...'
999
+ ```
1000
+
1001
+ **Returns:** [`QidProject`](#qidproject)
1002
+
1003
+ ---
1004
+
1005
+ ### `updateProject(userToken, tenantId, data)`
1006
+
1007
+ Update project details (e.g. rename).
1008
+
1009
+ | Param | Type | Required | Description |
1010
+ |-------|------|----------|-------------|
1011
+ | `data.name` | `string` | ✅ | New project name |
1012
+
1013
+ **Returns:** `{ success: boolean }`
1014
+
1015
+ ---
1016
+
1017
+ ### `rotateApiKey(userToken, tenantId)`
1018
+
1019
+ Rotate the API key for a project. The old key is immediately invalidated.
1020
+
1021
+ ```typescript
1022
+ const { apiKey } = await qid.projects.rotateApiKey(token, tenantId);
1023
+ // apiKey → new API key string
1024
+ ```
1025
+
1026
+ **Returns:** `{ success: boolean; apiKey: string }`
1027
+
1028
+ ---
1029
+
1030
+ ### `getSettings(userToken, tenantId)`
1031
+
1032
+ Get project-specific settings.
1033
+
1034
+ **Returns:** [`QidProjectSettings`](#qidprojectsettings)
1035
+
1036
+ ---
1037
+
1038
+ ### `updateSettings(userToken, tenantId, settings)`
1039
+
1040
+ Update project settings.
1041
+
1042
+ | Param | Type | Required | Description |
1043
+ |-------|------|----------|-------------|
1044
+ | `settings` | [`QidProjectSettings`](#qidprojectsettings) | ✅ | Settings object |
1045
+
1046
+ **Returns:** `{ success: boolean }`
1047
+
1048
+ ---
1049
+
1050
+ ### `inviteMember(userToken, tenantId, data)`
1051
+
1052
+ Invite a new member to the project.
1053
+
1054
+ | Param | Type | Required | Description |
1055
+ |-------|------|----------|-------------|
1056
+ | `data.email` | `string` | ✅ | Invitee's email |
1057
+ | `data.role` | `string` | ✅ | Role: `'developer'`, `'admin'`, `'viewer'` |
1058
+
1059
+ ```typescript
1060
+ await qid.projects.inviteMember(token, tenantId, {
1061
+ email: 'dev@company.com',
1062
+ role: 'developer'
1063
+ });
1064
+ ```
1065
+
1066
+ **Returns:** `{ success: boolean }`
1067
+
1068
+ ---
1069
+
1070
+ ### `removeMember(userToken, tenantId, userId)`
1071
+
1072
+ Remove a member from the project.
1073
+
1074
+ **Returns:** `{ success: boolean }`
1075
+
1076
+ ---
1077
+
1078
+ ### `getProjectUsers(userToken, tenantId)`
1079
+
1080
+ Get all users/members associated with a project.
1081
+
1082
+ **Returns:** `any[]`
1083
+
1084
+ ---
1085
+
1086
+ ### `getMyInvitations(userToken)`
1087
+
1088
+ Get all pending project invitations for the current user.
1089
+
1090
+ **Returns:** `any[]`
1091
+
1092
+ ---
1093
+
1094
+ ### `handleInvitationAction(userToken, tenantId, action)`
1095
+
1096
+ Accept or reject a project invitation.
1097
+
1098
+ | Param | Type | Required | Description |
1099
+ |-------|------|----------|-------------|
1100
+ | `action` | `'accept' \| 'reject'` | ✅ | Invitation action |
1101
+
1102
+ **Returns:** `{ success: boolean }`
1103
+
1104
+ ---
1105
+
1106
+ ### `updateMemberPermissions(userToken, tenantId, userId, permissions)`
1107
+
1108
+ Update a member's permissions within a project.
1109
+
1110
+ **Returns:** `{ success: boolean }`
1111
+
1112
+ ---
1113
+
1114
+ ### `updateProjectUserStatus(userToken, tenantId, regUserId, status)`
1115
+
1116
+ Update a project user's status (`'active'` or `'suspended'`).
1117
+
1118
+ **Returns:** `{ success: boolean }`
1119
+
1120
+ ---
1121
+
1122
+ ### `unlinkProjectUser(userToken, tenantId, regUserId)`
1123
+
1124
+ Remove a user from the project's identity enclave.
1125
+
1126
+ **Returns:** `{ success: boolean }`
1127
+
1128
+ ---
1129
+
1130
+ ### `generateServiceKey(userToken, tenantId, data)`
1131
+
1132
+ Generate a service key for server-to-server authentication.
1133
+
1134
+ | Param | Type | Required | Description |
1135
+ |-------|------|----------|-------------|
1136
+ | `data.name` | `string` | ✅ | Key name/label |
1137
+ | `data.permissions` | `object` | ✅ | Permission scopes |
1138
+
1139
+ **Returns:** Service key object with the generated key.
1140
+
1141
+ ---
1142
+
1143
+ ### `revokeServiceKey(userToken, tenantId, keyId)`
1144
+
1145
+ Revoke an existing service key.
1146
+
1147
+ **Returns:** `{ success: boolean }`
1148
+
1149
+ ---
1150
+
1151
+ ### `wipeProjectEnclave(userToken, tenantId)`
1152
+
1153
+ ⚠️ **DANGER**: Permanently erases all identity data within the project enclave. This action is irreversible.
1154
+
1155
+ ```typescript
1156
+ await qid.projects.wipeProjectEnclave(token, tenantId);
1157
+ ```
1158
+
1159
+ **Returns:** `{ success: boolean }`
1160
+
1161
+ ---
1162
+
1163
+ ### `getProjectSecurityLogs(userToken, tenantId)`
1164
+
1165
+ Retrieve security and audit logs for the project.
1166
+
1167
+ **Returns:** `any[]`
1168
+
1169
+ ---
1170
+
1171
+ ## 🚀 Resource Provisioning (`qid.resources`)
1172
+
1173
+ Provision and manage infrastructure resources (database, storage, edge runtime) for your project.
1174
+
1175
+ ### `getStatus(userToken, tenantId)`
1176
+
1177
+ Get the status of all provisioned resources.
1178
+
1179
+ ```typescript
1180
+ const resources = await qid.resources.getStatus(token, tenantId);
1181
+ // resources[0].resourceType → 'database'
1182
+ // resources[0].status → 'active'
1183
+ // resources[0].endpoint → 'db-endpoint.qidcloud.com'
1184
+ ```
1185
+
1186
+ **Returns:** [`QidResourceStatus[]`](#qidresourcestatus)
1187
+
1188
+ ---
1189
+
1190
+ ### `provision(userToken, tenantId, resourceType)`
1191
+
1192
+ Provision a new resource for the project.
1193
+
1194
+ | Param | Type | Required | Description |
1195
+ |-------|------|----------|-------------|
1196
+ | `resourceType` | `string` | ✅ | `'database'`, `'storage'`, or `'edge'` |
1197
+
1198
+ ```typescript
1199
+ await qid.resources.provision(token, tenantId, 'database');
1200
+ ```
1201
+
1202
+ **Returns:** `{ success: boolean; message: string }`
1203
+
1204
+ ---
1205
+
1206
+ ### `dissolve(userToken, tenantId, resourceType)`
1207
+
1208
+ Dissolve (permanently delete) a provisioned resource.
1209
+
1210
+ | Param | Type | Required | Description |
1211
+ |-------|------|----------|-------------|
1212
+ | `resourceType` | `string` | ✅ | `'database'`, `'storage'`, or `'edge'` |
1213
+
1214
+ **Returns:** `{ success: boolean; message: string }`
1215
+
1216
+ ---
1217
+
1218
+ ## 📊 Observability (`qid.logs`)
1219
+
1220
+ Write and retrieve structured application logs.
1221
+
1222
+ ### `write(message, source?, level?, metadata?)`
1223
+
1224
+ Write a custom application log entry.
1225
+
1226
+ | Param | Type | Required | Default | Description |
1227
+ |-------|------|----------|---------|-------------|
1228
+ | `message` | `string` | ✅ | — | Log message |
1229
+ | `source` | `string` | ❌ | `'app'` | Source label (e.g. `'auth-service'`, `'payment'`) |
1230
+ | `level` | `string` | ❌ | `'info'` | Log level: `'info'`, `'warn'`, `'error'` |
1231
+ | `metadata` | `object` | ❌ | `{}` | Structured data for searching/filtering |
1232
+
1233
+ ```typescript
1234
+ await qid.logs.write('User signed up', 'auth-service', 'info', {
1235
+ userId: 'abc-123',
1236
+ method: 'email'
1237
+ });
1238
+
1239
+ await qid.logs.write('Payment failed', 'billing', 'error', {
1240
+ orderId: 'ord-789',
1241
+ reason: 'insufficient_funds'
1242
+ });
1243
+ ```
1244
+
1245
+ **Returns:** `{ success: boolean }`
1246
+
1247
+ ---
1248
+
1249
+ ### `fetch(query?)`
1250
+
1251
+ Retrieve application logs with optional filters.
1252
+
1253
+ | Param | Type | Required | Default | Description |
1254
+ |-------|------|----------|---------|-------------|
1255
+ | `query.limit` | `number` | ❌ | — | Max number of logs to return |
1256
+ | `query.level` | `string` | ❌ | — | Filter by level |
1257
+
1258
+ ```typescript
1259
+ const logs = await qid.logs.fetch({ level: 'error', limit: 50 });
1260
+ ```
1261
+
1262
+ **Returns:** `any[]`
1263
+
1264
+ ---
1265
+
1266
+ ## 🌐 SDK Configuration (`qid.sdk`)
1267
+
1268
+ Manage allowed origins for CORS — control which domains can make SDK requests to your project.
1269
+
1270
+ ### `getOrigins(userToken, tenantId)`
1271
+
1272
+ List all allowed origins for the project.
1273
+
1274
+ ```typescript
1275
+ const origins = await qid.sdk.getOrigins(token, tenantId);
1276
+ // ['http://localhost:5173', 'https://myapp.com']
1277
+ ```
1278
+
1279
+ **Returns:** `string[]`
1280
+
1281
+ ---
1282
+
1283
+ ### `addOrigin(userToken, tenantId, domain)`
1284
+
1285
+ Add an allowed origin.
1286
+
1287
+ | Param | Type | Required | Description |
1288
+ |-------|------|----------|-------------|
1289
+ | `domain` | `string` | ✅ | Origin URL (e.g. `'https://myapp.com'`) |
1290
+
1291
+ **Returns:** `{ success: boolean; message: string }`
1292
+
1293
+ ---
1294
+
1295
+ ### `removeOrigin(userToken, tenantId, domain)`
1296
+
1297
+ Remove an allowed origin.
1298
+
1299
+ | Param | Type | Required | Description |
1300
+ |-------|------|----------|-------------|
1301
+ | `domain` | `string` | ✅ | Origin URL to remove |
1302
+
1303
+ **Returns:** `{ success: boolean; message: string }`
1304
+
1305
+ ---
1306
+
1307
+ ### `testOrigin(userToken, tenantId, domain)`
1308
+
1309
+ Test if a specific origin is allowed.
1310
+
1311
+ | Param | Type | Required | Description |
1312
+ |-------|------|----------|-------------|
1313
+ | `domain` | `string` | ✅ | Origin URL to test |
1314
+
1315
+ ```typescript
1316
+ const { allowed } = await qid.sdk.testOrigin(token, tenantId, 'https://myapp.com');
1317
+ ```
1318
+
1319
+ **Returns:** `{ allowed: boolean }`
1320
+
1321
+ ---
1322
+
1323
+ ## ⚛️ React Components
1324
+
1325
+ The SDK ships with pre-built React components for Post-Quantum authentication.
1326
+
1327
+ ### `<QidCloudLogin />`
1328
+
1329
+ A complete, styled login modal with QR scanning.
1330
+
1331
+ ```tsx
1332
+ import { QidCloudLogin } from '@qidcloud/sdk';
1333
+
1334
+ <QidCloudLogin
1335
+ sdk={qid}
1336
+ onSuccess={(user, token) => {
1337
+ console.log('Logged in as', user.username);
1338
+ localStorage.setItem('token', token);
1339
+ }}
1340
+ onError={(error) => console.error(error)}
1341
+ className="my-login-wrapper"
1342
+ />
1343
+ ```
1344
+
1345
+ | Prop | Type | Required | Description |
1346
+ |------|------|----------|-------------|
1347
+ | `sdk` | `QidCloud` | ✅ | Your initialized SDK instance |
1348
+ | `onSuccess` | `(user: QidUser, token: string) => void` | ✅ | Called on successful login |
1349
+ | `onError` | `(error: string) => void` | ❌ | Called on error |
1350
+ | `className` | `string` | ❌ | CSS class for the wrapper div |
1351
+
1352
+ > **Security Note**: This component only supports QR scanning (mobile app). Conventional login is intentionally excluded from the SDK to prevent password transmission in third-party apps.
1353
+
1354
+ ---
1355
+
1356
+ ### `<QidSignInButton />`
1357
+
1358
+ A minimal sign-in button that opens a QR modal when clicked.
1359
+
1360
+ ```tsx
1361
+ import { QidSignInButton } from '@qidcloud/sdk';
1362
+
1363
+ <QidSignInButton
1364
+ sdk={qid}
1365
+ onSuccess={(user, token) => { /* handle login */ }}
1366
+ onError={(err) => console.error(err)}
1367
+ buttonText="Sign in with QidCloud"
1368
+ className="my-btn"
1369
+ />
1370
+ ```
1371
+
1372
+ | Prop | Type | Required | Default | Description |
1373
+ |------|------|----------|---------|-------------|
1374
+ | `sdk` | `QidCloud` | ✅ | — | SDK instance |
1375
+ | `onSuccess` | `(user: QidUser, token: string) => void` | ✅ | — | Success callback |
1376
+ | `onError` | `(error: string) => void` | ❌ | — | Error callback |
1377
+ | `buttonText` | `string` | ❌ | `'LOGIN WITH QIDCLOUD'` | Button label |
1378
+ | `className` | `string` | ❌ | — | CSS class |
1379
+
1380
+ ---
1381
+
1382
+ ### `useQidAuth(sdk)`
1383
+
1384
+ A React hook for managing the full authentication lifecycle — handshake initialization, WebSocket listeners, session persistence, and auto-restore from localStorage.
1385
+
1386
+ ```tsx
1387
+ import { useQidAuth } from '@qidcloud/sdk';
1388
+
1389
+ function App() {
1390
+ const {
1391
+ user, // QidUser | null
1392
+ token, // string | null
1393
+ loading, // boolean — fetching profile
1394
+ error, // string | null
1395
+ session, // QidAuthSession | null — active QR session
1396
+ initializing, // boolean — creating handshake
1397
+ isExpired, // boolean — QR session expired
1398
+ timeLeft, // number — seconds until QR expires
1399
+ login, // () => Promise<void> — start handshake
1400
+ logout, // () => void — terminate session
1401
+ cancel, // () => void — cancel handshake
1402
+ setAuthenticated // (user, token) => void — manual auth set
1403
+ } = useQidAuth(qid);
1404
+
1405
+ if (user) return <div>Welcome, {user.username}!</div>;
1406
+
1407
+ return (
1408
+ <div>
1409
+ <button onClick={login}>Login</button>
1410
+ {session && <p>Scan QR... {timeLeft}s remaining</p>}
1411
+ {isExpired && <p>Session expired. <button onClick={login}>Retry</button></p>}
1412
+ {error && <p>Error: {error}</p>}
1413
+ </div>
1414
+ );
1415
+ }
1416
+ ```
1417
+
1418
+ | Return Field | Type | Description |
1419
+ |-------------|------|-------------|
1420
+ | `user` | `QidUser \| null` | Current authenticated user, or `null` |
1421
+ | `token` | `string \| null` | JWT session token |
1422
+ | `loading` | `boolean` | `true` while fetching user profile |
1423
+ | `error` | `string \| null` | Error message if login failed |
1424
+ | `session` | `QidAuthSession \| null` | Active QR handshake session |
1425
+ | `initializing` | `boolean` | `true` while creating handshake |
1426
+ | `isExpired` | `boolean` | `true` if QR session expired |
1427
+ | `timeLeft` | `number` | Seconds until QR session expires |
1428
+ | `login()` | `() => Promise<void>` | Start a new handshake session |
1429
+ | `logout()` | `() => void` | Terminate session and clear state |
1430
+ | `cancel()` | `() => void` | Cancel in-progress handshake |
1431
+ | `setAuthenticated()` | `(user, token) => void` | Manually set auth state (e.g. traditional login) |
1432
+
1433
+ ---
1434
+
1435
+ ## 📋 Type Reference
1436
+
1437
+ ### `QidConfig`
1438
+
1439
+ ```typescript
1440
+ interface QidConfig {
1441
+ apiKey: string;
1442
+ tenantId?: string;
1443
+ baseUrl?: string; // Default: 'https://api.qidcloud.com'
1444
+ }
1445
+ ```
1446
+
1447
+ ### `QidUser`
1448
+
1449
+ ```typescript
1450
+ interface QidUser {
1451
+ userId: string;
1452
+ regUserId: string;
1453
+ username: string;
1454
+ email?: string;
1455
+ role: string;
1456
+ fullName?: string;
1457
+ mfaEnabled?: boolean;
1458
+ createdAt?: Date;
1459
+ }
1460
+ ```
1461
+
1462
+ ### `QidSession`
1463
+
1464
+ ```typescript
1465
+ interface QidSession {
1466
+ token: string;
1467
+ lastActive: Date;
1468
+ ip?: string;
1469
+ userAgent?: string;
1470
+ }
1471
+ ```
1472
+
1473
+ ### `QidAuthSession`
1474
+
1475
+ ```typescript
1476
+ interface QidAuthSession {
1477
+ sessionId: string;
1478
+ qrData: string; // Stringified QR data for mobile app scanning
1479
+ expiresAt: number; // Milliseconds since epoch
1480
+ }
1481
+ ```
1482
+
1483
+ ### `QidDbResponse<T = any>`
1484
+
1485
+ ```typescript
1486
+ interface QidDbResponse<T = any> {
1487
+ success: boolean;
1488
+ data?: T;
1489
+ error?: string;
1490
+ count?: number;
1491
+ meta?: any;
1492
+ }
1493
+ ```
1494
+
1495
+ ### `QidEdgeResponse`
1496
+
1497
+ ```typescript
1498
+ interface QidEdgeResponse {
1499
+ success: boolean;
1500
+ result: any; // Return value from exports.handler
1501
+ computeTime: string; // e.g. '12ms'
1502
+ logs?: string[]; // Console output from the function
1503
+ }
1504
+ ```
1505
+
1506
+ ### `QidFile`
1507
+
1508
+ ```typescript
1509
+ interface QidFile {
1510
+ fileId: string;
1511
+ originalName: string;
1512
+ mimeType: string;
1513
+ size: number; // bytes
1514
+ createdAt: string;
1515
+ clientMetadata?: any;
1516
+ }
1517
+ ```
1518
+
1519
+ ### `QidUploadResponse`
1520
+
1521
+ ```typescript
1522
+ interface QidUploadResponse {
1523
+ success: boolean;
1524
+ message: string;
1525
+ file: {
1526
+ id: string;
1527
+ name: string;
1528
+ url: string; // Relative URL, prefix with baseUrl to access
1529
+ };
1530
+ }
1531
+ ```
1532
+
1533
+ ### `QidProject`
1534
+
1535
+ ```typescript
1536
+ interface QidProject {
1537
+ _id: string;
1538
+ name: string;
1539
+ tenantId: string;
1540
+ apiKey: string;
1541
+ owner: string;
1542
+ tier: 'free' | 'pro' | 'elite' | 'enterprise';
1543
+ status: 'active' | 'suspended' | 'pending';
1544
+ }
1545
+ ```
1546
+
1547
+ ### `QidProjectSettings`
1548
+
1549
+ ```typescript
1550
+ interface QidProjectSettings {
1551
+ displayName?: string;
1552
+ allowedOrigins?: string[];
1553
+ enclaveConfig?: any;
1554
+ }
1555
+ ```
1556
+
1557
+ ### `QidResourceStatus`
1558
+
1559
+ ```typescript
1560
+ interface QidResourceStatus {
1561
+ resourceType: string;
1562
+ status: 'active' | 'provisioning' | 'failed' | 'dissolved';
1563
+ endpoint?: string;
1564
+ details?: any;
1565
+ }
1566
+ ```
1567
+
1568
+ ### `QidBillingInfo`
1569
+
1570
+ ```typescript
1571
+ interface QidBillingInfo {
1572
+ plan: string;
1573
+ profile: string;
1574
+ estimatedMonthly: number;
1575
+ estimatedHourly: string;
1576
+ infraValueUSD: string;
1577
+ currency: string;
1578
+ activeEnclaves: string[];
1579
+ billingNote?: string;
1580
+ }
1581
+ ```
1582
+
1583
+ ### `QidTransaction`
1584
+
1585
+ ```typescript
1586
+ interface QidTransaction {
1587
+ transactionId: string;
1588
+ amount: number;
1589
+ date: string;
1590
+ status: string;
1591
+ planId: string;
1592
+ currency?: string;
1593
+ }
1594
+ ```
1595
+
1596
+ ### `QidBillingAlert`
1597
+
1598
+ ```typescript
1599
+ interface QidBillingAlert {
1600
+ _id: string;
1601
+ resource: string;
1602
+ threshold: number;
1603
+ status: string;
1604
+ }
1605
+ ```
1606
+
1607
+ ### `QidMigration`
1608
+
1609
+ ```typescript
1610
+ interface QidMigration {
1611
+ version: string; // Unique version identifier (e.g. '001', '002')
1612
+ name: string; // Human-readable name (e.g. 'create_users_table')
1613
+ up: () => Promise<void>; // Async function that applies the migration
1614
+ }
1615
+ ```
1616
+
1617
+ ### `QidMigrateResult`
1618
+
1619
+ ```typescript
1620
+ interface QidMigrateResult {
1621
+ success: boolean;
1622
+ applied: string[]; // Versions applied this run
1623
+ skipped: string[]; // Versions already applied previously
1624
+ failed?: {
1625
+ version: string;
1626
+ error: string;
1627
+ };
1628
+ total: number; // Total migrations in the registry
1629
+ }
1630
+ ```
1631
+
1632
+ ---
1633
+
1634
+ ## ❌ Error Handling
1635
+
1636
+ All SDK methods throw on network errors. Catch them with try/catch:
1637
+
1638
+ ```typescript
1639
+ try {
1640
+ const result = await qid.db.query('SELECT * FROM users');
1641
+ } catch (err) {
1642
+ // Network error, 401 Unauthorized, 403 Forbidden, etc.
1643
+ console.error(err.response?.status, err.response?.data);
1644
+ }
1645
+ ```
1646
+
1647
+ For methods that return `{ success, error }`, check the `success` field:
1648
+
1649
+ ```typescript
1650
+ const result = await qid.db.query('SELECT * FROM nonexistent');
1651
+ if (!result.success) {
1652
+ console.error('Query failed:', result.error);
1653
+ }
1654
+ ```
1655
+
1656
+ ---
1657
+
1658
+ ## 📄 License
1659
+
1660
+ ISC License — © 2026 QidCloud Enclave Security.