@insforge/sdk 1.4.3 → 1.4.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -143
- package/SDK-REFERENCE.md +146 -168
- package/dist/{client-B6eZHolm.d.mts → client-BZyLCjO1.d.mts} +44 -120
- package/dist/{client-DUmOm_3W.d.ts → client-Dh7GOydb.d.ts} +44 -120
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +556 -423
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +559 -423
- package/dist/index.mjs.map +1 -1
- 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 +1 -1
- package/dist/ssr.d.ts +1 -1
- package/dist/ssr.js +651 -539
- package/dist/ssr.js.map +1 -1
- package/dist/ssr.mjs +655 -540
- package/dist/ssr.mjs.map +1 -1
- 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");
|
|
@@ -1097,11 +1172,7 @@ var Auth = class {
|
|
|
1097
1172
|
if (!signInOptions || !signInOptions.redirectTo) {
|
|
1098
1173
|
return {
|
|
1099
1174
|
data: {},
|
|
1100
|
-
error: new InsForgeError(
|
|
1101
|
-
"Redirect URI is required",
|
|
1102
|
-
400,
|
|
1103
|
-
import_shared_schemas.ERROR_CODES.INVALID_INPUT
|
|
1104
|
-
)
|
|
1175
|
+
error: new InsForgeError("Redirect URI is required", 400, import_shared_schemas.ERROR_CODES.INVALID_INPUT)
|
|
1105
1176
|
};
|
|
1106
1177
|
}
|
|
1107
1178
|
const { provider } = signInOptions;
|
|
@@ -1176,10 +1247,7 @@ var Auth = class {
|
|
|
1176
1247
|
error: null
|
|
1177
1248
|
};
|
|
1178
1249
|
} catch (error) {
|
|
1179
|
-
return wrapError(
|
|
1180
|
-
error,
|
|
1181
|
-
"An unexpected error occurred during OAuth code exchange"
|
|
1182
|
-
);
|
|
1250
|
+
return wrapError(error, "An unexpected error occurred during OAuth code exchange");
|
|
1183
1251
|
}
|
|
1184
1252
|
}
|
|
1185
1253
|
/**
|
|
@@ -1206,10 +1274,7 @@ var Auth = class {
|
|
|
1206
1274
|
error: null
|
|
1207
1275
|
};
|
|
1208
1276
|
} catch (error) {
|
|
1209
|
-
return wrapError(
|
|
1210
|
-
error,
|
|
1211
|
-
"An unexpected error occurred during ID token sign in"
|
|
1212
|
-
);
|
|
1277
|
+
return wrapError(error, "An unexpected error occurred during ID token sign in");
|
|
1213
1278
|
}
|
|
1214
1279
|
}
|
|
1215
1280
|
// ============================================================================
|
|
@@ -1250,14 +1315,11 @@ var Auth = class {
|
|
|
1250
1315
|
}
|
|
1251
1316
|
);
|
|
1252
1317
|
if (response.accessToken) {
|
|
1253
|
-
this.saveSessionFromResponse(response);
|
|
1318
|
+
this.saveSessionFromResponse(response, AuthChangeEvent.TOKEN_REFRESHED);
|
|
1254
1319
|
}
|
|
1255
1320
|
return { data: response, error: null };
|
|
1256
1321
|
} catch (error) {
|
|
1257
|
-
return wrapError(
|
|
1258
|
-
error,
|
|
1259
|
-
"An unexpected error occurred during session refresh"
|
|
1260
|
-
);
|
|
1322
|
+
return wrapError(error, "An unexpected error occurred during session refresh");
|
|
1261
1323
|
}
|
|
1262
1324
|
}
|
|
1263
1325
|
/**
|
|
@@ -1268,11 +1330,11 @@ var Auth = class {
|
|
|
1268
1330
|
try {
|
|
1269
1331
|
if (this.isServerMode()) {
|
|
1270
1332
|
const accessToken = this.tokenManager.getAccessToken();
|
|
1271
|
-
if (!accessToken)
|
|
1333
|
+
if (!accessToken) {
|
|
1334
|
+
return { data: { user: null }, error: null };
|
|
1335
|
+
}
|
|
1272
1336
|
this.http.setAuthToken(accessToken);
|
|
1273
|
-
const response = await this.http.get(
|
|
1274
|
-
"/api/auth/sessions/current"
|
|
1275
|
-
);
|
|
1337
|
+
const response = await this.http.get("/api/auth/sessions/current");
|
|
1276
1338
|
const user = response.user ?? null;
|
|
1277
1339
|
return { data: { user }, error: null };
|
|
1278
1340
|
}
|
|
@@ -1310,25 +1372,17 @@ var Auth = class {
|
|
|
1310
1372
|
// ============================================================================
|
|
1311
1373
|
async getProfile(userId) {
|
|
1312
1374
|
try {
|
|
1313
|
-
const response = await this.http.get(
|
|
1314
|
-
`/api/auth/profiles/${userId}`
|
|
1315
|
-
);
|
|
1375
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
1316
1376
|
return { data: response, error: null };
|
|
1317
1377
|
} catch (error) {
|
|
1318
|
-
return wrapError(
|
|
1319
|
-
error,
|
|
1320
|
-
"An unexpected error occurred while fetching user profile"
|
|
1321
|
-
);
|
|
1378
|
+
return wrapError(error, "An unexpected error occurred while fetching user profile");
|
|
1322
1379
|
}
|
|
1323
1380
|
}
|
|
1324
1381
|
async setProfile(profile) {
|
|
1325
1382
|
try {
|
|
1326
|
-
const response = await this.http.patch(
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
profile
|
|
1330
|
-
}
|
|
1331
|
-
);
|
|
1383
|
+
const response = await this.http.patch("/api/auth/profiles/current", {
|
|
1384
|
+
profile
|
|
1385
|
+
});
|
|
1332
1386
|
const currentUser = this.tokenManager.getUser();
|
|
1333
1387
|
if (!this.isServerMode() && currentUser && response.profile !== void 0) {
|
|
1334
1388
|
this.tokenManager.setUser({
|
|
@@ -1338,10 +1392,7 @@ var Auth = class {
|
|
|
1338
1392
|
}
|
|
1339
1393
|
return { data: response, error: null };
|
|
1340
1394
|
} catch (error) {
|
|
1341
|
-
return wrapError(
|
|
1342
|
-
error,
|
|
1343
|
-
"An unexpected error occurred while updating user profile"
|
|
1344
|
-
);
|
|
1395
|
+
return wrapError(error, "An unexpected error occurred while updating user profile");
|
|
1345
1396
|
}
|
|
1346
1397
|
}
|
|
1347
1398
|
// ============================================================================
|
|
@@ -1354,10 +1405,7 @@ var Auth = class {
|
|
|
1354
1405
|
});
|
|
1355
1406
|
return { data: response, error: null };
|
|
1356
1407
|
} catch (error) {
|
|
1357
|
-
return wrapError(
|
|
1358
|
-
error,
|
|
1359
|
-
"An unexpected error occurred while sending verification email"
|
|
1360
|
-
);
|
|
1408
|
+
return wrapError(error, "An unexpected error occurred while sending verification email");
|
|
1361
1409
|
}
|
|
1362
1410
|
}
|
|
1363
1411
|
async verifyEmail(request) {
|
|
@@ -1373,10 +1421,7 @@ var Auth = class {
|
|
|
1373
1421
|
}
|
|
1374
1422
|
return { data: response, error: null };
|
|
1375
1423
|
} catch (error) {
|
|
1376
|
-
return wrapError(
|
|
1377
|
-
error,
|
|
1378
|
-
"An unexpected error occurred while verifying email"
|
|
1379
|
-
);
|
|
1424
|
+
return wrapError(error, "An unexpected error occurred while verifying email");
|
|
1380
1425
|
}
|
|
1381
1426
|
}
|
|
1382
1427
|
// ============================================================================
|
|
@@ -1389,10 +1434,7 @@ var Auth = class {
|
|
|
1389
1434
|
});
|
|
1390
1435
|
return { data: response, error: null };
|
|
1391
1436
|
} catch (error) {
|
|
1392
|
-
return wrapError(
|
|
1393
|
-
error,
|
|
1394
|
-
"An unexpected error occurred while sending password reset email"
|
|
1395
|
-
);
|
|
1437
|
+
return wrapError(error, "An unexpected error occurred while sending password reset email");
|
|
1396
1438
|
}
|
|
1397
1439
|
}
|
|
1398
1440
|
async exchangeResetPasswordToken(request) {
|
|
@@ -1404,10 +1446,7 @@ var Auth = class {
|
|
|
1404
1446
|
);
|
|
1405
1447
|
return { data: response, error: null };
|
|
1406
1448
|
} catch (error) {
|
|
1407
|
-
return wrapError(
|
|
1408
|
-
error,
|
|
1409
|
-
"An unexpected error occurred while verifying reset code"
|
|
1410
|
-
);
|
|
1449
|
+
return wrapError(error, "An unexpected error occurred while verifying reset code");
|
|
1411
1450
|
}
|
|
1412
1451
|
}
|
|
1413
1452
|
async resetPassword(request) {
|
|
@@ -1419,10 +1458,7 @@ var Auth = class {
|
|
|
1419
1458
|
);
|
|
1420
1459
|
return { data: response, error: null };
|
|
1421
1460
|
} catch (error) {
|
|
1422
|
-
return wrapError(
|
|
1423
|
-
error,
|
|
1424
|
-
"An unexpected error occurred while resetting password"
|
|
1425
|
-
);
|
|
1461
|
+
return wrapError(error, "An unexpected error occurred while resetting password");
|
|
1426
1462
|
}
|
|
1427
1463
|
}
|
|
1428
1464
|
// ============================================================================
|
|
@@ -1430,16 +1466,12 @@ var Auth = class {
|
|
|
1430
1466
|
// ============================================================================
|
|
1431
1467
|
async getPublicAuthConfig() {
|
|
1432
1468
|
try {
|
|
1433
|
-
const response = await this.http.get(
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
);
|
|
1469
|
+
const response = await this.http.get("/api/auth/public-config", {
|
|
1470
|
+
skipAuthRefresh: true
|
|
1471
|
+
});
|
|
1437
1472
|
return { data: response, error: null };
|
|
1438
1473
|
} catch (error) {
|
|
1439
|
-
return wrapError(
|
|
1440
|
-
error,
|
|
1441
|
-
"An unexpected error occurred while fetching auth configuration"
|
|
1442
|
-
);
|
|
1474
|
+
return wrapError(error, "An unexpected error occurred while fetching auth configuration");
|
|
1443
1475
|
}
|
|
1444
1476
|
}
|
|
1445
1477
|
};
|
|
@@ -1597,11 +1629,7 @@ var StorageBucket = class {
|
|
|
1597
1629
|
} catch (error) {
|
|
1598
1630
|
return {
|
|
1599
1631
|
data: null,
|
|
1600
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1601
|
-
"Upload failed",
|
|
1602
|
-
500,
|
|
1603
|
-
"STORAGE_ERROR"
|
|
1604
|
-
)
|
|
1632
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Upload failed", 500, "STORAGE_ERROR")
|
|
1605
1633
|
};
|
|
1606
1634
|
}
|
|
1607
1635
|
}
|
|
@@ -1647,11 +1675,7 @@ var StorageBucket = class {
|
|
|
1647
1675
|
} catch (error) {
|
|
1648
1676
|
return {
|
|
1649
1677
|
data: null,
|
|
1650
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1651
|
-
"Upload failed",
|
|
1652
|
-
500,
|
|
1653
|
-
"STORAGE_ERROR"
|
|
1654
|
-
)
|
|
1678
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Upload failed", 500, "STORAGE_ERROR")
|
|
1655
1679
|
};
|
|
1656
1680
|
}
|
|
1657
1681
|
}
|
|
@@ -1679,13 +1703,10 @@ var StorageBucket = class {
|
|
|
1679
1703
|
);
|
|
1680
1704
|
}
|
|
1681
1705
|
if (strategy.confirmRequired && strategy.confirmUrl) {
|
|
1682
|
-
const confirmResponse = await this.http.post(
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
contentType: file.type || "application/octet-stream"
|
|
1687
|
-
}
|
|
1688
|
-
);
|
|
1706
|
+
const confirmResponse = await this.http.post(strategy.confirmUrl, {
|
|
1707
|
+
size: file.size,
|
|
1708
|
+
contentType: file.type || "application/octet-stream"
|
|
1709
|
+
});
|
|
1689
1710
|
return { data: confirmResponse, error: null };
|
|
1690
1711
|
}
|
|
1691
1712
|
return {
|
|
@@ -1700,11 +1721,7 @@ var StorageBucket = class {
|
|
|
1700
1721
|
error: null
|
|
1701
1722
|
};
|
|
1702
1723
|
} catch (error) {
|
|
1703
|
-
throw error instanceof InsForgeError ? error : new InsForgeError(
|
|
1704
|
-
"Presigned upload failed",
|
|
1705
|
-
500,
|
|
1706
|
-
"STORAGE_ERROR"
|
|
1707
|
-
);
|
|
1724
|
+
throw error instanceof InsForgeError ? error : new InsForgeError("Presigned upload failed", 500, "STORAGE_ERROR");
|
|
1708
1725
|
}
|
|
1709
1726
|
}
|
|
1710
1727
|
/**
|
|
@@ -1758,11 +1775,7 @@ var StorageBucket = class {
|
|
|
1758
1775
|
} catch (error) {
|
|
1759
1776
|
return {
|
|
1760
1777
|
data: null,
|
|
1761
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1762
|
-
"Download failed",
|
|
1763
|
-
500,
|
|
1764
|
-
"STORAGE_ERROR"
|
|
1765
|
-
)
|
|
1778
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Download failed", 500, "STORAGE_ERROR")
|
|
1766
1779
|
};
|
|
1767
1780
|
}
|
|
1768
1781
|
}
|
|
@@ -1797,7 +1810,9 @@ var StorageBucket = class {
|
|
|
1797
1810
|
} catch (error) {
|
|
1798
1811
|
const status = error instanceof InsForgeError ? error.statusCode : void 0;
|
|
1799
1812
|
const isMissingRoute = (status === 404 || status === 405) && !(error instanceof InsForgeError && error.error === "STORAGE_NOT_FOUND");
|
|
1800
|
-
if (!isMissingRoute)
|
|
1813
|
+
if (!isMissingRoute) {
|
|
1814
|
+
throw error;
|
|
1815
|
+
}
|
|
1801
1816
|
return await this.http.post(
|
|
1802
1817
|
`/api/storage/buckets/${this.bucketName}/objects/${encoded}/download-strategy`,
|
|
1803
1818
|
{ expiresIn }
|
|
@@ -1873,10 +1888,18 @@ var StorageBucket = class {
|
|
|
1873
1888
|
async list(options) {
|
|
1874
1889
|
try {
|
|
1875
1890
|
const params = {};
|
|
1876
|
-
if (options?.prefix)
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
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
|
+
}
|
|
1880
1903
|
const response = await this.http.get(
|
|
1881
1904
|
`/api/storage/buckets/${this.bucketName}/objects`,
|
|
1882
1905
|
{ params }
|
|
@@ -1885,11 +1908,7 @@ var StorageBucket = class {
|
|
|
1885
1908
|
} catch (error) {
|
|
1886
1909
|
return {
|
|
1887
1910
|
data: null,
|
|
1888
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1889
|
-
"List failed",
|
|
1890
|
-
500,
|
|
1891
|
-
"STORAGE_ERROR"
|
|
1892
|
-
)
|
|
1911
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("List failed", 500, "STORAGE_ERROR")
|
|
1893
1912
|
};
|
|
1894
1913
|
}
|
|
1895
1914
|
}
|
|
@@ -1906,11 +1925,7 @@ var StorageBucket = class {
|
|
|
1906
1925
|
} catch (error) {
|
|
1907
1926
|
return {
|
|
1908
1927
|
data: null,
|
|
1909
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1910
|
-
"Delete failed",
|
|
1911
|
-
500,
|
|
1912
|
-
"STORAGE_ERROR"
|
|
1913
|
-
)
|
|
1928
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Delete failed", 500, "STORAGE_ERROR")
|
|
1914
1929
|
};
|
|
1915
1930
|
}
|
|
1916
1931
|
}
|
|
@@ -2032,14 +2047,11 @@ var ChatCompletions = class {
|
|
|
2032
2047
|
if (params.stream) {
|
|
2033
2048
|
const headers = this.http.getHeaders();
|
|
2034
2049
|
headers["Content-Type"] = "application/json";
|
|
2035
|
-
const response2 = await this.http.fetch(
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
body: JSON.stringify(backendParams)
|
|
2041
|
-
}
|
|
2042
|
-
);
|
|
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
|
+
});
|
|
2043
2055
|
if (!response2.ok) {
|
|
2044
2056
|
const error = await response2.json();
|
|
2045
2057
|
throw new Error(error.error || "Stream request failed");
|
|
@@ -2087,7 +2099,9 @@ var ChatCompletions = class {
|
|
|
2087
2099
|
try {
|
|
2088
2100
|
while (true) {
|
|
2089
2101
|
const { done, value } = await reader.read();
|
|
2090
|
-
if (done)
|
|
2102
|
+
if (done) {
|
|
2103
|
+
break;
|
|
2104
|
+
}
|
|
2091
2105
|
buffer += decoder.decode(value, { stream: true });
|
|
2092
2106
|
const lines = buffer.split("\n");
|
|
2093
2107
|
buffer = lines.pop() || "";
|
|
@@ -2135,7 +2149,7 @@ var ChatCompletions = class {
|
|
|
2135
2149
|
reader.releaseLock();
|
|
2136
2150
|
return;
|
|
2137
2151
|
}
|
|
2138
|
-
} catch
|
|
2152
|
+
} catch {
|
|
2139
2153
|
console.warn("Failed to parse SSE data:", dataStr);
|
|
2140
2154
|
}
|
|
2141
2155
|
}
|
|
@@ -2188,10 +2202,7 @@ var Embeddings = class {
|
|
|
2188
2202
|
* ```
|
|
2189
2203
|
*/
|
|
2190
2204
|
async create(params) {
|
|
2191
|
-
const response = await this.http.post(
|
|
2192
|
-
"/api/ai/embeddings",
|
|
2193
|
-
params
|
|
2194
|
-
);
|
|
2205
|
+
const response = await this.http.post("/api/ai/embeddings", params);
|
|
2195
2206
|
return {
|
|
2196
2207
|
object: response.object,
|
|
2197
2208
|
data: response.data,
|
|
@@ -2277,7 +2288,9 @@ var Functions = class _Functions {
|
|
|
2277
2288
|
static deriveSubhostingUrl(baseUrl) {
|
|
2278
2289
|
try {
|
|
2279
2290
|
const { hostname } = new URL(baseUrl);
|
|
2280
|
-
if (!hostname.endsWith(".insforge.app"))
|
|
2291
|
+
if (!hostname.endsWith(".insforge.app")) {
|
|
2292
|
+
return void 0;
|
|
2293
|
+
}
|
|
2281
2294
|
const appKey = hostname.split(".")[0];
|
|
2282
2295
|
return `https://${appKey}.functions.insforge.app`;
|
|
2283
2296
|
} catch {
|
|
@@ -2323,7 +2336,9 @@ var Functions = class _Functions {
|
|
|
2323
2336
|
const data = await parseResponse(res);
|
|
2324
2337
|
return { data, error: null };
|
|
2325
2338
|
} catch (error) {
|
|
2326
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2339
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2340
|
+
throw error;
|
|
2341
|
+
}
|
|
2327
2342
|
return {
|
|
2328
2343
|
data: null,
|
|
2329
2344
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2342,7 +2357,9 @@ var Functions = class _Functions {
|
|
|
2342
2357
|
});
|
|
2343
2358
|
return { data, error: null };
|
|
2344
2359
|
} catch (error) {
|
|
2345
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2360
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2361
|
+
throw error;
|
|
2362
|
+
}
|
|
2346
2363
|
if (error instanceof InsForgeError && error.statusCode === 404) {
|
|
2347
2364
|
} else {
|
|
2348
2365
|
return {
|
|
@@ -2361,7 +2378,9 @@ var Functions = class _Functions {
|
|
|
2361
2378
|
const data = await this.http.request(method, path, { body, headers });
|
|
2362
2379
|
return { data, error: null };
|
|
2363
2380
|
} catch (error) {
|
|
2364
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2381
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2382
|
+
throw error;
|
|
2383
|
+
}
|
|
2365
2384
|
return {
|
|
2366
2385
|
data: null,
|
|
2367
2386
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2376,32 +2395,37 @@ var Functions = class _Functions {
|
|
|
2376
2395
|
|
|
2377
2396
|
// src/modules/realtime.ts
|
|
2378
2397
|
var CONNECT_TIMEOUT = 1e4;
|
|
2398
|
+
var SUBSCRIBE_TIMEOUT = 1e4;
|
|
2379
2399
|
var Realtime = class {
|
|
2380
|
-
constructor(baseUrl, tokenManager, anonKey) {
|
|
2381
|
-
this.socket = null;
|
|
2382
|
-
this.connectPromise = null;
|
|
2383
|
-
this.subscribedChannels = /* @__PURE__ */ new Set();
|
|
2384
|
-
this.eventListeners = /* @__PURE__ */ new Map();
|
|
2400
|
+
constructor(baseUrl, tokenManager, anonKey, getValidAccessToken = async () => tokenManager.getAccessToken()) {
|
|
2385
2401
|
this.baseUrl = baseUrl;
|
|
2386
2402
|
this.tokenManager = tokenManager;
|
|
2387
2403
|
this.anonKey = anonKey;
|
|
2388
|
-
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
|
+
});
|
|
2389
2416
|
}
|
|
2390
2417
|
notifyListeners(event, payload) {
|
|
2391
|
-
const
|
|
2392
|
-
if (!listeners) return;
|
|
2393
|
-
for (const cb of listeners) {
|
|
2418
|
+
for (const callback of this.eventListeners.get(event) ?? []) {
|
|
2394
2419
|
try {
|
|
2395
|
-
|
|
2396
|
-
} catch (
|
|
2397
|
-
console.error(`Error in ${event} callback:`,
|
|
2420
|
+
callback(payload);
|
|
2421
|
+
} catch (error) {
|
|
2422
|
+
console.error(`Error in ${event} callback:`, error);
|
|
2398
2423
|
}
|
|
2399
2424
|
}
|
|
2400
2425
|
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
*/
|
|
2426
|
+
async getHandshakeToken() {
|
|
2427
|
+
return await this.getValidAccessToken() ?? this.anonKey ?? null;
|
|
2428
|
+
}
|
|
2405
2429
|
connect() {
|
|
2406
2430
|
if (this.socket?.connected) {
|
|
2407
2431
|
return Promise.resolve();
|
|
@@ -2409,210 +2433,324 @@ var Realtime = class {
|
|
|
2409
2433
|
if (this.connectPromise) {
|
|
2410
2434
|
return this.connectPromise;
|
|
2411
2435
|
}
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
}
|
|
2428
|
-
};
|
|
2429
|
-
timeoutId = setTimeout(() => {
|
|
2430
|
-
if (initialConnection) {
|
|
2431
|
-
initialConnection = false;
|
|
2432
|
-
this.connectPromise = null;
|
|
2433
|
-
this.socket?.disconnect();
|
|
2434
|
-
this.socket = null;
|
|
2435
|
-
reject(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`));
|
|
2436
|
-
}
|
|
2437
|
-
}, CONNECT_TIMEOUT);
|
|
2438
|
-
this.socket.on("connect", () => {
|
|
2439
|
-
cleanup();
|
|
2440
|
-
for (const channel of this.subscribedChannels) {
|
|
2441
|
-
this.socket.emit("realtime:subscribe", { channel });
|
|
2442
|
-
}
|
|
2443
|
-
this.notifyListeners("connect");
|
|
2444
|
-
if (initialConnection) {
|
|
2445
|
-
initialConnection = false;
|
|
2446
|
-
this.connectPromise = null;
|
|
2447
|
-
resolve();
|
|
2448
|
-
}
|
|
2449
|
-
});
|
|
2450
|
-
this.socket.on("connect_error", (error) => {
|
|
2451
|
-
cleanup();
|
|
2452
|
-
this.notifyListeners("connect_error", error);
|
|
2453
|
-
if (initialConnection) {
|
|
2454
|
-
initialConnection = false;
|
|
2455
|
-
this.connectPromise = null;
|
|
2456
|
-
reject(error);
|
|
2457
|
-
}
|
|
2458
|
-
});
|
|
2459
|
-
this.socket.on("disconnect", (reason) => {
|
|
2460
|
-
this.notifyListeners("disconnect", reason);
|
|
2461
|
-
});
|
|
2462
|
-
this.socket.on("realtime:error", (error) => {
|
|
2463
|
-
this.notifyListeners("error", error);
|
|
2464
|
-
});
|
|
2465
|
-
this.socket.onAny((event, message) => {
|
|
2466
|
-
if (event === "realtime:error") return;
|
|
2467
|
-
this.notifyListeners(event, message);
|
|
2468
|
-
});
|
|
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
|
+
}
|
|
2469
2451
|
});
|
|
2470
|
-
|
|
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) {
|
|
2471
2533
|
this.connectPromise = null;
|
|
2472
|
-
throw error;
|
|
2473
2534
|
}
|
|
2474
|
-
})
|
|
2475
|
-
|
|
2535
|
+
});
|
|
2536
|
+
this.connectPromise = trackedConnection;
|
|
2537
|
+
return trackedConnection;
|
|
2476
2538
|
}
|
|
2477
|
-
/**
|
|
2478
|
-
* Disconnect from the realtime server
|
|
2479
|
-
*/
|
|
2480
2539
|
disconnect() {
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
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
|
+
);
|
|
2484
2555
|
}
|
|
2485
|
-
this.
|
|
2556
|
+
this.subscriptions.clear();
|
|
2486
2557
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
onTokenChange() {
|
|
2493
|
-
const token = this.tokenManager.getAccessToken() ?? this.anonKey;
|
|
2494
|
-
if (this.socket) {
|
|
2495
|
-
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
|
+
}
|
|
2496
2563
|
}
|
|
2497
|
-
if (this.socket
|
|
2498
|
-
|
|
2499
|
-
|
|
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);
|
|
2500
2677
|
}
|
|
2501
2678
|
}
|
|
2502
|
-
/**
|
|
2503
|
-
* Check if connected to the realtime server
|
|
2504
|
-
*/
|
|
2505
2679
|
get isConnected() {
|
|
2506
2680
|
return this.socket?.connected ?? false;
|
|
2507
2681
|
}
|
|
2508
|
-
/**
|
|
2509
|
-
* Get the current connection state
|
|
2510
|
-
*/
|
|
2511
2682
|
get connectionState() {
|
|
2512
|
-
if (!this.socket)
|
|
2513
|
-
|
|
2514
|
-
|
|
2683
|
+
if (!this.socket) {
|
|
2684
|
+
return "disconnected";
|
|
2685
|
+
}
|
|
2686
|
+
return this.socket.connected ? "connected" : "connecting";
|
|
2515
2687
|
}
|
|
2516
|
-
/**
|
|
2517
|
-
* Get the socket ID (if connected)
|
|
2518
|
-
*/
|
|
2519
2688
|
get socketId() {
|
|
2520
2689
|
return this.socket?.id;
|
|
2521
2690
|
}
|
|
2522
|
-
/**
|
|
2523
|
-
* Subscribe to a channel
|
|
2524
|
-
*
|
|
2525
|
-
* Automatically connects if not already connected.
|
|
2526
|
-
*
|
|
2527
|
-
* @param channel - Channel name (e.g., 'orders:123', 'broadcast')
|
|
2528
|
-
* @returns Promise with the subscription response
|
|
2529
|
-
*/
|
|
2530
2691
|
async subscribe(channel) {
|
|
2531
|
-
|
|
2532
|
-
|
|
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);
|
|
2533
2703
|
}
|
|
2534
2704
|
if (!this.socket?.connected) {
|
|
2535
2705
|
try {
|
|
2536
2706
|
await this.connect();
|
|
2537
2707
|
} catch (error) {
|
|
2708
|
+
if (this.subscriptions.get(channel) === subscription) {
|
|
2709
|
+
this.subscriptions.delete(channel);
|
|
2710
|
+
}
|
|
2538
2711
|
const message = error instanceof Error ? error.message : "Connection failed";
|
|
2539
2712
|
return { ok: false, channel, error: { code: "CONNECTION_FAILED", message } };
|
|
2540
2713
|
}
|
|
2541
2714
|
}
|
|
2542
|
-
return
|
|
2543
|
-
this.socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
2544
|
-
if (response.ok) {
|
|
2545
|
-
this.subscribedChannels.add(channel);
|
|
2546
|
-
}
|
|
2547
|
-
resolve(response);
|
|
2548
|
-
});
|
|
2549
|
-
});
|
|
2715
|
+
return subscription.pending ?? this.requestSubscription(channel, subscription);
|
|
2550
2716
|
}
|
|
2551
|
-
/**
|
|
2552
|
-
* Unsubscribe from a channel (fire-and-forget)
|
|
2553
|
-
*
|
|
2554
|
-
* @param channel - Channel name to unsubscribe from
|
|
2555
|
-
*/
|
|
2556
2717
|
unsubscribe(channel) {
|
|
2557
|
-
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
|
+
);
|
|
2558
2732
|
if (this.socket?.connected) {
|
|
2559
2733
|
this.socket.emit("realtime:unsubscribe", { channel });
|
|
2560
2734
|
}
|
|
2561
2735
|
}
|
|
2562
|
-
/**
|
|
2563
|
-
* Publish a message to a channel
|
|
2564
|
-
*
|
|
2565
|
-
* @param channel - Channel name
|
|
2566
|
-
* @param event - Event name
|
|
2567
|
-
* @param payload - Message payload
|
|
2568
|
-
*/
|
|
2569
2736
|
async publish(channel, event, payload) {
|
|
2570
2737
|
if (!this.socket?.connected) {
|
|
2571
2738
|
throw new Error("Not connected to realtime server. Call connect() first.");
|
|
2572
2739
|
}
|
|
2573
2740
|
this.socket.emit("realtime:publish", { channel, event, payload });
|
|
2574
2741
|
}
|
|
2575
|
-
/**
|
|
2576
|
-
* Listen for events
|
|
2577
|
-
*
|
|
2578
|
-
* Reserved event names:
|
|
2579
|
-
* - 'connect' - Fired when connected to the server
|
|
2580
|
-
* - 'connect_error' - Fired when connection fails (payload: Error)
|
|
2581
|
-
* - 'disconnect' - Fired when disconnected (payload: reason string)
|
|
2582
|
-
* - 'error' - Fired when a realtime error occurs (payload: RealtimeErrorPayload)
|
|
2583
|
-
*
|
|
2584
|
-
* All other events receive a `SocketMessage` payload with metadata.
|
|
2585
|
-
*
|
|
2586
|
-
* @param event - Event name to listen for
|
|
2587
|
-
* @param callback - Callback function when event is received
|
|
2588
|
-
*/
|
|
2589
2742
|
on(event, callback) {
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
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);
|
|
2594
2746
|
}
|
|
2595
|
-
/**
|
|
2596
|
-
* Remove a listener for a specific event
|
|
2597
|
-
*
|
|
2598
|
-
* @param event - Event name
|
|
2599
|
-
* @param callback - The callback function to remove
|
|
2600
|
-
*/
|
|
2601
2747
|
off(event, callback) {
|
|
2602
2748
|
const listeners = this.eventListeners.get(event);
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
this.eventListeners.delete(event);
|
|
2607
|
-
}
|
|
2749
|
+
listeners?.delete(callback);
|
|
2750
|
+
if (listeners?.size === 0) {
|
|
2751
|
+
this.eventListeners.delete(event);
|
|
2608
2752
|
}
|
|
2609
2753
|
}
|
|
2610
|
-
/**
|
|
2611
|
-
* Listen for an event only once, then automatically remove the listener
|
|
2612
|
-
*
|
|
2613
|
-
* @param event - Event name to listen for
|
|
2614
|
-
* @param callback - Callback function when event is received
|
|
2615
|
-
*/
|
|
2616
2754
|
once(event, callback) {
|
|
2617
2755
|
const wrapper = (payload) => {
|
|
2618
2756
|
this.off(event, wrapper);
|
|
@@ -2620,13 +2758,11 @@ var Realtime = class {
|
|
|
2620
2758
|
};
|
|
2621
2759
|
this.on(event, wrapper);
|
|
2622
2760
|
}
|
|
2623
|
-
/**
|
|
2624
|
-
* Get all currently subscribed channels
|
|
2625
|
-
*
|
|
2626
|
-
* @returns Array of channel names
|
|
2627
|
-
*/
|
|
2628
2761
|
getSubscribedChannels() {
|
|
2629
|
-
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() ?? []];
|
|
2630
2766
|
}
|
|
2631
2767
|
};
|
|
2632
2768
|
|
|
@@ -2641,13 +2777,12 @@ var Emails = class {
|
|
|
2641
2777
|
*/
|
|
2642
2778
|
async send(options) {
|
|
2643
2779
|
try {
|
|
2644
|
-
const data = await this.http.post(
|
|
2645
|
-
"/api/email/send-raw",
|
|
2646
|
-
options
|
|
2647
|
-
);
|
|
2780
|
+
const data = await this.http.post("/api/email/send-raw", options);
|
|
2648
2781
|
return { data, error: null };
|
|
2649
2782
|
} catch (error) {
|
|
2650
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2783
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2784
|
+
throw error;
|
|
2785
|
+
}
|
|
2651
2786
|
return {
|
|
2652
2787
|
data: null,
|
|
2653
2788
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2730,10 +2865,7 @@ var RazorpayPayments = class {
|
|
|
2730
2865
|
);
|
|
2731
2866
|
return { data, error: null };
|
|
2732
2867
|
} catch (error) {
|
|
2733
|
-
return wrapError(
|
|
2734
|
-
error,
|
|
2735
|
-
"Razorpay order creation failed"
|
|
2736
|
-
);
|
|
2868
|
+
return wrapError(error, "Razorpay order creation failed");
|
|
2737
2869
|
}
|
|
2738
2870
|
}
|
|
2739
2871
|
async verifyOrder(environment, request) {
|
|
@@ -2744,10 +2876,7 @@ var RazorpayPayments = class {
|
|
|
2744
2876
|
);
|
|
2745
2877
|
return { data, error: null };
|
|
2746
2878
|
} catch (error) {
|
|
2747
|
-
return wrapError(
|
|
2748
|
-
error,
|
|
2749
|
-
"Razorpay order verification failed"
|
|
2750
|
-
);
|
|
2879
|
+
return wrapError(error, "Razorpay order verification failed");
|
|
2751
2880
|
}
|
|
2752
2881
|
}
|
|
2753
2882
|
async createSubscription(environment, request) {
|
|
@@ -2856,7 +2985,8 @@ var InsForgeClient = class {
|
|
|
2856
2985
|
this.realtime = new Realtime(
|
|
2857
2986
|
this.http.baseUrl,
|
|
2858
2987
|
this.tokenManager,
|
|
2859
|
-
config.anonKey
|
|
2988
|
+
config.anonKey,
|
|
2989
|
+
() => this.http.getValidAccessToken()
|
|
2860
2990
|
);
|
|
2861
2991
|
this.emails = new Emails(this.http);
|
|
2862
2992
|
this.payments = new Payments(this.http);
|
|
@@ -2876,32 +3006,35 @@ var InsForgeClient = class {
|
|
|
2876
3006
|
/**
|
|
2877
3007
|
* Set the access token used by every SDK surface. Updates both the HTTP
|
|
2878
3008
|
* client (database / storage / functions / AI / emails) and the realtime
|
|
2879
|
-
* token manager
|
|
2880
|
-
*
|
|
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.
|
|
2881
3013
|
*
|
|
2882
3014
|
* Use this when an external auth provider (Better Auth, Clerk, Auth0,
|
|
2883
3015
|
* WorkOS, Kinde, Stytch, …) issues the JWT and you need to keep the
|
|
2884
3016
|
* long-lived InsForge client in sync. Without this, you'd have to call
|
|
2885
3017
|
* `client.getHttpClient().setAuthToken(token)` AND reach into the private
|
|
2886
|
-
*
|
|
2887
|
-
* forgetting the second one silently breaks realtime auth.
|
|
3018
|
+
* realtime token manager separately.
|
|
2888
3019
|
*
|
|
2889
3020
|
* @example
|
|
2890
3021
|
* ```typescript
|
|
3022
|
+
* import { AuthChangeEvent } from '@insforge/sdk';
|
|
3023
|
+
*
|
|
2891
3024
|
* // Refresh a third-party-issued JWT periodically
|
|
2892
3025
|
* const { token } = await fetch('/api/insforge-token').then((r) => r.json());
|
|
2893
|
-
* client.setAccessToken(token);
|
|
3026
|
+
* client.setAccessToken(token, AuthChangeEvent.TOKEN_REFRESHED);
|
|
2894
3027
|
*
|
|
2895
3028
|
* // Sign-out
|
|
2896
3029
|
* client.setAccessToken(null);
|
|
2897
3030
|
* ```
|
|
2898
3031
|
*/
|
|
2899
|
-
setAccessToken(token) {
|
|
3032
|
+
setAccessToken(token, event = AuthChangeEvent.SIGNED_IN) {
|
|
2900
3033
|
this.http.setAuthToken(token);
|
|
2901
3034
|
if (token === null) {
|
|
2902
3035
|
this.tokenManager.clearSession();
|
|
2903
3036
|
} else {
|
|
2904
|
-
this.tokenManager.setAccessToken(token);
|
|
3037
|
+
this.tokenManager.setAccessToken(token, event);
|
|
2905
3038
|
}
|
|
2906
3039
|
}
|
|
2907
3040
|
/**
|
|
@@ -2914,38 +3047,6 @@ var InsForgeClient = class {
|
|
|
2914
3047
|
*/
|
|
2915
3048
|
};
|
|
2916
3049
|
|
|
2917
|
-
// src/lib/jwt.ts
|
|
2918
|
-
function decodeBase64Url(input) {
|
|
2919
|
-
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
2920
|
-
const padded = normalized.padEnd(
|
|
2921
|
-
normalized.length + (4 - normalized.length % 4) % 4,
|
|
2922
|
-
"="
|
|
2923
|
-
);
|
|
2924
|
-
const binary = atob(padded);
|
|
2925
|
-
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
2926
|
-
return new TextDecoder().decode(bytes);
|
|
2927
|
-
}
|
|
2928
|
-
function getJwtExpiration(token) {
|
|
2929
|
-
if (!token) return null;
|
|
2930
|
-
const [, payload] = token.split(".");
|
|
2931
|
-
if (!payload) return null;
|
|
2932
|
-
try {
|
|
2933
|
-
const parsed = JSON.parse(decodeBase64Url(payload));
|
|
2934
|
-
if (typeof parsed.exp !== "number" || !Number.isFinite(parsed.exp)) {
|
|
2935
|
-
return null;
|
|
2936
|
-
}
|
|
2937
|
-
return new Date(parsed.exp * 1e3);
|
|
2938
|
-
} catch {
|
|
2939
|
-
return null;
|
|
2940
|
-
}
|
|
2941
|
-
}
|
|
2942
|
-
function isJwtExpiredOrExpiring(token, leewaySeconds = 60) {
|
|
2943
|
-
if (!token) return false;
|
|
2944
|
-
const expires = getJwtExpiration(token);
|
|
2945
|
-
if (!expires) return true;
|
|
2946
|
-
return expires.getTime() <= Date.now() + leewaySeconds * 1e3;
|
|
2947
|
-
}
|
|
2948
|
-
|
|
2949
3050
|
// src/ssr/browser-client.ts
|
|
2950
3051
|
var import_shared_schemas2 = require("@insforge/shared-schemas");
|
|
2951
3052
|
|
|
@@ -2960,18 +3061,28 @@ function getRefreshTokenCookieName(names) {
|
|
|
2960
3061
|
return names?.refreshToken ?? DEFAULT_REFRESH_TOKEN_COOKIE;
|
|
2961
3062
|
}
|
|
2962
3063
|
function getCookieValue(cookies, name) {
|
|
2963
|
-
if (!cookies)
|
|
3064
|
+
if (!cookies) {
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
2964
3067
|
const value = cookies.get(name);
|
|
2965
|
-
if (typeof value === "string")
|
|
2966
|
-
|
|
3068
|
+
if (typeof value === "string") {
|
|
3069
|
+
return value || null;
|
|
3070
|
+
}
|
|
3071
|
+
if (value && typeof value.value === "string") {
|
|
3072
|
+
return value.value || null;
|
|
3073
|
+
}
|
|
2967
3074
|
return null;
|
|
2968
3075
|
}
|
|
2969
3076
|
function getCookieValueFromHeader(cookieHeader, name) {
|
|
2970
|
-
if (!cookieHeader)
|
|
3077
|
+
if (!cookieHeader) {
|
|
3078
|
+
return null;
|
|
3079
|
+
}
|
|
2971
3080
|
const parts = cookieHeader.split(";");
|
|
2972
3081
|
for (const part of parts) {
|
|
2973
3082
|
const [rawName, ...rawValue] = part.trim().split("=");
|
|
2974
|
-
if (rawName !== name)
|
|
3083
|
+
if (rawName !== name) {
|
|
3084
|
+
continue;
|
|
3085
|
+
}
|
|
2975
3086
|
try {
|
|
2976
3087
|
return decodeURIComponent(rawValue.join("="));
|
|
2977
3088
|
} catch {
|
|
@@ -2981,7 +3092,9 @@ function getCookieValueFromHeader(cookieHeader, name) {
|
|
|
2981
3092
|
return null;
|
|
2982
3093
|
}
|
|
2983
3094
|
function getBrowserCookie(name) {
|
|
2984
|
-
if (typeof document === "undefined")
|
|
3095
|
+
if (typeof document === "undefined") {
|
|
3096
|
+
return null;
|
|
3097
|
+
}
|
|
2985
3098
|
return getCookieValueFromHeader(document.cookie, name);
|
|
2986
3099
|
}
|
|
2987
3100
|
function defaultCookieOptions() {
|
|
@@ -3018,11 +3131,15 @@ function expiredCookieOptions(overrides) {
|
|
|
3018
3131
|
};
|
|
3019
3132
|
}
|
|
3020
3133
|
function setCookie(cookies, name, value, options) {
|
|
3021
|
-
if (!cookies?.set)
|
|
3134
|
+
if (!cookies?.set) {
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3022
3137
|
cookies.set(name, value, options);
|
|
3023
3138
|
}
|
|
3024
3139
|
function deleteCookie(cookies, name, options) {
|
|
3025
|
-
if (!cookies)
|
|
3140
|
+
if (!cookies) {
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3026
3143
|
if (cookies.set) {
|
|
3027
3144
|
cookies.set(name, "", expiredCookieOptions(options));
|
|
3028
3145
|
return;
|
|
@@ -3031,12 +3148,24 @@ function deleteCookie(cookies, name, options) {
|
|
|
3031
3148
|
}
|
|
3032
3149
|
function serializeCookie(name, value, options = {}) {
|
|
3033
3150
|
const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
|
|
3034
|
-
if (options.maxAge !== void 0)
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
if (options.
|
|
3038
|
-
|
|
3039
|
-
|
|
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
|
+
}
|
|
3040
3169
|
if (options.sameSite) {
|
|
3041
3170
|
const sameSite = options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1);
|
|
3042
3171
|
parts.push(`SameSite=${sameSite}`);
|
|
@@ -3049,20 +3178,14 @@ function appendSetCookie(headers, name, value, options) {
|
|
|
3049
3178
|
function setAuthCookies(cookies, tokens, settings = {}) {
|
|
3050
3179
|
const accessName = getAccessTokenCookieName(settings.names);
|
|
3051
3180
|
const refreshName = getRefreshTokenCookieName(settings.names);
|
|
3052
|
-
const accessOptions = accessTokenCookieOptions(
|
|
3053
|
-
tokens.accessToken,
|
|
3054
|
-
settings.options?.accessToken
|
|
3055
|
-
);
|
|
3181
|
+
const accessOptions = accessTokenCookieOptions(tokens.accessToken, settings.options?.accessToken);
|
|
3056
3182
|
setCookie(cookies, accessName, tokens.accessToken, accessOptions);
|
|
3057
3183
|
if (tokens.refreshToken) {
|
|
3058
3184
|
setCookie(
|
|
3059
3185
|
cookies,
|
|
3060
3186
|
refreshName,
|
|
3061
3187
|
tokens.refreshToken,
|
|
3062
|
-
refreshTokenCookieOptions(
|
|
3063
|
-
tokens.refreshToken,
|
|
3064
|
-
settings.options?.refreshToken
|
|
3065
|
-
)
|
|
3188
|
+
refreshTokenCookieOptions(tokens.refreshToken, settings.options?.refreshToken)
|
|
3066
3189
|
);
|
|
3067
3190
|
}
|
|
3068
3191
|
}
|
|
@@ -3088,10 +3211,7 @@ function setAuthCookieHeaders(headers, tokens, settings = {}) {
|
|
|
3088
3211
|
headers,
|
|
3089
3212
|
refreshName,
|
|
3090
3213
|
tokens.refreshToken,
|
|
3091
|
-
refreshTokenCookieOptions(
|
|
3092
|
-
tokens.refreshToken,
|
|
3093
|
-
settings.options?.refreshToken
|
|
3094
|
-
)
|
|
3214
|
+
refreshTokenCookieOptions(tokens.refreshToken, settings.options?.refreshToken)
|
|
3095
3215
|
);
|
|
3096
3216
|
}
|
|
3097
3217
|
}
|
|
@@ -3134,10 +3254,14 @@ function toRefreshError(response, body) {
|
|
|
3134
3254
|
);
|
|
3135
3255
|
}
|
|
3136
3256
|
async function readErrorCode(response) {
|
|
3137
|
-
if (response.status !== 401)
|
|
3257
|
+
if (response.status !== 401) {
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3138
3260
|
try {
|
|
3139
3261
|
const body = await response.clone().json();
|
|
3140
|
-
if (!body || typeof body !== "object")
|
|
3262
|
+
if (!body || typeof body !== "object") {
|
|
3263
|
+
return null;
|
|
3264
|
+
}
|
|
3141
3265
|
const candidate = body.error ?? body.code;
|
|
3142
3266
|
return typeof candidate === "string" ? candidate : null;
|
|
3143
3267
|
} catch {
|
|
@@ -3156,7 +3280,9 @@ function withAuthHeader(init, token) {
|
|
|
3156
3280
|
};
|
|
3157
3281
|
}
|
|
3158
3282
|
function getRequestUrl(input) {
|
|
3159
|
-
if (typeof input === "string")
|
|
3283
|
+
if (typeof input === "string") {
|
|
3284
|
+
return input;
|
|
3285
|
+
}
|
|
3160
3286
|
if (typeof Request !== "undefined" && input instanceof Request) {
|
|
3161
3287
|
return input.url;
|
|
3162
3288
|
}
|
|
@@ -3174,21 +3300,19 @@ function createBrowserClient(options = {}) {
|
|
|
3174
3300
|
"Missing InsForge baseUrl or anonKey. Pass baseUrl and anonKey to createBrowserClient() or set NEXT_PUBLIC_INSFORGE_URL and NEXT_PUBLIC_INSFORGE_ANON_KEY."
|
|
3175
3301
|
);
|
|
3176
3302
|
}
|
|
3177
|
-
let accessToken = getBrowserCookie(
|
|
3178
|
-
getAccessTokenCookieName(options.names)
|
|
3179
|
-
);
|
|
3303
|
+
let accessToken = getBrowserCookie(getAccessTokenCookieName(options.names));
|
|
3180
3304
|
const refreshUrl = options.refreshUrl ?? "/api/auth/refresh";
|
|
3181
3305
|
const fetchImpl = options.fetch ?? (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
3182
3306
|
let client;
|
|
3183
3307
|
let sessionChecked = false;
|
|
3184
3308
|
let refreshPromise = null;
|
|
3185
3309
|
const refreshFromRoute = () => {
|
|
3186
|
-
if (refreshPromise)
|
|
3310
|
+
if (refreshPromise) {
|
|
3311
|
+
return refreshPromise;
|
|
3312
|
+
}
|
|
3187
3313
|
refreshPromise = (async () => {
|
|
3188
3314
|
if (!fetchImpl) {
|
|
3189
|
-
throw new Error(
|
|
3190
|
-
"Fetch is not available. Please provide a fetch implementation."
|
|
3191
|
-
);
|
|
3315
|
+
throw new Error("Fetch is not available. Please provide a fetch implementation.");
|
|
3192
3316
|
}
|
|
3193
3317
|
const response = await fetchImpl(refreshUrl, {
|
|
3194
3318
|
method: "POST",
|
|
@@ -3205,11 +3329,15 @@ function createBrowserClient(options = {}) {
|
|
|
3205
3329
|
}
|
|
3206
3330
|
throw error;
|
|
3207
3331
|
}
|
|
3208
|
-
if (!body || typeof body !== "object")
|
|
3332
|
+
if (!body || typeof body !== "object") {
|
|
3333
|
+
return null;
|
|
3334
|
+
}
|
|
3209
3335
|
const refreshBody = body;
|
|
3210
|
-
if (!refreshBody.accessToken || !refreshBody.user)
|
|
3336
|
+
if (!refreshBody.accessToken || !refreshBody.user) {
|
|
3337
|
+
return null;
|
|
3338
|
+
}
|
|
3211
3339
|
accessToken = refreshBody.accessToken;
|
|
3212
|
-
client?.setAccessToken(refreshBody.accessToken);
|
|
3340
|
+
client?.setAccessToken(refreshBody.accessToken, AuthChangeEvent.TOKEN_REFRESHED);
|
|
3213
3341
|
return refreshBody;
|
|
3214
3342
|
})().finally(() => {
|
|
3215
3343
|
sessionChecked = true;
|
|
@@ -3223,9 +3351,7 @@ function createBrowserClient(options = {}) {
|
|
|
3223
3351
|
};
|
|
3224
3352
|
const ssrFetch = async (input, init) => {
|
|
3225
3353
|
if (!fetchImpl) {
|
|
3226
|
-
throw new Error(
|
|
3227
|
-
"Fetch is not available. Please provide a fetch implementation."
|
|
3228
|
-
);
|
|
3354
|
+
throw new Error("Fetch is not available. Please provide a fetch implementation.");
|
|
3229
3355
|
}
|
|
3230
3356
|
if (shouldSkipRefresh(input)) {
|
|
3231
3357
|
return fetchImpl(input, init);
|
|
@@ -3262,9 +3388,9 @@ function createBrowserClient(options = {}) {
|
|
|
3262
3388
|
}
|
|
3263
3389
|
});
|
|
3264
3390
|
const setAccessToken = client.setAccessToken.bind(client);
|
|
3265
|
-
client.setAccessToken = (token) => {
|
|
3391
|
+
client.setAccessToken = (token, event) => {
|
|
3266
3392
|
accessToken = token;
|
|
3267
|
-
setAccessToken(token);
|
|
3393
|
+
setAccessToken(token, event);
|
|
3268
3394
|
};
|
|
3269
3395
|
if (accessToken) {
|
|
3270
3396
|
client.setAccessToken(accessToken);
|
|
@@ -3288,10 +3414,7 @@ function createServerClient(options = {}) {
|
|
|
3288
3414
|
"Missing InsForge baseUrl or anonKey. Pass baseUrl and anonKey to createServerClient() or set NEXT_PUBLIC_INSFORGE_URL and NEXT_PUBLIC_INSFORGE_ANON_KEY."
|
|
3289
3415
|
);
|
|
3290
3416
|
}
|
|
3291
|
-
const accessToken = options.accessToken ?? getCookieValue(
|
|
3292
|
-
options.cookies,
|
|
3293
|
-
getAccessTokenCookieName(options.names)
|
|
3294
|
-
);
|
|
3417
|
+
const accessToken = options.accessToken ?? getCookieValue(options.cookies, getAccessTokenCookieName(options.names));
|
|
3295
3418
|
return new InsForgeClient({
|
|
3296
3419
|
...options,
|
|
3297
3420
|
baseUrl,
|
|
@@ -3314,7 +3437,9 @@ function jsonResponse(body, init = {}, headers = new Headers(init.headers)) {
|
|
|
3314
3437
|
});
|
|
3315
3438
|
}
|
|
3316
3439
|
function normalizeError(error) {
|
|
3317
|
-
if (error instanceof InsForgeError)
|
|
3440
|
+
if (error instanceof InsForgeError) {
|
|
3441
|
+
return error;
|
|
3442
|
+
}
|
|
3318
3443
|
if (error && typeof error === "object") {
|
|
3319
3444
|
const body = error;
|
|
3320
3445
|
return new InsForgeError(
|
|
@@ -3331,18 +3456,21 @@ function normalizeError(error) {
|
|
|
3331
3456
|
}
|
|
3332
3457
|
async function readJson(response) {
|
|
3333
3458
|
const contentType = response.headers.get("content-type");
|
|
3334
|
-
if (!contentType?.includes("json"))
|
|
3459
|
+
if (!contentType?.includes("json")) {
|
|
3460
|
+
return null;
|
|
3461
|
+
}
|
|
3335
3462
|
return response.json();
|
|
3336
3463
|
}
|
|
3337
3464
|
function readRefreshToken(options) {
|
|
3338
|
-
if (options.refreshToken)
|
|
3465
|
+
if (options.refreshToken) {
|
|
3466
|
+
return options.refreshToken;
|
|
3467
|
+
}
|
|
3339
3468
|
const refreshCookieName = getRefreshTokenCookieName(options.names);
|
|
3340
3469
|
const cookieValue = getCookieValue(options.cookies, refreshCookieName);
|
|
3341
|
-
if (cookieValue)
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
);
|
|
3470
|
+
if (cookieValue) {
|
|
3471
|
+
return cookieValue;
|
|
3472
|
+
}
|
|
3473
|
+
return getCookieValueFromHeader(options.request?.headers.get("cookie"), refreshCookieName);
|
|
3346
3474
|
}
|
|
3347
3475
|
async function refreshAuth(options = {}) {
|
|
3348
3476
|
const headers = new Headers();
|
|
@@ -3383,9 +3511,7 @@ async function refreshAuth(options = {}) {
|
|
|
3383
3511
|
}
|
|
3384
3512
|
const fetchImpl = options.fetch ?? (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
3385
3513
|
if (!fetchImpl) {
|
|
3386
|
-
throw new Error(
|
|
3387
|
-
"Fetch is not available. Please provide a fetch implementation."
|
|
3388
|
-
);
|
|
3514
|
+
throw new Error("Fetch is not available. Please provide a fetch implementation.");
|
|
3389
3515
|
}
|
|
3390
3516
|
const requestHeaders = new Headers(options.headers);
|
|
3391
3517
|
requestHeaders.set("Authorization", `Bearer ${anonKey}`);
|
|
@@ -3466,7 +3592,9 @@ function createRefreshAuthRouter(options = {}) {
|
|
|
3466
3592
|
|
|
3467
3593
|
// src/ssr/auth-actions.ts
|
|
3468
3594
|
function persistSessionCookies(cookies, data, settings) {
|
|
3469
|
-
if (!data?.accessToken)
|
|
3595
|
+
if (!data?.accessToken) {
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
3470
3598
|
setAuthCookies(
|
|
3471
3599
|
cookies,
|
|
3472
3600
|
{
|
|
@@ -3477,7 +3605,9 @@ function persistSessionCookies(cookies, data, settings) {
|
|
|
3477
3605
|
);
|
|
3478
3606
|
}
|
|
3479
3607
|
function sanitizeAuthData(data) {
|
|
3480
|
-
if (!data)
|
|
3608
|
+
if (!data) {
|
|
3609
|
+
return null;
|
|
3610
|
+
}
|
|
3481
3611
|
const {
|
|
3482
3612
|
accessToken: _accessToken,
|
|
3483
3613
|
refreshToken: _refreshToken,
|
|
@@ -3527,32 +3657,20 @@ function createAuthActions(options = {}) {
|
|
|
3527
3657
|
signInWithPassword: async (request) => {
|
|
3528
3658
|
const result = await createClient().auth.signInWithPassword(request);
|
|
3529
3659
|
persistSessionCookies(writeCookies, result.data, cookieSettings);
|
|
3530
|
-
return toSafeAuthResult(
|
|
3531
|
-
result
|
|
3532
|
-
);
|
|
3660
|
+
return toSafeAuthResult(result);
|
|
3533
3661
|
},
|
|
3534
3662
|
signInWithOAuth: async (providerOrOptions, signInOptions) => {
|
|
3535
|
-
return createClient().auth.signInWithOAuth(
|
|
3536
|
-
providerOrOptions,
|
|
3537
|
-
signInOptions
|
|
3538
|
-
);
|
|
3663
|
+
return createClient().auth.signInWithOAuth(providerOrOptions, signInOptions);
|
|
3539
3664
|
},
|
|
3540
3665
|
signInWithIdToken: async (credentials) => {
|
|
3541
3666
|
const result = await createClient().auth.signInWithIdToken(credentials);
|
|
3542
3667
|
persistSessionCookies(writeCookies, result.data, cookieSettings);
|
|
3543
|
-
return toSafeAuthResult(
|
|
3544
|
-
result
|
|
3545
|
-
);
|
|
3668
|
+
return toSafeAuthResult(result);
|
|
3546
3669
|
},
|
|
3547
3670
|
exchangeOAuthCode: async (code, codeVerifier) => {
|
|
3548
|
-
const result = await createClient().auth.exchangeOAuthCode(
|
|
3549
|
-
code,
|
|
3550
|
-
codeVerifier
|
|
3551
|
-
);
|
|
3671
|
+
const result = await createClient().auth.exchangeOAuthCode(code, codeVerifier);
|
|
3552
3672
|
persistSessionCookies(writeCookies, result.data, cookieSettings);
|
|
3553
|
-
return toSafeAuthResult(
|
|
3554
|
-
result
|
|
3555
|
-
);
|
|
3673
|
+
return toSafeAuthResult(result);
|
|
3556
3674
|
},
|
|
3557
3675
|
verifyEmail: async (request) => {
|
|
3558
3676
|
const result = await createClient().auth.verifyEmail(request);
|
|
@@ -3571,10 +3689,7 @@ function createAuthActions(options = {}) {
|
|
|
3571
3689
|
async function updateSession(options) {
|
|
3572
3690
|
const accessCookieName = getAccessTokenCookieName(options.names);
|
|
3573
3691
|
const refreshCookieName = getRefreshTokenCookieName(options.names);
|
|
3574
|
-
const accessToken = getCookieValue(
|
|
3575
|
-
options.requestCookies,
|
|
3576
|
-
accessCookieName
|
|
3577
|
-
);
|
|
3692
|
+
const accessToken = getCookieValue(options.requestCookies, accessCookieName);
|
|
3578
3693
|
if (accessToken && !isJwtExpiredOrExpiring(accessToken, options.refreshLeewaySeconds)) {
|
|
3579
3694
|
return {
|
|
3580
3695
|
refreshed: false,
|
|
@@ -3582,10 +3697,7 @@ async function updateSession(options) {
|
|
|
3582
3697
|
error: null
|
|
3583
3698
|
};
|
|
3584
3699
|
}
|
|
3585
|
-
const refreshToken = getCookieValue(
|
|
3586
|
-
options.requestCookies,
|
|
3587
|
-
refreshCookieName
|
|
3588
|
-
);
|
|
3700
|
+
const refreshToken = getCookieValue(options.requestCookies, refreshCookieName);
|
|
3589
3701
|
if (!refreshToken) {
|
|
3590
3702
|
if (accessToken) {
|
|
3591
3703
|
clearAuthCookies(options.requestCookies, options);
|