@thinkingdifferently/core 1.2.0 → 1.3.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/dist/index.d.mts +83 -3
- package/dist/index.d.ts +83 -3
- package/dist/index.js +293 -22
- package/dist/index.mjs +291 -22
- package/package.json +3 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,15 +1,55 @@
|
|
|
1
1
|
interface SDKConfig {
|
|
2
2
|
apiKey: string;
|
|
3
|
+
securityKey?: string;
|
|
4
|
+
publicKey?: string;
|
|
3
5
|
}
|
|
4
6
|
interface GetOptions {
|
|
5
7
|
limit?: number;
|
|
6
8
|
}
|
|
9
|
+
interface CredentialParams {
|
|
10
|
+
adminEmail?: string;
|
|
11
|
+
password?: string;
|
|
12
|
+
}
|
|
13
|
+
interface LoginResponse {
|
|
14
|
+
message: string;
|
|
15
|
+
token: string;
|
|
16
|
+
admin: {
|
|
17
|
+
id: string;
|
|
18
|
+
adminName: string;
|
|
19
|
+
adminEmail: string;
|
|
20
|
+
projectId: string;
|
|
21
|
+
projectName: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
declare class TDClient {
|
|
9
26
|
private api;
|
|
10
27
|
private apikey;
|
|
11
|
-
|
|
12
|
-
|
|
28
|
+
private securityKey?;
|
|
29
|
+
private publicKey?;
|
|
30
|
+
private adminToken;
|
|
31
|
+
constructor(apiKey: string, securityKey?: string, publicKey?: string);
|
|
32
|
+
setSecurityKey(key: string): void;
|
|
33
|
+
setPublicKey(key: string): void;
|
|
34
|
+
setAdminToken(token: string): void;
|
|
35
|
+
getAdminToken(): string | null;
|
|
36
|
+
getPublicKey(): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Reusable private error formatting utility
|
|
39
|
+
*/
|
|
40
|
+
private handleError;
|
|
41
|
+
/**
|
|
42
|
+
* Handles authentication HTTP requests targeting /auth/admin/login
|
|
43
|
+
*/
|
|
44
|
+
adminLogin(adminEmail?: string, password?: string): Promise<LoginResponse>;
|
|
45
|
+
/**
|
|
46
|
+
* Determines if an API request is a database write operation.
|
|
47
|
+
*/
|
|
48
|
+
private isWriteOperation;
|
|
49
|
+
/**
|
|
50
|
+
* Executes queries and mutations against /data endpoint.
|
|
51
|
+
*/
|
|
52
|
+
sendDataRequest(method: "POST" | "GET" | "PATCH" | "DELETE", body?: any): Promise<any>;
|
|
13
53
|
}
|
|
14
54
|
|
|
15
55
|
type Operator = "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "contains";
|
|
@@ -48,13 +88,53 @@ declare class QueryBuilder {
|
|
|
48
88
|
deleteMany(): Promise<any>;
|
|
49
89
|
}
|
|
50
90
|
|
|
91
|
+
declare class AdminAuth {
|
|
92
|
+
private client;
|
|
93
|
+
constructor(client: TDClient);
|
|
94
|
+
/**
|
|
95
|
+
* Authenticate an administrator using credentials.
|
|
96
|
+
* The token returned will be automatically stored in the SDK client.
|
|
97
|
+
*/
|
|
98
|
+
credentials(adminEmail?: string, password?: string): Promise<LoginResponse>;
|
|
99
|
+
/**
|
|
100
|
+
* Get the currently active administrator JWT token.
|
|
101
|
+
*/
|
|
102
|
+
getToken(): string | null;
|
|
103
|
+
/**
|
|
104
|
+
* Manually set the administrator JWT token (e.g. for server-side requests).
|
|
105
|
+
*/
|
|
106
|
+
setToken(token: string): void;
|
|
107
|
+
/**
|
|
108
|
+
* Offline verification of Ed25519 (EdDSA) JWT admin token using public key.
|
|
109
|
+
* Supported in Node.js / Server-side environments.
|
|
110
|
+
*/
|
|
111
|
+
verifyJwt(token: string, publicKeyPem?: string): any;
|
|
112
|
+
}
|
|
113
|
+
declare class AuthModule {
|
|
114
|
+
admin: AdminAuth;
|
|
115
|
+
constructor(client: TDClient);
|
|
116
|
+
}
|
|
117
|
+
|
|
51
118
|
declare class ThinkingDifferently {
|
|
52
119
|
private client;
|
|
120
|
+
auth: AuthModule;
|
|
53
121
|
constructor(config: SDKConfig);
|
|
122
|
+
/**
|
|
123
|
+
* Dynamically update the security key (useful for backend projects).
|
|
124
|
+
*/
|
|
125
|
+
setSecurityKey(key: string): void;
|
|
126
|
+
/**
|
|
127
|
+
* Dynamically update the public key used for offline verification.
|
|
128
|
+
*/
|
|
129
|
+
setPublicKey(key: string): void;
|
|
130
|
+
/**
|
|
131
|
+
* Helper to verify Ed25519 admin JWT token offline.
|
|
132
|
+
*/
|
|
133
|
+
verifyJwt(token: string, publicKey?: string): any;
|
|
54
134
|
collection(name: string): QueryBuilder;
|
|
55
135
|
insert(key: string, data: any): Promise<any>;
|
|
56
136
|
update(key: string, id: string, data: Record<string, any>): Promise<any>;
|
|
57
137
|
delete(key: string, id: string): Promise<any>;
|
|
58
138
|
}
|
|
59
139
|
|
|
60
|
-
export { type GetOptions, type SDKConfig, TDClient, ThinkingDifferently };
|
|
140
|
+
export { AdminAuth, AuthModule, type CredentialParams, type GetOptions, type LoginResponse, type SDKConfig, TDClient, ThinkingDifferently };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,55 @@
|
|
|
1
1
|
interface SDKConfig {
|
|
2
2
|
apiKey: string;
|
|
3
|
+
securityKey?: string;
|
|
4
|
+
publicKey?: string;
|
|
3
5
|
}
|
|
4
6
|
interface GetOptions {
|
|
5
7
|
limit?: number;
|
|
6
8
|
}
|
|
9
|
+
interface CredentialParams {
|
|
10
|
+
adminEmail?: string;
|
|
11
|
+
password?: string;
|
|
12
|
+
}
|
|
13
|
+
interface LoginResponse {
|
|
14
|
+
message: string;
|
|
15
|
+
token: string;
|
|
16
|
+
admin: {
|
|
17
|
+
id: string;
|
|
18
|
+
adminName: string;
|
|
19
|
+
adminEmail: string;
|
|
20
|
+
projectId: string;
|
|
21
|
+
projectName: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
declare class TDClient {
|
|
9
26
|
private api;
|
|
10
27
|
private apikey;
|
|
11
|
-
|
|
12
|
-
|
|
28
|
+
private securityKey?;
|
|
29
|
+
private publicKey?;
|
|
30
|
+
private adminToken;
|
|
31
|
+
constructor(apiKey: string, securityKey?: string, publicKey?: string);
|
|
32
|
+
setSecurityKey(key: string): void;
|
|
33
|
+
setPublicKey(key: string): void;
|
|
34
|
+
setAdminToken(token: string): void;
|
|
35
|
+
getAdminToken(): string | null;
|
|
36
|
+
getPublicKey(): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Reusable private error formatting utility
|
|
39
|
+
*/
|
|
40
|
+
private handleError;
|
|
41
|
+
/**
|
|
42
|
+
* Handles authentication HTTP requests targeting /auth/admin/login
|
|
43
|
+
*/
|
|
44
|
+
adminLogin(adminEmail?: string, password?: string): Promise<LoginResponse>;
|
|
45
|
+
/**
|
|
46
|
+
* Determines if an API request is a database write operation.
|
|
47
|
+
*/
|
|
48
|
+
private isWriteOperation;
|
|
49
|
+
/**
|
|
50
|
+
* Executes queries and mutations against /data endpoint.
|
|
51
|
+
*/
|
|
52
|
+
sendDataRequest(method: "POST" | "GET" | "PATCH" | "DELETE", body?: any): Promise<any>;
|
|
13
53
|
}
|
|
14
54
|
|
|
15
55
|
type Operator = "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "contains";
|
|
@@ -48,13 +88,53 @@ declare class QueryBuilder {
|
|
|
48
88
|
deleteMany(): Promise<any>;
|
|
49
89
|
}
|
|
50
90
|
|
|
91
|
+
declare class AdminAuth {
|
|
92
|
+
private client;
|
|
93
|
+
constructor(client: TDClient);
|
|
94
|
+
/**
|
|
95
|
+
* Authenticate an administrator using credentials.
|
|
96
|
+
* The token returned will be automatically stored in the SDK client.
|
|
97
|
+
*/
|
|
98
|
+
credentials(adminEmail?: string, password?: string): Promise<LoginResponse>;
|
|
99
|
+
/**
|
|
100
|
+
* Get the currently active administrator JWT token.
|
|
101
|
+
*/
|
|
102
|
+
getToken(): string | null;
|
|
103
|
+
/**
|
|
104
|
+
* Manually set the administrator JWT token (e.g. for server-side requests).
|
|
105
|
+
*/
|
|
106
|
+
setToken(token: string): void;
|
|
107
|
+
/**
|
|
108
|
+
* Offline verification of Ed25519 (EdDSA) JWT admin token using public key.
|
|
109
|
+
* Supported in Node.js / Server-side environments.
|
|
110
|
+
*/
|
|
111
|
+
verifyJwt(token: string, publicKeyPem?: string): any;
|
|
112
|
+
}
|
|
113
|
+
declare class AuthModule {
|
|
114
|
+
admin: AdminAuth;
|
|
115
|
+
constructor(client: TDClient);
|
|
116
|
+
}
|
|
117
|
+
|
|
51
118
|
declare class ThinkingDifferently {
|
|
52
119
|
private client;
|
|
120
|
+
auth: AuthModule;
|
|
53
121
|
constructor(config: SDKConfig);
|
|
122
|
+
/**
|
|
123
|
+
* Dynamically update the security key (useful for backend projects).
|
|
124
|
+
*/
|
|
125
|
+
setSecurityKey(key: string): void;
|
|
126
|
+
/**
|
|
127
|
+
* Dynamically update the public key used for offline verification.
|
|
128
|
+
*/
|
|
129
|
+
setPublicKey(key: string): void;
|
|
130
|
+
/**
|
|
131
|
+
* Helper to verify Ed25519 admin JWT token offline.
|
|
132
|
+
*/
|
|
133
|
+
verifyJwt(token: string, publicKey?: string): any;
|
|
54
134
|
collection(name: string): QueryBuilder;
|
|
55
135
|
insert(key: string, data: any): Promise<any>;
|
|
56
136
|
update(key: string, id: string, data: Record<string, any>): Promise<any>;
|
|
57
137
|
delete(key: string, id: string): Promise<any>;
|
|
58
138
|
}
|
|
59
139
|
|
|
60
|
-
export { type GetOptions, type SDKConfig, TDClient, ThinkingDifferently };
|
|
140
|
+
export { AdminAuth, AuthModule, type CredentialParams, type GetOptions, type LoginResponse, type SDKConfig, TDClient, ThinkingDifferently };
|
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,19 +40,155 @@ 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;
|
|
42
45
|
this.apikey = apiKey;
|
|
46
|
+
this.securityKey = securityKey;
|
|
47
|
+
this.publicKey = publicKey;
|
|
43
48
|
this.api = import_axios.default.create({
|
|
49
|
+
// baseURL: "http://localhost:3000/api/v1",
|
|
44
50
|
baseURL: "https://www.thinkingdifferently.dev/api/v1",
|
|
45
51
|
headers: {
|
|
46
|
-
"
|
|
52
|
+
"X-API-Key": apiKey,
|
|
47
53
|
"Content-Type": "application/json"
|
|
48
|
-
}
|
|
54
|
+
},
|
|
55
|
+
withCredentials: true
|
|
56
|
+
// Enable browser cookie sharing automatically
|
|
49
57
|
});
|
|
50
58
|
}
|
|
51
|
-
|
|
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) {
|
|
52
134
|
try {
|
|
53
|
-
const
|
|
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
|
+
}
|
|
180
|
+
try {
|
|
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
|
+
}
|
|
54
192
|
if (isFormData) {
|
|
55
193
|
body.append("key", this.apikey);
|
|
56
194
|
} else {
|
|
@@ -62,14 +200,12 @@ var TDClient = class {
|
|
|
62
200
|
const response = await this.api.request({
|
|
63
201
|
url: "/data",
|
|
64
202
|
method,
|
|
65
|
-
headers
|
|
203
|
+
headers,
|
|
66
204
|
...method === "GET" ? { params: body } : { data: body }
|
|
67
205
|
});
|
|
68
206
|
return response.data;
|
|
69
207
|
} catch (error) {
|
|
70
|
-
throw
|
|
71
|
-
error?.response?.data?.message || "Something went wrong"
|
|
72
|
-
);
|
|
208
|
+
throw this.handleError(error);
|
|
73
209
|
}
|
|
74
210
|
}
|
|
75
211
|
};
|
|
@@ -121,7 +257,7 @@ var QueryBuilder = class {
|
|
|
121
257
|
console.log("\n================ COUNT REQUEST ================");
|
|
122
258
|
console.log("[SDK] Final Query:", this.query);
|
|
123
259
|
try {
|
|
124
|
-
const response = await this.client.
|
|
260
|
+
const response = await this.client.sendDataRequest(
|
|
125
261
|
"POST",
|
|
126
262
|
this.query
|
|
127
263
|
);
|
|
@@ -136,11 +272,16 @@ var QueryBuilder = class {
|
|
|
136
272
|
return structuredClone(this.query);
|
|
137
273
|
}
|
|
138
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
|
+
}
|
|
139
280
|
this.query.operation = "find";
|
|
140
281
|
console.log("\n================ GET REQUEST ================");
|
|
141
282
|
console.log("[SDK] Final Query:", this.query);
|
|
142
283
|
try {
|
|
143
|
-
const response = await this.client.
|
|
284
|
+
const response = await this.client.sendDataRequest(
|
|
144
285
|
"POST",
|
|
145
286
|
this.query
|
|
146
287
|
);
|
|
@@ -181,14 +322,14 @@ var QueryBuilder = class {
|
|
|
181
322
|
JSON.stringify(this.query)
|
|
182
323
|
);
|
|
183
324
|
console.log("[SDK] Query:", this.query);
|
|
184
|
-
return await this.client.
|
|
325
|
+
return await this.client.sendDataRequest(
|
|
185
326
|
"POST",
|
|
186
327
|
payload
|
|
187
328
|
);
|
|
188
329
|
}
|
|
189
330
|
this.query.data = data;
|
|
190
331
|
console.log("[SDK] Final Query:", this.query);
|
|
191
|
-
return await this.client.
|
|
332
|
+
return await this.client.sendDataRequest(
|
|
192
333
|
"POST",
|
|
193
334
|
this.query
|
|
194
335
|
);
|
|
@@ -205,7 +346,7 @@ var QueryBuilder = class {
|
|
|
205
346
|
console.log("\n================ UPDATE REQUEST ================");
|
|
206
347
|
console.log("[SDK] Final Query:", this.query);
|
|
207
348
|
try {
|
|
208
|
-
const response = await this.client.
|
|
349
|
+
const response = await this.client.sendDataRequest(
|
|
209
350
|
"PATCH",
|
|
210
351
|
this.query
|
|
211
352
|
);
|
|
@@ -226,7 +367,7 @@ var QueryBuilder = class {
|
|
|
226
367
|
console.log("\n================ UPDATE MANY REQUEST ================");
|
|
227
368
|
console.log("[SDK] Final Query:", this.query);
|
|
228
369
|
try {
|
|
229
|
-
const response = await this.client.
|
|
370
|
+
const response = await this.client.sendDataRequest(
|
|
230
371
|
"PATCH",
|
|
231
372
|
this.query
|
|
232
373
|
);
|
|
@@ -244,7 +385,7 @@ var QueryBuilder = class {
|
|
|
244
385
|
console.log("\n================ DELETE REQUEST ================");
|
|
245
386
|
console.log("[SDK] Final Query:", this.query);
|
|
246
387
|
try {
|
|
247
|
-
const response = await this.client.
|
|
388
|
+
const response = await this.client.sendDataRequest(
|
|
248
389
|
"DELETE",
|
|
249
390
|
this.query
|
|
250
391
|
);
|
|
@@ -264,7 +405,7 @@ var QueryBuilder = class {
|
|
|
264
405
|
console.log("\n================ DELETE MANY REQUEST ================");
|
|
265
406
|
console.log("[SDK] Final Query:", this.query);
|
|
266
407
|
try {
|
|
267
|
-
const response = await this.client.
|
|
408
|
+
const response = await this.client.sendDataRequest(
|
|
268
409
|
"DELETE",
|
|
269
410
|
this.query
|
|
270
411
|
);
|
|
@@ -278,11 +419,139 @@ var QueryBuilder = class {
|
|
|
278
419
|
}
|
|
279
420
|
};
|
|
280
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);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
281
531
|
// src/index.ts
|
|
282
532
|
var ThinkingDifferently = class {
|
|
283
533
|
constructor(config) {
|
|
284
534
|
console.log("[ThinkingDifferently SDK] Initializing SDK");
|
|
285
|
-
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);
|
|
286
555
|
}
|
|
287
556
|
collection(name) {
|
|
288
557
|
return new QueryBuilder(
|
|
@@ -316,7 +585,7 @@ var ThinkingDifferently = class {
|
|
|
316
585
|
for (const [k, v] of payload.entries()) {
|
|
317
586
|
console.log(k, v);
|
|
318
587
|
}
|
|
319
|
-
return this.client.
|
|
588
|
+
return this.client.sendDataRequest(
|
|
320
589
|
"POST",
|
|
321
590
|
payload
|
|
322
591
|
);
|
|
@@ -326,7 +595,7 @@ var ThinkingDifferently = class {
|
|
|
326
595
|
key,
|
|
327
596
|
data
|
|
328
597
|
});
|
|
329
|
-
const response = await this.client.
|
|
598
|
+
const response = await this.client.sendDataRequest(
|
|
330
599
|
"POST",
|
|
331
600
|
{
|
|
332
601
|
key,
|
|
@@ -347,7 +616,7 @@ var ThinkingDifferently = class {
|
|
|
347
616
|
console.log("[SDK] Document ID:", id);
|
|
348
617
|
console.log("[SDK] Update Data:", data);
|
|
349
618
|
try {
|
|
350
|
-
const response = await this.client.
|
|
619
|
+
const response = await this.client.sendDataRequest(
|
|
351
620
|
"PATCH",
|
|
352
621
|
{
|
|
353
622
|
key,
|
|
@@ -368,7 +637,7 @@ var ThinkingDifferently = class {
|
|
|
368
637
|
console.log("[SDK] Collection Key:", key);
|
|
369
638
|
console.log("[SDK] Document ID:", id);
|
|
370
639
|
try {
|
|
371
|
-
const response = await this.client.
|
|
640
|
+
const response = await this.client.sendDataRequest(
|
|
372
641
|
"DELETE",
|
|
373
642
|
{
|
|
374
643
|
key,
|
|
@@ -386,6 +655,8 @@ var ThinkingDifferently = class {
|
|
|
386
655
|
};
|
|
387
656
|
// Annotate the CommonJS export names for ESM import in node:
|
|
388
657
|
0 && (module.exports = {
|
|
658
|
+
AdminAuth,
|
|
659
|
+
AuthModule,
|
|
389
660
|
TDClient,
|
|
390
661
|
ThinkingDifferently
|
|
391
662
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,155 @@
|
|
|
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;
|
|
5
6
|
this.apikey = apiKey;
|
|
7
|
+
this.securityKey = securityKey;
|
|
8
|
+
this.publicKey = publicKey;
|
|
6
9
|
this.api = axios.create({
|
|
10
|
+
// baseURL: "http://localhost:3000/api/v1",
|
|
7
11
|
baseURL: "https://www.thinkingdifferently.dev/api/v1",
|
|
8
12
|
headers: {
|
|
9
|
-
"
|
|
13
|
+
"X-API-Key": apiKey,
|
|
10
14
|
"Content-Type": "application/json"
|
|
11
|
-
}
|
|
15
|
+
},
|
|
16
|
+
withCredentials: true
|
|
17
|
+
// Enable browser cookie sharing automatically
|
|
12
18
|
});
|
|
13
19
|
}
|
|
14
|
-
|
|
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) {
|
|
15
95
|
try {
|
|
16
|
-
const
|
|
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
|
+
}
|
|
141
|
+
try {
|
|
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
|
+
}
|
|
17
153
|
if (isFormData) {
|
|
18
154
|
body.append("key", this.apikey);
|
|
19
155
|
} else {
|
|
@@ -25,14 +161,12 @@ var TDClient = class {
|
|
|
25
161
|
const response = await this.api.request({
|
|
26
162
|
url: "/data",
|
|
27
163
|
method,
|
|
28
|
-
headers
|
|
164
|
+
headers,
|
|
29
165
|
...method === "GET" ? { params: body } : { data: body }
|
|
30
166
|
});
|
|
31
167
|
return response.data;
|
|
32
168
|
} catch (error) {
|
|
33
|
-
throw
|
|
34
|
-
error?.response?.data?.message || "Something went wrong"
|
|
35
|
-
);
|
|
169
|
+
throw this.handleError(error);
|
|
36
170
|
}
|
|
37
171
|
}
|
|
38
172
|
};
|
|
@@ -84,7 +218,7 @@ var QueryBuilder = class {
|
|
|
84
218
|
console.log("\n================ COUNT REQUEST ================");
|
|
85
219
|
console.log("[SDK] Final Query:", this.query);
|
|
86
220
|
try {
|
|
87
|
-
const response = await this.client.
|
|
221
|
+
const response = await this.client.sendDataRequest(
|
|
88
222
|
"POST",
|
|
89
223
|
this.query
|
|
90
224
|
);
|
|
@@ -99,11 +233,16 @@ var QueryBuilder = class {
|
|
|
99
233
|
return structuredClone(this.query);
|
|
100
234
|
}
|
|
101
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
|
+
}
|
|
102
241
|
this.query.operation = "find";
|
|
103
242
|
console.log("\n================ GET REQUEST ================");
|
|
104
243
|
console.log("[SDK] Final Query:", this.query);
|
|
105
244
|
try {
|
|
106
|
-
const response = await this.client.
|
|
245
|
+
const response = await this.client.sendDataRequest(
|
|
107
246
|
"POST",
|
|
108
247
|
this.query
|
|
109
248
|
);
|
|
@@ -144,14 +283,14 @@ var QueryBuilder = class {
|
|
|
144
283
|
JSON.stringify(this.query)
|
|
145
284
|
);
|
|
146
285
|
console.log("[SDK] Query:", this.query);
|
|
147
|
-
return await this.client.
|
|
286
|
+
return await this.client.sendDataRequest(
|
|
148
287
|
"POST",
|
|
149
288
|
payload
|
|
150
289
|
);
|
|
151
290
|
}
|
|
152
291
|
this.query.data = data;
|
|
153
292
|
console.log("[SDK] Final Query:", this.query);
|
|
154
|
-
return await this.client.
|
|
293
|
+
return await this.client.sendDataRequest(
|
|
155
294
|
"POST",
|
|
156
295
|
this.query
|
|
157
296
|
);
|
|
@@ -168,7 +307,7 @@ var QueryBuilder = class {
|
|
|
168
307
|
console.log("\n================ UPDATE REQUEST ================");
|
|
169
308
|
console.log("[SDK] Final Query:", this.query);
|
|
170
309
|
try {
|
|
171
|
-
const response = await this.client.
|
|
310
|
+
const response = await this.client.sendDataRequest(
|
|
172
311
|
"PATCH",
|
|
173
312
|
this.query
|
|
174
313
|
);
|
|
@@ -189,7 +328,7 @@ var QueryBuilder = class {
|
|
|
189
328
|
console.log("\n================ UPDATE MANY REQUEST ================");
|
|
190
329
|
console.log("[SDK] Final Query:", this.query);
|
|
191
330
|
try {
|
|
192
|
-
const response = await this.client.
|
|
331
|
+
const response = await this.client.sendDataRequest(
|
|
193
332
|
"PATCH",
|
|
194
333
|
this.query
|
|
195
334
|
);
|
|
@@ -207,7 +346,7 @@ var QueryBuilder = class {
|
|
|
207
346
|
console.log("\n================ DELETE REQUEST ================");
|
|
208
347
|
console.log("[SDK] Final Query:", this.query);
|
|
209
348
|
try {
|
|
210
|
-
const response = await this.client.
|
|
349
|
+
const response = await this.client.sendDataRequest(
|
|
211
350
|
"DELETE",
|
|
212
351
|
this.query
|
|
213
352
|
);
|
|
@@ -227,7 +366,7 @@ var QueryBuilder = class {
|
|
|
227
366
|
console.log("\n================ DELETE MANY REQUEST ================");
|
|
228
367
|
console.log("[SDK] Final Query:", this.query);
|
|
229
368
|
try {
|
|
230
|
-
const response = await this.client.
|
|
369
|
+
const response = await this.client.sendDataRequest(
|
|
231
370
|
"DELETE",
|
|
232
371
|
this.query
|
|
233
372
|
);
|
|
@@ -241,11 +380,139 @@ var QueryBuilder = class {
|
|
|
241
380
|
}
|
|
242
381
|
};
|
|
243
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);
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
244
492
|
// src/index.ts
|
|
245
493
|
var ThinkingDifferently = class {
|
|
246
494
|
constructor(config) {
|
|
247
495
|
console.log("[ThinkingDifferently SDK] Initializing SDK");
|
|
248
|
-
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);
|
|
249
516
|
}
|
|
250
517
|
collection(name) {
|
|
251
518
|
return new QueryBuilder(
|
|
@@ -279,7 +546,7 @@ var ThinkingDifferently = class {
|
|
|
279
546
|
for (const [k, v] of payload.entries()) {
|
|
280
547
|
console.log(k, v);
|
|
281
548
|
}
|
|
282
|
-
return this.client.
|
|
549
|
+
return this.client.sendDataRequest(
|
|
283
550
|
"POST",
|
|
284
551
|
payload
|
|
285
552
|
);
|
|
@@ -289,7 +556,7 @@ var ThinkingDifferently = class {
|
|
|
289
556
|
key,
|
|
290
557
|
data
|
|
291
558
|
});
|
|
292
|
-
const response = await this.client.
|
|
559
|
+
const response = await this.client.sendDataRequest(
|
|
293
560
|
"POST",
|
|
294
561
|
{
|
|
295
562
|
key,
|
|
@@ -310,7 +577,7 @@ var ThinkingDifferently = class {
|
|
|
310
577
|
console.log("[SDK] Document ID:", id);
|
|
311
578
|
console.log("[SDK] Update Data:", data);
|
|
312
579
|
try {
|
|
313
|
-
const response = await this.client.
|
|
580
|
+
const response = await this.client.sendDataRequest(
|
|
314
581
|
"PATCH",
|
|
315
582
|
{
|
|
316
583
|
key,
|
|
@@ -331,7 +598,7 @@ var ThinkingDifferently = class {
|
|
|
331
598
|
console.log("[SDK] Collection Key:", key);
|
|
332
599
|
console.log("[SDK] Document ID:", id);
|
|
333
600
|
try {
|
|
334
|
-
const response = await this.client.
|
|
601
|
+
const response = await this.client.sendDataRequest(
|
|
335
602
|
"DELETE",
|
|
336
603
|
{
|
|
337
604
|
key,
|
|
@@ -348,6 +615,8 @@ var ThinkingDifferently = class {
|
|
|
348
615
|
}
|
|
349
616
|
};
|
|
350
617
|
export {
|
|
618
|
+
AdminAuth,
|
|
619
|
+
AuthModule,
|
|
351
620
|
TDClient,
|
|
352
621
|
ThinkingDifferently
|
|
353
622
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thinkingdifferently/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
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",
|