@qidcloud/sdk 1.2.3 → 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 +1561 -158
- package/dist/index.js +730 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +730 -13
- package/dist/index.mjs.map +1 -1
- package/dist/{index.d.ts → src/index.d.ts} +8 -0
- package/dist/src/modules/auth.d.ts +155 -0
- package/dist/src/modules/billing.d.ts +57 -0
- package/dist/src/modules/db.d.ts +49 -0
- package/dist/src/modules/project.d.ts +112 -0
- package/dist/src/modules/resource.d.ts +25 -0
- package/dist/src/modules/sdk.d.ts +29 -0
- package/dist/{modules → src/modules}/vault.d.ts +26 -14
- package/dist/src/types.d.ts +126 -0
- package/package.json +17 -3
- package/dist/modules/auth.d.ts +0 -27
- package/dist/modules/db.d.ts +0 -21
- package/dist/types.d.ts +0 -46
- package/rollup.config.mjs +0 -26
- package/src/components/QidCloudLogin.tsx +0 -64
- package/src/components/QidSignInButton.tsx +0 -260
- package/src/hooks/useQidAuth.ts +0 -179
- package/src/index.ts +0 -58
- package/src/modules/auth.ts +0 -106
- package/src/modules/db.ts +0 -40
- package/src/modules/edge.ts +0 -98
- package/src/modules/logs.ts +0 -30
- package/src/modules/vault.ts +0 -124
- package/src/types.ts +0 -52
- package/tsconfig.json +0 -31
- /package/dist/{components → src/components}/QidCloudLogin.d.ts +0 -0
- /package/dist/{components → src/components}/QidSignInButton.d.ts +0 -0
- /package/dist/{hooks → src/hooks}/useQidAuth.d.ts +0 -0
- /package/dist/{modules → src/modules}/edge.d.ts +0 -0
- /package/dist/{modules → src/modules}/logs.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,257 +1,1660 @@
|
|
|
1
1
|
# @qidcloud/sdk
|
|
2
2
|
|
|
3
|
-
The official JavaScript/TypeScript SDK for QidCloud
|
|
3
|
+
The official JavaScript/TypeScript SDK for **QidCloud** — build PQC-secured, enclave-backed applications with ease.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm install @qidcloud/sdk
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
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: '
|
|
18
|
-
tenantId: '
|
|
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
|
-
##
|
|
53
|
+
## Modules
|
|
25
54
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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 |
|
|
30
68
|
|
|
31
69
|
---
|
|
32
70
|
|
|
33
|
-
##
|
|
71
|
+
## 🔐 Authentication (`qid.auth`)
|
|
34
72
|
|
|
35
|
-
QidCloud
|
|
73
|
+
QidCloud supports **Post-Quantum Cryptography (PQC)** authentication via QR scanning, as well as traditional username/password login.
|
|
36
74
|
|
|
37
|
-
###
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
75
|
+
### `createSession()`
|
|
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
|
+
|
|
100
|
+
```typescript
|
|
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)`
|
|
41
111
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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'
|
|
45
124
|
});
|
|
125
|
+
```
|
|
46
126
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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'
|
|
59
151
|
```
|
|
60
152
|
|
|
61
|
-
|
|
62
|
-
For building your own UI while keeping the SDK's state management.
|
|
63
|
-
```tsx
|
|
64
|
-
import { useQidAuth } from '@qidcloud/sdk';
|
|
153
|
+
**Returns:** `{ success: boolean; message: string }`
|
|
65
154
|
|
|
66
|
-
|
|
67
|
-
const {
|
|
68
|
-
login, // Generates QR and starts polling
|
|
69
|
-
session, // Contains { qrData } to display
|
|
70
|
-
user, // Populated on success
|
|
71
|
-
logout
|
|
72
|
-
} = useQidAuth(sdk);
|
|
155
|
+
---
|
|
73
156
|
|
|
74
|
-
|
|
157
|
+
### `initiateRegistration(email)`
|
|
75
158
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
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
|
+
|
|
183
|
+
### `getProfile(token)`
|
|
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
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const user = await qid.auth.getProfile(token);
|
|
193
|
+
// user.userId, user.username, user.email, user.role, etc.
|
|
83
194
|
```
|
|
84
195
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
qidcloud://authorize?data=<JSON_PAYLOAD>&callback=<YOUR_APP_SCHEME>
|
|
100
|
-
```
|
|
101
|
-
- **data**: URL-encoded JSON containing `{ "sessionId": "..." }`.
|
|
102
|
-
- **callback**: Your app's deep link scheme (e.g., `myapp://auth-callback`).
|
|
103
|
-
3. **Handle Callback**: The QidCloud app will redirect back to your callback URL with status results:
|
|
104
|
-
- **Success**: `myapp://auth-callback?status=accepted&userId=<APPROVER_ID>`
|
|
105
|
-
- **Rejection**: `myapp://auth-callback?status=rejected`
|
|
106
|
-
4. **Finalize**: Your app should listen for the `authenticated` event on the socket (as shown in Step 2) to receive the final session token.
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
### 🔐 Session Management
|
|
110
|
-
- **Persistence**: The SDK automatically caches the token in `localStorage` under `qid_auth_token`.
|
|
111
|
-
- **Restoration**: On page load, `useQidAuth` checks for this token and verifies it via `/api/me`.
|
|
112
|
-
- **Expiry**: Tokens expire after **1 hour of inactivity**. The backend implements a sliding window, so active sessions automatically extend their validity. Handle 401 errors by calling `logout()`.
|
|
113
|
-
|
|
114
|
-
### 🚪 Logout Handling
|
|
115
|
-
Always use the SDK's `logout()` method to preventing "ghost sessions".
|
|
116
|
-
```tsx
|
|
117
|
-
const { logout } = useQidAuth(sdk);
|
|
118
|
-
// ...
|
|
119
|
-
<button onClick={logout}>Sign Out</button>
|
|
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
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
await qid.auth.logout(token);
|
|
120
210
|
```
|
|
121
|
-
**What `logout()` does:**
|
|
122
|
-
1. Calls `POST /api/logout` to invalidate the session on the server.
|
|
123
|
-
2. Removes the token from `localStorage`.
|
|
124
|
-
3. Clears in-memory state.
|
|
125
211
|
|
|
126
212
|
---
|
|
127
213
|
|
|
128
|
-
|
|
214
|
+
### `disconnect()`
|
|
215
|
+
|
|
216
|
+
Stop listening on the WebSocket and disconnect. Does not invalidate the session server-side.
|
|
129
217
|
|
|
130
|
-
### `upload(file, name, metadata?, userToken?)`
|
|
131
|
-
Encrypt and upload a file to the secure enclave.
|
|
132
|
-
- **Returns**: `Promise<QidUploadResponse>`
|
|
133
218
|
```typescript
|
|
134
|
-
|
|
219
|
+
qid.auth.disconnect();
|
|
135
220
|
```
|
|
136
221
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
|
|
140
232
|
```typescript
|
|
141
|
-
const
|
|
233
|
+
const { token: newToken } = await qid.auth.refreshSession(token);
|
|
142
234
|
```
|
|
143
235
|
|
|
144
|
-
|
|
145
|
-
|
|
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
|
+
|
|
146
248
|
```typescript
|
|
147
|
-
const
|
|
249
|
+
const sessions = await qid.auth.getSessions(token);
|
|
250
|
+
// sessions[0].token, sessions[0].lastActive, sessions[0].ip, sessions[0].userAgent
|
|
148
251
|
```
|
|
149
252
|
|
|
150
|
-
|
|
151
|
-
|
|
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
|
+
|
|
152
266
|
```typescript
|
|
153
|
-
await qid.
|
|
267
|
+
await qid.auth.revokeSession(myToken, 'target-session-token');
|
|
154
268
|
```
|
|
155
269
|
|
|
270
|
+
**Returns:** `{ success: boolean }`
|
|
271
|
+
|
|
156
272
|
---
|
|
157
273
|
|
|
158
|
-
|
|
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 |
|
|
159
335
|
|
|
160
|
-
### `invoke(name, params?, userToken?)`
|
|
161
|
-
Invoke a serverless edge function.
|
|
162
|
-
- **Returns**: `Promise<QidEdgeResponse>`
|
|
163
336
|
```typescript
|
|
164
|
-
|
|
337
|
+
await qid.auth.toggleMFA(token, { enabled: true, type: 'email' });
|
|
165
338
|
```
|
|
166
339
|
|
|
167
|
-
|
|
168
|
-
|
|
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
|
+
|
|
169
391
|
```typescript
|
|
170
|
-
const
|
|
392
|
+
const data = await qid.auth.exportUserData(token);
|
|
171
393
|
```
|
|
172
394
|
|
|
173
|
-
|
|
174
|
-
|
|
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
|
+
|
|
413
|
+
```typescript
|
|
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
|
+
`);
|
|
434
|
+
```
|
|
435
|
+
|
|
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
|
+
|
|
175
562
|
```typescript
|
|
176
|
-
const
|
|
563
|
+
const result = await qid.edge.invoke('calculate-tax', {
|
|
564
|
+
amount: 1000,
|
|
565
|
+
region: 'US'
|
|
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']
|
|
177
571
|
```
|
|
178
572
|
|
|
573
|
+
**Returns:** [`QidEdgeResponse`](#qidedgeresponse)
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
179
577
|
### `deploy(data, userToken?)`
|
|
180
|
-
|
|
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 |
|
|
589
|
+
|
|
181
590
|
```typescript
|
|
182
|
-
await qid.edge.deploy({
|
|
183
|
-
name: '
|
|
184
|
-
code:
|
|
185
|
-
|
|
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
|
|
186
602
|
}, userToken);
|
|
187
603
|
```
|
|
188
604
|
|
|
605
|
+
**Returns:** `{ success: boolean; message: string }`
|
|
606
|
+
|
|
189
607
|
---
|
|
190
608
|
|
|
191
|
-
|
|
609
|
+
### `list()`
|
|
610
|
+
|
|
611
|
+
List all deployed edge functions in the project.
|
|
192
612
|
|
|
193
|
-
### `write(message, source?, level?, meta?)`
|
|
194
|
-
Ingest custom telemetry into the enclave.
|
|
195
613
|
```typescript
|
|
196
|
-
await qid.
|
|
614
|
+
const functions = await qid.edge.list();
|
|
197
615
|
```
|
|
198
616
|
|
|
199
|
-
|
|
200
|
-
|
|
617
|
+
**Returns:** `any[]` — Array of function metadata objects.
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
### `delete(name, userToken?)`
|
|
622
|
+
|
|
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
|
+
|
|
201
630
|
```typescript
|
|
202
|
-
|
|
631
|
+
await qid.edge.delete('old-function', userToken);
|
|
203
632
|
```
|
|
204
633
|
|
|
634
|
+
**Returns:** `{ success: boolean }`
|
|
635
|
+
|
|
205
636
|
---
|
|
206
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[]`
|
|
207
648
|
|
|
208
649
|
---
|
|
209
650
|
|
|
210
|
-
|
|
651
|
+
### `getProjectLogs(userToken?)`
|
|
211
652
|
|
|
212
|
-
|
|
653
|
+
Get all edge function logs across the entire project.
|
|
213
654
|
|
|
214
|
-
|
|
215
|
-
- [ ] **Environment Variables**: Store your `apiKey` and `tenantId` in environment variables (e.g., `VITE_QID_API_KEY`) ensuring they are not hardcoded in version control.
|
|
216
|
-
- [ ] **Error Boundaries**: Wrap the `QidCloudLogin` component in a React Error Boundary to gracefully handle any unexpected runtime failures.
|
|
217
|
-
- [ ] **Strict Mode**: If using React Strict Mode, ensure your `useEffect` logic handles double-invocation (The SDK's hooks are already optimized for this).
|
|
218
|
-
- [ ] **CORS**: Double-check that all your production domains (including `www` and non-`www`) are whitelisted in the QidCloud Console.
|
|
655
|
+
**Returns:** `any[]`
|
|
219
656
|
|
|
220
|
-
|
|
657
|
+
---
|
|
221
658
|
|
|
222
|
-
### `
|
|
223
|
-
Hook for managing authentication state.
|
|
224
|
-
```tsx
|
|
225
|
-
const { user, login, logout } = useQidAuth(qid);
|
|
226
|
-
```
|
|
659
|
+
### `getRuntimes()`
|
|
227
660
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
```
|
|
231
|
-
|
|
661
|
+
List available serverless runtimes.
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
const runtimes = await qid.edge.getRuntimes();
|
|
665
|
+
// ['nodejs18.x', 'nodejs20.x', ...]
|
|
232
666
|
```
|
|
233
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
|
+
|
|
234
683
|
---
|
|
235
684
|
|
|
236
|
-
##
|
|
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.
|
|
237
688
|
|
|
238
|
-
###
|
|
239
|
-
If you are developing the SDK locally and linking it to your project (via `npm link` or `npm install ../sdk-path`), you might encounter an "Invalid hook call" error. This is usually caused by **Duplicate React** (the app and the SDK are using different React instances).
|
|
689
|
+
### `upload(file, fileName, metadata?, userToken?, options?)`
|
|
240
690
|
|
|
241
|
-
**
|
|
242
|
-
|
|
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) |
|
|
243
700
|
|
|
244
701
|
```typescript
|
|
245
|
-
//
|
|
246
|
-
|
|
702
|
+
// Single-shot upload (< 10MB)
|
|
703
|
+
const result = await qid.vault.upload(fileBlob, 'secret.pdf');
|
|
247
704
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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`);
|
|
252
710
|
}
|
|
253
|
-
})
|
|
711
|
+
});
|
|
254
712
|
```
|
|
255
713
|
|
|
256
|
-
**
|
|
257
|
-
|
|
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.
|