@qidcloud/sdk 1.2.0 → 1.2.1

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
@@ -28,112 +28,35 @@ Enforce Pure Post-Quantum Identity with zero passwords.
28
28
  ### `createSession()`
29
29
  Initiates a new QR handshake session.
30
30
  - **Returns**: `Promise<QidAuthSession>`
31
- - `sessionId`: String
32
- - `qrData`: String (Format: `qid:handshake:...`)
33
- - `expiresAt`: Number (Timestamp)
34
-
35
- ### `listen(sessionId, onAuthorized, onDenied)`
36
- Listens for the mobile app to approve the session.
37
- - **`sessionId`**: String (from `createSession`)
38
- - **`onAuthorized`**: `(token: string) => void` - Callback when login succeeds.
39
- - **`onDenied`**: `(msg: string) => void` - Callback when user rejects.
40
-
41
- ### `getProfile(token)`
42
- Fetches user details.
43
- - **`token`**: String (The session token)
44
- - **Returns**: `Promise<QidUser>`
45
-
46
- ---
47
-
48
- ## 🗄️ Enclave DB (`qid.db`)
49
-
50
- Programmatic SQL access to your project's isolated Postgres schema.
51
-
52
- ### `query(sql, params, userToken?)`
53
- Executor for **all** SQL operations (Select, Insert, Update, Delete, and DDL).
54
- - **`sql`**: String (e.g., `'INSERT INTO users (name) VALUES ($1) RETURNING *'`)
55
- - **`params`**: Array (Parameterized values to prevent SQL injection)
56
- - **`userToken`**: Optional String (Required if the user's role must be checked for Mutations/Schema changes)
57
- - **Returns**: `Promise<QidDbResponse>`
58
-
59
- **Examples:**
60
- ```javascript
61
- // Mutation (Write)
62
- await qid.db.query('INSERT INTO logs (message) VALUES ($1)', ['User logged in']);
63
-
64
- // Schema (DDL)
65
- await qid.db.query('CREATE TABLE IF NOT EXISTS test (id SERIAL PRIMARY KEY)');
66
- ```
67
-
68
- ### `setup(userToken?)`
69
- Ensures the project's isolated Postgres schema is properly initialized.
70
- - **`userToken`**: Optional String
71
- - **Returns**: `Promise<{ success: boolean; schemaName: string }>`
72
31
 
73
32
  ---
74
33
 
75
- ## Edge Compute (`qid.edge`)
76
-
77
- Invoke serverless functions within your enclave.
34
+ ## 🏗️ React Integration
78
35
 
79
- ### `invoke(name, params, userToken?)`
80
- - **`name`**: String (Function name)
81
- - **`params`**: Object (Arguments)
82
- - **`userToken`**: Optional String
83
- - **Returns**: `Promise<QidEdgeResponse>`
36
+ The SDK provides powerful React tools for seamless integration.
84
37
 
85
- ### `list()`
86
- Lists all deployed functions in the project.
38
+ ### `useQidAuth(sdk)`
39
+ The recommended way to manage auth state in React.
87
40
 
88
- ### `getLogs(name, userToken?)`
89
- Fetches the latest execution logs for a specific function.
90
- - **`name`**: String (Function name)
91
-
92
- ### `getProjectLogs(userToken?)`
93
- Fetches all enclave-wide system logs. Great for debugging connectivity/auth issues.
94
-
95
- ### `delete(name, userToken?)`
96
- Permanently destroys a function from the enclave.
97
-
98
- ---
99
-
100
- ## 📦 Vault Storage (`qid.vault`)
101
-
102
- Secure file management.
103
-
104
- ### `upload(file, fileName, metadata?, userToken?)`
105
- - **`file`**: Buffer | Blob
106
- - **`fileName`**: String
107
- - **`metadata`**: Object (Custom tags/E2EE keys)
108
- - **`userToken`**: Optional String
109
-
110
- ### `list(userToken?)`
111
- Lists enclave files.
112
-
113
- ### `delete(fileId, userToken?)`
114
- Permanently destroys a file from storage.
115
-
116
- ---
117
-
118
- ## 📝 App Logs (`qid.logs`)
119
-
120
- Audit logs for your application logic.
121
-
122
- ### `write(message, source?, level?, metadata?)`
123
- - **`message`**: String (Log content)
124
- - **`source`**: String (Optional, e.g., 'frontend', 'auth-service')
125
- - **`level`**: String (Default: `'info'`)
126
- - **`metadata`**: Object (e.g., `{ userId: '123', event: 'checkout' }`)
41
+ ```tsx
42
+ import { useQidAuth } from '@qidcloud/sdk';
127
43
 
128
- ### `fetch(query?)`
129
- - **`query`**: Object (e.g., `{ limit: 50, level: 'error' }`)
44
+ function MyComponent({ qid }) {
45
+ const { user, token, session, login, initializing } = useQidAuth(qid);
130
46
 
131
- ---
47
+ if (user) return <p>Welcome, {user.username}</p>;
132
48
 
133
- ## 🎨 UI Components (React)
49
+ return (
50
+ <button onClick={login}>
51
+ {initializing ? 'Connecting...' : 'Login'}
52
+ </button>
53
+ );
54
+ }
55
+ ```
134
56
 
135
57
  ### `QidSignInButton`
136
- The easiest way to add QR login.
58
+ A plug-and-play button that handles the entire handshake UI.
59
+
137
60
  ```tsx
138
61
  import { QidSignInButton } from '@qidcloud/sdk';
139
62
 
@@ -141,13 +64,5 @@ import { QidSignInButton } from '@qidcloud/sdk';
141
64
  sdk={qid}
142
65
  onSuccess={(user, token) => console.log(user)}
143
66
  onError={(err) => alert(err)}
144
- buttonText="Authorize with QidCloud"
145
67
  />
146
68
  ```
147
-
148
- **Props:**
149
- - `sdk`: Instance of `QidCloud`
150
- - `onSuccess`: `(user, token) => void`
151
- - `onError`: `(error) => void`
152
- - `buttonText`: String (Default: "Login with QidCloud")
153
- - `className`: String (Custom CSS class)
@@ -8,5 +8,8 @@ interface QidSignInButtonProps {
8
8
  className?: string;
9
9
  buttonText?: string;
10
10
  }
11
+ /**
12
+ * A ready-to-use React component for QidCloud QR identity authentication.
13
+ */
11
14
  export declare const QidSignInButton: React.FC<QidSignInButtonProps>;
12
15
  export {};
@@ -0,0 +1,18 @@
1
+ import { QidCloud } from '../index';
2
+ import { QidUser, QidAuthSession } from '../types';
3
+ export interface UseQidAuthReturn {
4
+ user: QidUser | null;
5
+ token: string | null;
6
+ loading: boolean;
7
+ error: string | null;
8
+ session: QidAuthSession | null;
9
+ initializing: boolean;
10
+ login: () => Promise<void>;
11
+ logout: () => void;
12
+ cancel: () => void;
13
+ }
14
+ /**
15
+ * A React hook for managing QidCloud authentication lifecycle.
16
+ * Handles handshake initialization, WebSocket listeners, and profile fetching.
17
+ */
18
+ export declare function useQidAuth(sdk: QidCloud): UseQidAuthReturn;
package/dist/index.d.ts CHANGED
@@ -22,3 +22,4 @@ export declare class QidCloud {
22
22
  export default QidCloud;
23
23
  export * from './types';
24
24
  export { QidSignInButton } from './components/QidSignInButton';
25
+ export { useQidAuth } from './hooks/useQidAuth';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var axios = require('axios');
6
6
  var socket_ioClient = require('socket.io-client');
7
+ var qrcode_react = require('qrcode.react');
7
8
 
8
9
  class AuthModule {
9
10
  sdk;
@@ -160,6 +161,37 @@ class EdgeModule {
160
161
  const resp = await this.sdk.api.delete(`/api/edge/${name}`, { headers });
161
162
  return resp.data;
162
163
  }
164
+ /**
165
+ * Deploy a new function or update an existing one
166
+ * @param data Deployment data including name, code, and optional envVars
167
+ * @param userToken Optional session token
168
+ */
169
+ async deploy(data, userToken) {
170
+ const headers = {};
171
+ if (userToken)
172
+ headers['Authorization'] = `Bearer ${userToken}`;
173
+ const resp = await this.sdk.api.post('/api/edge/deploy', data, { headers });
174
+ return resp.data;
175
+ }
176
+ /**
177
+ * Get available serverless runtimes
178
+ */
179
+ async getRuntimes() {
180
+ const resp = await this.sdk.api.get('/api/edge/runtimes');
181
+ return resp.data;
182
+ }
183
+ /**
184
+ * Toggle centralized logging for the project's edge functions
185
+ * @param enabled Whether to enable or disable logging
186
+ * @param userToken Optional session token
187
+ */
188
+ async toggleLogging(enabled, userToken) {
189
+ const headers = {};
190
+ if (userToken)
191
+ headers['Authorization'] = `Bearer ${userToken}`;
192
+ const resp = await this.sdk.api.post('/api/edge/logs/settings', { enabled }, { headers });
193
+ return resp.data;
194
+ }
163
195
  }
164
196
 
165
197
  class VaultModule {
@@ -216,7 +248,7 @@ class VaultModule {
216
248
  return resp.data;
217
249
  }
218
250
  /**
219
- * Delete a file from storage
251
+ * Delete a file from storage (Soft Delete)
220
252
  * @param fileId Unique ID of the file
221
253
  * @param userToken Session token for user-scoped storage
222
254
  */
@@ -228,6 +260,44 @@ class VaultModule {
228
260
  const resp = await this.sdk.api.delete(`/api/storage/${fileId}`, { headers });
229
261
  return resp.data;
230
262
  }
263
+ /**
264
+ * List deleted files in the project enclave (Recycle Bin)
265
+ * @param userToken Session token for user-scoped storage
266
+ */
267
+ async listDeleted(userToken) {
268
+ const headers = {};
269
+ if (userToken) {
270
+ headers['Authorization'] = `Bearer ${userToken}`;
271
+ }
272
+ const resp = await this.sdk.api.get('/api/storage/deleted', { headers });
273
+ return resp.data;
274
+ }
275
+ /**
276
+ * Restore a deleted file from the recycle bin
277
+ * @param fileId Unique ID of the file
278
+ * @param userToken Session token for user-scoped storage
279
+ */
280
+ async restore(fileId, userToken) {
281
+ const headers = {};
282
+ if (userToken) {
283
+ headers['Authorization'] = `Bearer ${userToken}`;
284
+ }
285
+ const resp = await this.sdk.api.post(`/api/storage/restore/${fileId}`, {}, { headers });
286
+ return resp.data;
287
+ }
288
+ /**
289
+ * Permanently purge a deleted file
290
+ * @param fileId Unique ID of the file
291
+ * @param userToken Session token for user-scoped storage
292
+ */
293
+ async purge(fileId, userToken) {
294
+ const headers = {};
295
+ if (userToken) {
296
+ headers['Authorization'] = `Bearer ${userToken}`;
297
+ }
298
+ const resp = await this.sdk.api.delete(`/api/storage/purge/${fileId}`, { headers });
299
+ return resp.data;
300
+ }
231
301
  }
232
302
 
233
303
  class LogsModule {
@@ -2126,87 +2196,199 @@ function requireReact () {
2126
2196
  var reactExports = requireReact();
2127
2197
  var React = /*@__PURE__*/getDefaultExportFromCjs(reactExports);
2128
2198
 
2129
- const QidSignInButton = ({ sdk, onSuccess, onError, className, buttonText = 'Login with QidCloud' }) => {
2130
- const [modalOpen, setModalOpen] = reactExports.useState(false);
2131
- const [qrData, setQrData] = reactExports.useState(null);
2199
+ /**
2200
+ * A React hook for managing QidCloud authentication lifecycle.
2201
+ * Handles handshake initialization, WebSocket listeners, and profile fetching.
2202
+ */
2203
+ function useQidAuth(sdk) {
2204
+ const [user, setUser] = reactExports.useState(null);
2205
+ const [token, setToken] = reactExports.useState(null);
2206
+ const [session, setSession] = reactExports.useState(null);
2132
2207
  const [loading, setLoading] = reactExports.useState(false);
2133
- const startLogin = async () => {
2134
- setLoading(true);
2208
+ const [initializing, setInitializing] = reactExports.useState(false);
2209
+ const [error, setError] = reactExports.useState(null);
2210
+ // Use ref to track if we should still be listening (to avoid state updates after unmount/cancel)
2211
+ const activeSessionId = reactExports.useRef(null);
2212
+ const logout = reactExports.useCallback(() => {
2213
+ setUser(null);
2214
+ setToken(null);
2215
+ setSession(null);
2216
+ sdk.auth.disconnect();
2217
+ }, [sdk]);
2218
+ const cancel = reactExports.useCallback(() => {
2219
+ activeSessionId.current = null;
2220
+ setSession(null);
2221
+ setInitializing(false);
2222
+ sdk.auth.disconnect();
2223
+ }, [sdk]);
2224
+ const login = reactExports.useCallback(async () => {
2225
+ setInitializing(true);
2226
+ setError(null);
2135
2227
  try {
2136
- const session = await sdk.auth.createSession();
2137
- setQrData(session.qrData);
2138
- setModalOpen(true);
2139
- sdk.auth.listen(session.sessionId, async (token) => {
2140
- const user = await sdk.auth.getProfile(token);
2141
- setModalOpen(false);
2142
- onSuccess(user, token);
2228
+ const newSession = await sdk.auth.createSession();
2229
+ setSession(newSession);
2230
+ activeSessionId.current = newSession.sessionId;
2231
+ sdk.auth.listen(newSession.sessionId, async (receivedToken) => {
2232
+ // Safety check: is this still the active session?
2233
+ if (activeSessionId.current !== newSession.sessionId)
2234
+ return;
2235
+ setLoading(true);
2236
+ setInitializing(false);
2237
+ try {
2238
+ const profile = await sdk.auth.getProfile(receivedToken);
2239
+ setToken(receivedToken);
2240
+ setUser(profile);
2241
+ }
2242
+ catch (err) {
2243
+ setError(err.message || 'Failed to fetch user profile');
2244
+ }
2245
+ finally {
2246
+ setLoading(false);
2247
+ setSession(null);
2248
+ }
2143
2249
  }, (err) => {
2144
- if (onError)
2145
- onError(err);
2146
- setModalOpen(false);
2250
+ if (activeSessionId.current !== newSession.sessionId)
2251
+ return;
2252
+ setError(err);
2253
+ setInitializing(false);
2254
+ setSession(null);
2147
2255
  });
2148
2256
  }
2149
2257
  catch (err) {
2150
- if (onError)
2151
- onError(err.message || 'Failed to initiate login');
2152
- }
2153
- finally {
2154
- setLoading(false);
2258
+ setError(err.message || 'Failed to initiate login handshake');
2259
+ setInitializing(false);
2155
2260
  }
2261
+ }, [sdk]);
2262
+ // Cleanup on unmount
2263
+ reactExports.useEffect(() => {
2264
+ return () => {
2265
+ sdk.auth.disconnect();
2266
+ };
2267
+ }, [sdk]);
2268
+ return {
2269
+ user,
2270
+ token,
2271
+ loading,
2272
+ error,
2273
+ session,
2274
+ initializing,
2275
+ login,
2276
+ logout,
2277
+ cancel
2156
2278
  };
2279
+ }
2280
+
2281
+ /**
2282
+ * A ready-to-use React component for QidCloud QR identity authentication.
2283
+ */
2284
+ const QidSignInButton = ({ sdk, onSuccess, onError, className, buttonText = 'Login with QidCloud' }) => {
2285
+ const { user, token, error, session, initializing, login, cancel } = useQidAuth(sdk);
2286
+ // Watch for success
2287
+ React.useEffect(() => {
2288
+ if (user && token) {
2289
+ onSuccess(user, token);
2290
+ }
2291
+ }, [user, token, onSuccess]);
2292
+ // Watch for errors
2293
+ React.useEffect(() => {
2294
+ if (error && onError) {
2295
+ onError(error);
2296
+ }
2297
+ }, [error, onError]);
2157
2298
  return (React.createElement(React.Fragment, null,
2158
- React.createElement("button", { onClick: startLogin, disabled: loading, className: className || 'qid-signin-btn', style: {
2299
+ React.createElement("button", { onClick: login, disabled: initializing || !!session, className: className || 'qid-signin-btn', style: {
2159
2300
  backgroundColor: '#00e5ff',
2160
2301
  color: '#000',
2161
- padding: '10px 20px',
2162
- borderRadius: '8px',
2302
+ padding: '12px 24px',
2303
+ borderRadius: '12px',
2163
2304
  border: 'none',
2164
- fontWeight: 'bold',
2165
- cursor: 'pointer',
2305
+ fontWeight: '900',
2306
+ cursor: (initializing || !!session) ? 'not-allowed' : 'pointer',
2166
2307
  display: 'flex',
2167
2308
  alignItems: 'center',
2168
- gap: '10px'
2169
- } }, loading ? 'Initializing...' : buttonText),
2170
- modalOpen && (React.createElement("div", { className: "qid-modal-overlay", style: {
2309
+ gap: '10px',
2310
+ textTransform: 'uppercase',
2311
+ fontSize: '0.85rem',
2312
+ letterSpacing: '0.5px',
2313
+ boxShadow: '0 4px 15px rgba(0, 229, 255, 0.3)',
2314
+ transition: 'all 0.2s'
2315
+ } },
2316
+ React.createElement("div", { style: {
2317
+ width: '8px',
2318
+ height: '8px',
2319
+ borderRadius: '50%',
2320
+ backgroundColor: (initializing || !!session) ? '#333' : '#000',
2321
+ boxShadow: (initializing || !!session) ? 'none' : '0 0 8px rgba(0,0,0,0.5)'
2322
+ } }),
2323
+ initializing ? 'Preparing Handshake...' : (session ? 'Awaiting Scan...' : buttonText)),
2324
+ session && (React.createElement("div", { className: "qid-modal-overlay", style: {
2171
2325
  position: 'fixed',
2172
2326
  top: 0, left: 0, right: 0, bottom: 0,
2173
- backgroundColor: 'rgba(0,0,0,0.8)',
2327
+ backgroundColor: 'rgba(0,0,0,0.92)',
2174
2328
  display: 'flex',
2175
2329
  justifyContent: 'center',
2176
2330
  alignItems: 'center',
2177
- zIndex: 9999
2331
+ zIndex: 9999,
2332
+ backdropFilter: 'blur(8px)'
2178
2333
  } },
2179
2334
  React.createElement("div", { className: "qid-modal-content", style: {
2180
- backgroundColor: '#1a1a1a',
2181
- padding: '30px',
2182
- borderRadius: '16px',
2335
+ backgroundColor: '#0a0a0a',
2336
+ padding: '40px',
2337
+ borderRadius: '28px',
2183
2338
  textAlign: 'center',
2184
2339
  color: '#fff',
2185
- maxWidth: '400px',
2186
- width: '90%'
2340
+ maxWidth: '420px',
2341
+ width: '90%',
2342
+ border: '1px solid rgba(0, 229, 255, 0.2)',
2343
+ boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5)'
2187
2344
  } },
2188
- React.createElement("h2", { style: { marginBottom: '10px' } }, "Identity Handshake"),
2189
- React.createElement("p", { style: { marginBottom: '20px', fontSize: '14px', color: '#ccc' } }, "Open your QidCloud Mobile App and scan this PQC-secured QR code."),
2345
+ React.createElement("div", { style: { marginBottom: '25px' } },
2346
+ React.createElement("div", { style: {
2347
+ display: 'inline-block',
2348
+ padding: '12px 20px',
2349
+ borderRadius: '30px',
2350
+ backgroundColor: 'rgba(0, 229, 255, 0.1)',
2351
+ border: '1px solid rgba(0, 229, 255, 0.3)',
2352
+ marginBottom: '15px'
2353
+ } },
2354
+ React.createElement("span", { style: { color: '#00e5ff', fontSize: '0.7rem', fontWeight: '900', letterSpacing: '2px' } }, "PQC ENCLAVE SECURED")),
2355
+ React.createElement("h2", { style: { fontSize: '1.75rem', fontWeight: '900', marginBottom: '10px', letterSpacing: '-0.5px' } }, "Identity Handshake"),
2356
+ React.createElement("p", { style: { fontSize: '0.95rem', color: '#94a3b8', lineHeight: '1.6' } }, "Scan this encrypted gateway with your QidCloud app to authorize access.")),
2190
2357
  React.createElement("div", { style: {
2191
2358
  backgroundColor: '#fff',
2192
- padding: '10px',
2359
+ padding: '24px',
2193
2360
  display: 'inline-block',
2194
- borderRadius: '8px',
2195
- marginBottom: '20px'
2361
+ borderRadius: '24px',
2362
+ marginBottom: '30px',
2363
+ boxShadow: '0 10px 40px rgba(0,0,0,0.3)'
2196
2364
  } },
2197
- React.createElement("div", { style: { color: '#000', fontSize: '12px', wordBreak: 'break-all', width: '200px' } },
2198
- "QR: ",
2199
- qrData)),
2200
- React.createElement("button", { onClick: () => setModalOpen(false), style: {
2201
- display: 'block',
2202
- width: '100%',
2203
- padding: '10px',
2204
- backgroundColor: '#333',
2205
- color: '#fff',
2206
- border: 'none',
2207
- borderRadius: '8px',
2208
- cursor: 'pointer'
2209
- } }, "Cancel"))))));
2365
+ React.createElement(qrcode_react.QRCodeSVG, { value: session.qrData, size: 220, level: "H", includeMargin: false, imageSettings: {
2366
+ src: "https://api.qidcloud.com/favicon.ico",
2367
+ x: undefined,
2368
+ y: undefined,
2369
+ height: 40,
2370
+ width: 40,
2371
+ excavate: true,
2372
+ } })),
2373
+ React.createElement("div", { style: { display: 'flex', flexDirection: 'column', gap: '15px' } },
2374
+ React.createElement("div", { style: { height: '4px', width: '60px', backgroundColor: '#00e5ff', margin: '0 auto', borderRadius: '2px', opacity: 0.5 } }),
2375
+ React.createElement("button", { onClick: cancel, style: {
2376
+ display: 'block',
2377
+ width: '100%',
2378
+ padding: '16px',
2379
+ backgroundColor: 'transparent',
2380
+ color: '#64748b',
2381
+ border: '1px solid rgba(148, 163, 184, 0.2)',
2382
+ borderRadius: '16px',
2383
+ cursor: 'pointer',
2384
+ fontWeight: '700',
2385
+ transition: 'all 0.2s',
2386
+ fontSize: '0.9rem'
2387
+ } }, "Cancel Handshake")),
2388
+ React.createElement("div", { style: { marginTop: '25px', fontSize: '0.75rem', color: '#475569' } },
2389
+ "Session ID: ",
2390
+ session.sessionId.slice(0, 8),
2391
+ "..."))))));
2210
2392
  };
2211
2393
 
2212
2394
  class QidCloud {
@@ -2246,4 +2428,5 @@ class QidCloud {
2246
2428
  exports.QidCloud = QidCloud;
2247
2429
  exports.QidSignInButton = QidSignInButton;
2248
2430
  exports.default = QidCloud;
2431
+ exports.useQidAuth = useQidAuth;
2249
2432
  //# sourceMappingURL=index.js.map