@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.js
CHANGED
|
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AdminAuth: () => AdminAuth,
|
|
34
|
+
AuthModule: () => AuthModule,
|
|
33
35
|
TDClient: () => TDClient,
|
|
34
36
|
ThinkingDifferently: () => ThinkingDifferently
|
|
35
37
|
});
|
|
@@ -38,29 +40,172 @@ module.exports = __toCommonJS(index_exports);
|
|
|
38
40
|
// src/client.ts
|
|
39
41
|
var import_axios = __toESM(require("axios"));
|
|
40
42
|
var TDClient = class {
|
|
41
|
-
constructor(apiKey) {
|
|
43
|
+
constructor(apiKey, securityKey, publicKey) {
|
|
44
|
+
this.adminToken = null;
|
|
45
|
+
this.apikey = apiKey;
|
|
46
|
+
this.securityKey = securityKey;
|
|
47
|
+
this.publicKey = publicKey;
|
|
42
48
|
this.api = import_axios.default.create({
|
|
43
|
-
baseURL: "
|
|
49
|
+
baseURL: "http://localhost:3000/api/v1",
|
|
50
|
+
// baseURL: "https://www.thinkingdifferently.dev/api/v1",
|
|
44
51
|
headers: {
|
|
45
|
-
"
|
|
52
|
+
"X-API-Key": apiKey,
|
|
46
53
|
"Content-Type": "application/json"
|
|
47
|
-
}
|
|
54
|
+
},
|
|
55
|
+
withCredentials: true
|
|
56
|
+
// Enable browser cookie sharing automatically
|
|
48
57
|
});
|
|
49
58
|
}
|
|
50
|
-
|
|
59
|
+
//what is hte use of withCredentials: true in axios config?
|
|
60
|
+
//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,
|
|
61
|
+
// 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
|
|
62
|
+
// 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
|
|
63
|
+
// that is on a different domain than the frontend application.
|
|
64
|
+
setSecurityKey(key) {
|
|
65
|
+
this.securityKey = key;
|
|
66
|
+
}
|
|
67
|
+
setPublicKey(key) {
|
|
68
|
+
this.publicKey = key;
|
|
69
|
+
}
|
|
70
|
+
setAdminToken(token) {
|
|
71
|
+
this.adminToken = token;
|
|
72
|
+
}
|
|
73
|
+
getAdminToken() {
|
|
74
|
+
return this.adminToken;
|
|
75
|
+
}
|
|
76
|
+
getPublicKey() {
|
|
77
|
+
return this.publicKey;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reusable private error formatting utility
|
|
81
|
+
*/
|
|
82
|
+
// private handleError(error: any): Error {
|
|
83
|
+
// if (error.response && error.response.data && error.response.data.error) {
|
|
84
|
+
// const backendError = error.response.data.error;
|
|
85
|
+
//
|
|
86
|
+
// let errorMessage = `[ThinkingDifferently SDK Error] ${backendError.code || error.response.status}: ${backendError.message}`;
|
|
87
|
+
//
|
|
88
|
+
// if (backendError.details) {
|
|
89
|
+
// errorMessage += `\nDetails: ${JSON.stringify(backendError.details, null, 2)}`;
|
|
90
|
+
// }
|
|
91
|
+
//
|
|
92
|
+
// return new Error(errorMessage);
|
|
93
|
+
// }
|
|
94
|
+
//
|
|
95
|
+
// return new Error(
|
|
96
|
+
// `[ThinkingDifferently Network Error] ${error.message || "Could not connect to the API."}`
|
|
97
|
+
// );
|
|
98
|
+
// }
|
|
99
|
+
//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?
|
|
100
|
+
//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.
|
|
101
|
+
// In a well-designed API, errors should be returned in a consistent format (e.g., with an "error" object containing "code" and "message").
|
|
102
|
+
// 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.
|
|
103
|
+
//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?
|
|
104
|
+
// To improve the error handling and make it more robust and informative, we can implement a few strategies:
|
|
105
|
+
// 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.
|
|
106
|
+
// 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.
|
|
107
|
+
// 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."
|
|
108
|
+
// 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.
|
|
109
|
+
// Here's an improved version of the error handling method:
|
|
110
|
+
handleError(error) {
|
|
111
|
+
console.error("[ThinkingDifferently SDK] Raw Error:", error);
|
|
112
|
+
if (error.response) {
|
|
113
|
+
const status = error.response.status;
|
|
114
|
+
const data = error.response.data;
|
|
115
|
+
if (data && data.error) {
|
|
116
|
+
const backendError = data.error;
|
|
117
|
+
let errorMessage = `[ThinkingDifferently SDK Error] ${backendError.code || status}: ${backendError.message}`;
|
|
118
|
+
if (backendError.details) {
|
|
119
|
+
errorMessage += `
|
|
120
|
+
Details: ${JSON.stringify(backendError.details, null, 2)}`;
|
|
121
|
+
}
|
|
122
|
+
return new Error(errorMessage);
|
|
123
|
+
}
|
|
124
|
+
return new Error(`[ThinkingDifferently SDK Error] HTTP ${status}: ${data.message || "An error occurred."}`);
|
|
125
|
+
}
|
|
126
|
+
return new Error(
|
|
127
|
+
`[ThinkingDifferently Network Error] ${error.message || "Could not connect to the API."}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Handles authentication HTTP requests targeting /auth/admin/login
|
|
132
|
+
*/
|
|
133
|
+
async adminLogin(adminEmail, password) {
|
|
134
|
+
try {
|
|
135
|
+
const response = await this.api.post("/auth/admin/login", {
|
|
136
|
+
adminEmail,
|
|
137
|
+
password
|
|
138
|
+
});
|
|
139
|
+
const { token } = response.data;
|
|
140
|
+
if (token) {
|
|
141
|
+
this.setAdminToken(token);
|
|
142
|
+
}
|
|
143
|
+
return response.data;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw this.handleError(error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Determines if an API request is a database write operation.
|
|
150
|
+
*/
|
|
151
|
+
isWriteOperation(method, body) {
|
|
152
|
+
if (method === "GET") return false;
|
|
153
|
+
if (method === "PATCH" || method === "DELETE") return true;
|
|
154
|
+
if (body) {
|
|
155
|
+
if (typeof FormData !== "undefined" && body instanceof FormData) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
const query = body.query;
|
|
159
|
+
if (query) {
|
|
160
|
+
const parsed = typeof query === "string" ? JSON.parse(query) : query;
|
|
161
|
+
if (parsed && parsed.operation === "find") {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Executes queries and mutations against /data endpoint.
|
|
170
|
+
*/
|
|
171
|
+
async sendDataRequest(method, body) {
|
|
172
|
+
if (this.isWriteOperation(method, body)) {
|
|
173
|
+
const isBrowser = typeof window !== "undefined";
|
|
174
|
+
if (!isBrowser && !this.securityKey && !this.adminToken) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"[ThinkingDifferently SDK Error] Write operations require either a valid Security Key or an Admin Session Token."
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
51
180
|
try {
|
|
52
|
-
const isFormData = body instanceof FormData;
|
|
181
|
+
const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
|
|
182
|
+
const headers = {};
|
|
183
|
+
if (!isFormData) {
|
|
184
|
+
headers["Content-Type"] = "application/json";
|
|
185
|
+
}
|
|
186
|
+
if (this.securityKey) {
|
|
187
|
+
headers["x-security-key"] = this.securityKey;
|
|
188
|
+
}
|
|
189
|
+
if (this.adminToken) {
|
|
190
|
+
headers["x-admin-token"] = this.adminToken;
|
|
191
|
+
}
|
|
192
|
+
if (isFormData) {
|
|
193
|
+
body.append("key", this.apikey);
|
|
194
|
+
} else {
|
|
195
|
+
body = {
|
|
196
|
+
key: this.apikey,
|
|
197
|
+
query: body
|
|
198
|
+
};
|
|
199
|
+
}
|
|
53
200
|
const response = await this.api.request({
|
|
54
201
|
url: "/data",
|
|
55
202
|
method,
|
|
56
|
-
headers
|
|
203
|
+
headers,
|
|
57
204
|
...method === "GET" ? { params: body } : { data: body }
|
|
58
205
|
});
|
|
59
206
|
return response.data;
|
|
60
207
|
} catch (error) {
|
|
61
|
-
throw
|
|
62
|
-
error?.response?.data?.message || "Something went wrong"
|
|
63
|
-
);
|
|
208
|
+
throw this.handleError(error);
|
|
64
209
|
}
|
|
65
210
|
}
|
|
66
211
|
};
|
|
@@ -70,6 +215,7 @@ var QueryBuilder = class {
|
|
|
70
215
|
constructor(collection, client) {
|
|
71
216
|
this.client = client;
|
|
72
217
|
this.query = {
|
|
218
|
+
operation: null,
|
|
73
219
|
collection,
|
|
74
220
|
filters: [],
|
|
75
221
|
limit: null,
|
|
@@ -103,11 +249,39 @@ var QueryBuilder = class {
|
|
|
103
249
|
// build() {
|
|
104
250
|
// return this.query;
|
|
105
251
|
// }
|
|
252
|
+
//to do writing the count method for the query builder
|
|
253
|
+
//the conditions are specified , so now the count method should return the number of documents that match the specified conditions in the query builder
|
|
254
|
+
//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
|
|
255
|
+
async count() {
|
|
256
|
+
this.query.operation = "count";
|
|
257
|
+
console.log("\n================ COUNT REQUEST ================");
|
|
258
|
+
console.log("[SDK] Final Query:", this.query);
|
|
259
|
+
try {
|
|
260
|
+
const response = await this.client.sendDataRequest(
|
|
261
|
+
"POST",
|
|
262
|
+
this.query
|
|
263
|
+
);
|
|
264
|
+
return response.count;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error("[SDK] COUNT ERROR");
|
|
267
|
+
console.error(error);
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
toJSON() {
|
|
272
|
+
return structuredClone(this.query);
|
|
273
|
+
}
|
|
106
274
|
async get() {
|
|
275
|
+
if (arguments.length > 0) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
"[ThinkingDifferently SDK Error] The .get() method takes no arguments. Please use chainable methods like .where() and .limit()."
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
this.query.operation = "find";
|
|
107
281
|
console.log("\n================ GET REQUEST ================");
|
|
108
282
|
console.log("[SDK] Final Query:", this.query);
|
|
109
283
|
try {
|
|
110
|
-
const response = await this.client.
|
|
284
|
+
const response = await this.client.sendDataRequest(
|
|
111
285
|
"POST",
|
|
112
286
|
this.query
|
|
113
287
|
);
|
|
@@ -115,45 +289,242 @@ var QueryBuilder = class {
|
|
|
115
289
|
if (!Array.isArray(response.data)) {
|
|
116
290
|
throw new Error("Invalid response format");
|
|
117
291
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.log("[SDK] Parsed Data:", parsed);
|
|
122
|
-
return parsed;
|
|
292
|
+
console.log("sdk response ", response);
|
|
293
|
+
console.log("[SDK] Data:", response.data);
|
|
294
|
+
return response.data;
|
|
123
295
|
} catch (error) {
|
|
124
296
|
console.error("[SDK] GET ERROR");
|
|
125
297
|
console.error(error);
|
|
126
298
|
throw error;
|
|
127
299
|
}
|
|
128
300
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
async count() {
|
|
133
|
-
console.log("\n================ COUNT REQUEST ================");
|
|
134
|
-
console.log("[SDK] Final Query for Count:", this.query);
|
|
301
|
+
async insert(data) {
|
|
302
|
+
this.query.operation = "insert";
|
|
303
|
+
console.log("\n================ INSERT REQUEST ================");
|
|
135
304
|
try {
|
|
136
|
-
|
|
137
|
-
"
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
305
|
+
if (data instanceof FormData) {
|
|
306
|
+
console.log("[SDK] FormData detected");
|
|
307
|
+
const payload = new FormData();
|
|
308
|
+
const extractedData = {};
|
|
309
|
+
for (const [key, value] of data.entries()) {
|
|
310
|
+
if (value instanceof File) {
|
|
311
|
+
payload.append(
|
|
312
|
+
key,
|
|
313
|
+
value
|
|
314
|
+
);
|
|
315
|
+
} else {
|
|
316
|
+
extractedData[key] = value;
|
|
317
|
+
}
|
|
142
318
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
319
|
+
this.query.data = extractedData;
|
|
320
|
+
payload.append(
|
|
321
|
+
"query",
|
|
322
|
+
JSON.stringify(this.query)
|
|
323
|
+
);
|
|
324
|
+
console.log("[SDK] Query:", this.query);
|
|
325
|
+
return await this.client.sendDataRequest(
|
|
326
|
+
"POST",
|
|
327
|
+
payload
|
|
328
|
+
);
|
|
147
329
|
}
|
|
148
|
-
|
|
330
|
+
this.query.data = data;
|
|
331
|
+
console.log("[SDK] Final Query:", this.query);
|
|
332
|
+
return await this.client.sendDataRequest(
|
|
333
|
+
"POST",
|
|
334
|
+
this.query
|
|
335
|
+
);
|
|
149
336
|
} catch (error) {
|
|
150
|
-
console.error("[SDK]
|
|
337
|
+
console.error("[SDK] INSERT ERROR");
|
|
151
338
|
console.error(error);
|
|
152
339
|
throw error;
|
|
153
340
|
}
|
|
154
341
|
}
|
|
155
|
-
|
|
156
|
-
|
|
342
|
+
async UpdateById(id, data) {
|
|
343
|
+
this.query.operation = "update";
|
|
344
|
+
this.query.id = id;
|
|
345
|
+
this.query.data = data;
|
|
346
|
+
console.log("\n================ UPDATE REQUEST ================");
|
|
347
|
+
console.log("[SDK] Final Query:", this.query);
|
|
348
|
+
try {
|
|
349
|
+
const response = await this.client.sendDataRequest(
|
|
350
|
+
"PATCH",
|
|
351
|
+
this.query
|
|
352
|
+
);
|
|
353
|
+
console.log("[SDK] Update Response:", response);
|
|
354
|
+
return response;
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error("[SDK] UPDATE ERROR");
|
|
357
|
+
console.error(error);
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
//updateMany
|
|
362
|
+
// its just like the get() it will call hte client
|
|
363
|
+
//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
|
|
364
|
+
async updateMany(data) {
|
|
365
|
+
this.query.operation = "updateMany";
|
|
366
|
+
this.query.data = data;
|
|
367
|
+
console.log("\n================ UPDATE MANY REQUEST ================");
|
|
368
|
+
console.log("[SDK] Final Query:", this.query);
|
|
369
|
+
try {
|
|
370
|
+
const response = await this.client.sendDataRequest(
|
|
371
|
+
"PATCH",
|
|
372
|
+
this.query
|
|
373
|
+
);
|
|
374
|
+
console.log("[SDK] Update Many Response:", response);
|
|
375
|
+
return response;
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error("[SDK] UPDATE MANY ERROR");
|
|
378
|
+
console.error(error);
|
|
379
|
+
throw error;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async DeleteById(id) {
|
|
383
|
+
this.query.operation = "delete";
|
|
384
|
+
this.query.id = id;
|
|
385
|
+
console.log("\n================ DELETE REQUEST ================");
|
|
386
|
+
console.log("[SDK] Final Query:", this.query);
|
|
387
|
+
try {
|
|
388
|
+
const response = await this.client.sendDataRequest(
|
|
389
|
+
"DELETE",
|
|
390
|
+
this.query
|
|
391
|
+
);
|
|
392
|
+
console.log("[SDK] Delete Response:", response);
|
|
393
|
+
return response;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.error("[SDK] DELETE ERROR");
|
|
396
|
+
console.error(error);
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
//deleteMany
|
|
401
|
+
// its just like the get() it will call hte client
|
|
402
|
+
//example sdk.collection("animals").where("price", ">", 1000).deleteMany() it will delete all the animals whose price is greater than 1000
|
|
403
|
+
async deleteMany() {
|
|
404
|
+
this.query.operation = "deleteMany";
|
|
405
|
+
console.log("\n================ DELETE MANY REQUEST ================");
|
|
406
|
+
console.log("[SDK] Final Query:", this.query);
|
|
407
|
+
try {
|
|
408
|
+
const response = await this.client.sendDataRequest(
|
|
409
|
+
"DELETE",
|
|
410
|
+
this.query
|
|
411
|
+
);
|
|
412
|
+
console.log("[SDK] Delete Many Response:", response);
|
|
413
|
+
return response;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error("[SDK] DELETE MANY ERROR");
|
|
416
|
+
console.error(error);
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
// src/auth.ts
|
|
423
|
+
var crypto = __toESM(require("crypto"));
|
|
424
|
+
var AdminAuth = class {
|
|
425
|
+
constructor(client) {
|
|
426
|
+
this.client = client;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Authenticate an administrator using credentials.
|
|
430
|
+
* The token returned will be automatically stored in the SDK client.
|
|
431
|
+
*/
|
|
432
|
+
async credentials(adminEmail, password) {
|
|
433
|
+
return this.client.adminLogin(adminEmail, password);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Get the currently active administrator JWT token.
|
|
437
|
+
*/
|
|
438
|
+
getToken() {
|
|
439
|
+
return this.client.getAdminToken();
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Manually set the administrator JWT token (e.g. for server-side requests).
|
|
443
|
+
*/
|
|
444
|
+
setToken(token) {
|
|
445
|
+
this.client.setAdminToken(token);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Offline verification of Ed25519 (EdDSA) JWT admin token using public key.
|
|
449
|
+
* Supported in Node.js / Server-side environments.
|
|
450
|
+
*/
|
|
451
|
+
//detailed explaination of the verifyJwt function:
|
|
452
|
+
// The verifyJwt function is designed to validate a JWT (JSON Web Token) using
|
|
453
|
+
// the Ed25519 signature algorithm (EdDSA). It takes a JWT token as input and an optional public key in PEM format.
|
|
454
|
+
// The function performs several steps to ensure the token's integrity and authenticity:
|
|
455
|
+
// 1. It first determines which public key to use for verification, either from the argument or from the client's configuration.
|
|
456
|
+
// 2. It checks if the runtime environment supports the necessary crypto capabilities for JWT verification.
|
|
457
|
+
// 3. It splits the JWT into its three components: header, payload, and signature.
|
|
458
|
+
// 4. It decodes the signature from base64url format into a Buffer.
|
|
459
|
+
// 5. It imports the public key using Node.js's crypto module.
|
|
460
|
+
// 6. It verifies the signature against the message (header + payload) using the Ed25519 algorithm.
|
|
461
|
+
// 7. If verification succeeds, it decodes and parses the payload JSON.
|
|
462
|
+
// 8. It checks for token expiration based on the "exp" claim in the payload.
|
|
463
|
+
// 9. Finally, it returns the decoded payload if all checks pass,
|
|
464
|
+
// or throws descriptive errors if any step fails.
|
|
465
|
+
verifyJwt(token, publicKeyPem) {
|
|
466
|
+
const keyToUse = publicKeyPem || this.client.getPublicKey();
|
|
467
|
+
if (!keyToUse) {
|
|
468
|
+
throw new Error(
|
|
469
|
+
"[ThinkingDifferently SDK Error] Public key is required for JWT verification. Please configure it in SDKConfig or pass it as an argument."
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
if (typeof crypto === "undefined" || !crypto.createPublicKey || !crypto.verify) {
|
|
473
|
+
throw new Error(
|
|
474
|
+
"[ThinkingDifferently SDK Error] JWT verification is only supported in Node.js / Server-side environments."
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
const parts = token.split(".");
|
|
478
|
+
if (parts.length !== 3) {
|
|
479
|
+
throw new Error("Invalid JWT format");
|
|
480
|
+
}
|
|
481
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
482
|
+
const message = `${headerB64}.${payloadB64}`;
|
|
483
|
+
let signature;
|
|
484
|
+
try {
|
|
485
|
+
signature = Buffer.from(signatureB64, "base64url");
|
|
486
|
+
} catch (err) {
|
|
487
|
+
throw new Error("Invalid JWT signature encoding");
|
|
488
|
+
}
|
|
489
|
+
let publicKey;
|
|
490
|
+
try {
|
|
491
|
+
publicKey = crypto.createPublicKey({
|
|
492
|
+
key: keyToUse,
|
|
493
|
+
format: "pem",
|
|
494
|
+
type: "spki"
|
|
495
|
+
});
|
|
496
|
+
} catch (err) {
|
|
497
|
+
throw new Error(`Failed to import public key: ${err.message}`);
|
|
498
|
+
}
|
|
499
|
+
const verified = crypto.verify(
|
|
500
|
+
void 0,
|
|
501
|
+
// Algorithm must be undefined for Ed25519 (EdDSA) in Node
|
|
502
|
+
Buffer.from(message),
|
|
503
|
+
publicKey,
|
|
504
|
+
signature
|
|
505
|
+
);
|
|
506
|
+
if (!verified) {
|
|
507
|
+
throw new Error("Invalid JWT signature");
|
|
508
|
+
}
|
|
509
|
+
let payload;
|
|
510
|
+
try {
|
|
511
|
+
const payloadJson = Buffer.from(payloadB64, "base64url").toString("utf8");
|
|
512
|
+
payload = JSON.parse(payloadJson);
|
|
513
|
+
} catch (err) {
|
|
514
|
+
throw new Error("Invalid JWT payload JSON");
|
|
515
|
+
}
|
|
516
|
+
if (payload.exp && typeof payload.exp === "number") {
|
|
517
|
+
const currentTime = Math.floor(Date.now() / 1e3);
|
|
518
|
+
if (payload.exp < currentTime) {
|
|
519
|
+
throw new Error("JWT has expired");
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return payload;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
var AuthModule = class {
|
|
526
|
+
constructor(client) {
|
|
527
|
+
this.admin = new AdminAuth(client);
|
|
157
528
|
}
|
|
158
529
|
};
|
|
159
530
|
|
|
@@ -161,7 +532,26 @@ var QueryBuilder = class {
|
|
|
161
532
|
var ThinkingDifferently = class {
|
|
162
533
|
constructor(config) {
|
|
163
534
|
console.log("[ThinkingDifferently SDK] Initializing SDK");
|
|
164
|
-
this.client = new TDClient(config.apiKey);
|
|
535
|
+
this.client = new TDClient(config.apiKey, config.securityKey, config.publicKey);
|
|
536
|
+
this.auth = new AuthModule(this.client);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Dynamically update the security key (useful for backend projects).
|
|
540
|
+
*/
|
|
541
|
+
setSecurityKey(key) {
|
|
542
|
+
this.client.setSecurityKey(key);
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Dynamically update the public key used for offline verification.
|
|
546
|
+
*/
|
|
547
|
+
setPublicKey(key) {
|
|
548
|
+
this.client.setPublicKey(key);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Helper to verify Ed25519 admin JWT token offline.
|
|
552
|
+
*/
|
|
553
|
+
verifyJwt(token, publicKey) {
|
|
554
|
+
return this.auth.admin.verifyJwt(token, publicKey);
|
|
165
555
|
}
|
|
166
556
|
collection(name) {
|
|
167
557
|
return new QueryBuilder(
|
|
@@ -195,7 +585,7 @@ var ThinkingDifferently = class {
|
|
|
195
585
|
for (const [k, v] of payload.entries()) {
|
|
196
586
|
console.log(k, v);
|
|
197
587
|
}
|
|
198
|
-
return this.client.
|
|
588
|
+
return this.client.sendDataRequest(
|
|
199
589
|
"POST",
|
|
200
590
|
payload
|
|
201
591
|
);
|
|
@@ -205,7 +595,7 @@ var ThinkingDifferently = class {
|
|
|
205
595
|
key,
|
|
206
596
|
data
|
|
207
597
|
});
|
|
208
|
-
const response = await this.client.
|
|
598
|
+
const response = await this.client.sendDataRequest(
|
|
209
599
|
"POST",
|
|
210
600
|
{
|
|
211
601
|
key,
|
|
@@ -226,7 +616,7 @@ var ThinkingDifferently = class {
|
|
|
226
616
|
console.log("[SDK] Document ID:", id);
|
|
227
617
|
console.log("[SDK] Update Data:", data);
|
|
228
618
|
try {
|
|
229
|
-
const response = await this.client.
|
|
619
|
+
const response = await this.client.sendDataRequest(
|
|
230
620
|
"PATCH",
|
|
231
621
|
{
|
|
232
622
|
key,
|
|
@@ -247,7 +637,7 @@ var ThinkingDifferently = class {
|
|
|
247
637
|
console.log("[SDK] Collection Key:", key);
|
|
248
638
|
console.log("[SDK] Document ID:", id);
|
|
249
639
|
try {
|
|
250
|
-
const response = await this.client.
|
|
640
|
+
const response = await this.client.sendDataRequest(
|
|
251
641
|
"DELETE",
|
|
252
642
|
{
|
|
253
643
|
key,
|
|
@@ -265,6 +655,8 @@ var ThinkingDifferently = class {
|
|
|
265
655
|
};
|
|
266
656
|
// Annotate the CommonJS export names for ESM import in node:
|
|
267
657
|
0 && (module.exports = {
|
|
658
|
+
AdminAuth,
|
|
659
|
+
AuthModule,
|
|
268
660
|
TDClient,
|
|
269
661
|
ThinkingDifferently
|
|
270
662
|
});
|