@trainly/react 1.1.0 → 1.1.3

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.
@@ -8,7 +8,8 @@ export interface TrainlyProviderProps {
8
8
  baseUrl?: string;
9
9
  userId?: string;
10
10
  userEmail?: string;
11
+ getToken?: () => Promise<string | null>;
11
12
  }
12
13
  export declare function TrainlyProvider({ children, appSecret, apiKey, appId, // NEW: For V1 authentication
13
- baseUrl, userId, userEmail, }: TrainlyProviderProps): import("react/jsx-runtime").JSX.Element;
14
+ baseUrl, userId, userEmail, getToken, }: TrainlyProviderProps): import("react/jsx-runtime").JSX.Element;
14
15
  export declare function useTrainlyContext(): TrainlyContextValue;
@@ -52,7 +52,7 @@ export function TrainlyProvider(_a) {
52
52
  var _this = this;
53
53
  var children = _a.children, appSecret = _a.appSecret, apiKey = _a.apiKey, appId = _a.appId, // NEW: For V1 authentication
54
54
  _b = _a.baseUrl, // NEW: For V1 authentication
55
- baseUrl = _b === void 0 ? "http://localhost:8000" : _b, userId = _a.userId, userEmail = _a.userEmail;
55
+ baseUrl = _b === void 0 ? "http://localhost:8000" : _b, userId = _a.userId, userEmail = _a.userEmail, getToken = _a.getToken;
56
56
  var client = React.useState(function () {
57
57
  return new TrainlyClient({
58
58
  appSecret: appSecret,
@@ -67,9 +67,67 @@ export function TrainlyProvider(_a) {
67
67
  var _d = React.useState(false), isConnected = _d[0], setIsConnected = _d[1];
68
68
  var _e = React.useState(null), error = _e[0], setError = _e[1];
69
69
  var _f = React.useState([]), messages = _f[0], setMessages = _f[1];
70
- // Auto-connect on mount
70
+ var _g = React.useState(null), currentToken = _g[0], setCurrentToken = _g[1];
71
+ var refreshIntervalRef = React.useRef(null);
72
+ // Auto-connect on mount (only for non-OAuth modes)
71
73
  React.useEffect(function () {
72
- connect();
74
+ if (!appId && !getToken) {
75
+ connect();
76
+ }
77
+ }, []);
78
+ // NEW: Automatic OAuth state management
79
+ React.useEffect(function () {
80
+ if (!getToken || !appId)
81
+ return;
82
+ var manageOAuthConnection = function () { return __awaiter(_this, void 0, void 0, function () {
83
+ var token, error_1;
84
+ return __generator(this, function (_a) {
85
+ switch (_a.label) {
86
+ case 0:
87
+ _a.trys.push([0, 6, , 7]);
88
+ return [4 /*yield*/, getToken()];
89
+ case 1:
90
+ token = _a.sent();
91
+ if (!(token && token !== currentToken)) return [3 /*break*/, 4];
92
+ if (!(!isConnected || token !== currentToken)) return [3 /*break*/, 3];
93
+ console.log("🔐 New OAuth session detected, connecting to Trainly...");
94
+ return [4 /*yield*/, connectWithOAuthToken(token)];
95
+ case 2:
96
+ _a.sent();
97
+ _a.label = 3;
98
+ case 3: return [3 /*break*/, 5];
99
+ case 4:
100
+ if (!token && isConnected) {
101
+ // User signed out, disconnect from Trainly
102
+ console.log("🚪 User signed out, disconnecting from Trainly...");
103
+ clearRefreshInterval();
104
+ setIsConnected(false);
105
+ setCurrentToken(null);
106
+ setError(null);
107
+ }
108
+ _a.label = 5;
109
+ case 5: return [3 /*break*/, 7];
110
+ case 6:
111
+ error_1 = _a.sent();
112
+ console.error("OAuth state management error:", error_1);
113
+ return [3 /*break*/, 7];
114
+ case 7: return [2 /*return*/];
115
+ }
116
+ });
117
+ }); };
118
+ // Check OAuth state immediately
119
+ manageOAuthConnection();
120
+ // Set up periodic OAuth state checking (every 10 seconds)
121
+ var stateCheckInterval = setInterval(manageOAuthConnection, 10000);
122
+ return function () {
123
+ clearInterval(stateCheckInterval);
124
+ };
125
+ }, [getToken, appId, currentToken, isConnected]);
126
+ // Cleanup on unmount
127
+ React.useEffect(function () {
128
+ return function () {
129
+ clearRefreshInterval();
130
+ };
73
131
  }, []);
74
132
  var connect = function () { return __awaiter(_this, void 0, void 0, function () {
75
133
  var err_1;
@@ -100,7 +158,62 @@ export function TrainlyProvider(_a) {
100
158
  }
101
159
  });
102
160
  }); };
103
- // NEW: V1 OAuth Token connection method
161
+ // Clear any existing refresh interval
162
+ var clearRefreshInterval = function () {
163
+ if (refreshIntervalRef.current) {
164
+ clearInterval(refreshIntervalRef.current);
165
+ refreshIntervalRef.current = null;
166
+ }
167
+ };
168
+ // Set up automatic token refresh
169
+ var setupTokenRefresh = function (token) {
170
+ if (!getToken || !appId)
171
+ return;
172
+ // Clear any existing interval
173
+ clearRefreshInterval();
174
+ // Decode token to get expiration (without verification)
175
+ try {
176
+ var payload = JSON.parse(atob(token.split(".")[1]));
177
+ var exp = payload.exp * 1000; // Convert to milliseconds
178
+ var now = Date.now();
179
+ var timeUntilExpiry = exp - now;
180
+ // Refresh 30 seconds before expiry (or immediately if already expired)
181
+ var refreshIn = Math.max(timeUntilExpiry - 30000, 1000);
182
+ console.log("\uD83D\uDD04 Token refresh scheduled in ".concat(Math.round(refreshIn / 1000), " seconds"));
183
+ refreshIntervalRef.current = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
184
+ var newToken, error_2;
185
+ return __generator(this, function (_a) {
186
+ switch (_a.label) {
187
+ case 0:
188
+ _a.trys.push([0, 4, , 5]);
189
+ console.log("🔄 Auto-refreshing token...");
190
+ return [4 /*yield*/, getToken()];
191
+ case 1:
192
+ newToken = _a.sent();
193
+ if (!(newToken && newToken !== currentToken)) return [3 /*break*/, 3];
194
+ return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
195
+ case 2:
196
+ _a.sent();
197
+ setCurrentToken(newToken);
198
+ setupTokenRefresh(newToken); // Schedule next refresh
199
+ console.log("✅ Token auto-refreshed successfully");
200
+ _a.label = 3;
201
+ case 3: return [3 /*break*/, 5];
202
+ case 4:
203
+ error_2 = _a.sent();
204
+ console.error("❌ Auto token refresh failed:", error_2);
205
+ setIsConnected(false);
206
+ return [3 /*break*/, 5];
207
+ case 5: return [2 /*return*/];
208
+ }
209
+ });
210
+ }); }, refreshIn);
211
+ }
212
+ catch (error) {
213
+ console.warn("Could not decode token for refresh scheduling:", error);
214
+ }
215
+ };
216
+ // NEW: V1 OAuth Token connection method with auto-refresh
104
217
  var connectWithOAuthToken = function (idToken) { return __awaiter(_this, void 0, void 0, function () {
105
218
  var err_2;
106
219
  return __generator(this, function (_a) {
@@ -112,7 +225,10 @@ export function TrainlyProvider(_a) {
112
225
  return [4 /*yield*/, client.connectWithOAuthToken(idToken)];
113
226
  case 1:
114
227
  _a.sent();
228
+ setCurrentToken(idToken);
115
229
  setIsConnected(true);
230
+ // Set up automatic token refresh
231
+ setupTokenRefresh(idToken);
116
232
  return [3 /*break*/, 4];
117
233
  case 2:
118
234
  err_2 = _a.sent();
@@ -131,11 +247,11 @@ export function TrainlyProvider(_a) {
131
247
  });
132
248
  }); };
133
249
  var ask = function (question) { return __awaiter(_this, void 0, void 0, function () {
134
- var response, err_3, error_1;
250
+ var response, err_3, errorMessage, newToken, response, refreshError_1, error_3;
135
251
  return __generator(this, function (_a) {
136
252
  switch (_a.label) {
137
253
  case 0:
138
- _a.trys.push([0, 2, 3, 4]);
254
+ _a.trys.push([0, 2, 10, 11]);
139
255
  setIsLoading(true);
140
256
  setError(null);
141
257
  return [4 /*yield*/, client.ask(question)];
@@ -144,22 +260,50 @@ export function TrainlyProvider(_a) {
144
260
  return [2 /*return*/, response.answer];
145
261
  case 2:
146
262
  err_3 = _a.sent();
147
- error_1 = {
263
+ errorMessage = err_3 instanceof Error ? err_3.message : String(err_3);
264
+ if (!(getToken &&
265
+ appId &&
266
+ (errorMessage.includes("401") ||
267
+ errorMessage.includes("authentication") ||
268
+ errorMessage.includes("Unauthorized")))) return [3 /*break*/, 9];
269
+ _a.label = 3;
270
+ case 3:
271
+ _a.trys.push([3, 8, , 9]);
272
+ console.log("🔄 Token expired, refreshing...");
273
+ return [4 /*yield*/, getToken()];
274
+ case 4:
275
+ newToken = _a.sent();
276
+ if (!newToken) return [3 /*break*/, 7];
277
+ return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
278
+ case 5:
279
+ _a.sent();
280
+ return [4 /*yield*/, client.ask(question)];
281
+ case 6:
282
+ response = _a.sent();
283
+ console.log("✅ Query succeeded after token refresh");
284
+ return [2 /*return*/, response.answer];
285
+ case 7: return [3 /*break*/, 9];
286
+ case 8:
287
+ refreshError_1 = _a.sent();
288
+ console.error("❌ Token refresh failed:", refreshError_1);
289
+ return [3 /*break*/, 9];
290
+ case 9:
291
+ error_3 = {
148
292
  code: "QUERY_FAILED",
149
293
  message: "Failed to get answer",
150
294
  details: err_3,
151
295
  };
152
- setError(error_1);
153
- throw error_1;
154
- case 3:
296
+ setError(error_3);
297
+ throw error_3;
298
+ case 10:
155
299
  setIsLoading(false);
156
300
  return [7 /*endfinally*/];
157
- case 4: return [2 /*return*/];
301
+ case 11: return [2 /*return*/];
158
302
  }
159
303
  });
160
304
  }); };
161
305
  var askWithCitations = function (question) { return __awaiter(_this, void 0, void 0, function () {
162
- var response, err_4, error_2;
306
+ var response, err_4, error_4;
163
307
  return __generator(this, function (_a) {
164
308
  switch (_a.label) {
165
309
  case 0:
@@ -175,13 +319,13 @@ export function TrainlyProvider(_a) {
175
319
  }];
176
320
  case 2:
177
321
  err_4 = _a.sent();
178
- error_2 = {
322
+ error_4 = {
179
323
  code: "QUERY_FAILED",
180
324
  message: "Failed to get answer with citations",
181
325
  details: err_4,
182
326
  };
183
- setError(error_2);
184
- throw error_2;
327
+ setError(error_4);
328
+ throw error_4;
185
329
  case 3:
186
330
  setIsLoading(false);
187
331
  return [7 /*endfinally*/];
@@ -190,11 +334,11 @@ export function TrainlyProvider(_a) {
190
334
  });
191
335
  }); };
192
336
  var upload = function (file) { return __awaiter(_this, void 0, void 0, function () {
193
- var result, err_5, error_3;
337
+ var result, err_5, errorMessage, newToken, result, refreshError_2, error_5;
194
338
  return __generator(this, function (_a) {
195
339
  switch (_a.label) {
196
340
  case 0:
197
- _a.trys.push([0, 2, 3, 4]);
341
+ _a.trys.push([0, 2, 10, 11]);
198
342
  setIsLoading(true);
199
343
  setError(null);
200
344
  return [4 /*yield*/, client.upload(file)];
@@ -203,17 +347,45 @@ export function TrainlyProvider(_a) {
203
347
  return [2 /*return*/, result];
204
348
  case 2:
205
349
  err_5 = _a.sent();
206
- error_3 = {
350
+ errorMessage = err_5 instanceof Error ? err_5.message : String(err_5);
351
+ if (!(getToken &&
352
+ appId &&
353
+ (errorMessage.includes("401") ||
354
+ errorMessage.includes("authentication") ||
355
+ errorMessage.includes("Unauthorized")))) return [3 /*break*/, 9];
356
+ _a.label = 3;
357
+ case 3:
358
+ _a.trys.push([3, 8, , 9]);
359
+ console.log("🔄 Token expired during upload, refreshing...");
360
+ return [4 /*yield*/, getToken()];
361
+ case 4:
362
+ newToken = _a.sent();
363
+ if (!newToken) return [3 /*break*/, 7];
364
+ return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
365
+ case 5:
366
+ _a.sent();
367
+ return [4 /*yield*/, client.upload(file)];
368
+ case 6:
369
+ result = _a.sent();
370
+ console.log("✅ Upload succeeded after token refresh");
371
+ return [2 /*return*/, result];
372
+ case 7: return [3 /*break*/, 9];
373
+ case 8:
374
+ refreshError_2 = _a.sent();
375
+ console.error("❌ Token refresh failed:", refreshError_2);
376
+ return [3 /*break*/, 9];
377
+ case 9:
378
+ error_5 = {
207
379
  code: "UPLOAD_FAILED",
208
380
  message: "Failed to upload file",
209
381
  details: err_5,
210
382
  };
211
- setError(error_3);
212
- throw error_3;
213
- case 3:
383
+ setError(error_5);
384
+ throw error_5;
385
+ case 10:
214
386
  setIsLoading(false);
215
387
  return [7 /*endfinally*/];
216
- case 4: return [2 /*return*/];
388
+ case 11: return [2 /*return*/];
217
389
  }
218
390
  });
219
391
  }); };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trainly/react",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "description": "Dead simple RAG integration for React apps with OAuth authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",