@thinkingdifferently/core 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +321 -0
- package/dist/index.d.mts +94 -4
- package/dist/index.d.ts +94 -4
- package/dist/index.js +433 -41
- package/dist/index.mjs +431 -41
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,29 +1,172 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
var TDClient = class {
|
|
4
|
-
constructor(apiKey) {
|
|
4
|
+
constructor(apiKey, securityKey, publicKey) {
|
|
5
|
+
this.adminToken = null;
|
|
6
|
+
this.apikey = apiKey;
|
|
7
|
+
this.securityKey = securityKey;
|
|
8
|
+
this.publicKey = publicKey;
|
|
5
9
|
this.api = axios.create({
|
|
6
|
-
baseURL: "
|
|
10
|
+
baseURL: "http://localhost:3000/api/v1",
|
|
11
|
+
// baseURL: "https://www.thinkingdifferently.dev/api/v1",
|
|
7
12
|
headers: {
|
|
8
|
-
"
|
|
13
|
+
"X-API-Key": apiKey,
|
|
9
14
|
"Content-Type": "application/json"
|
|
10
|
-
}
|
|
15
|
+
},
|
|
16
|
+
withCredentials: true
|
|
17
|
+
// Enable browser cookie sharing automatically
|
|
11
18
|
});
|
|
12
19
|
}
|
|
13
|
-
|
|
20
|
+
//what is hte use of withCredentials: true in axios config?
|
|
21
|
+
//The withCredentials: true option in the Axios configuration is used to indicate that cross-site Access-Control requests should be made using credentials such as cookies,
|
|
22
|
+
// authorization headers, or TLS client certificates. When this option is set to true, it allows the browser to include cookies and other credentials in requests made to
|
|
23
|
+
// a different domain than the one serving the web page. This is particularly important for maintaining sessions and authentication when making API calls to a backend server
|
|
24
|
+
// that is on a different domain than the frontend application.
|
|
25
|
+
setSecurityKey(key) {
|
|
26
|
+
this.securityKey = key;
|
|
27
|
+
}
|
|
28
|
+
setPublicKey(key) {
|
|
29
|
+
this.publicKey = key;
|
|
30
|
+
}
|
|
31
|
+
setAdminToken(token) {
|
|
32
|
+
this.adminToken = token;
|
|
33
|
+
}
|
|
34
|
+
getAdminToken() {
|
|
35
|
+
return this.adminToken;
|
|
36
|
+
}
|
|
37
|
+
getPublicKey() {
|
|
38
|
+
return this.publicKey;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reusable private error formatting utility
|
|
42
|
+
*/
|
|
43
|
+
// private handleError(error: any): Error {
|
|
44
|
+
// if (error.response && error.response.data && error.response.data.error) {
|
|
45
|
+
// const backendError = error.response.data.error;
|
|
46
|
+
//
|
|
47
|
+
// let errorMessage = `[ThinkingDifferently SDK Error] ${backendError.code || error.response.status}: ${backendError.message}`;
|
|
48
|
+
//
|
|
49
|
+
// if (backendError.details) {
|
|
50
|
+
// errorMessage += `\nDetails: ${JSON.stringify(backendError.details, null, 2)}`;
|
|
51
|
+
// }
|
|
52
|
+
//
|
|
53
|
+
// return new Error(errorMessage);
|
|
54
|
+
// }
|
|
55
|
+
//
|
|
56
|
+
// return new Error(
|
|
57
|
+
// `[ThinkingDifferently Network Error] ${error.message || "Could not connect to the API."}`
|
|
58
|
+
// );
|
|
59
|
+
// }
|
|
60
|
+
//why if everything fails then we say that could not connect to the api? is it because we assume that if it's not a structured error from the backend, then it's likely a network or connectivity issue?
|
|
61
|
+
//explain: Yes, that's correct. The rationale behind returning a generic "Could not connect to the API" message when the error doesn't match the expected structured format from the backend is based on the assumption that if the error isn't coming from the API in a recognizable way, it's likely due to a network issue or some other problem preventing communication with the server.
|
|
62
|
+
// In a well-designed API, errors should be returned in a consistent format (e.g., with an "error" object containing "code" and "message").
|
|
63
|
+
// If the error doesn't conform to this structure, it suggests that the request may not have reached the server at all, or there was a failure in the network layer (like DNS issues, server downtime, CORS errors, etc.). Therefore, providing a generic message helps guide developers towards checking their network connection or server status when they encounter such unstructured errors.
|
|
64
|
+
//what if the developer of server forgets to send the error in this structured format? then the sdk will always say could not connect to the api even if the api is working fine but the error is not structured properly. so how can we improve this error handling to be more robust and informative?
|
|
65
|
+
// To improve the error handling and make it more robust and informative, we can implement a few strategies:
|
|
66
|
+
// 1. **Log the Raw Error**: Always log the raw error object to the console or a logging service. This way, developers can see the full context of the error, even if it's not structured.
|
|
67
|
+
// 2. **Check for Common Error Patterns**: Instead of only checking for a specific structured format, we can look for common patterns in error responses (like status codes, message fields, etc.) to provide more context.
|
|
68
|
+
// 3. **Fallback Messages**: If the error doesn't match the expected structure, we can provide a fallback message that includes any available information from the error object (like status code or message) instead of just saying "Could not connect to the API."
|
|
69
|
+
// 4. **Documentation and Guidelines**: Encourage backend developers to follow a consistent error response format through documentation and guidelines. This can help ensure that errors are structured properly, making it easier for the SDK to handle them effectively.
|
|
70
|
+
// Here's an improved version of the error handling method:
|
|
71
|
+
handleError(error) {
|
|
72
|
+
console.error("[ThinkingDifferently SDK] Raw Error:", error);
|
|
73
|
+
if (error.response) {
|
|
74
|
+
const status = error.response.status;
|
|
75
|
+
const data = error.response.data;
|
|
76
|
+
if (data && data.error) {
|
|
77
|
+
const backendError = data.error;
|
|
78
|
+
let errorMessage = `[ThinkingDifferently SDK Error] ${backendError.code || status}: ${backendError.message}`;
|
|
79
|
+
if (backendError.details) {
|
|
80
|
+
errorMessage += `
|
|
81
|
+
Details: ${JSON.stringify(backendError.details, null, 2)}`;
|
|
82
|
+
}
|
|
83
|
+
return new Error(errorMessage);
|
|
84
|
+
}
|
|
85
|
+
return new Error(`[ThinkingDifferently SDK Error] HTTP ${status}: ${data.message || "An error occurred."}`);
|
|
86
|
+
}
|
|
87
|
+
return new Error(
|
|
88
|
+
`[ThinkingDifferently Network Error] ${error.message || "Could not connect to the API."}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Handles authentication HTTP requests targeting /auth/admin/login
|
|
93
|
+
*/
|
|
94
|
+
async adminLogin(adminEmail, password) {
|
|
95
|
+
try {
|
|
96
|
+
const response = await this.api.post("/auth/admin/login", {
|
|
97
|
+
adminEmail,
|
|
98
|
+
password
|
|
99
|
+
});
|
|
100
|
+
const { token } = response.data;
|
|
101
|
+
if (token) {
|
|
102
|
+
this.setAdminToken(token);
|
|
103
|
+
}
|
|
104
|
+
return response.data;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw this.handleError(error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Determines if an API request is a database write operation.
|
|
111
|
+
*/
|
|
112
|
+
isWriteOperation(method, body) {
|
|
113
|
+
if (method === "GET") return false;
|
|
114
|
+
if (method === "PATCH" || method === "DELETE") return true;
|
|
115
|
+
if (body) {
|
|
116
|
+
if (typeof FormData !== "undefined" && body instanceof FormData) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
const query = body.query;
|
|
120
|
+
if (query) {
|
|
121
|
+
const parsed = typeof query === "string" ? JSON.parse(query) : query;
|
|
122
|
+
if (parsed && parsed.operation === "find") {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Executes queries and mutations against /data endpoint.
|
|
131
|
+
*/
|
|
132
|
+
async sendDataRequest(method, body) {
|
|
133
|
+
if (this.isWriteOperation(method, body)) {
|
|
134
|
+
const isBrowser = typeof window !== "undefined";
|
|
135
|
+
if (!isBrowser && !this.securityKey && !this.adminToken) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
"[ThinkingDifferently SDK Error] Write operations require either a valid Security Key or an Admin Session Token."
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
14
141
|
try {
|
|
15
|
-
const isFormData = body instanceof FormData;
|
|
142
|
+
const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
|
|
143
|
+
const headers = {};
|
|
144
|
+
if (!isFormData) {
|
|
145
|
+
headers["Content-Type"] = "application/json";
|
|
146
|
+
}
|
|
147
|
+
if (this.securityKey) {
|
|
148
|
+
headers["x-security-key"] = this.securityKey;
|
|
149
|
+
}
|
|
150
|
+
if (this.adminToken) {
|
|
151
|
+
headers["x-admin-token"] = this.adminToken;
|
|
152
|
+
}
|
|
153
|
+
if (isFormData) {
|
|
154
|
+
body.append("key", this.apikey);
|
|
155
|
+
} else {
|
|
156
|
+
body = {
|
|
157
|
+
key: this.apikey,
|
|
158
|
+
query: body
|
|
159
|
+
};
|
|
160
|
+
}
|
|
16
161
|
const response = await this.api.request({
|
|
17
162
|
url: "/data",
|
|
18
163
|
method,
|
|
19
|
-
headers
|
|
164
|
+
headers,
|
|
20
165
|
...method === "GET" ? { params: body } : { data: body }
|
|
21
166
|
});
|
|
22
167
|
return response.data;
|
|
23
168
|
} catch (error) {
|
|
24
|
-
throw
|
|
25
|
-
error?.response?.data?.message || "Something went wrong"
|
|
26
|
-
);
|
|
169
|
+
throw this.handleError(error);
|
|
27
170
|
}
|
|
28
171
|
}
|
|
29
172
|
};
|
|
@@ -33,6 +176,7 @@ var QueryBuilder = class {
|
|
|
33
176
|
constructor(collection, client) {
|
|
34
177
|
this.client = client;
|
|
35
178
|
this.query = {
|
|
179
|
+
operation: null,
|
|
36
180
|
collection,
|
|
37
181
|
filters: [],
|
|
38
182
|
limit: null,
|
|
@@ -66,11 +210,39 @@ var QueryBuilder = class {
|
|
|
66
210
|
// build() {
|
|
67
211
|
// return this.query;
|
|
68
212
|
// }
|
|
213
|
+
//to do writing the count method for the query builder
|
|
214
|
+
//the conditions are specified , so now the count method should return the number of documents that match the specified conditions in the query builder
|
|
215
|
+
//means count will first get the data based on the filters and then return the length of the data array as the count of matching documents
|
|
216
|
+
async count() {
|
|
217
|
+
this.query.operation = "count";
|
|
218
|
+
console.log("\n================ COUNT REQUEST ================");
|
|
219
|
+
console.log("[SDK] Final Query:", this.query);
|
|
220
|
+
try {
|
|
221
|
+
const response = await this.client.sendDataRequest(
|
|
222
|
+
"POST",
|
|
223
|
+
this.query
|
|
224
|
+
);
|
|
225
|
+
return response.count;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error("[SDK] COUNT ERROR");
|
|
228
|
+
console.error(error);
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
toJSON() {
|
|
233
|
+
return structuredClone(this.query);
|
|
234
|
+
}
|
|
69
235
|
async get() {
|
|
236
|
+
if (arguments.length > 0) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
"[ThinkingDifferently SDK Error] The .get() method takes no arguments. Please use chainable methods like .where() and .limit()."
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
this.query.operation = "find";
|
|
70
242
|
console.log("\n================ GET REQUEST ================");
|
|
71
243
|
console.log("[SDK] Final Query:", this.query);
|
|
72
244
|
try {
|
|
73
|
-
const response = await this.client.
|
|
245
|
+
const response = await this.client.sendDataRequest(
|
|
74
246
|
"POST",
|
|
75
247
|
this.query
|
|
76
248
|
);
|
|
@@ -78,45 +250,242 @@ var QueryBuilder = class {
|
|
|
78
250
|
if (!Array.isArray(response.data)) {
|
|
79
251
|
throw new Error("Invalid response format");
|
|
80
252
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
console.log("[SDK] Parsed Data:", parsed);
|
|
85
|
-
return parsed;
|
|
253
|
+
console.log("sdk response ", response);
|
|
254
|
+
console.log("[SDK] Data:", response.data);
|
|
255
|
+
return response.data;
|
|
86
256
|
} catch (error) {
|
|
87
257
|
console.error("[SDK] GET ERROR");
|
|
88
258
|
console.error(error);
|
|
89
259
|
throw error;
|
|
90
260
|
}
|
|
91
261
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
async count() {
|
|
96
|
-
console.log("\n================ COUNT REQUEST ================");
|
|
97
|
-
console.log("[SDK] Final Query for Count:", this.query);
|
|
262
|
+
async insert(data) {
|
|
263
|
+
this.query.operation = "insert";
|
|
264
|
+
console.log("\n================ INSERT REQUEST ================");
|
|
98
265
|
try {
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
266
|
+
if (data instanceof FormData) {
|
|
267
|
+
console.log("[SDK] FormData detected");
|
|
268
|
+
const payload = new FormData();
|
|
269
|
+
const extractedData = {};
|
|
270
|
+
for (const [key, value] of data.entries()) {
|
|
271
|
+
if (value instanceof File) {
|
|
272
|
+
payload.append(
|
|
273
|
+
key,
|
|
274
|
+
value
|
|
275
|
+
);
|
|
276
|
+
} else {
|
|
277
|
+
extractedData[key] = value;
|
|
278
|
+
}
|
|
105
279
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
280
|
+
this.query.data = extractedData;
|
|
281
|
+
payload.append(
|
|
282
|
+
"query",
|
|
283
|
+
JSON.stringify(this.query)
|
|
284
|
+
);
|
|
285
|
+
console.log("[SDK] Query:", this.query);
|
|
286
|
+
return await this.client.sendDataRequest(
|
|
287
|
+
"POST",
|
|
288
|
+
payload
|
|
289
|
+
);
|
|
110
290
|
}
|
|
111
|
-
|
|
291
|
+
this.query.data = data;
|
|
292
|
+
console.log("[SDK] Final Query:", this.query);
|
|
293
|
+
return await this.client.sendDataRequest(
|
|
294
|
+
"POST",
|
|
295
|
+
this.query
|
|
296
|
+
);
|
|
112
297
|
} catch (error) {
|
|
113
|
-
console.error("[SDK]
|
|
298
|
+
console.error("[SDK] INSERT ERROR");
|
|
114
299
|
console.error(error);
|
|
115
300
|
throw error;
|
|
116
301
|
}
|
|
117
302
|
}
|
|
118
|
-
|
|
119
|
-
|
|
303
|
+
async UpdateById(id, data) {
|
|
304
|
+
this.query.operation = "update";
|
|
305
|
+
this.query.id = id;
|
|
306
|
+
this.query.data = data;
|
|
307
|
+
console.log("\n================ UPDATE REQUEST ================");
|
|
308
|
+
console.log("[SDK] Final Query:", this.query);
|
|
309
|
+
try {
|
|
310
|
+
const response = await this.client.sendDataRequest(
|
|
311
|
+
"PATCH",
|
|
312
|
+
this.query
|
|
313
|
+
);
|
|
314
|
+
console.log("[SDK] Update Response:", response);
|
|
315
|
+
return response;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.error("[SDK] UPDATE ERROR");
|
|
318
|
+
console.error(error);
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
//updateMany
|
|
323
|
+
// its just like the get() it will call hte client
|
|
324
|
+
//example sdk.collection("animals").where("price", ">", 1000).updateMany({status : "expensive"}) it will update all the animals whose price is greater than 1000 and set their status to expensive
|
|
325
|
+
async updateMany(data) {
|
|
326
|
+
this.query.operation = "updateMany";
|
|
327
|
+
this.query.data = data;
|
|
328
|
+
console.log("\n================ UPDATE MANY REQUEST ================");
|
|
329
|
+
console.log("[SDK] Final Query:", this.query);
|
|
330
|
+
try {
|
|
331
|
+
const response = await this.client.sendDataRequest(
|
|
332
|
+
"PATCH",
|
|
333
|
+
this.query
|
|
334
|
+
);
|
|
335
|
+
console.log("[SDK] Update Many Response:", response);
|
|
336
|
+
return response;
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error("[SDK] UPDATE MANY ERROR");
|
|
339
|
+
console.error(error);
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async DeleteById(id) {
|
|
344
|
+
this.query.operation = "delete";
|
|
345
|
+
this.query.id = id;
|
|
346
|
+
console.log("\n================ DELETE REQUEST ================");
|
|
347
|
+
console.log("[SDK] Final Query:", this.query);
|
|
348
|
+
try {
|
|
349
|
+
const response = await this.client.sendDataRequest(
|
|
350
|
+
"DELETE",
|
|
351
|
+
this.query
|
|
352
|
+
);
|
|
353
|
+
console.log("[SDK] Delete Response:", response);
|
|
354
|
+
return response;
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error("[SDK] DELETE ERROR");
|
|
357
|
+
console.error(error);
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
//deleteMany
|
|
362
|
+
// its just like the get() it will call hte client
|
|
363
|
+
//example sdk.collection("animals").where("price", ">", 1000).deleteMany() it will delete all the animals whose price is greater than 1000
|
|
364
|
+
async deleteMany() {
|
|
365
|
+
this.query.operation = "deleteMany";
|
|
366
|
+
console.log("\n================ DELETE MANY REQUEST ================");
|
|
367
|
+
console.log("[SDK] Final Query:", this.query);
|
|
368
|
+
try {
|
|
369
|
+
const response = await this.client.sendDataRequest(
|
|
370
|
+
"DELETE",
|
|
371
|
+
this.query
|
|
372
|
+
);
|
|
373
|
+
console.log("[SDK] Delete Many Response:", response);
|
|
374
|
+
return response;
|
|
375
|
+
} catch (error) {
|
|
376
|
+
console.error("[SDK] DELETE MANY ERROR");
|
|
377
|
+
console.error(error);
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// src/auth.ts
|
|
384
|
+
import * as crypto from "crypto";
|
|
385
|
+
var AdminAuth = class {
|
|
386
|
+
constructor(client) {
|
|
387
|
+
this.client = client;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Authenticate an administrator using credentials.
|
|
391
|
+
* The token returned will be automatically stored in the SDK client.
|
|
392
|
+
*/
|
|
393
|
+
async credentials(adminEmail, password) {
|
|
394
|
+
return this.client.adminLogin(adminEmail, password);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Get the currently active administrator JWT token.
|
|
398
|
+
*/
|
|
399
|
+
getToken() {
|
|
400
|
+
return this.client.getAdminToken();
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Manually set the administrator JWT token (e.g. for server-side requests).
|
|
404
|
+
*/
|
|
405
|
+
setToken(token) {
|
|
406
|
+
this.client.setAdminToken(token);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Offline verification of Ed25519 (EdDSA) JWT admin token using public key.
|
|
410
|
+
* Supported in Node.js / Server-side environments.
|
|
411
|
+
*/
|
|
412
|
+
//detailed explaination of the verifyJwt function:
|
|
413
|
+
// The verifyJwt function is designed to validate a JWT (JSON Web Token) using
|
|
414
|
+
// the Ed25519 signature algorithm (EdDSA). It takes a JWT token as input and an optional public key in PEM format.
|
|
415
|
+
// The function performs several steps to ensure the token's integrity and authenticity:
|
|
416
|
+
// 1. It first determines which public key to use for verification, either from the argument or from the client's configuration.
|
|
417
|
+
// 2. It checks if the runtime environment supports the necessary crypto capabilities for JWT verification.
|
|
418
|
+
// 3. It splits the JWT into its three components: header, payload, and signature.
|
|
419
|
+
// 4. It decodes the signature from base64url format into a Buffer.
|
|
420
|
+
// 5. It imports the public key using Node.js's crypto module.
|
|
421
|
+
// 6. It verifies the signature against the message (header + payload) using the Ed25519 algorithm.
|
|
422
|
+
// 7. If verification succeeds, it decodes and parses the payload JSON.
|
|
423
|
+
// 8. It checks for token expiration based on the "exp" claim in the payload.
|
|
424
|
+
// 9. Finally, it returns the decoded payload if all checks pass,
|
|
425
|
+
// or throws descriptive errors if any step fails.
|
|
426
|
+
verifyJwt(token, publicKeyPem) {
|
|
427
|
+
const keyToUse = publicKeyPem || this.client.getPublicKey();
|
|
428
|
+
if (!keyToUse) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
"[ThinkingDifferently SDK Error] Public key is required for JWT verification. Please configure it in SDKConfig or pass it as an argument."
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
if (typeof crypto === "undefined" || !crypto.createPublicKey || !crypto.verify) {
|
|
434
|
+
throw new Error(
|
|
435
|
+
"[ThinkingDifferently SDK Error] JWT verification is only supported in Node.js / Server-side environments."
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
const parts = token.split(".");
|
|
439
|
+
if (parts.length !== 3) {
|
|
440
|
+
throw new Error("Invalid JWT format");
|
|
441
|
+
}
|
|
442
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
443
|
+
const message = `${headerB64}.${payloadB64}`;
|
|
444
|
+
let signature;
|
|
445
|
+
try {
|
|
446
|
+
signature = Buffer.from(signatureB64, "base64url");
|
|
447
|
+
} catch (err) {
|
|
448
|
+
throw new Error("Invalid JWT signature encoding");
|
|
449
|
+
}
|
|
450
|
+
let publicKey;
|
|
451
|
+
try {
|
|
452
|
+
publicKey = crypto.createPublicKey({
|
|
453
|
+
key: keyToUse,
|
|
454
|
+
format: "pem",
|
|
455
|
+
type: "spki"
|
|
456
|
+
});
|
|
457
|
+
} catch (err) {
|
|
458
|
+
throw new Error(`Failed to import public key: ${err.message}`);
|
|
459
|
+
}
|
|
460
|
+
const verified = crypto.verify(
|
|
461
|
+
void 0,
|
|
462
|
+
// Algorithm must be undefined for Ed25519 (EdDSA) in Node
|
|
463
|
+
Buffer.from(message),
|
|
464
|
+
publicKey,
|
|
465
|
+
signature
|
|
466
|
+
);
|
|
467
|
+
if (!verified) {
|
|
468
|
+
throw new Error("Invalid JWT signature");
|
|
469
|
+
}
|
|
470
|
+
let payload;
|
|
471
|
+
try {
|
|
472
|
+
const payloadJson = Buffer.from(payloadB64, "base64url").toString("utf8");
|
|
473
|
+
payload = JSON.parse(payloadJson);
|
|
474
|
+
} catch (err) {
|
|
475
|
+
throw new Error("Invalid JWT payload JSON");
|
|
476
|
+
}
|
|
477
|
+
if (payload.exp && typeof payload.exp === "number") {
|
|
478
|
+
const currentTime = Math.floor(Date.now() / 1e3);
|
|
479
|
+
if (payload.exp < currentTime) {
|
|
480
|
+
throw new Error("JWT has expired");
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return payload;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
var AuthModule = class {
|
|
487
|
+
constructor(client) {
|
|
488
|
+
this.admin = new AdminAuth(client);
|
|
120
489
|
}
|
|
121
490
|
};
|
|
122
491
|
|
|
@@ -124,7 +493,26 @@ var QueryBuilder = class {
|
|
|
124
493
|
var ThinkingDifferently = class {
|
|
125
494
|
constructor(config) {
|
|
126
495
|
console.log("[ThinkingDifferently SDK] Initializing SDK");
|
|
127
|
-
this.client = new TDClient(config.apiKey);
|
|
496
|
+
this.client = new TDClient(config.apiKey, config.securityKey, config.publicKey);
|
|
497
|
+
this.auth = new AuthModule(this.client);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Dynamically update the security key (useful for backend projects).
|
|
501
|
+
*/
|
|
502
|
+
setSecurityKey(key) {
|
|
503
|
+
this.client.setSecurityKey(key);
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Dynamically update the public key used for offline verification.
|
|
507
|
+
*/
|
|
508
|
+
setPublicKey(key) {
|
|
509
|
+
this.client.setPublicKey(key);
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Helper to verify Ed25519 admin JWT token offline.
|
|
513
|
+
*/
|
|
514
|
+
verifyJwt(token, publicKey) {
|
|
515
|
+
return this.auth.admin.verifyJwt(token, publicKey);
|
|
128
516
|
}
|
|
129
517
|
collection(name) {
|
|
130
518
|
return new QueryBuilder(
|
|
@@ -158,7 +546,7 @@ var ThinkingDifferently = class {
|
|
|
158
546
|
for (const [k, v] of payload.entries()) {
|
|
159
547
|
console.log(k, v);
|
|
160
548
|
}
|
|
161
|
-
return this.client.
|
|
549
|
+
return this.client.sendDataRequest(
|
|
162
550
|
"POST",
|
|
163
551
|
payload
|
|
164
552
|
);
|
|
@@ -168,7 +556,7 @@ var ThinkingDifferently = class {
|
|
|
168
556
|
key,
|
|
169
557
|
data
|
|
170
558
|
});
|
|
171
|
-
const response = await this.client.
|
|
559
|
+
const response = await this.client.sendDataRequest(
|
|
172
560
|
"POST",
|
|
173
561
|
{
|
|
174
562
|
key,
|
|
@@ -189,7 +577,7 @@ var ThinkingDifferently = class {
|
|
|
189
577
|
console.log("[SDK] Document ID:", id);
|
|
190
578
|
console.log("[SDK] Update Data:", data);
|
|
191
579
|
try {
|
|
192
|
-
const response = await this.client.
|
|
580
|
+
const response = await this.client.sendDataRequest(
|
|
193
581
|
"PATCH",
|
|
194
582
|
{
|
|
195
583
|
key,
|
|
@@ -210,7 +598,7 @@ var ThinkingDifferently = class {
|
|
|
210
598
|
console.log("[SDK] Collection Key:", key);
|
|
211
599
|
console.log("[SDK] Document ID:", id);
|
|
212
600
|
try {
|
|
213
|
-
const response = await this.client.
|
|
601
|
+
const response = await this.client.sendDataRequest(
|
|
214
602
|
"DELETE",
|
|
215
603
|
{
|
|
216
604
|
key,
|
|
@@ -227,6 +615,8 @@ var ThinkingDifferently = class {
|
|
|
227
615
|
}
|
|
228
616
|
};
|
|
229
617
|
export {
|
|
618
|
+
AdminAuth,
|
|
619
|
+
AuthModule,
|
|
230
620
|
TDClient,
|
|
231
621
|
ThinkingDifferently
|
|
232
622
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thinkingdifferently/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Official SDK for Thinking Differently API",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"author": "Krrish Savlani",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"axios": "^1.16.0"
|
|
24
|
+
"axios": "^1.16.0",
|
|
25
|
+
"crypto-js": "^4.2.0"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@types/node": "^25.6.2",
|