@insforge/sdk 1.4.2 → 1.4.4
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 +141 -135
- package/SDK-REFERENCE.md +146 -168
- package/dist/{client-BR9o-WUm.d.ts → client-BZyLCjO1.d.mts} +62 -122
- package/dist/{client-C-qBRoea.d.mts → client-Dh7GOydb.d.ts} +62 -122
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +585 -428
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +588 -428
- package/dist/index.mjs.map +1 -1
- package/dist/{middleware-Tu_RlUAt.d.ts → middleware-CicBLgnO.d.ts} +1 -1
- package/dist/{middleware-K59XjpUX.d.mts → middleware-o3zPqPvY.d.mts} +1 -1
- package/dist/ssr/middleware.d.mts +2 -2
- package/dist/ssr/middleware.d.ts +2 -2
- package/dist/ssr/middleware.js +71 -52
- package/dist/ssr/middleware.js.map +1 -1
- package/dist/ssr/middleware.mjs +71 -52
- package/dist/ssr/middleware.mjs.map +1 -1
- package/dist/ssr.d.mts +4 -4
- package/dist/ssr.d.ts +4 -4
- package/dist/ssr.js +680 -544
- package/dist/ssr.js.map +1 -1
- package/dist/ssr.mjs +684 -545
- package/dist/ssr.mjs.map +1 -1
- package/dist/{types-Dk-44JJf.d.mts → types-MKmYAYeg.d.mts} +12 -0
- package/dist/{types-Dk-44JJf.d.ts → types-MKmYAYeg.d.ts} +12 -0
- package/package.json +12 -5
package/dist/ssr.js
CHANGED
|
@@ -94,7 +94,9 @@ function redactHeaders(headers) {
|
|
|
94
94
|
return redacted;
|
|
95
95
|
}
|
|
96
96
|
function sanitizeBody(body) {
|
|
97
|
-
if (body === null || body === void 0)
|
|
97
|
+
if (body === null || body === void 0) {
|
|
98
|
+
return body;
|
|
99
|
+
}
|
|
98
100
|
if (typeof body === "string") {
|
|
99
101
|
try {
|
|
100
102
|
const parsed = JSON.parse(body);
|
|
@@ -103,7 +105,9 @@ function sanitizeBody(body) {
|
|
|
103
105
|
return body;
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
|
-
if (Array.isArray(body))
|
|
108
|
+
if (Array.isArray(body)) {
|
|
109
|
+
return body.map(sanitizeBody);
|
|
110
|
+
}
|
|
107
111
|
if (typeof body === "object") {
|
|
108
112
|
const sanitized = {};
|
|
109
113
|
for (const [key, value] of Object.entries(body)) {
|
|
@@ -118,7 +122,9 @@ function sanitizeBody(body) {
|
|
|
118
122
|
return body;
|
|
119
123
|
}
|
|
120
124
|
function formatBody(body) {
|
|
121
|
-
if (body === void 0 || body === null)
|
|
125
|
+
if (body === void 0 || body === null) {
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
122
128
|
if (typeof body === "string") {
|
|
123
129
|
try {
|
|
124
130
|
return JSON.stringify(JSON.parse(body), null, 2);
|
|
@@ -155,7 +161,9 @@ var Logger = class {
|
|
|
155
161
|
* @param args - Additional arguments to pass to the log function
|
|
156
162
|
*/
|
|
157
163
|
log(message, ...args) {
|
|
158
|
-
if (!this.enabled)
|
|
164
|
+
if (!this.enabled) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
159
167
|
const formatted = `[InsForge Debug] ${message}`;
|
|
160
168
|
if (this.customLog) {
|
|
161
169
|
this.customLog(formatted, ...args);
|
|
@@ -169,7 +177,9 @@ var Logger = class {
|
|
|
169
177
|
* @param args - Additional arguments to pass to the log function
|
|
170
178
|
*/
|
|
171
179
|
warn(message, ...args) {
|
|
172
|
-
if (!this.enabled)
|
|
180
|
+
if (!this.enabled) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
173
183
|
const formatted = `[InsForge Debug] ${message}`;
|
|
174
184
|
if (this.customLog) {
|
|
175
185
|
this.customLog(formatted, ...args);
|
|
@@ -183,7 +193,9 @@ var Logger = class {
|
|
|
183
193
|
* @param args - Additional arguments to pass to the log function
|
|
184
194
|
*/
|
|
185
195
|
error(message, ...args) {
|
|
186
|
-
if (!this.enabled)
|
|
196
|
+
if (!this.enabled) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
187
199
|
const formatted = `[InsForge Debug] ${message}`;
|
|
188
200
|
if (this.customLog) {
|
|
189
201
|
this.customLog(formatted, ...args);
|
|
@@ -200,10 +212,10 @@ var Logger = class {
|
|
|
200
212
|
* @param body - Request body (sensitive fields will be masked)
|
|
201
213
|
*/
|
|
202
214
|
logRequest(method, url, headers, body) {
|
|
203
|
-
if (!this.enabled)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
];
|
|
215
|
+
if (!this.enabled) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const parts = [`\u2192 ${method} ${url}`];
|
|
207
219
|
if (headers && Object.keys(headers).length > 0) {
|
|
208
220
|
parts.push(` Headers: ${JSON.stringify(redactHeaders(headers))}`);
|
|
209
221
|
}
|
|
@@ -224,10 +236,10 @@ var Logger = class {
|
|
|
224
236
|
* @param body - Response body (sensitive fields will be masked, large bodies truncated)
|
|
225
237
|
*/
|
|
226
238
|
logResponse(method, url, status, durationMs, body) {
|
|
227
|
-
if (!this.enabled)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
];
|
|
239
|
+
if (!this.enabled) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const parts = [`\u2190 ${method} ${url} ${status} (${durationMs}ms)`];
|
|
231
243
|
const formattedBody = formatBody(sanitizeBody(body));
|
|
232
244
|
if (formattedBody) {
|
|
233
245
|
const truncated = formattedBody.length > 1e3 ? formattedBody.slice(0, 1e3) + "... [truncated]" : formattedBody;
|
|
@@ -242,21 +254,34 @@ var Logger = class {
|
|
|
242
254
|
};
|
|
243
255
|
|
|
244
256
|
// src/lib/token-manager.ts
|
|
257
|
+
var AuthChangeEvent = {
|
|
258
|
+
SIGNED_IN: "signedIn",
|
|
259
|
+
SIGNED_OUT: "signedOut",
|
|
260
|
+
TOKEN_REFRESHED: "tokenRefreshed"
|
|
261
|
+
};
|
|
245
262
|
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
246
263
|
function getCsrfToken() {
|
|
247
|
-
if (typeof document === "undefined")
|
|
264
|
+
if (typeof document === "undefined") {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
248
267
|
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
249
|
-
if (!match)
|
|
268
|
+
if (!match) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
250
271
|
return match.split("=")[1] || null;
|
|
251
272
|
}
|
|
252
273
|
function setCsrfToken(token) {
|
|
253
|
-
if (typeof document === "undefined")
|
|
274
|
+
if (typeof document === "undefined") {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
254
277
|
const maxAge = 7 * 24 * 60 * 60;
|
|
255
278
|
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
256
279
|
document.cookie = `${CSRF_TOKEN_COOKIE}=${encodeURIComponent(token)}; path=/; max-age=${maxAge}; SameSite=Lax${secure}`;
|
|
257
280
|
}
|
|
258
281
|
function clearCsrfToken() {
|
|
259
|
-
if (typeof document === "undefined")
|
|
282
|
+
if (typeof document === "undefined") {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
260
285
|
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
261
286
|
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax${secure}`;
|
|
262
287
|
}
|
|
@@ -265,25 +290,26 @@ var TokenManager = class {
|
|
|
265
290
|
// In-memory storage
|
|
266
291
|
this.accessToken = null;
|
|
267
292
|
this.user = null;
|
|
268
|
-
|
|
269
|
-
this.onTokenChange = null;
|
|
293
|
+
this.authStateChangeCallbacks = /* @__PURE__ */ new Map();
|
|
270
294
|
}
|
|
271
295
|
/**
|
|
272
296
|
* Save session in memory
|
|
273
297
|
*/
|
|
274
|
-
saveSession(session) {
|
|
298
|
+
saveSession(session, event = AuthChangeEvent.SIGNED_IN) {
|
|
275
299
|
const tokenChanged = session.accessToken !== this.accessToken;
|
|
276
300
|
this.accessToken = session.accessToken;
|
|
277
301
|
this.user = session.user;
|
|
278
|
-
if (tokenChanged
|
|
279
|
-
this.
|
|
302
|
+
if (tokenChanged) {
|
|
303
|
+
this.notifyAuthStateChange(event);
|
|
280
304
|
}
|
|
281
305
|
}
|
|
282
306
|
/**
|
|
283
307
|
* Get current session
|
|
284
308
|
*/
|
|
285
309
|
getSession() {
|
|
286
|
-
if (!this.accessToken || !this.user)
|
|
310
|
+
if (!this.accessToken || !this.user) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
287
313
|
return {
|
|
288
314
|
accessToken: this.accessToken,
|
|
289
315
|
user: this.user
|
|
@@ -298,11 +324,11 @@ var TokenManager = class {
|
|
|
298
324
|
/**
|
|
299
325
|
* Set access token
|
|
300
326
|
*/
|
|
301
|
-
setAccessToken(token) {
|
|
327
|
+
setAccessToken(token, event = AuthChangeEvent.SIGNED_IN) {
|
|
302
328
|
const tokenChanged = token !== this.accessToken;
|
|
303
329
|
this.accessToken = token;
|
|
304
|
-
if (tokenChanged
|
|
305
|
-
this.
|
|
330
|
+
if (tokenChanged) {
|
|
331
|
+
this.notifyAuthStateChange(event);
|
|
306
332
|
}
|
|
307
333
|
}
|
|
308
334
|
/**
|
|
@@ -324,22 +350,74 @@ var TokenManager = class {
|
|
|
324
350
|
const hadToken = this.accessToken !== null;
|
|
325
351
|
this.accessToken = null;
|
|
326
352
|
this.user = null;
|
|
327
|
-
if (hadToken
|
|
328
|
-
this.
|
|
353
|
+
if (hadToken) {
|
|
354
|
+
this.notifyAuthStateChange(AuthChangeEvent.SIGNED_OUT);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
onAuthStateChange(callback) {
|
|
358
|
+
const id = Symbol("auth-state-change");
|
|
359
|
+
this.authStateChangeCallbacks.set(id, callback);
|
|
360
|
+
return () => this.authStateChangeCallbacks.delete(id);
|
|
361
|
+
}
|
|
362
|
+
notifyAuthStateChange(event) {
|
|
363
|
+
for (const callback of this.authStateChangeCallbacks.values()) {
|
|
364
|
+
try {
|
|
365
|
+
callback(event);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error("Error in auth state change callback:", error);
|
|
368
|
+
}
|
|
329
369
|
}
|
|
330
370
|
}
|
|
331
371
|
};
|
|
332
372
|
|
|
373
|
+
// src/lib/jwt.ts
|
|
374
|
+
function decodeBase64Url(input) {
|
|
375
|
+
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
376
|
+
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
377
|
+
const binary = atob(padded);
|
|
378
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
379
|
+
return new TextDecoder().decode(bytes);
|
|
380
|
+
}
|
|
381
|
+
function getJwtExpiration(token) {
|
|
382
|
+
if (!token) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
const [, payload] = token.split(".");
|
|
386
|
+
if (!payload) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
try {
|
|
390
|
+
const parsed = JSON.parse(decodeBase64Url(payload));
|
|
391
|
+
if (typeof parsed.exp !== "number" || !Number.isFinite(parsed.exp)) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return new Date(parsed.exp * 1e3);
|
|
395
|
+
} catch {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function isJwtExpiredOrExpiring(token, leewaySeconds = 60) {
|
|
400
|
+
if (!token) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
const expires = getJwtExpiration(token);
|
|
404
|
+
if (!expires) {
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
return expires.getTime() <= Date.now() + leewaySeconds * 1e3;
|
|
408
|
+
}
|
|
409
|
+
|
|
333
410
|
// src/lib/http-client.ts
|
|
334
411
|
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
|
|
335
412
|
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
336
|
-
var REFRESHABLE_AUTH_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
337
|
-
"AUTH_UNAUTHORIZED",
|
|
338
|
-
"PGRST301"
|
|
339
|
-
]);
|
|
413
|
+
var REFRESHABLE_AUTH_ERROR_CODES = /* @__PURE__ */ new Set(["AUTH_UNAUTHORIZED", "PGRST301"]);
|
|
340
414
|
function serializeBody(method, body, headers) {
|
|
341
|
-
if (body === void 0)
|
|
342
|
-
|
|
415
|
+
if (body === void 0) {
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
if (method === "GET" || method === "HEAD") {
|
|
419
|
+
return void 0;
|
|
420
|
+
}
|
|
343
421
|
if (typeof FormData !== "undefined" && body instanceof FormData) {
|
|
344
422
|
return body;
|
|
345
423
|
}
|
|
@@ -347,7 +425,9 @@ function serializeBody(method, body, headers) {
|
|
|
347
425
|
return JSON.stringify(body);
|
|
348
426
|
}
|
|
349
427
|
async function parseResponse(response) {
|
|
350
|
-
if (response.status === 204)
|
|
428
|
+
if (response.status === 204) {
|
|
429
|
+
return void 0;
|
|
430
|
+
}
|
|
351
431
|
let data;
|
|
352
432
|
const contentType = response.headers.get("content-type");
|
|
353
433
|
try {
|
|
@@ -449,31 +529,24 @@ var HttpClient = class {
|
|
|
449
529
|
return statusCode === 401 && REFRESHABLE_AUTH_ERROR_CODES.has(errorCode ?? "") && !this.config.isServerMode && !this.config.accessToken && !this.config.edgeFunctionToken && !options.skipAuthRefresh && authToken !== null;
|
|
450
530
|
}
|
|
451
531
|
async fetchWithRetry(args) {
|
|
452
|
-
const {
|
|
453
|
-
method,
|
|
454
|
-
url,
|
|
455
|
-
headers,
|
|
456
|
-
body,
|
|
457
|
-
fetchOptions,
|
|
458
|
-
callerSignal,
|
|
459
|
-
maxAttempts
|
|
460
|
-
} = args;
|
|
532
|
+
const { method, url, headers, body, fetchOptions, callerSignal, maxAttempts } = args;
|
|
461
533
|
let lastError;
|
|
462
534
|
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
|
|
463
535
|
if (attempt > 0) {
|
|
464
536
|
const delay = this.computeRetryDelay(attempt);
|
|
465
|
-
this.logger.warn(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
537
|
+
this.logger.warn(`Retry ${attempt}/${maxAttempts} for ${method} ${url} in ${delay}ms`);
|
|
538
|
+
if (callerSignal?.aborted) {
|
|
539
|
+
throw callerSignal.reason;
|
|
540
|
+
}
|
|
469
541
|
await new Promise((resolve, reject) => {
|
|
470
542
|
const onAbort = () => {
|
|
471
543
|
clearTimeout(timer2);
|
|
472
544
|
reject(callerSignal.reason);
|
|
473
545
|
};
|
|
474
546
|
const timer2 = setTimeout(() => {
|
|
475
|
-
if (callerSignal)
|
|
547
|
+
if (callerSignal) {
|
|
476
548
|
callerSignal.removeEventListener("abort", onAbort);
|
|
549
|
+
}
|
|
477
550
|
resolve();
|
|
478
551
|
}, delay);
|
|
479
552
|
if (callerSignal) {
|
|
@@ -515,7 +588,9 @@ var HttpClient = class {
|
|
|
515
588
|
...controller ? { signal: controller.signal } : {}
|
|
516
589
|
});
|
|
517
590
|
if (this.isRetryableStatus(response.status) && attempt < maxAttempts) {
|
|
518
|
-
if (timer !== void 0)
|
|
591
|
+
if (timer !== void 0) {
|
|
592
|
+
clearTimeout(timer);
|
|
593
|
+
}
|
|
519
594
|
await response.body?.cancel();
|
|
520
595
|
lastError = new InsForgeError(
|
|
521
596
|
`Server error: ${response.status} ${response.statusText}`,
|
|
@@ -524,10 +599,14 @@ var HttpClient = class {
|
|
|
524
599
|
);
|
|
525
600
|
continue;
|
|
526
601
|
}
|
|
527
|
-
if (timer !== void 0)
|
|
602
|
+
if (timer !== void 0) {
|
|
603
|
+
clearTimeout(timer);
|
|
604
|
+
}
|
|
528
605
|
return response;
|
|
529
606
|
} catch (err) {
|
|
530
|
-
if (timer !== void 0)
|
|
607
|
+
if (timer !== void 0) {
|
|
608
|
+
clearTimeout(timer);
|
|
609
|
+
}
|
|
531
610
|
if (err?.name === "AbortError") {
|
|
532
611
|
if (controller && controller.signal.aborted && this.timeout > 0 && !callerSignal?.aborted) {
|
|
533
612
|
throw new InsForgeError(
|
|
@@ -549,11 +628,7 @@ var HttpClient = class {
|
|
|
549
628
|
);
|
|
550
629
|
}
|
|
551
630
|
}
|
|
552
|
-
throw lastError || new InsForgeError(
|
|
553
|
-
"Request failed after all retry attempts",
|
|
554
|
-
0,
|
|
555
|
-
"NETWORK_ERROR"
|
|
556
|
-
);
|
|
631
|
+
throw lastError || new InsForgeError("Request failed after all retry attempts", 0, "NETWORK_ERROR");
|
|
557
632
|
}
|
|
558
633
|
/**
|
|
559
634
|
* Performs an HTTP request with automatic retry and timeout handling.
|
|
@@ -633,31 +708,15 @@ var HttpClient = class {
|
|
|
633
708
|
}
|
|
634
709
|
throw err;
|
|
635
710
|
}
|
|
636
|
-
this.logger.logResponse(
|
|
637
|
-
method,
|
|
638
|
-
url,
|
|
639
|
-
response.status,
|
|
640
|
-
Date.now() - startTime,
|
|
641
|
-
data
|
|
642
|
-
);
|
|
711
|
+
this.logger.logResponse(method, url, response.status, Date.now() - startTime, data);
|
|
643
712
|
return data;
|
|
644
713
|
}
|
|
645
714
|
async request(method, path, options = {}) {
|
|
646
715
|
const tokenUsed = this.userToken;
|
|
647
716
|
try {
|
|
648
|
-
return await this.handleRequest(
|
|
649
|
-
method,
|
|
650
|
-
path,
|
|
651
|
-
{ ...options },
|
|
652
|
-
tokenUsed
|
|
653
|
-
);
|
|
717
|
+
return await this.handleRequest(method, path, { ...options }, tokenUsed);
|
|
654
718
|
} catch (error) {
|
|
655
|
-
if (!(error instanceof InsForgeError) || !this.shouldRefreshAccessToken(
|
|
656
|
-
error.statusCode,
|
|
657
|
-
error.error,
|
|
658
|
-
tokenUsed,
|
|
659
|
-
options
|
|
660
|
-
)) {
|
|
719
|
+
if (!(error instanceof InsForgeError) || !this.shouldRefreshAccessToken(error.statusCode, error.error, tokenUsed, options)) {
|
|
661
720
|
throw error;
|
|
662
721
|
}
|
|
663
722
|
if (tokenUsed !== this.userToken) {
|
|
@@ -742,12 +801,7 @@ var HttpClient = class {
|
|
|
742
801
|
callerSignal,
|
|
743
802
|
maxAttempts
|
|
744
803
|
});
|
|
745
|
-
this.logger.logResponse(
|
|
746
|
-
method,
|
|
747
|
-
url,
|
|
748
|
-
response.status,
|
|
749
|
-
Date.now() - startTime
|
|
750
|
-
);
|
|
804
|
+
this.logger.logResponse(method, url, response.status, Date.now() - startTime);
|
|
751
805
|
let errorCode = null;
|
|
752
806
|
if (response.status === 401) {
|
|
753
807
|
try {
|
|
@@ -761,12 +815,7 @@ var HttpClient = class {
|
|
|
761
815
|
} catch {
|
|
762
816
|
}
|
|
763
817
|
}
|
|
764
|
-
if (!this.shouldRefreshAccessToken(
|
|
765
|
-
response.status,
|
|
766
|
-
errorCode,
|
|
767
|
-
tokenUsed,
|
|
768
|
-
options
|
|
769
|
-
)) {
|
|
818
|
+
if (!this.shouldRefreshAccessToken(response.status, errorCode, tokenUsed, options)) {
|
|
770
819
|
return response;
|
|
771
820
|
}
|
|
772
821
|
if (tokenUsed !== this.userToken) {
|
|
@@ -860,10 +909,30 @@ var HttpClient = class {
|
|
|
860
909
|
})();
|
|
861
910
|
return this.refreshPromise;
|
|
862
911
|
}
|
|
912
|
+
/** Returns a token safe to use for a new connection handshake. */
|
|
913
|
+
async getValidAccessToken(leewaySeconds = 60) {
|
|
914
|
+
const accessToken = this.tokenManager.getAccessToken() ?? this.userToken;
|
|
915
|
+
if (!accessToken || !isJwtExpiredOrExpiring(accessToken, leewaySeconds)) {
|
|
916
|
+
return accessToken;
|
|
917
|
+
}
|
|
918
|
+
const canRefresh = !this.config.isServerMode && !this.config.accessToken && !this.config.edgeFunctionToken && this.userToken !== null;
|
|
919
|
+
if (!canRefresh) {
|
|
920
|
+
return accessToken;
|
|
921
|
+
}
|
|
922
|
+
try {
|
|
923
|
+
const refreshed = await this.refreshAndSaveSession();
|
|
924
|
+
return refreshed.accessToken;
|
|
925
|
+
} catch (error) {
|
|
926
|
+
if (error instanceof InsForgeError && (error.statusCode === 401 || error.statusCode === 403) && this.userToken === accessToken) {
|
|
927
|
+
this.clearAuthSession();
|
|
928
|
+
}
|
|
929
|
+
throw error;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
863
932
|
async refreshAndSaveSession() {
|
|
864
933
|
const newTokenData = await this.refreshAccessToken();
|
|
865
934
|
this.setAuthToken(newTokenData.accessToken);
|
|
866
|
-
this.tokenManager.saveSession(newTokenData);
|
|
935
|
+
this.tokenManager.saveSession(newTokenData, AuthChangeEvent.TOKEN_REFRESHED);
|
|
867
936
|
if (newTokenData.csrfToken) {
|
|
868
937
|
setCsrfToken(newTokenData.csrfToken);
|
|
869
938
|
}
|
|
@@ -959,11 +1028,15 @@ var Auth = class {
|
|
|
959
1028
|
isServerMode() {
|
|
960
1029
|
return !!this.options.isServerMode;
|
|
961
1030
|
}
|
|
1031
|
+
/** Subscribe to SDK authentication state changes. */
|
|
1032
|
+
onAuthStateChange(callback) {
|
|
1033
|
+
return this.tokenManager.onAuthStateChange(callback);
|
|
1034
|
+
}
|
|
962
1035
|
/**
|
|
963
1036
|
* Save session from API response
|
|
964
1037
|
* Handles token storage, CSRF token, and HTTP auth header
|
|
965
1038
|
*/
|
|
966
|
-
saveSessionFromResponse(response) {
|
|
1039
|
+
saveSessionFromResponse(response, event = AuthChangeEvent.SIGNED_IN) {
|
|
967
1040
|
if (!response.accessToken || !response.user) {
|
|
968
1041
|
return false;
|
|
969
1042
|
}
|
|
@@ -975,7 +1048,7 @@ var Auth = class {
|
|
|
975
1048
|
setCsrfToken(response.csrfToken);
|
|
976
1049
|
}
|
|
977
1050
|
if (!this.isServerMode()) {
|
|
978
|
-
this.tokenManager.saveSession(session);
|
|
1051
|
+
this.tokenManager.saveSession(session, event);
|
|
979
1052
|
}
|
|
980
1053
|
this.http.setAuthToken(response.accessToken);
|
|
981
1054
|
this.http.setRefreshToken(response.refreshToken ?? null);
|
|
@@ -989,7 +1062,9 @@ var Auth = class {
|
|
|
989
1062
|
* Supports PKCE flow (insforge_code)
|
|
990
1063
|
*/
|
|
991
1064
|
async detectAuthCallback() {
|
|
992
|
-
if (this.isServerMode() || typeof window === "undefined")
|
|
1065
|
+
if (this.isServerMode() || typeof window === "undefined") {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
993
1068
|
try {
|
|
994
1069
|
const params = new URLSearchParams(window.location.search);
|
|
995
1070
|
const error = params.get("error");
|
|
@@ -1051,10 +1126,16 @@ var Auth = class {
|
|
|
1051
1126
|
async signOut() {
|
|
1052
1127
|
try {
|
|
1053
1128
|
try {
|
|
1129
|
+
const serverMode = this.isServerMode();
|
|
1130
|
+
const csrfToken = !serverMode ? getCsrfToken() : null;
|
|
1054
1131
|
await this.http.post(
|
|
1055
|
-
|
|
1132
|
+
serverMode ? "/api/auth/logout?client_type=mobile" : "/api/auth/logout",
|
|
1056
1133
|
void 0,
|
|
1057
|
-
{
|
|
1134
|
+
{
|
|
1135
|
+
credentials: "include",
|
|
1136
|
+
skipAuthRefresh: true,
|
|
1137
|
+
...csrfToken ? { headers: { "X-CSRF-Token": csrfToken } } : {}
|
|
1138
|
+
}
|
|
1058
1139
|
);
|
|
1059
1140
|
} catch {
|
|
1060
1141
|
}
|
|
@@ -1091,11 +1172,7 @@ var Auth = class {
|
|
|
1091
1172
|
if (!signInOptions || !signInOptions.redirectTo) {
|
|
1092
1173
|
return {
|
|
1093
1174
|
data: {},
|
|
1094
|
-
error: new InsForgeError(
|
|
1095
|
-
"Redirect URI is required",
|
|
1096
|
-
400,
|
|
1097
|
-
import_shared_schemas.ERROR_CODES.INVALID_INPUT
|
|
1098
|
-
)
|
|
1175
|
+
error: new InsForgeError("Redirect URI is required", 400, import_shared_schemas.ERROR_CODES.INVALID_INPUT)
|
|
1099
1176
|
};
|
|
1100
1177
|
}
|
|
1101
1178
|
const { provider } = signInOptions;
|
|
@@ -1170,10 +1247,7 @@ var Auth = class {
|
|
|
1170
1247
|
error: null
|
|
1171
1248
|
};
|
|
1172
1249
|
} catch (error) {
|
|
1173
|
-
return wrapError(
|
|
1174
|
-
error,
|
|
1175
|
-
"An unexpected error occurred during OAuth code exchange"
|
|
1176
|
-
);
|
|
1250
|
+
return wrapError(error, "An unexpected error occurred during OAuth code exchange");
|
|
1177
1251
|
}
|
|
1178
1252
|
}
|
|
1179
1253
|
/**
|
|
@@ -1200,10 +1274,7 @@ var Auth = class {
|
|
|
1200
1274
|
error: null
|
|
1201
1275
|
};
|
|
1202
1276
|
} catch (error) {
|
|
1203
|
-
return wrapError(
|
|
1204
|
-
error,
|
|
1205
|
-
"An unexpected error occurred during ID token sign in"
|
|
1206
|
-
);
|
|
1277
|
+
return wrapError(error, "An unexpected error occurred during ID token sign in");
|
|
1207
1278
|
}
|
|
1208
1279
|
}
|
|
1209
1280
|
// ============================================================================
|
|
@@ -1244,14 +1315,11 @@ var Auth = class {
|
|
|
1244
1315
|
}
|
|
1245
1316
|
);
|
|
1246
1317
|
if (response.accessToken) {
|
|
1247
|
-
this.saveSessionFromResponse(response);
|
|
1318
|
+
this.saveSessionFromResponse(response, AuthChangeEvent.TOKEN_REFRESHED);
|
|
1248
1319
|
}
|
|
1249
1320
|
return { data: response, error: null };
|
|
1250
1321
|
} catch (error) {
|
|
1251
|
-
return wrapError(
|
|
1252
|
-
error,
|
|
1253
|
-
"An unexpected error occurred during session refresh"
|
|
1254
|
-
);
|
|
1322
|
+
return wrapError(error, "An unexpected error occurred during session refresh");
|
|
1255
1323
|
}
|
|
1256
1324
|
}
|
|
1257
1325
|
/**
|
|
@@ -1262,11 +1330,11 @@ var Auth = class {
|
|
|
1262
1330
|
try {
|
|
1263
1331
|
if (this.isServerMode()) {
|
|
1264
1332
|
const accessToken = this.tokenManager.getAccessToken();
|
|
1265
|
-
if (!accessToken)
|
|
1333
|
+
if (!accessToken) {
|
|
1334
|
+
return { data: { user: null }, error: null };
|
|
1335
|
+
}
|
|
1266
1336
|
this.http.setAuthToken(accessToken);
|
|
1267
|
-
const response = await this.http.get(
|
|
1268
|
-
"/api/auth/sessions/current"
|
|
1269
|
-
);
|
|
1337
|
+
const response = await this.http.get("/api/auth/sessions/current");
|
|
1270
1338
|
const user = response.user ?? null;
|
|
1271
1339
|
return { data: { user }, error: null };
|
|
1272
1340
|
}
|
|
@@ -1304,25 +1372,17 @@ var Auth = class {
|
|
|
1304
1372
|
// ============================================================================
|
|
1305
1373
|
async getProfile(userId) {
|
|
1306
1374
|
try {
|
|
1307
|
-
const response = await this.http.get(
|
|
1308
|
-
`/api/auth/profiles/${userId}`
|
|
1309
|
-
);
|
|
1375
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
1310
1376
|
return { data: response, error: null };
|
|
1311
1377
|
} catch (error) {
|
|
1312
|
-
return wrapError(
|
|
1313
|
-
error,
|
|
1314
|
-
"An unexpected error occurred while fetching user profile"
|
|
1315
|
-
);
|
|
1378
|
+
return wrapError(error, "An unexpected error occurred while fetching user profile");
|
|
1316
1379
|
}
|
|
1317
1380
|
}
|
|
1318
1381
|
async setProfile(profile) {
|
|
1319
1382
|
try {
|
|
1320
|
-
const response = await this.http.patch(
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
profile
|
|
1324
|
-
}
|
|
1325
|
-
);
|
|
1383
|
+
const response = await this.http.patch("/api/auth/profiles/current", {
|
|
1384
|
+
profile
|
|
1385
|
+
});
|
|
1326
1386
|
const currentUser = this.tokenManager.getUser();
|
|
1327
1387
|
if (!this.isServerMode() && currentUser && response.profile !== void 0) {
|
|
1328
1388
|
this.tokenManager.setUser({
|
|
@@ -1332,10 +1392,7 @@ var Auth = class {
|
|
|
1332
1392
|
}
|
|
1333
1393
|
return { data: response, error: null };
|
|
1334
1394
|
} catch (error) {
|
|
1335
|
-
return wrapError(
|
|
1336
|
-
error,
|
|
1337
|
-
"An unexpected error occurred while updating user profile"
|
|
1338
|
-
);
|
|
1395
|
+
return wrapError(error, "An unexpected error occurred while updating user profile");
|
|
1339
1396
|
}
|
|
1340
1397
|
}
|
|
1341
1398
|
// ============================================================================
|
|
@@ -1348,10 +1405,7 @@ var Auth = class {
|
|
|
1348
1405
|
});
|
|
1349
1406
|
return { data: response, error: null };
|
|
1350
1407
|
} catch (error) {
|
|
1351
|
-
return wrapError(
|
|
1352
|
-
error,
|
|
1353
|
-
"An unexpected error occurred while sending verification email"
|
|
1354
|
-
);
|
|
1408
|
+
return wrapError(error, "An unexpected error occurred while sending verification email");
|
|
1355
1409
|
}
|
|
1356
1410
|
}
|
|
1357
1411
|
async verifyEmail(request) {
|
|
@@ -1367,10 +1421,7 @@ var Auth = class {
|
|
|
1367
1421
|
}
|
|
1368
1422
|
return { data: response, error: null };
|
|
1369
1423
|
} catch (error) {
|
|
1370
|
-
return wrapError(
|
|
1371
|
-
error,
|
|
1372
|
-
"An unexpected error occurred while verifying email"
|
|
1373
|
-
);
|
|
1424
|
+
return wrapError(error, "An unexpected error occurred while verifying email");
|
|
1374
1425
|
}
|
|
1375
1426
|
}
|
|
1376
1427
|
// ============================================================================
|
|
@@ -1383,10 +1434,7 @@ var Auth = class {
|
|
|
1383
1434
|
});
|
|
1384
1435
|
return { data: response, error: null };
|
|
1385
1436
|
} catch (error) {
|
|
1386
|
-
return wrapError(
|
|
1387
|
-
error,
|
|
1388
|
-
"An unexpected error occurred while sending password reset email"
|
|
1389
|
-
);
|
|
1437
|
+
return wrapError(error, "An unexpected error occurred while sending password reset email");
|
|
1390
1438
|
}
|
|
1391
1439
|
}
|
|
1392
1440
|
async exchangeResetPasswordToken(request) {
|
|
@@ -1398,10 +1446,7 @@ var Auth = class {
|
|
|
1398
1446
|
);
|
|
1399
1447
|
return { data: response, error: null };
|
|
1400
1448
|
} catch (error) {
|
|
1401
|
-
return wrapError(
|
|
1402
|
-
error,
|
|
1403
|
-
"An unexpected error occurred while verifying reset code"
|
|
1404
|
-
);
|
|
1449
|
+
return wrapError(error, "An unexpected error occurred while verifying reset code");
|
|
1405
1450
|
}
|
|
1406
1451
|
}
|
|
1407
1452
|
async resetPassword(request) {
|
|
@@ -1413,10 +1458,7 @@ var Auth = class {
|
|
|
1413
1458
|
);
|
|
1414
1459
|
return { data: response, error: null };
|
|
1415
1460
|
} catch (error) {
|
|
1416
|
-
return wrapError(
|
|
1417
|
-
error,
|
|
1418
|
-
"An unexpected error occurred while resetting password"
|
|
1419
|
-
);
|
|
1461
|
+
return wrapError(error, "An unexpected error occurred while resetting password");
|
|
1420
1462
|
}
|
|
1421
1463
|
}
|
|
1422
1464
|
// ============================================================================
|
|
@@ -1424,16 +1466,12 @@ var Auth = class {
|
|
|
1424
1466
|
// ============================================================================
|
|
1425
1467
|
async getPublicAuthConfig() {
|
|
1426
1468
|
try {
|
|
1427
|
-
const response = await this.http.get(
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
);
|
|
1469
|
+
const response = await this.http.get("/api/auth/public-config", {
|
|
1470
|
+
skipAuthRefresh: true
|
|
1471
|
+
});
|
|
1431
1472
|
return { data: response, error: null };
|
|
1432
1473
|
} catch (error) {
|
|
1433
|
-
return wrapError(
|
|
1434
|
-
error,
|
|
1435
|
-
"An unexpected error occurred while fetching auth configuration"
|
|
1436
|
-
);
|
|
1474
|
+
return wrapError(error, "An unexpected error occurred while fetching auth configuration");
|
|
1437
1475
|
}
|
|
1438
1476
|
}
|
|
1439
1477
|
};
|
|
@@ -1460,12 +1498,30 @@ function createInsForgePostgrestFetch(httpClient) {
|
|
|
1460
1498
|
};
|
|
1461
1499
|
}
|
|
1462
1500
|
var Database = class {
|
|
1463
|
-
constructor(httpClient) {
|
|
1501
|
+
constructor(httpClient, defaultSchema) {
|
|
1464
1502
|
this.postgrest = new import_postgrest_js.PostgrestClient("http://dummy", {
|
|
1465
1503
|
fetch: createInsForgePostgrestFetch(httpClient),
|
|
1466
|
-
headers: {}
|
|
1504
|
+
headers: {},
|
|
1505
|
+
...defaultSchema ? { schema: defaultSchema } : {}
|
|
1467
1506
|
});
|
|
1468
1507
|
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Select a non-default Postgres schema for the chained query. Maps to
|
|
1510
|
+
* PostgREST's `Accept-Profile` (reads) / `Content-Profile` (writes) header.
|
|
1511
|
+
* The schema must be exposed by the backend.
|
|
1512
|
+
*
|
|
1513
|
+
* @example
|
|
1514
|
+
* const { data } = await client.database
|
|
1515
|
+
* .schema('analytics')
|
|
1516
|
+
* .from('events')
|
|
1517
|
+
* .select('*');
|
|
1518
|
+
*
|
|
1519
|
+
* @example
|
|
1520
|
+
* await client.database.schema('analytics').rpc('rollup', { day: '2026-01-01' });
|
|
1521
|
+
*/
|
|
1522
|
+
schema(schemaName) {
|
|
1523
|
+
return this.postgrest.schema(schemaName);
|
|
1524
|
+
}
|
|
1469
1525
|
/**
|
|
1470
1526
|
* Create a query builder for a table
|
|
1471
1527
|
*
|
|
@@ -1573,11 +1629,7 @@ var StorageBucket = class {
|
|
|
1573
1629
|
} catch (error) {
|
|
1574
1630
|
return {
|
|
1575
1631
|
data: null,
|
|
1576
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1577
|
-
"Upload failed",
|
|
1578
|
-
500,
|
|
1579
|
-
"STORAGE_ERROR"
|
|
1580
|
-
)
|
|
1632
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Upload failed", 500, "STORAGE_ERROR")
|
|
1581
1633
|
};
|
|
1582
1634
|
}
|
|
1583
1635
|
}
|
|
@@ -1623,11 +1675,7 @@ var StorageBucket = class {
|
|
|
1623
1675
|
} catch (error) {
|
|
1624
1676
|
return {
|
|
1625
1677
|
data: null,
|
|
1626
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1627
|
-
"Upload failed",
|
|
1628
|
-
500,
|
|
1629
|
-
"STORAGE_ERROR"
|
|
1630
|
-
)
|
|
1678
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Upload failed", 500, "STORAGE_ERROR")
|
|
1631
1679
|
};
|
|
1632
1680
|
}
|
|
1633
1681
|
}
|
|
@@ -1655,13 +1703,10 @@ var StorageBucket = class {
|
|
|
1655
1703
|
);
|
|
1656
1704
|
}
|
|
1657
1705
|
if (strategy.confirmRequired && strategy.confirmUrl) {
|
|
1658
|
-
const confirmResponse = await this.http.post(
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
contentType: file.type || "application/octet-stream"
|
|
1663
|
-
}
|
|
1664
|
-
);
|
|
1706
|
+
const confirmResponse = await this.http.post(strategy.confirmUrl, {
|
|
1707
|
+
size: file.size,
|
|
1708
|
+
contentType: file.type || "application/octet-stream"
|
|
1709
|
+
});
|
|
1665
1710
|
return { data: confirmResponse, error: null };
|
|
1666
1711
|
}
|
|
1667
1712
|
return {
|
|
@@ -1676,11 +1721,7 @@ var StorageBucket = class {
|
|
|
1676
1721
|
error: null
|
|
1677
1722
|
};
|
|
1678
1723
|
} catch (error) {
|
|
1679
|
-
throw error instanceof InsForgeError ? error : new InsForgeError(
|
|
1680
|
-
"Presigned upload failed",
|
|
1681
|
-
500,
|
|
1682
|
-
"STORAGE_ERROR"
|
|
1683
|
-
);
|
|
1724
|
+
throw error instanceof InsForgeError ? error : new InsForgeError("Presigned upload failed", 500, "STORAGE_ERROR");
|
|
1684
1725
|
}
|
|
1685
1726
|
}
|
|
1686
1727
|
/**
|
|
@@ -1734,11 +1775,7 @@ var StorageBucket = class {
|
|
|
1734
1775
|
} catch (error) {
|
|
1735
1776
|
return {
|
|
1736
1777
|
data: null,
|
|
1737
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1738
|
-
"Download failed",
|
|
1739
|
-
500,
|
|
1740
|
-
"STORAGE_ERROR"
|
|
1741
|
-
)
|
|
1778
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Download failed", 500, "STORAGE_ERROR")
|
|
1742
1779
|
};
|
|
1743
1780
|
}
|
|
1744
1781
|
}
|
|
@@ -1773,7 +1810,9 @@ var StorageBucket = class {
|
|
|
1773
1810
|
} catch (error) {
|
|
1774
1811
|
const status = error instanceof InsForgeError ? error.statusCode : void 0;
|
|
1775
1812
|
const isMissingRoute = (status === 404 || status === 405) && !(error instanceof InsForgeError && error.error === "STORAGE_NOT_FOUND");
|
|
1776
|
-
if (!isMissingRoute)
|
|
1813
|
+
if (!isMissingRoute) {
|
|
1814
|
+
throw error;
|
|
1815
|
+
}
|
|
1777
1816
|
return await this.http.post(
|
|
1778
1817
|
`/api/storage/buckets/${this.bucketName}/objects/${encoded}/download-strategy`,
|
|
1779
1818
|
{ expiresIn }
|
|
@@ -1849,10 +1888,18 @@ var StorageBucket = class {
|
|
|
1849
1888
|
async list(options) {
|
|
1850
1889
|
try {
|
|
1851
1890
|
const params = {};
|
|
1852
|
-
if (options?.prefix)
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
if (options?.
|
|
1891
|
+
if (options?.prefix) {
|
|
1892
|
+
params.prefix = options.prefix;
|
|
1893
|
+
}
|
|
1894
|
+
if (options?.search) {
|
|
1895
|
+
params.search = options.search;
|
|
1896
|
+
}
|
|
1897
|
+
if (options?.limit) {
|
|
1898
|
+
params.limit = options.limit.toString();
|
|
1899
|
+
}
|
|
1900
|
+
if (options?.offset) {
|
|
1901
|
+
params.offset = options.offset.toString();
|
|
1902
|
+
}
|
|
1856
1903
|
const response = await this.http.get(
|
|
1857
1904
|
`/api/storage/buckets/${this.bucketName}/objects`,
|
|
1858
1905
|
{ params }
|
|
@@ -1861,11 +1908,7 @@ var StorageBucket = class {
|
|
|
1861
1908
|
} catch (error) {
|
|
1862
1909
|
return {
|
|
1863
1910
|
data: null,
|
|
1864
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1865
|
-
"List failed",
|
|
1866
|
-
500,
|
|
1867
|
-
"STORAGE_ERROR"
|
|
1868
|
-
)
|
|
1911
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("List failed", 500, "STORAGE_ERROR")
|
|
1869
1912
|
};
|
|
1870
1913
|
}
|
|
1871
1914
|
}
|
|
@@ -1882,11 +1925,7 @@ var StorageBucket = class {
|
|
|
1882
1925
|
} catch (error) {
|
|
1883
1926
|
return {
|
|
1884
1927
|
data: null,
|
|
1885
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1886
|
-
"Delete failed",
|
|
1887
|
-
500,
|
|
1888
|
-
"STORAGE_ERROR"
|
|
1889
|
-
)
|
|
1928
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Delete failed", 500, "STORAGE_ERROR")
|
|
1890
1929
|
};
|
|
1891
1930
|
}
|
|
1892
1931
|
}
|
|
@@ -2008,14 +2047,11 @@ var ChatCompletions = class {
|
|
|
2008
2047
|
if (params.stream) {
|
|
2009
2048
|
const headers = this.http.getHeaders();
|
|
2010
2049
|
headers["Content-Type"] = "application/json";
|
|
2011
|
-
const response2 = await this.http.fetch(
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
body: JSON.stringify(backendParams)
|
|
2017
|
-
}
|
|
2018
|
-
);
|
|
2050
|
+
const response2 = await this.http.fetch(`${this.http.baseUrl}/api/ai/chat/completion`, {
|
|
2051
|
+
method: "POST",
|
|
2052
|
+
headers,
|
|
2053
|
+
body: JSON.stringify(backendParams)
|
|
2054
|
+
});
|
|
2019
2055
|
if (!response2.ok) {
|
|
2020
2056
|
const error = await response2.json();
|
|
2021
2057
|
throw new Error(error.error || "Stream request failed");
|
|
@@ -2063,7 +2099,9 @@ var ChatCompletions = class {
|
|
|
2063
2099
|
try {
|
|
2064
2100
|
while (true) {
|
|
2065
2101
|
const { done, value } = await reader.read();
|
|
2066
|
-
if (done)
|
|
2102
|
+
if (done) {
|
|
2103
|
+
break;
|
|
2104
|
+
}
|
|
2067
2105
|
buffer += decoder.decode(value, { stream: true });
|
|
2068
2106
|
const lines = buffer.split("\n");
|
|
2069
2107
|
buffer = lines.pop() || "";
|
|
@@ -2111,7 +2149,7 @@ var ChatCompletions = class {
|
|
|
2111
2149
|
reader.releaseLock();
|
|
2112
2150
|
return;
|
|
2113
2151
|
}
|
|
2114
|
-
} catch
|
|
2152
|
+
} catch {
|
|
2115
2153
|
console.warn("Failed to parse SSE data:", dataStr);
|
|
2116
2154
|
}
|
|
2117
2155
|
}
|
|
@@ -2164,10 +2202,7 @@ var Embeddings = class {
|
|
|
2164
2202
|
* ```
|
|
2165
2203
|
*/
|
|
2166
2204
|
async create(params) {
|
|
2167
|
-
const response = await this.http.post(
|
|
2168
|
-
"/api/ai/embeddings",
|
|
2169
|
-
params
|
|
2170
|
-
);
|
|
2205
|
+
const response = await this.http.post("/api/ai/embeddings", params);
|
|
2171
2206
|
return {
|
|
2172
2207
|
object: response.object,
|
|
2173
2208
|
data: response.data,
|
|
@@ -2253,7 +2288,9 @@ var Functions = class _Functions {
|
|
|
2253
2288
|
static deriveSubhostingUrl(baseUrl) {
|
|
2254
2289
|
try {
|
|
2255
2290
|
const { hostname } = new URL(baseUrl);
|
|
2256
|
-
if (!hostname.endsWith(".insforge.app"))
|
|
2291
|
+
if (!hostname.endsWith(".insforge.app")) {
|
|
2292
|
+
return void 0;
|
|
2293
|
+
}
|
|
2257
2294
|
const appKey = hostname.split(".")[0];
|
|
2258
2295
|
return `https://${appKey}.functions.insforge.app`;
|
|
2259
2296
|
} catch {
|
|
@@ -2299,7 +2336,9 @@ var Functions = class _Functions {
|
|
|
2299
2336
|
const data = await parseResponse(res);
|
|
2300
2337
|
return { data, error: null };
|
|
2301
2338
|
} catch (error) {
|
|
2302
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2339
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2340
|
+
throw error;
|
|
2341
|
+
}
|
|
2303
2342
|
return {
|
|
2304
2343
|
data: null,
|
|
2305
2344
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2318,7 +2357,9 @@ var Functions = class _Functions {
|
|
|
2318
2357
|
});
|
|
2319
2358
|
return { data, error: null };
|
|
2320
2359
|
} catch (error) {
|
|
2321
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2360
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2361
|
+
throw error;
|
|
2362
|
+
}
|
|
2322
2363
|
if (error instanceof InsForgeError && error.statusCode === 404) {
|
|
2323
2364
|
} else {
|
|
2324
2365
|
return {
|
|
@@ -2337,7 +2378,9 @@ var Functions = class _Functions {
|
|
|
2337
2378
|
const data = await this.http.request(method, path, { body, headers });
|
|
2338
2379
|
return { data, error: null };
|
|
2339
2380
|
} catch (error) {
|
|
2340
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2381
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2382
|
+
throw error;
|
|
2383
|
+
}
|
|
2341
2384
|
return {
|
|
2342
2385
|
data: null,
|
|
2343
2386
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2352,32 +2395,37 @@ var Functions = class _Functions {
|
|
|
2352
2395
|
|
|
2353
2396
|
// src/modules/realtime.ts
|
|
2354
2397
|
var CONNECT_TIMEOUT = 1e4;
|
|
2398
|
+
var SUBSCRIBE_TIMEOUT = 1e4;
|
|
2355
2399
|
var Realtime = class {
|
|
2356
|
-
constructor(baseUrl, tokenManager, anonKey) {
|
|
2357
|
-
this.socket = null;
|
|
2358
|
-
this.connectPromise = null;
|
|
2359
|
-
this.subscribedChannels = /* @__PURE__ */ new Set();
|
|
2360
|
-
this.eventListeners = /* @__PURE__ */ new Map();
|
|
2400
|
+
constructor(baseUrl, tokenManager, anonKey, getValidAccessToken = async () => tokenManager.getAccessToken()) {
|
|
2361
2401
|
this.baseUrl = baseUrl;
|
|
2362
2402
|
this.tokenManager = tokenManager;
|
|
2363
2403
|
this.anonKey = anonKey;
|
|
2364
|
-
this.
|
|
2404
|
+
this.getValidAccessToken = getValidAccessToken;
|
|
2405
|
+
this.socket = null;
|
|
2406
|
+
this.connectPromise = null;
|
|
2407
|
+
this.connectionAttempt = null;
|
|
2408
|
+
this.nextConnectionAttemptId = 0;
|
|
2409
|
+
this.subscriptions = /* @__PURE__ */ new Map();
|
|
2410
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
2411
|
+
this.tokenManager.onAuthStateChange((event) => {
|
|
2412
|
+
if (event !== AuthChangeEvent.TOKEN_REFRESHED) {
|
|
2413
|
+
this.reconnectForAuthChange();
|
|
2414
|
+
}
|
|
2415
|
+
});
|
|
2365
2416
|
}
|
|
2366
2417
|
notifyListeners(event, payload) {
|
|
2367
|
-
const
|
|
2368
|
-
if (!listeners) return;
|
|
2369
|
-
for (const cb of listeners) {
|
|
2418
|
+
for (const callback of this.eventListeners.get(event) ?? []) {
|
|
2370
2419
|
try {
|
|
2371
|
-
|
|
2372
|
-
} catch (
|
|
2373
|
-
console.error(`Error in ${event} callback:`,
|
|
2420
|
+
callback(payload);
|
|
2421
|
+
} catch (error) {
|
|
2422
|
+
console.error(`Error in ${event} callback:`, error);
|
|
2374
2423
|
}
|
|
2375
2424
|
}
|
|
2376
2425
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
*/
|
|
2426
|
+
async getHandshakeToken() {
|
|
2427
|
+
return await this.getValidAccessToken() ?? this.anonKey ?? null;
|
|
2428
|
+
}
|
|
2381
2429
|
connect() {
|
|
2382
2430
|
if (this.socket?.connected) {
|
|
2383
2431
|
return Promise.resolve();
|
|
@@ -2385,210 +2433,324 @@ var Realtime = class {
|
|
|
2385
2433
|
if (this.connectPromise) {
|
|
2386
2434
|
return this.connectPromise;
|
|
2387
2435
|
}
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
}
|
|
2404
|
-
};
|
|
2405
|
-
timeoutId = setTimeout(() => {
|
|
2406
|
-
if (initialConnection) {
|
|
2407
|
-
initialConnection = false;
|
|
2408
|
-
this.connectPromise = null;
|
|
2409
|
-
this.socket?.disconnect();
|
|
2410
|
-
this.socket = null;
|
|
2411
|
-
reject(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`));
|
|
2412
|
-
}
|
|
2413
|
-
}, CONNECT_TIMEOUT);
|
|
2414
|
-
this.socket.on("connect", () => {
|
|
2415
|
-
cleanup();
|
|
2416
|
-
for (const channel of this.subscribedChannels) {
|
|
2417
|
-
this.socket.emit("realtime:subscribe", { channel });
|
|
2418
|
-
}
|
|
2419
|
-
this.notifyListeners("connect");
|
|
2420
|
-
if (initialConnection) {
|
|
2421
|
-
initialConnection = false;
|
|
2422
|
-
this.connectPromise = null;
|
|
2423
|
-
resolve();
|
|
2424
|
-
}
|
|
2425
|
-
});
|
|
2426
|
-
this.socket.on("connect_error", (error) => {
|
|
2427
|
-
cleanup();
|
|
2428
|
-
this.notifyListeners("connect_error", error);
|
|
2429
|
-
if (initialConnection) {
|
|
2430
|
-
initialConnection = false;
|
|
2431
|
-
this.connectPromise = null;
|
|
2432
|
-
reject(error);
|
|
2433
|
-
}
|
|
2434
|
-
});
|
|
2435
|
-
this.socket.on("disconnect", (reason) => {
|
|
2436
|
-
this.notifyListeners("disconnect", reason);
|
|
2437
|
-
});
|
|
2438
|
-
this.socket.on("realtime:error", (error) => {
|
|
2439
|
-
this.notifyListeners("error", error);
|
|
2440
|
-
});
|
|
2441
|
-
this.socket.onAny((event, message) => {
|
|
2442
|
-
if (event === "realtime:error") return;
|
|
2443
|
-
this.notifyListeners(event, message);
|
|
2444
|
-
});
|
|
2436
|
+
const attemptId = ++this.nextConnectionAttemptId;
|
|
2437
|
+
const connection = (async () => {
|
|
2438
|
+
const { io } = await import("socket.io-client");
|
|
2439
|
+
if (attemptId !== this.nextConnectionAttemptId) {
|
|
2440
|
+
throw new Error("Connection cancelled");
|
|
2441
|
+
}
|
|
2442
|
+
await new Promise((resolve, reject) => {
|
|
2443
|
+
const socket = io(this.baseUrl, {
|
|
2444
|
+
transports: ["websocket"],
|
|
2445
|
+
auth: (callback) => {
|
|
2446
|
+
void this.getHandshakeToken().then(
|
|
2447
|
+
(token) => callback(token ? { token } : {}),
|
|
2448
|
+
() => callback({})
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2445
2451
|
});
|
|
2446
|
-
|
|
2452
|
+
this.socket = socket;
|
|
2453
|
+
let initialConnection = true;
|
|
2454
|
+
let timeoutId = null;
|
|
2455
|
+
const clearConnectTimeout = () => {
|
|
2456
|
+
if (timeoutId) {
|
|
2457
|
+
clearTimeout(timeoutId);
|
|
2458
|
+
timeoutId = null;
|
|
2459
|
+
}
|
|
2460
|
+
};
|
|
2461
|
+
const dispose = () => {
|
|
2462
|
+
clearConnectTimeout();
|
|
2463
|
+
socket.off("connect", onConnect);
|
|
2464
|
+
socket.off("connect_error", onConnectError);
|
|
2465
|
+
socket.off("disconnect", onDisconnect);
|
|
2466
|
+
socket.off("realtime:error", onRealtimeError);
|
|
2467
|
+
socket.offAny(onAny);
|
|
2468
|
+
socket.disconnect();
|
|
2469
|
+
if (this.socket === socket) {
|
|
2470
|
+
this.socket = null;
|
|
2471
|
+
}
|
|
2472
|
+
if (this.connectionAttempt?.id === attemptId) {
|
|
2473
|
+
this.connectionAttempt = null;
|
|
2474
|
+
}
|
|
2475
|
+
};
|
|
2476
|
+
const fail = (error) => {
|
|
2477
|
+
if (!initialConnection) {
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
initialConnection = false;
|
|
2481
|
+
dispose();
|
|
2482
|
+
reject(error);
|
|
2483
|
+
};
|
|
2484
|
+
const onConnect = () => {
|
|
2485
|
+
if (this.socket !== socket) {
|
|
2486
|
+
return;
|
|
2487
|
+
}
|
|
2488
|
+
clearConnectTimeout();
|
|
2489
|
+
this.resubscribeChannels();
|
|
2490
|
+
this.notifyListeners("connect");
|
|
2491
|
+
if (initialConnection) {
|
|
2492
|
+
initialConnection = false;
|
|
2493
|
+
if (this.connectionAttempt?.id === attemptId) {
|
|
2494
|
+
this.connectionAttempt = null;
|
|
2495
|
+
}
|
|
2496
|
+
resolve();
|
|
2497
|
+
}
|
|
2498
|
+
};
|
|
2499
|
+
const onConnectError = (error) => {
|
|
2500
|
+
clearConnectTimeout();
|
|
2501
|
+
this.notifyListeners("connect_error", error);
|
|
2502
|
+
if (initialConnection) {
|
|
2503
|
+
fail(error);
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
const onDisconnect = (reason) => {
|
|
2507
|
+
this.handleDisconnect(reason);
|
|
2508
|
+
};
|
|
2509
|
+
const onRealtimeError = (error) => {
|
|
2510
|
+
this.notifyListeners("error", error);
|
|
2511
|
+
};
|
|
2512
|
+
const onAny = (event, message) => {
|
|
2513
|
+
if (event === "realtime:error") {
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
this.applyPresenceEvent(event, message);
|
|
2517
|
+
this.notifyListeners(event, message);
|
|
2518
|
+
};
|
|
2519
|
+
this.connectionAttempt = { id: attemptId, socket, cancel: fail };
|
|
2520
|
+
socket.on("connect", onConnect);
|
|
2521
|
+
socket.on("connect_error", onConnectError);
|
|
2522
|
+
socket.on("disconnect", onDisconnect);
|
|
2523
|
+
socket.on("realtime:error", onRealtimeError);
|
|
2524
|
+
socket.onAny(onAny);
|
|
2525
|
+
timeoutId = setTimeout(
|
|
2526
|
+
() => fail(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`)),
|
|
2527
|
+
CONNECT_TIMEOUT
|
|
2528
|
+
);
|
|
2529
|
+
});
|
|
2530
|
+
})();
|
|
2531
|
+
const trackedConnection = connection.finally(() => {
|
|
2532
|
+
if (this.connectPromise === trackedConnection) {
|
|
2447
2533
|
this.connectPromise = null;
|
|
2448
|
-
throw error;
|
|
2449
2534
|
}
|
|
2450
|
-
})
|
|
2451
|
-
|
|
2535
|
+
});
|
|
2536
|
+
this.connectPromise = trackedConnection;
|
|
2537
|
+
return trackedConnection;
|
|
2452
2538
|
}
|
|
2453
|
-
/**
|
|
2454
|
-
* Disconnect from the realtime server
|
|
2455
|
-
*/
|
|
2456
2539
|
disconnect() {
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2540
|
+
this.nextConnectionAttemptId++;
|
|
2541
|
+
this.connectionAttempt?.cancel(new Error("Disconnected"));
|
|
2542
|
+
this.socket?.disconnect();
|
|
2543
|
+
this.socket = null;
|
|
2544
|
+
this.connectPromise = null;
|
|
2545
|
+
for (const subscription of this.subscriptions.values()) {
|
|
2546
|
+
this.settleSubscription(
|
|
2547
|
+
subscription,
|
|
2548
|
+
{
|
|
2549
|
+
ok: false,
|
|
2550
|
+
channel: subscription.channel,
|
|
2551
|
+
error: { code: "DISCONNECTED", message: "Disconnected" }
|
|
2552
|
+
},
|
|
2553
|
+
false
|
|
2554
|
+
);
|
|
2460
2555
|
}
|
|
2461
|
-
this.
|
|
2556
|
+
this.subscriptions.clear();
|
|
2462
2557
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
onTokenChange() {
|
|
2469
|
-
const token = this.tokenManager.getAccessToken() ?? this.anonKey;
|
|
2470
|
-
if (this.socket) {
|
|
2471
|
-
this.socket.auth = token ? { token } : {};
|
|
2558
|
+
reconnectForAuthChange() {
|
|
2559
|
+
for (const subscription of this.subscriptions.values()) {
|
|
2560
|
+
if (subscription.status === "rejected") {
|
|
2561
|
+
subscription.status = "pending";
|
|
2562
|
+
}
|
|
2472
2563
|
}
|
|
2473
|
-
if (this.socket
|
|
2474
|
-
|
|
2475
|
-
|
|
2564
|
+
if (!this.socket) {
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2567
|
+
this.socket.disconnect();
|
|
2568
|
+
this.socket.connect();
|
|
2569
|
+
}
|
|
2570
|
+
handleDisconnect(reason) {
|
|
2571
|
+
for (const subscription of this.subscriptions.values()) {
|
|
2572
|
+
if (subscription.status === "rejected") {
|
|
2573
|
+
continue;
|
|
2574
|
+
}
|
|
2575
|
+
subscription.status = "pending";
|
|
2576
|
+
this.settleSubscription(
|
|
2577
|
+
subscription,
|
|
2578
|
+
{
|
|
2579
|
+
ok: false,
|
|
2580
|
+
channel: subscription.channel,
|
|
2581
|
+
error: { code: "DISCONNECTED", message: "Connection lost before subscription completed" }
|
|
2582
|
+
},
|
|
2583
|
+
true
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
this.notifyListeners("disconnect", reason);
|
|
2587
|
+
}
|
|
2588
|
+
resubscribeChannels() {
|
|
2589
|
+
for (const [channel, subscription] of this.subscriptions) {
|
|
2590
|
+
if (subscription.status === "pending") {
|
|
2591
|
+
this.requestSubscription(channel, subscription);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
requestSubscription(channel, subscription) {
|
|
2596
|
+
if (subscription.pending) {
|
|
2597
|
+
return subscription.pending;
|
|
2598
|
+
}
|
|
2599
|
+
const socket = this.socket;
|
|
2600
|
+
if (!socket?.connected) {
|
|
2601
|
+
return Promise.resolve({
|
|
2602
|
+
ok: false,
|
|
2603
|
+
channel,
|
|
2604
|
+
error: { code: "CONNECTION_FAILED", message: "Not connected to realtime server" }
|
|
2605
|
+
});
|
|
2606
|
+
}
|
|
2607
|
+
subscription.status = "pending";
|
|
2608
|
+
const epoch = ++subscription.epoch;
|
|
2609
|
+
let timeoutId;
|
|
2610
|
+
subscription.pending = new Promise((resolve) => {
|
|
2611
|
+
subscription.settlePending = (response) => {
|
|
2612
|
+
if (timeoutId) {
|
|
2613
|
+
clearTimeout(timeoutId);
|
|
2614
|
+
}
|
|
2615
|
+
subscription.pending = void 0;
|
|
2616
|
+
subscription.settlePending = void 0;
|
|
2617
|
+
resolve(response);
|
|
2618
|
+
};
|
|
2619
|
+
timeoutId = setTimeout(() => {
|
|
2620
|
+
if (this.subscriptions.get(channel) === subscription && subscription.epoch === epoch) {
|
|
2621
|
+
this.settleSubscription(
|
|
2622
|
+
subscription,
|
|
2623
|
+
{
|
|
2624
|
+
ok: false,
|
|
2625
|
+
channel,
|
|
2626
|
+
error: {
|
|
2627
|
+
code: "SUBSCRIBE_TIMEOUT",
|
|
2628
|
+
message: "Subscription acknowledgement timed out"
|
|
2629
|
+
}
|
|
2630
|
+
},
|
|
2631
|
+
true
|
|
2632
|
+
);
|
|
2633
|
+
}
|
|
2634
|
+
}, SUBSCRIBE_TIMEOUT);
|
|
2635
|
+
socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
2636
|
+
if (this.subscriptions.get(channel) !== subscription || subscription.epoch !== epoch) {
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
if (response.ok) {
|
|
2640
|
+
subscription.status = "subscribed";
|
|
2641
|
+
subscription.members = new Map(
|
|
2642
|
+
response.presence.members.map((member) => [member.presenceId, member])
|
|
2643
|
+
);
|
|
2644
|
+
} else {
|
|
2645
|
+
subscription.status = "rejected";
|
|
2646
|
+
subscription.members.clear();
|
|
2647
|
+
}
|
|
2648
|
+
this.settleSubscription(subscription, response, false);
|
|
2649
|
+
});
|
|
2650
|
+
});
|
|
2651
|
+
return subscription.pending;
|
|
2652
|
+
}
|
|
2653
|
+
settleSubscription(subscription, response, incrementEpoch) {
|
|
2654
|
+
if (incrementEpoch) {
|
|
2655
|
+
subscription.epoch++;
|
|
2656
|
+
}
|
|
2657
|
+
subscription.settlePending?.(response);
|
|
2658
|
+
}
|
|
2659
|
+
applyPresenceEvent(event, message) {
|
|
2660
|
+
if (event !== "presence:join" && event !== "presence:leave") {
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
const presenceEvent = message;
|
|
2664
|
+
const channel = presenceEvent.meta?.channel;
|
|
2665
|
+
const member = presenceEvent.member;
|
|
2666
|
+
if (!channel || !member) {
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
const subscription = this.subscriptions.get(channel);
|
|
2670
|
+
if (!subscription) {
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
if (event === "presence:join") {
|
|
2674
|
+
subscription.members.set(member.presenceId, member);
|
|
2675
|
+
} else {
|
|
2676
|
+
subscription.members.delete(member.presenceId);
|
|
2476
2677
|
}
|
|
2477
2678
|
}
|
|
2478
|
-
/**
|
|
2479
|
-
* Check if connected to the realtime server
|
|
2480
|
-
*/
|
|
2481
2679
|
get isConnected() {
|
|
2482
2680
|
return this.socket?.connected ?? false;
|
|
2483
2681
|
}
|
|
2484
|
-
/**
|
|
2485
|
-
* Get the current connection state
|
|
2486
|
-
*/
|
|
2487
2682
|
get connectionState() {
|
|
2488
|
-
if (!this.socket)
|
|
2489
|
-
|
|
2490
|
-
|
|
2683
|
+
if (!this.socket) {
|
|
2684
|
+
return "disconnected";
|
|
2685
|
+
}
|
|
2686
|
+
return this.socket.connected ? "connected" : "connecting";
|
|
2491
2687
|
}
|
|
2492
|
-
/**
|
|
2493
|
-
* Get the socket ID (if connected)
|
|
2494
|
-
*/
|
|
2495
2688
|
get socketId() {
|
|
2496
2689
|
return this.socket?.id;
|
|
2497
2690
|
}
|
|
2498
|
-
/**
|
|
2499
|
-
* Subscribe to a channel
|
|
2500
|
-
*
|
|
2501
|
-
* Automatically connects if not already connected.
|
|
2502
|
-
*
|
|
2503
|
-
* @param channel - Channel name (e.g., 'orders:123', 'broadcast')
|
|
2504
|
-
* @returns Promise with the subscription response
|
|
2505
|
-
*/
|
|
2506
2691
|
async subscribe(channel) {
|
|
2507
|
-
|
|
2508
|
-
|
|
2692
|
+
let subscription = this.subscriptions.get(channel);
|
|
2693
|
+
if (subscription) {
|
|
2694
|
+
if (subscription.pending) {
|
|
2695
|
+
return subscription.pending;
|
|
2696
|
+
}
|
|
2697
|
+
if (subscription.status === "subscribed") {
|
|
2698
|
+
return { ok: true, channel, presence: { members: [...subscription.members.values()] } };
|
|
2699
|
+
}
|
|
2700
|
+
} else {
|
|
2701
|
+
subscription = { channel, epoch: 0, status: "pending", members: /* @__PURE__ */ new Map() };
|
|
2702
|
+
this.subscriptions.set(channel, subscription);
|
|
2509
2703
|
}
|
|
2510
2704
|
if (!this.socket?.connected) {
|
|
2511
2705
|
try {
|
|
2512
2706
|
await this.connect();
|
|
2513
2707
|
} catch (error) {
|
|
2708
|
+
if (this.subscriptions.get(channel) === subscription) {
|
|
2709
|
+
this.subscriptions.delete(channel);
|
|
2710
|
+
}
|
|
2514
2711
|
const message = error instanceof Error ? error.message : "Connection failed";
|
|
2515
2712
|
return { ok: false, channel, error: { code: "CONNECTION_FAILED", message } };
|
|
2516
2713
|
}
|
|
2517
2714
|
}
|
|
2518
|
-
return
|
|
2519
|
-
this.socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
2520
|
-
if (response.ok) {
|
|
2521
|
-
this.subscribedChannels.add(channel);
|
|
2522
|
-
}
|
|
2523
|
-
resolve(response);
|
|
2524
|
-
});
|
|
2525
|
-
});
|
|
2715
|
+
return subscription.pending ?? this.requestSubscription(channel, subscription);
|
|
2526
2716
|
}
|
|
2527
|
-
/**
|
|
2528
|
-
* Unsubscribe from a channel (fire-and-forget)
|
|
2529
|
-
*
|
|
2530
|
-
* @param channel - Channel name to unsubscribe from
|
|
2531
|
-
*/
|
|
2532
2717
|
unsubscribe(channel) {
|
|
2533
|
-
this.
|
|
2718
|
+
const subscription = this.subscriptions.get(channel);
|
|
2719
|
+
if (!subscription) {
|
|
2720
|
+
return;
|
|
2721
|
+
}
|
|
2722
|
+
this.subscriptions.delete(channel);
|
|
2723
|
+
this.settleSubscription(
|
|
2724
|
+
subscription,
|
|
2725
|
+
{
|
|
2726
|
+
ok: false,
|
|
2727
|
+
channel,
|
|
2728
|
+
error: { code: "SUBSCRIPTION_CANCELLED", message: "Subscription cancelled" }
|
|
2729
|
+
},
|
|
2730
|
+
true
|
|
2731
|
+
);
|
|
2534
2732
|
if (this.socket?.connected) {
|
|
2535
2733
|
this.socket.emit("realtime:unsubscribe", { channel });
|
|
2536
2734
|
}
|
|
2537
2735
|
}
|
|
2538
|
-
/**
|
|
2539
|
-
* Publish a message to a channel
|
|
2540
|
-
*
|
|
2541
|
-
* @param channel - Channel name
|
|
2542
|
-
* @param event - Event name
|
|
2543
|
-
* @param payload - Message payload
|
|
2544
|
-
*/
|
|
2545
2736
|
async publish(channel, event, payload) {
|
|
2546
2737
|
if (!this.socket?.connected) {
|
|
2547
2738
|
throw new Error("Not connected to realtime server. Call connect() first.");
|
|
2548
2739
|
}
|
|
2549
2740
|
this.socket.emit("realtime:publish", { channel, event, payload });
|
|
2550
2741
|
}
|
|
2551
|
-
/**
|
|
2552
|
-
* Listen for events
|
|
2553
|
-
*
|
|
2554
|
-
* Reserved event names:
|
|
2555
|
-
* - 'connect' - Fired when connected to the server
|
|
2556
|
-
* - 'connect_error' - Fired when connection fails (payload: Error)
|
|
2557
|
-
* - 'disconnect' - Fired when disconnected (payload: reason string)
|
|
2558
|
-
* - 'error' - Fired when a realtime error occurs (payload: RealtimeErrorPayload)
|
|
2559
|
-
*
|
|
2560
|
-
* All other events receive a `SocketMessage` payload with metadata.
|
|
2561
|
-
*
|
|
2562
|
-
* @param event - Event name to listen for
|
|
2563
|
-
* @param callback - Callback function when event is received
|
|
2564
|
-
*/
|
|
2565
2742
|
on(event, callback) {
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
this.eventListeners.get(event).add(callback);
|
|
2743
|
+
const listeners = this.eventListeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
2744
|
+
listeners.add(callback);
|
|
2745
|
+
this.eventListeners.set(event, listeners);
|
|
2570
2746
|
}
|
|
2571
|
-
/**
|
|
2572
|
-
* Remove a listener for a specific event
|
|
2573
|
-
*
|
|
2574
|
-
* @param event - Event name
|
|
2575
|
-
* @param callback - The callback function to remove
|
|
2576
|
-
*/
|
|
2577
2747
|
off(event, callback) {
|
|
2578
2748
|
const listeners = this.eventListeners.get(event);
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
this.eventListeners.delete(event);
|
|
2583
|
-
}
|
|
2749
|
+
listeners?.delete(callback);
|
|
2750
|
+
if (listeners?.size === 0) {
|
|
2751
|
+
this.eventListeners.delete(event);
|
|
2584
2752
|
}
|
|
2585
2753
|
}
|
|
2586
|
-
/**
|
|
2587
|
-
* Listen for an event only once, then automatically remove the listener
|
|
2588
|
-
*
|
|
2589
|
-
* @param event - Event name to listen for
|
|
2590
|
-
* @param callback - Callback function when event is received
|
|
2591
|
-
*/
|
|
2592
2754
|
once(event, callback) {
|
|
2593
2755
|
const wrapper = (payload) => {
|
|
2594
2756
|
this.off(event, wrapper);
|
|
@@ -2596,13 +2758,11 @@ var Realtime = class {
|
|
|
2596
2758
|
};
|
|
2597
2759
|
this.on(event, wrapper);
|
|
2598
2760
|
}
|
|
2599
|
-
/**
|
|
2600
|
-
* Get all currently subscribed channels
|
|
2601
|
-
*
|
|
2602
|
-
* @returns Array of channel names
|
|
2603
|
-
*/
|
|
2604
2761
|
getSubscribedChannels() {
|
|
2605
|
-
return
|
|
2762
|
+
return [...this.subscriptions.values()].filter((subscription) => subscription.status === "subscribed").map((subscription) => subscription.channel);
|
|
2763
|
+
}
|
|
2764
|
+
getPresenceState(channel) {
|
|
2765
|
+
return [...this.subscriptions.get(channel)?.members.values() ?? []];
|
|
2606
2766
|
}
|
|
2607
2767
|
};
|
|
2608
2768
|
|
|
@@ -2617,13 +2777,12 @@ var Emails = class {
|
|
|
2617
2777
|
*/
|
|
2618
2778
|
async send(options) {
|
|
2619
2779
|
try {
|
|
2620
|
-
const data = await this.http.post(
|
|
2621
|
-
"/api/email/send-raw",
|
|
2622
|
-
options
|
|
2623
|
-
);
|
|
2780
|
+
const data = await this.http.post("/api/email/send-raw", options);
|
|
2624
2781
|
return { data, error: null };
|
|
2625
2782
|
} catch (error) {
|
|
2626
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2783
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2784
|
+
throw error;
|
|
2785
|
+
}
|
|
2627
2786
|
return {
|
|
2628
2787
|
data: null,
|
|
2629
2788
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2706,10 +2865,7 @@ var RazorpayPayments = class {
|
|
|
2706
2865
|
);
|
|
2707
2866
|
return { data, error: null };
|
|
2708
2867
|
} catch (error) {
|
|
2709
|
-
return wrapError(
|
|
2710
|
-
error,
|
|
2711
|
-
"Razorpay order creation failed"
|
|
2712
|
-
);
|
|
2868
|
+
return wrapError(error, "Razorpay order creation failed");
|
|
2713
2869
|
}
|
|
2714
2870
|
}
|
|
2715
2871
|
async verifyOrder(environment, request) {
|
|
@@ -2720,10 +2876,7 @@ var RazorpayPayments = class {
|
|
|
2720
2876
|
);
|
|
2721
2877
|
return { data, error: null };
|
|
2722
2878
|
} catch (error) {
|
|
2723
|
-
return wrapError(
|
|
2724
|
-
error,
|
|
2725
|
-
"Razorpay order verification failed"
|
|
2726
|
-
);
|
|
2879
|
+
return wrapError(error, "Razorpay order verification failed");
|
|
2727
2880
|
}
|
|
2728
2881
|
}
|
|
2729
2882
|
async createSubscription(environment, request) {
|
|
@@ -2825,14 +2978,15 @@ var InsForgeClient = class {
|
|
|
2825
2978
|
isServerMode: config.isServerMode ?? !!accessToken,
|
|
2826
2979
|
detectOAuthCallback: config.auth?.detectOAuthCallback
|
|
2827
2980
|
});
|
|
2828
|
-
this.database = new Database(this.http);
|
|
2981
|
+
this.database = new Database(this.http, config.db?.schema);
|
|
2829
2982
|
this.storage = new Storage(this.http);
|
|
2830
2983
|
this.ai = new AI(this.http);
|
|
2831
2984
|
this.functions = new Functions(this.http, config.functionsUrl);
|
|
2832
2985
|
this.realtime = new Realtime(
|
|
2833
2986
|
this.http.baseUrl,
|
|
2834
2987
|
this.tokenManager,
|
|
2835
|
-
config.anonKey
|
|
2988
|
+
config.anonKey,
|
|
2989
|
+
() => this.http.getValidAccessToken()
|
|
2836
2990
|
);
|
|
2837
2991
|
this.emails = new Emails(this.http);
|
|
2838
2992
|
this.payments = new Payments(this.http);
|
|
@@ -2852,32 +3006,35 @@ var InsForgeClient = class {
|
|
|
2852
3006
|
/**
|
|
2853
3007
|
* Set the access token used by every SDK surface. Updates both the HTTP
|
|
2854
3008
|
* client (database / storage / functions / AI / emails) and the realtime
|
|
2855
|
-
* token manager
|
|
2856
|
-
*
|
|
3009
|
+
* token manager. Pass `null` to sign out. By default a token replacement is
|
|
3010
|
+
* treated as a sign-in boundary and reconnects realtime. Pass
|
|
3011
|
+
* `AuthChangeEvent.TOKEN_REFRESHED` for a same-identity refresh to preserve a live socket; the
|
|
3012
|
+
* refreshed token is then used at the next handshake.
|
|
2857
3013
|
*
|
|
2858
3014
|
* Use this when an external auth provider (Better Auth, Clerk, Auth0,
|
|
2859
3015
|
* WorkOS, Kinde, Stytch, …) issues the JWT and you need to keep the
|
|
2860
3016
|
* long-lived InsForge client in sync. Without this, you'd have to call
|
|
2861
3017
|
* `client.getHttpClient().setAuthToken(token)` AND reach into the private
|
|
2862
|
-
*
|
|
2863
|
-
* forgetting the second one silently breaks realtime auth.
|
|
3018
|
+
* realtime token manager separately.
|
|
2864
3019
|
*
|
|
2865
3020
|
* @example
|
|
2866
3021
|
* ```typescript
|
|
3022
|
+
* import { AuthChangeEvent } from '@insforge/sdk';
|
|
3023
|
+
*
|
|
2867
3024
|
* // Refresh a third-party-issued JWT periodically
|
|
2868
3025
|
* const { token } = await fetch('/api/insforge-token').then((r) => r.json());
|
|
2869
|
-
* client.setAccessToken(token);
|
|
3026
|
+
* client.setAccessToken(token, AuthChangeEvent.TOKEN_REFRESHED);
|
|
2870
3027
|
*
|
|
2871
3028
|
* // Sign-out
|
|
2872
3029
|
* client.setAccessToken(null);
|
|
2873
3030
|
* ```
|
|
2874
3031
|
*/
|
|
2875
|
-
setAccessToken(token) {
|
|
3032
|
+
setAccessToken(token, event = AuthChangeEvent.SIGNED_IN) {
|
|
2876
3033
|
this.http.setAuthToken(token);
|
|
2877
3034
|
if (token === null) {
|
|
2878
3035
|
this.tokenManager.clearSession();
|
|
2879
3036
|
} else {
|
|
2880
|
-
this.tokenManager.setAccessToken(token);
|
|
3037
|
+
this.tokenManager.setAccessToken(token, event);
|
|
2881
3038
|
}
|
|
2882
3039
|
}
|
|
2883
3040
|
/**
|
|
@@ -2890,38 +3047,6 @@ var InsForgeClient = class {
|
|
|
2890
3047
|
*/
|
|
2891
3048
|
};
|
|
2892
3049
|
|
|
2893
|
-
// src/lib/jwt.ts
|
|
2894
|
-
function decodeBase64Url(input) {
|
|
2895
|
-
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
2896
|
-
const padded = normalized.padEnd(
|
|
2897
|
-
normalized.length + (4 - normalized.length % 4) % 4,
|
|
2898
|
-
"="
|
|
2899
|
-
);
|
|
2900
|
-
const binary = atob(padded);
|
|
2901
|
-
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
2902
|
-
return new TextDecoder().decode(bytes);
|
|
2903
|
-
}
|
|
2904
|
-
function getJwtExpiration(token) {
|
|
2905
|
-
if (!token) return null;
|
|
2906
|
-
const [, payload] = token.split(".");
|
|
2907
|
-
if (!payload) return null;
|
|
2908
|
-
try {
|
|
2909
|
-
const parsed = JSON.parse(decodeBase64Url(payload));
|
|
2910
|
-
if (typeof parsed.exp !== "number" || !Number.isFinite(parsed.exp)) {
|
|
2911
|
-
return null;
|
|
2912
|
-
}
|
|
2913
|
-
return new Date(parsed.exp * 1e3);
|
|
2914
|
-
} catch {
|
|
2915
|
-
return null;
|
|
2916
|
-
}
|
|
2917
|
-
}
|
|
2918
|
-
function isJwtExpiredOrExpiring(token, leewaySeconds = 60) {
|
|
2919
|
-
if (!token) return false;
|
|
2920
|
-
const expires = getJwtExpiration(token);
|
|
2921
|
-
if (!expires) return true;
|
|
2922
|
-
return expires.getTime() <= Date.now() + leewaySeconds * 1e3;
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
3050
|
// src/ssr/browser-client.ts
|
|
2926
3051
|
var import_shared_schemas2 = require("@insforge/shared-schemas");
|
|
2927
3052
|
|
|
@@ -2936,18 +3061,28 @@ function getRefreshTokenCookieName(names) {
|
|
|
2936
3061
|
return names?.refreshToken ?? DEFAULT_REFRESH_TOKEN_COOKIE;
|
|
2937
3062
|
}
|
|
2938
3063
|
function getCookieValue(cookies, name) {
|
|
2939
|
-
if (!cookies)
|
|
3064
|
+
if (!cookies) {
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
2940
3067
|
const value = cookies.get(name);
|
|
2941
|
-
if (typeof value === "string")
|
|
2942
|
-
|
|
3068
|
+
if (typeof value === "string") {
|
|
3069
|
+
return value || null;
|
|
3070
|
+
}
|
|
3071
|
+
if (value && typeof value.value === "string") {
|
|
3072
|
+
return value.value || null;
|
|
3073
|
+
}
|
|
2943
3074
|
return null;
|
|
2944
3075
|
}
|
|
2945
3076
|
function getCookieValueFromHeader(cookieHeader, name) {
|
|
2946
|
-
if (!cookieHeader)
|
|
3077
|
+
if (!cookieHeader) {
|
|
3078
|
+
return null;
|
|
3079
|
+
}
|
|
2947
3080
|
const parts = cookieHeader.split(";");
|
|
2948
3081
|
for (const part of parts) {
|
|
2949
3082
|
const [rawName, ...rawValue] = part.trim().split("=");
|
|
2950
|
-
if (rawName !== name)
|
|
3083
|
+
if (rawName !== name) {
|
|
3084
|
+
continue;
|
|
3085
|
+
}
|
|
2951
3086
|
try {
|
|
2952
3087
|
return decodeURIComponent(rawValue.join("="));
|
|
2953
3088
|
} catch {
|
|
@@ -2957,7 +3092,9 @@ function getCookieValueFromHeader(cookieHeader, name) {
|
|
|
2957
3092
|
return null;
|
|
2958
3093
|
}
|
|
2959
3094
|
function getBrowserCookie(name) {
|
|
2960
|
-
if (typeof document === "undefined")
|
|
3095
|
+
if (typeof document === "undefined") {
|
|
3096
|
+
return null;
|
|
3097
|
+
}
|
|
2961
3098
|
return getCookieValueFromHeader(document.cookie, name);
|
|
2962
3099
|
}
|
|
2963
3100
|
function defaultCookieOptions() {
|
|
@@ -2994,11 +3131,15 @@ function expiredCookieOptions(overrides) {
|
|
|
2994
3131
|
};
|
|
2995
3132
|
}
|
|
2996
3133
|
function setCookie(cookies, name, value, options) {
|
|
2997
|
-
if (!cookies?.set)
|
|
3134
|
+
if (!cookies?.set) {
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
2998
3137
|
cookies.set(name, value, options);
|
|
2999
3138
|
}
|
|
3000
3139
|
function deleteCookie(cookies, name, options) {
|
|
3001
|
-
if (!cookies)
|
|
3140
|
+
if (!cookies) {
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3002
3143
|
if (cookies.set) {
|
|
3003
3144
|
cookies.set(name, "", expiredCookieOptions(options));
|
|
3004
3145
|
return;
|
|
@@ -3007,12 +3148,24 @@ function deleteCookie(cookies, name, options) {
|
|
|
3007
3148
|
}
|
|
3008
3149
|
function serializeCookie(name, value, options = {}) {
|
|
3009
3150
|
const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
|
|
3010
|
-
if (options.maxAge !== void 0)
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
if (options.
|
|
3014
|
-
|
|
3015
|
-
|
|
3151
|
+
if (options.maxAge !== void 0) {
|
|
3152
|
+
parts.push(`Max-Age=${options.maxAge}`);
|
|
3153
|
+
}
|
|
3154
|
+
if (options.domain) {
|
|
3155
|
+
parts.push(`Domain=${options.domain}`);
|
|
3156
|
+
}
|
|
3157
|
+
if (options.path) {
|
|
3158
|
+
parts.push(`Path=${options.path}`);
|
|
3159
|
+
}
|
|
3160
|
+
if (options.expires) {
|
|
3161
|
+
parts.push(`Expires=${options.expires.toUTCString()}`);
|
|
3162
|
+
}
|
|
3163
|
+
if (options.httpOnly) {
|
|
3164
|
+
parts.push("HttpOnly");
|
|
3165
|
+
}
|
|
3166
|
+
if (options.secure) {
|
|
3167
|
+
parts.push("Secure");
|
|
3168
|
+
}
|
|
3016
3169
|
if (options.sameSite) {
|
|
3017
3170
|
const sameSite = options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1);
|
|
3018
3171
|
parts.push(`SameSite=${sameSite}`);
|
|
@@ -3025,20 +3178,14 @@ function appendSetCookie(headers, name, value, options) {
|
|
|
3025
3178
|
function setAuthCookies(cookies, tokens, settings = {}) {
|
|
3026
3179
|
const accessName = getAccessTokenCookieName(settings.names);
|
|
3027
3180
|
const refreshName = getRefreshTokenCookieName(settings.names);
|
|
3028
|
-
const accessOptions = accessTokenCookieOptions(
|
|
3029
|
-
tokens.accessToken,
|
|
3030
|
-
settings.options?.accessToken
|
|
3031
|
-
);
|
|
3181
|
+
const accessOptions = accessTokenCookieOptions(tokens.accessToken, settings.options?.accessToken);
|
|
3032
3182
|
setCookie(cookies, accessName, tokens.accessToken, accessOptions);
|
|
3033
3183
|
if (tokens.refreshToken) {
|
|
3034
3184
|
setCookie(
|
|
3035
3185
|
cookies,
|
|
3036
3186
|
refreshName,
|
|
3037
3187
|
tokens.refreshToken,
|
|
3038
|
-
refreshTokenCookieOptions(
|
|
3039
|
-
tokens.refreshToken,
|
|
3040
|
-
settings.options?.refreshToken
|
|
3041
|
-
)
|
|
3188
|
+
refreshTokenCookieOptions(tokens.refreshToken, settings.options?.refreshToken)
|
|
3042
3189
|
);
|
|
3043
3190
|
}
|
|
3044
3191
|
}
|
|
@@ -3064,10 +3211,7 @@ function setAuthCookieHeaders(headers, tokens, settings = {}) {
|
|
|
3064
3211
|
headers,
|
|
3065
3212
|
refreshName,
|
|
3066
3213
|
tokens.refreshToken,
|
|
3067
|
-
refreshTokenCookieOptions(
|
|
3068
|
-
tokens.refreshToken,
|
|
3069
|
-
settings.options?.refreshToken
|
|
3070
|
-
)
|
|
3214
|
+
refreshTokenCookieOptions(tokens.refreshToken, settings.options?.refreshToken)
|
|
3071
3215
|
);
|
|
3072
3216
|
}
|
|
3073
3217
|
}
|
|
@@ -3110,10 +3254,14 @@ function toRefreshError(response, body) {
|
|
|
3110
3254
|
);
|
|
3111
3255
|
}
|
|
3112
3256
|
async function readErrorCode(response) {
|
|
3113
|
-
if (response.status !== 401)
|
|
3257
|
+
if (response.status !== 401) {
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3114
3260
|
try {
|
|
3115
3261
|
const body = await response.clone().json();
|
|
3116
|
-
if (!body || typeof body !== "object")
|
|
3262
|
+
if (!body || typeof body !== "object") {
|
|
3263
|
+
return null;
|
|
3264
|
+
}
|
|
3117
3265
|
const candidate = body.error ?? body.code;
|
|
3118
3266
|
return typeof candidate === "string" ? candidate : null;
|
|
3119
3267
|
} catch {
|
|
@@ -3132,7 +3280,9 @@ function withAuthHeader(init, token) {
|
|
|
3132
3280
|
};
|
|
3133
3281
|
}
|
|
3134
3282
|
function getRequestUrl(input) {
|
|
3135
|
-
if (typeof input === "string")
|
|
3283
|
+
if (typeof input === "string") {
|
|
3284
|
+
return input;
|
|
3285
|
+
}
|
|
3136
3286
|
if (typeof Request !== "undefined" && input instanceof Request) {
|
|
3137
3287
|
return input.url;
|
|
3138
3288
|
}
|
|
@@ -3150,21 +3300,19 @@ function createBrowserClient(options = {}) {
|
|
|
3150
3300
|
"Missing InsForge baseUrl or anonKey. Pass baseUrl and anonKey to createBrowserClient() or set NEXT_PUBLIC_INSFORGE_URL and NEXT_PUBLIC_INSFORGE_ANON_KEY."
|
|
3151
3301
|
);
|
|
3152
3302
|
}
|
|
3153
|
-
let accessToken = getBrowserCookie(
|
|
3154
|
-
getAccessTokenCookieName(options.names)
|
|
3155
|
-
);
|
|
3303
|
+
let accessToken = getBrowserCookie(getAccessTokenCookieName(options.names));
|
|
3156
3304
|
const refreshUrl = options.refreshUrl ?? "/api/auth/refresh";
|
|
3157
3305
|
const fetchImpl = options.fetch ?? (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
3158
3306
|
let client;
|
|
3159
3307
|
let sessionChecked = false;
|
|
3160
3308
|
let refreshPromise = null;
|
|
3161
3309
|
const refreshFromRoute = () => {
|
|
3162
|
-
if (refreshPromise)
|
|
3310
|
+
if (refreshPromise) {
|
|
3311
|
+
return refreshPromise;
|
|
3312
|
+
}
|
|
3163
3313
|
refreshPromise = (async () => {
|
|
3164
3314
|
if (!fetchImpl) {
|
|
3165
|
-
throw new Error(
|
|
3166
|
-
"Fetch is not available. Please provide a fetch implementation."
|
|
3167
|
-
);
|
|
3315
|
+
throw new Error("Fetch is not available. Please provide a fetch implementation.");
|
|
3168
3316
|
}
|
|
3169
3317
|
const response = await fetchImpl(refreshUrl, {
|
|
3170
3318
|
method: "POST",
|
|
@@ -3181,11 +3329,15 @@ function createBrowserClient(options = {}) {
|
|
|
3181
3329
|
}
|
|
3182
3330
|
throw error;
|
|
3183
3331
|
}
|
|
3184
|
-
if (!body || typeof body !== "object")
|
|
3332
|
+
if (!body || typeof body !== "object") {
|
|
3333
|
+
return null;
|
|
3334
|
+
}
|
|
3185
3335
|
const refreshBody = body;
|
|
3186
|
-
if (!refreshBody.accessToken || !refreshBody.user)
|
|
3336
|
+
if (!refreshBody.accessToken || !refreshBody.user) {
|
|
3337
|
+
return null;
|
|
3338
|
+
}
|
|
3187
3339
|
accessToken = refreshBody.accessToken;
|
|
3188
|
-
client?.setAccessToken(refreshBody.accessToken);
|
|
3340
|
+
client?.setAccessToken(refreshBody.accessToken, AuthChangeEvent.TOKEN_REFRESHED);
|
|
3189
3341
|
return refreshBody;
|
|
3190
3342
|
})().finally(() => {
|
|
3191
3343
|
sessionChecked = true;
|
|
@@ -3199,9 +3351,7 @@ function createBrowserClient(options = {}) {
|
|
|
3199
3351
|
};
|
|
3200
3352
|
const ssrFetch = async (input, init) => {
|
|
3201
3353
|
if (!fetchImpl) {
|
|
3202
|
-
throw new Error(
|
|
3203
|
-
"Fetch is not available. Please provide a fetch implementation."
|
|
3204
|
-
);
|
|
3354
|
+
throw new Error("Fetch is not available. Please provide a fetch implementation.");
|
|
3205
3355
|
}
|
|
3206
3356
|
if (shouldSkipRefresh(input)) {
|
|
3207
3357
|
return fetchImpl(input, init);
|
|
@@ -3238,9 +3388,9 @@ function createBrowserClient(options = {}) {
|
|
|
3238
3388
|
}
|
|
3239
3389
|
});
|
|
3240
3390
|
const setAccessToken = client.setAccessToken.bind(client);
|
|
3241
|
-
client.setAccessToken = (token) => {
|
|
3391
|
+
client.setAccessToken = (token, event) => {
|
|
3242
3392
|
accessToken = token;
|
|
3243
|
-
setAccessToken(token);
|
|
3393
|
+
setAccessToken(token, event);
|
|
3244
3394
|
};
|
|
3245
3395
|
if (accessToken) {
|
|
3246
3396
|
client.setAccessToken(accessToken);
|
|
@@ -3264,10 +3414,7 @@ function createServerClient(options = {}) {
|
|
|
3264
3414
|
"Missing InsForge baseUrl or anonKey. Pass baseUrl and anonKey to createServerClient() or set NEXT_PUBLIC_INSFORGE_URL and NEXT_PUBLIC_INSFORGE_ANON_KEY."
|
|
3265
3415
|
);
|
|
3266
3416
|
}
|
|
3267
|
-
const accessToken = options.accessToken ?? getCookieValue(
|
|
3268
|
-
options.cookies,
|
|
3269
|
-
getAccessTokenCookieName(options.names)
|
|
3270
|
-
);
|
|
3417
|
+
const accessToken = options.accessToken ?? getCookieValue(options.cookies, getAccessTokenCookieName(options.names));
|
|
3271
3418
|
return new InsForgeClient({
|
|
3272
3419
|
...options,
|
|
3273
3420
|
baseUrl,
|
|
@@ -3290,7 +3437,9 @@ function jsonResponse(body, init = {}, headers = new Headers(init.headers)) {
|
|
|
3290
3437
|
});
|
|
3291
3438
|
}
|
|
3292
3439
|
function normalizeError(error) {
|
|
3293
|
-
if (error instanceof InsForgeError)
|
|
3440
|
+
if (error instanceof InsForgeError) {
|
|
3441
|
+
return error;
|
|
3442
|
+
}
|
|
3294
3443
|
if (error && typeof error === "object") {
|
|
3295
3444
|
const body = error;
|
|
3296
3445
|
return new InsForgeError(
|
|
@@ -3307,18 +3456,21 @@ function normalizeError(error) {
|
|
|
3307
3456
|
}
|
|
3308
3457
|
async function readJson(response) {
|
|
3309
3458
|
const contentType = response.headers.get("content-type");
|
|
3310
|
-
if (!contentType?.includes("json"))
|
|
3459
|
+
if (!contentType?.includes("json")) {
|
|
3460
|
+
return null;
|
|
3461
|
+
}
|
|
3311
3462
|
return response.json();
|
|
3312
3463
|
}
|
|
3313
3464
|
function readRefreshToken(options) {
|
|
3314
|
-
if (options.refreshToken)
|
|
3465
|
+
if (options.refreshToken) {
|
|
3466
|
+
return options.refreshToken;
|
|
3467
|
+
}
|
|
3315
3468
|
const refreshCookieName = getRefreshTokenCookieName(options.names);
|
|
3316
3469
|
const cookieValue = getCookieValue(options.cookies, refreshCookieName);
|
|
3317
|
-
if (cookieValue)
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
);
|
|
3470
|
+
if (cookieValue) {
|
|
3471
|
+
return cookieValue;
|
|
3472
|
+
}
|
|
3473
|
+
return getCookieValueFromHeader(options.request?.headers.get("cookie"), refreshCookieName);
|
|
3322
3474
|
}
|
|
3323
3475
|
async function refreshAuth(options = {}) {
|
|
3324
3476
|
const headers = new Headers();
|
|
@@ -3359,9 +3511,7 @@ async function refreshAuth(options = {}) {
|
|
|
3359
3511
|
}
|
|
3360
3512
|
const fetchImpl = options.fetch ?? (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
3361
3513
|
if (!fetchImpl) {
|
|
3362
|
-
throw new Error(
|
|
3363
|
-
"Fetch is not available. Please provide a fetch implementation."
|
|
3364
|
-
);
|
|
3514
|
+
throw new Error("Fetch is not available. Please provide a fetch implementation.");
|
|
3365
3515
|
}
|
|
3366
3516
|
const requestHeaders = new Headers(options.headers);
|
|
3367
3517
|
requestHeaders.set("Authorization", `Bearer ${anonKey}`);
|
|
@@ -3442,7 +3592,9 @@ function createRefreshAuthRouter(options = {}) {
|
|
|
3442
3592
|
|
|
3443
3593
|
// src/ssr/auth-actions.ts
|
|
3444
3594
|
function persistSessionCookies(cookies, data, settings) {
|
|
3445
|
-
if (!data?.accessToken)
|
|
3595
|
+
if (!data?.accessToken) {
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
3446
3598
|
setAuthCookies(
|
|
3447
3599
|
cookies,
|
|
3448
3600
|
{
|
|
@@ -3453,7 +3605,9 @@ function persistSessionCookies(cookies, data, settings) {
|
|
|
3453
3605
|
);
|
|
3454
3606
|
}
|
|
3455
3607
|
function sanitizeAuthData(data) {
|
|
3456
|
-
if (!data)
|
|
3608
|
+
if (!data) {
|
|
3609
|
+
return null;
|
|
3610
|
+
}
|
|
3457
3611
|
const {
|
|
3458
3612
|
accessToken: _accessToken,
|
|
3459
3613
|
refreshToken: _refreshToken,
|
|
@@ -3503,32 +3657,20 @@ function createAuthActions(options = {}) {
|
|
|
3503
3657
|
signInWithPassword: async (request) => {
|
|
3504
3658
|
const result = await createClient().auth.signInWithPassword(request);
|
|
3505
3659
|
persistSessionCookies(writeCookies, result.data, cookieSettings);
|
|
3506
|
-
return toSafeAuthResult(
|
|
3507
|
-
result
|
|
3508
|
-
);
|
|
3660
|
+
return toSafeAuthResult(result);
|
|
3509
3661
|
},
|
|
3510
3662
|
signInWithOAuth: async (providerOrOptions, signInOptions) => {
|
|
3511
|
-
return createClient().auth.signInWithOAuth(
|
|
3512
|
-
providerOrOptions,
|
|
3513
|
-
signInOptions
|
|
3514
|
-
);
|
|
3663
|
+
return createClient().auth.signInWithOAuth(providerOrOptions, signInOptions);
|
|
3515
3664
|
},
|
|
3516
3665
|
signInWithIdToken: async (credentials) => {
|
|
3517
3666
|
const result = await createClient().auth.signInWithIdToken(credentials);
|
|
3518
3667
|
persistSessionCookies(writeCookies, result.data, cookieSettings);
|
|
3519
|
-
return toSafeAuthResult(
|
|
3520
|
-
result
|
|
3521
|
-
);
|
|
3668
|
+
return toSafeAuthResult(result);
|
|
3522
3669
|
},
|
|
3523
3670
|
exchangeOAuthCode: async (code, codeVerifier) => {
|
|
3524
|
-
const result = await createClient().auth.exchangeOAuthCode(
|
|
3525
|
-
code,
|
|
3526
|
-
codeVerifier
|
|
3527
|
-
);
|
|
3671
|
+
const result = await createClient().auth.exchangeOAuthCode(code, codeVerifier);
|
|
3528
3672
|
persistSessionCookies(writeCookies, result.data, cookieSettings);
|
|
3529
|
-
return toSafeAuthResult(
|
|
3530
|
-
result
|
|
3531
|
-
);
|
|
3673
|
+
return toSafeAuthResult(result);
|
|
3532
3674
|
},
|
|
3533
3675
|
verifyEmail: async (request) => {
|
|
3534
3676
|
const result = await createClient().auth.verifyEmail(request);
|
|
@@ -3547,10 +3689,7 @@ function createAuthActions(options = {}) {
|
|
|
3547
3689
|
async function updateSession(options) {
|
|
3548
3690
|
const accessCookieName = getAccessTokenCookieName(options.names);
|
|
3549
3691
|
const refreshCookieName = getRefreshTokenCookieName(options.names);
|
|
3550
|
-
const accessToken = getCookieValue(
|
|
3551
|
-
options.requestCookies,
|
|
3552
|
-
accessCookieName
|
|
3553
|
-
);
|
|
3692
|
+
const accessToken = getCookieValue(options.requestCookies, accessCookieName);
|
|
3554
3693
|
if (accessToken && !isJwtExpiredOrExpiring(accessToken, options.refreshLeewaySeconds)) {
|
|
3555
3694
|
return {
|
|
3556
3695
|
refreshed: false,
|
|
@@ -3558,10 +3697,7 @@ async function updateSession(options) {
|
|
|
3558
3697
|
error: null
|
|
3559
3698
|
};
|
|
3560
3699
|
}
|
|
3561
|
-
const refreshToken = getCookieValue(
|
|
3562
|
-
options.requestCookies,
|
|
3563
|
-
refreshCookieName
|
|
3564
|
-
);
|
|
3700
|
+
const refreshToken = getCookieValue(options.requestCookies, refreshCookieName);
|
|
3565
3701
|
if (!refreshToken) {
|
|
3566
3702
|
if (accessToken) {
|
|
3567
3703
|
clearAuthCookies(options.requestCookies, options);
|