@insforge/sdk 1.4.3 → 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 +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/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
AI: () => AI,
|
|
34
34
|
Auth: () => Auth,
|
|
35
|
+
AuthChangeEvent: () => AuthChangeEvent,
|
|
35
36
|
Database: () => Database,
|
|
36
37
|
Emails: () => Emails,
|
|
37
38
|
Functions: () => Functions,
|
|
@@ -43,7 +44,6 @@ __export(src_exports, {
|
|
|
43
44
|
Realtime: () => Realtime,
|
|
44
45
|
Storage: () => Storage,
|
|
45
46
|
StorageBucket: () => StorageBucket,
|
|
46
|
-
TokenManager: () => TokenManager,
|
|
47
47
|
createAdminClient: () => createAdminClient,
|
|
48
48
|
createClient: () => createClient,
|
|
49
49
|
default: () => src_default
|
|
@@ -97,7 +97,9 @@ function redactHeaders(headers) {
|
|
|
97
97
|
return redacted;
|
|
98
98
|
}
|
|
99
99
|
function sanitizeBody(body) {
|
|
100
|
-
if (body === null || body === void 0)
|
|
100
|
+
if (body === null || body === void 0) {
|
|
101
|
+
return body;
|
|
102
|
+
}
|
|
101
103
|
if (typeof body === "string") {
|
|
102
104
|
try {
|
|
103
105
|
const parsed = JSON.parse(body);
|
|
@@ -106,7 +108,9 @@ function sanitizeBody(body) {
|
|
|
106
108
|
return body;
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
|
-
if (Array.isArray(body))
|
|
111
|
+
if (Array.isArray(body)) {
|
|
112
|
+
return body.map(sanitizeBody);
|
|
113
|
+
}
|
|
110
114
|
if (typeof body === "object") {
|
|
111
115
|
const sanitized = {};
|
|
112
116
|
for (const [key, value] of Object.entries(body)) {
|
|
@@ -121,7 +125,9 @@ function sanitizeBody(body) {
|
|
|
121
125
|
return body;
|
|
122
126
|
}
|
|
123
127
|
function formatBody(body) {
|
|
124
|
-
if (body === void 0 || body === null)
|
|
128
|
+
if (body === void 0 || body === null) {
|
|
129
|
+
return "";
|
|
130
|
+
}
|
|
125
131
|
if (typeof body === "string") {
|
|
126
132
|
try {
|
|
127
133
|
return JSON.stringify(JSON.parse(body), null, 2);
|
|
@@ -158,7 +164,9 @@ var Logger = class {
|
|
|
158
164
|
* @param args - Additional arguments to pass to the log function
|
|
159
165
|
*/
|
|
160
166
|
log(message, ...args) {
|
|
161
|
-
if (!this.enabled)
|
|
167
|
+
if (!this.enabled) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
162
170
|
const formatted = `[InsForge Debug] ${message}`;
|
|
163
171
|
if (this.customLog) {
|
|
164
172
|
this.customLog(formatted, ...args);
|
|
@@ -172,7 +180,9 @@ var Logger = class {
|
|
|
172
180
|
* @param args - Additional arguments to pass to the log function
|
|
173
181
|
*/
|
|
174
182
|
warn(message, ...args) {
|
|
175
|
-
if (!this.enabled)
|
|
183
|
+
if (!this.enabled) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
176
186
|
const formatted = `[InsForge Debug] ${message}`;
|
|
177
187
|
if (this.customLog) {
|
|
178
188
|
this.customLog(formatted, ...args);
|
|
@@ -186,7 +196,9 @@ var Logger = class {
|
|
|
186
196
|
* @param args - Additional arguments to pass to the log function
|
|
187
197
|
*/
|
|
188
198
|
error(message, ...args) {
|
|
189
|
-
if (!this.enabled)
|
|
199
|
+
if (!this.enabled) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
190
202
|
const formatted = `[InsForge Debug] ${message}`;
|
|
191
203
|
if (this.customLog) {
|
|
192
204
|
this.customLog(formatted, ...args);
|
|
@@ -203,10 +215,10 @@ var Logger = class {
|
|
|
203
215
|
* @param body - Request body (sensitive fields will be masked)
|
|
204
216
|
*/
|
|
205
217
|
logRequest(method, url, headers, body) {
|
|
206
|
-
if (!this.enabled)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
];
|
|
218
|
+
if (!this.enabled) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const parts = [`\u2192 ${method} ${url}`];
|
|
210
222
|
if (headers && Object.keys(headers).length > 0) {
|
|
211
223
|
parts.push(` Headers: ${JSON.stringify(redactHeaders(headers))}`);
|
|
212
224
|
}
|
|
@@ -227,10 +239,10 @@ var Logger = class {
|
|
|
227
239
|
* @param body - Response body (sensitive fields will be masked, large bodies truncated)
|
|
228
240
|
*/
|
|
229
241
|
logResponse(method, url, status, durationMs, body) {
|
|
230
|
-
if (!this.enabled)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
];
|
|
242
|
+
if (!this.enabled) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const parts = [`\u2190 ${method} ${url} ${status} (${durationMs}ms)`];
|
|
234
246
|
const formattedBody = formatBody(sanitizeBody(body));
|
|
235
247
|
if (formattedBody) {
|
|
236
248
|
const truncated = formattedBody.length > 1e3 ? formattedBody.slice(0, 1e3) + "... [truncated]" : formattedBody;
|
|
@@ -245,21 +257,34 @@ var Logger = class {
|
|
|
245
257
|
};
|
|
246
258
|
|
|
247
259
|
// src/lib/token-manager.ts
|
|
260
|
+
var AuthChangeEvent = {
|
|
261
|
+
SIGNED_IN: "signedIn",
|
|
262
|
+
SIGNED_OUT: "signedOut",
|
|
263
|
+
TOKEN_REFRESHED: "tokenRefreshed"
|
|
264
|
+
};
|
|
248
265
|
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
249
266
|
function getCsrfToken() {
|
|
250
|
-
if (typeof document === "undefined")
|
|
267
|
+
if (typeof document === "undefined") {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
251
270
|
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
252
|
-
if (!match)
|
|
271
|
+
if (!match) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
253
274
|
return match.split("=")[1] || null;
|
|
254
275
|
}
|
|
255
276
|
function setCsrfToken(token) {
|
|
256
|
-
if (typeof document === "undefined")
|
|
277
|
+
if (typeof document === "undefined") {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
257
280
|
const maxAge = 7 * 24 * 60 * 60;
|
|
258
281
|
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
259
282
|
document.cookie = `${CSRF_TOKEN_COOKIE}=${encodeURIComponent(token)}; path=/; max-age=${maxAge}; SameSite=Lax${secure}`;
|
|
260
283
|
}
|
|
261
284
|
function clearCsrfToken() {
|
|
262
|
-
if (typeof document === "undefined")
|
|
285
|
+
if (typeof document === "undefined") {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
263
288
|
const secure = typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "";
|
|
264
289
|
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax${secure}`;
|
|
265
290
|
}
|
|
@@ -268,25 +293,26 @@ var TokenManager = class {
|
|
|
268
293
|
// In-memory storage
|
|
269
294
|
this.accessToken = null;
|
|
270
295
|
this.user = null;
|
|
271
|
-
|
|
272
|
-
this.onTokenChange = null;
|
|
296
|
+
this.authStateChangeCallbacks = /* @__PURE__ */ new Map();
|
|
273
297
|
}
|
|
274
298
|
/**
|
|
275
299
|
* Save session in memory
|
|
276
300
|
*/
|
|
277
|
-
saveSession(session) {
|
|
301
|
+
saveSession(session, event = AuthChangeEvent.SIGNED_IN) {
|
|
278
302
|
const tokenChanged = session.accessToken !== this.accessToken;
|
|
279
303
|
this.accessToken = session.accessToken;
|
|
280
304
|
this.user = session.user;
|
|
281
|
-
if (tokenChanged
|
|
282
|
-
this.
|
|
305
|
+
if (tokenChanged) {
|
|
306
|
+
this.notifyAuthStateChange(event);
|
|
283
307
|
}
|
|
284
308
|
}
|
|
285
309
|
/**
|
|
286
310
|
* Get current session
|
|
287
311
|
*/
|
|
288
312
|
getSession() {
|
|
289
|
-
if (!this.accessToken || !this.user)
|
|
313
|
+
if (!this.accessToken || !this.user) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
290
316
|
return {
|
|
291
317
|
accessToken: this.accessToken,
|
|
292
318
|
user: this.user
|
|
@@ -301,11 +327,11 @@ var TokenManager = class {
|
|
|
301
327
|
/**
|
|
302
328
|
* Set access token
|
|
303
329
|
*/
|
|
304
|
-
setAccessToken(token) {
|
|
330
|
+
setAccessToken(token, event = AuthChangeEvent.SIGNED_IN) {
|
|
305
331
|
const tokenChanged = token !== this.accessToken;
|
|
306
332
|
this.accessToken = token;
|
|
307
|
-
if (tokenChanged
|
|
308
|
-
this.
|
|
333
|
+
if (tokenChanged) {
|
|
334
|
+
this.notifyAuthStateChange(event);
|
|
309
335
|
}
|
|
310
336
|
}
|
|
311
337
|
/**
|
|
@@ -327,22 +353,74 @@ var TokenManager = class {
|
|
|
327
353
|
const hadToken = this.accessToken !== null;
|
|
328
354
|
this.accessToken = null;
|
|
329
355
|
this.user = null;
|
|
330
|
-
if (hadToken
|
|
331
|
-
this.
|
|
356
|
+
if (hadToken) {
|
|
357
|
+
this.notifyAuthStateChange(AuthChangeEvent.SIGNED_OUT);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
onAuthStateChange(callback) {
|
|
361
|
+
const id = Symbol("auth-state-change");
|
|
362
|
+
this.authStateChangeCallbacks.set(id, callback);
|
|
363
|
+
return () => this.authStateChangeCallbacks.delete(id);
|
|
364
|
+
}
|
|
365
|
+
notifyAuthStateChange(event) {
|
|
366
|
+
for (const callback of this.authStateChangeCallbacks.values()) {
|
|
367
|
+
try {
|
|
368
|
+
callback(event);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error("Error in auth state change callback:", error);
|
|
371
|
+
}
|
|
332
372
|
}
|
|
333
373
|
}
|
|
334
374
|
};
|
|
335
375
|
|
|
376
|
+
// src/lib/jwt.ts
|
|
377
|
+
function decodeBase64Url(input) {
|
|
378
|
+
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
379
|
+
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
380
|
+
const binary = atob(padded);
|
|
381
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
382
|
+
return new TextDecoder().decode(bytes);
|
|
383
|
+
}
|
|
384
|
+
function getJwtExpiration(token) {
|
|
385
|
+
if (!token) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
const [, payload] = token.split(".");
|
|
389
|
+
if (!payload) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
const parsed = JSON.parse(decodeBase64Url(payload));
|
|
394
|
+
if (typeof parsed.exp !== "number" || !Number.isFinite(parsed.exp)) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
return new Date(parsed.exp * 1e3);
|
|
398
|
+
} catch {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function isJwtExpiredOrExpiring(token, leewaySeconds = 60) {
|
|
403
|
+
if (!token) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
const expires = getJwtExpiration(token);
|
|
407
|
+
if (!expires) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
return expires.getTime() <= Date.now() + leewaySeconds * 1e3;
|
|
411
|
+
}
|
|
412
|
+
|
|
336
413
|
// src/lib/http-client.ts
|
|
337
414
|
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
|
|
338
415
|
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
339
|
-
var REFRESHABLE_AUTH_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
340
|
-
"AUTH_UNAUTHORIZED",
|
|
341
|
-
"PGRST301"
|
|
342
|
-
]);
|
|
416
|
+
var REFRESHABLE_AUTH_ERROR_CODES = /* @__PURE__ */ new Set(["AUTH_UNAUTHORIZED", "PGRST301"]);
|
|
343
417
|
function serializeBody(method, body, headers) {
|
|
344
|
-
if (body === void 0)
|
|
345
|
-
|
|
418
|
+
if (body === void 0) {
|
|
419
|
+
return void 0;
|
|
420
|
+
}
|
|
421
|
+
if (method === "GET" || method === "HEAD") {
|
|
422
|
+
return void 0;
|
|
423
|
+
}
|
|
346
424
|
if (typeof FormData !== "undefined" && body instanceof FormData) {
|
|
347
425
|
return body;
|
|
348
426
|
}
|
|
@@ -350,7 +428,9 @@ function serializeBody(method, body, headers) {
|
|
|
350
428
|
return JSON.stringify(body);
|
|
351
429
|
}
|
|
352
430
|
async function parseResponse(response) {
|
|
353
|
-
if (response.status === 204)
|
|
431
|
+
if (response.status === 204) {
|
|
432
|
+
return void 0;
|
|
433
|
+
}
|
|
354
434
|
let data;
|
|
355
435
|
const contentType = response.headers.get("content-type");
|
|
356
436
|
try {
|
|
@@ -452,31 +532,24 @@ var HttpClient = class {
|
|
|
452
532
|
return statusCode === 401 && REFRESHABLE_AUTH_ERROR_CODES.has(errorCode ?? "") && !this.config.isServerMode && !this.config.accessToken && !this.config.edgeFunctionToken && !options.skipAuthRefresh && authToken !== null;
|
|
453
533
|
}
|
|
454
534
|
async fetchWithRetry(args) {
|
|
455
|
-
const {
|
|
456
|
-
method,
|
|
457
|
-
url,
|
|
458
|
-
headers,
|
|
459
|
-
body,
|
|
460
|
-
fetchOptions,
|
|
461
|
-
callerSignal,
|
|
462
|
-
maxAttempts
|
|
463
|
-
} = args;
|
|
535
|
+
const { method, url, headers, body, fetchOptions, callerSignal, maxAttempts } = args;
|
|
464
536
|
let lastError;
|
|
465
537
|
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
|
|
466
538
|
if (attempt > 0) {
|
|
467
539
|
const delay = this.computeRetryDelay(attempt);
|
|
468
|
-
this.logger.warn(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
540
|
+
this.logger.warn(`Retry ${attempt}/${maxAttempts} for ${method} ${url} in ${delay}ms`);
|
|
541
|
+
if (callerSignal?.aborted) {
|
|
542
|
+
throw callerSignal.reason;
|
|
543
|
+
}
|
|
472
544
|
await new Promise((resolve, reject) => {
|
|
473
545
|
const onAbort = () => {
|
|
474
546
|
clearTimeout(timer2);
|
|
475
547
|
reject(callerSignal.reason);
|
|
476
548
|
};
|
|
477
549
|
const timer2 = setTimeout(() => {
|
|
478
|
-
if (callerSignal)
|
|
550
|
+
if (callerSignal) {
|
|
479
551
|
callerSignal.removeEventListener("abort", onAbort);
|
|
552
|
+
}
|
|
480
553
|
resolve();
|
|
481
554
|
}, delay);
|
|
482
555
|
if (callerSignal) {
|
|
@@ -518,7 +591,9 @@ var HttpClient = class {
|
|
|
518
591
|
...controller ? { signal: controller.signal } : {}
|
|
519
592
|
});
|
|
520
593
|
if (this.isRetryableStatus(response.status) && attempt < maxAttempts) {
|
|
521
|
-
if (timer !== void 0)
|
|
594
|
+
if (timer !== void 0) {
|
|
595
|
+
clearTimeout(timer);
|
|
596
|
+
}
|
|
522
597
|
await response.body?.cancel();
|
|
523
598
|
lastError = new InsForgeError(
|
|
524
599
|
`Server error: ${response.status} ${response.statusText}`,
|
|
@@ -527,10 +602,14 @@ var HttpClient = class {
|
|
|
527
602
|
);
|
|
528
603
|
continue;
|
|
529
604
|
}
|
|
530
|
-
if (timer !== void 0)
|
|
605
|
+
if (timer !== void 0) {
|
|
606
|
+
clearTimeout(timer);
|
|
607
|
+
}
|
|
531
608
|
return response;
|
|
532
609
|
} catch (err) {
|
|
533
|
-
if (timer !== void 0)
|
|
610
|
+
if (timer !== void 0) {
|
|
611
|
+
clearTimeout(timer);
|
|
612
|
+
}
|
|
534
613
|
if (err?.name === "AbortError") {
|
|
535
614
|
if (controller && controller.signal.aborted && this.timeout > 0 && !callerSignal?.aborted) {
|
|
536
615
|
throw new InsForgeError(
|
|
@@ -552,11 +631,7 @@ var HttpClient = class {
|
|
|
552
631
|
);
|
|
553
632
|
}
|
|
554
633
|
}
|
|
555
|
-
throw lastError || new InsForgeError(
|
|
556
|
-
"Request failed after all retry attempts",
|
|
557
|
-
0,
|
|
558
|
-
"NETWORK_ERROR"
|
|
559
|
-
);
|
|
634
|
+
throw lastError || new InsForgeError("Request failed after all retry attempts", 0, "NETWORK_ERROR");
|
|
560
635
|
}
|
|
561
636
|
/**
|
|
562
637
|
* Performs an HTTP request with automatic retry and timeout handling.
|
|
@@ -636,31 +711,15 @@ var HttpClient = class {
|
|
|
636
711
|
}
|
|
637
712
|
throw err;
|
|
638
713
|
}
|
|
639
|
-
this.logger.logResponse(
|
|
640
|
-
method,
|
|
641
|
-
url,
|
|
642
|
-
response.status,
|
|
643
|
-
Date.now() - startTime,
|
|
644
|
-
data
|
|
645
|
-
);
|
|
714
|
+
this.logger.logResponse(method, url, response.status, Date.now() - startTime, data);
|
|
646
715
|
return data;
|
|
647
716
|
}
|
|
648
717
|
async request(method, path, options = {}) {
|
|
649
718
|
const tokenUsed = this.userToken;
|
|
650
719
|
try {
|
|
651
|
-
return await this.handleRequest(
|
|
652
|
-
method,
|
|
653
|
-
path,
|
|
654
|
-
{ ...options },
|
|
655
|
-
tokenUsed
|
|
656
|
-
);
|
|
720
|
+
return await this.handleRequest(method, path, { ...options }, tokenUsed);
|
|
657
721
|
} catch (error) {
|
|
658
|
-
if (!(error instanceof InsForgeError) || !this.shouldRefreshAccessToken(
|
|
659
|
-
error.statusCode,
|
|
660
|
-
error.error,
|
|
661
|
-
tokenUsed,
|
|
662
|
-
options
|
|
663
|
-
)) {
|
|
722
|
+
if (!(error instanceof InsForgeError) || !this.shouldRefreshAccessToken(error.statusCode, error.error, tokenUsed, options)) {
|
|
664
723
|
throw error;
|
|
665
724
|
}
|
|
666
725
|
if (tokenUsed !== this.userToken) {
|
|
@@ -745,12 +804,7 @@ var HttpClient = class {
|
|
|
745
804
|
callerSignal,
|
|
746
805
|
maxAttempts
|
|
747
806
|
});
|
|
748
|
-
this.logger.logResponse(
|
|
749
|
-
method,
|
|
750
|
-
url,
|
|
751
|
-
response.status,
|
|
752
|
-
Date.now() - startTime
|
|
753
|
-
);
|
|
807
|
+
this.logger.logResponse(method, url, response.status, Date.now() - startTime);
|
|
754
808
|
let errorCode = null;
|
|
755
809
|
if (response.status === 401) {
|
|
756
810
|
try {
|
|
@@ -764,12 +818,7 @@ var HttpClient = class {
|
|
|
764
818
|
} catch {
|
|
765
819
|
}
|
|
766
820
|
}
|
|
767
|
-
if (!this.shouldRefreshAccessToken(
|
|
768
|
-
response.status,
|
|
769
|
-
errorCode,
|
|
770
|
-
tokenUsed,
|
|
771
|
-
options
|
|
772
|
-
)) {
|
|
821
|
+
if (!this.shouldRefreshAccessToken(response.status, errorCode, tokenUsed, options)) {
|
|
773
822
|
return response;
|
|
774
823
|
}
|
|
775
824
|
if (tokenUsed !== this.userToken) {
|
|
@@ -863,10 +912,30 @@ var HttpClient = class {
|
|
|
863
912
|
})();
|
|
864
913
|
return this.refreshPromise;
|
|
865
914
|
}
|
|
915
|
+
/** Returns a token safe to use for a new connection handshake. */
|
|
916
|
+
async getValidAccessToken(leewaySeconds = 60) {
|
|
917
|
+
const accessToken = this.tokenManager.getAccessToken() ?? this.userToken;
|
|
918
|
+
if (!accessToken || !isJwtExpiredOrExpiring(accessToken, leewaySeconds)) {
|
|
919
|
+
return accessToken;
|
|
920
|
+
}
|
|
921
|
+
const canRefresh = !this.config.isServerMode && !this.config.accessToken && !this.config.edgeFunctionToken && this.userToken !== null;
|
|
922
|
+
if (!canRefresh) {
|
|
923
|
+
return accessToken;
|
|
924
|
+
}
|
|
925
|
+
try {
|
|
926
|
+
const refreshed = await this.refreshAndSaveSession();
|
|
927
|
+
return refreshed.accessToken;
|
|
928
|
+
} catch (error) {
|
|
929
|
+
if (error instanceof InsForgeError && (error.statusCode === 401 || error.statusCode === 403) && this.userToken === accessToken) {
|
|
930
|
+
this.clearAuthSession();
|
|
931
|
+
}
|
|
932
|
+
throw error;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
866
935
|
async refreshAndSaveSession() {
|
|
867
936
|
const newTokenData = await this.refreshAccessToken();
|
|
868
937
|
this.setAuthToken(newTokenData.accessToken);
|
|
869
|
-
this.tokenManager.saveSession(newTokenData);
|
|
938
|
+
this.tokenManager.saveSession(newTokenData, AuthChangeEvent.TOKEN_REFRESHED);
|
|
870
939
|
if (newTokenData.csrfToken) {
|
|
871
940
|
setCsrfToken(newTokenData.csrfToken);
|
|
872
941
|
}
|
|
@@ -962,11 +1031,15 @@ var Auth = class {
|
|
|
962
1031
|
isServerMode() {
|
|
963
1032
|
return !!this.options.isServerMode;
|
|
964
1033
|
}
|
|
1034
|
+
/** Subscribe to SDK authentication state changes. */
|
|
1035
|
+
onAuthStateChange(callback) {
|
|
1036
|
+
return this.tokenManager.onAuthStateChange(callback);
|
|
1037
|
+
}
|
|
965
1038
|
/**
|
|
966
1039
|
* Save session from API response
|
|
967
1040
|
* Handles token storage, CSRF token, and HTTP auth header
|
|
968
1041
|
*/
|
|
969
|
-
saveSessionFromResponse(response) {
|
|
1042
|
+
saveSessionFromResponse(response, event = AuthChangeEvent.SIGNED_IN) {
|
|
970
1043
|
if (!response.accessToken || !response.user) {
|
|
971
1044
|
return false;
|
|
972
1045
|
}
|
|
@@ -978,7 +1051,7 @@ var Auth = class {
|
|
|
978
1051
|
setCsrfToken(response.csrfToken);
|
|
979
1052
|
}
|
|
980
1053
|
if (!this.isServerMode()) {
|
|
981
|
-
this.tokenManager.saveSession(session);
|
|
1054
|
+
this.tokenManager.saveSession(session, event);
|
|
982
1055
|
}
|
|
983
1056
|
this.http.setAuthToken(response.accessToken);
|
|
984
1057
|
this.http.setRefreshToken(response.refreshToken ?? null);
|
|
@@ -992,7 +1065,9 @@ var Auth = class {
|
|
|
992
1065
|
* Supports PKCE flow (insforge_code)
|
|
993
1066
|
*/
|
|
994
1067
|
async detectAuthCallback() {
|
|
995
|
-
if (this.isServerMode() || typeof window === "undefined")
|
|
1068
|
+
if (this.isServerMode() || typeof window === "undefined") {
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
996
1071
|
try {
|
|
997
1072
|
const params = new URLSearchParams(window.location.search);
|
|
998
1073
|
const error = params.get("error");
|
|
@@ -1100,11 +1175,7 @@ var Auth = class {
|
|
|
1100
1175
|
if (!signInOptions || !signInOptions.redirectTo) {
|
|
1101
1176
|
return {
|
|
1102
1177
|
data: {},
|
|
1103
|
-
error: new InsForgeError(
|
|
1104
|
-
"Redirect URI is required",
|
|
1105
|
-
400,
|
|
1106
|
-
import_shared_schemas.ERROR_CODES.INVALID_INPUT
|
|
1107
|
-
)
|
|
1178
|
+
error: new InsForgeError("Redirect URI is required", 400, import_shared_schemas.ERROR_CODES.INVALID_INPUT)
|
|
1108
1179
|
};
|
|
1109
1180
|
}
|
|
1110
1181
|
const { provider } = signInOptions;
|
|
@@ -1179,10 +1250,7 @@ var Auth = class {
|
|
|
1179
1250
|
error: null
|
|
1180
1251
|
};
|
|
1181
1252
|
} catch (error) {
|
|
1182
|
-
return wrapError(
|
|
1183
|
-
error,
|
|
1184
|
-
"An unexpected error occurred during OAuth code exchange"
|
|
1185
|
-
);
|
|
1253
|
+
return wrapError(error, "An unexpected error occurred during OAuth code exchange");
|
|
1186
1254
|
}
|
|
1187
1255
|
}
|
|
1188
1256
|
/**
|
|
@@ -1209,10 +1277,7 @@ var Auth = class {
|
|
|
1209
1277
|
error: null
|
|
1210
1278
|
};
|
|
1211
1279
|
} catch (error) {
|
|
1212
|
-
return wrapError(
|
|
1213
|
-
error,
|
|
1214
|
-
"An unexpected error occurred during ID token sign in"
|
|
1215
|
-
);
|
|
1280
|
+
return wrapError(error, "An unexpected error occurred during ID token sign in");
|
|
1216
1281
|
}
|
|
1217
1282
|
}
|
|
1218
1283
|
// ============================================================================
|
|
@@ -1253,14 +1318,11 @@ var Auth = class {
|
|
|
1253
1318
|
}
|
|
1254
1319
|
);
|
|
1255
1320
|
if (response.accessToken) {
|
|
1256
|
-
this.saveSessionFromResponse(response);
|
|
1321
|
+
this.saveSessionFromResponse(response, AuthChangeEvent.TOKEN_REFRESHED);
|
|
1257
1322
|
}
|
|
1258
1323
|
return { data: response, error: null };
|
|
1259
1324
|
} catch (error) {
|
|
1260
|
-
return wrapError(
|
|
1261
|
-
error,
|
|
1262
|
-
"An unexpected error occurred during session refresh"
|
|
1263
|
-
);
|
|
1325
|
+
return wrapError(error, "An unexpected error occurred during session refresh");
|
|
1264
1326
|
}
|
|
1265
1327
|
}
|
|
1266
1328
|
/**
|
|
@@ -1271,11 +1333,11 @@ var Auth = class {
|
|
|
1271
1333
|
try {
|
|
1272
1334
|
if (this.isServerMode()) {
|
|
1273
1335
|
const accessToken = this.tokenManager.getAccessToken();
|
|
1274
|
-
if (!accessToken)
|
|
1336
|
+
if (!accessToken) {
|
|
1337
|
+
return { data: { user: null }, error: null };
|
|
1338
|
+
}
|
|
1275
1339
|
this.http.setAuthToken(accessToken);
|
|
1276
|
-
const response = await this.http.get(
|
|
1277
|
-
"/api/auth/sessions/current"
|
|
1278
|
-
);
|
|
1340
|
+
const response = await this.http.get("/api/auth/sessions/current");
|
|
1279
1341
|
const user = response.user ?? null;
|
|
1280
1342
|
return { data: { user }, error: null };
|
|
1281
1343
|
}
|
|
@@ -1313,25 +1375,17 @@ var Auth = class {
|
|
|
1313
1375
|
// ============================================================================
|
|
1314
1376
|
async getProfile(userId) {
|
|
1315
1377
|
try {
|
|
1316
|
-
const response = await this.http.get(
|
|
1317
|
-
`/api/auth/profiles/${userId}`
|
|
1318
|
-
);
|
|
1378
|
+
const response = await this.http.get(`/api/auth/profiles/${userId}`);
|
|
1319
1379
|
return { data: response, error: null };
|
|
1320
1380
|
} catch (error) {
|
|
1321
|
-
return wrapError(
|
|
1322
|
-
error,
|
|
1323
|
-
"An unexpected error occurred while fetching user profile"
|
|
1324
|
-
);
|
|
1381
|
+
return wrapError(error, "An unexpected error occurred while fetching user profile");
|
|
1325
1382
|
}
|
|
1326
1383
|
}
|
|
1327
1384
|
async setProfile(profile) {
|
|
1328
1385
|
try {
|
|
1329
|
-
const response = await this.http.patch(
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
profile
|
|
1333
|
-
}
|
|
1334
|
-
);
|
|
1386
|
+
const response = await this.http.patch("/api/auth/profiles/current", {
|
|
1387
|
+
profile
|
|
1388
|
+
});
|
|
1335
1389
|
const currentUser = this.tokenManager.getUser();
|
|
1336
1390
|
if (!this.isServerMode() && currentUser && response.profile !== void 0) {
|
|
1337
1391
|
this.tokenManager.setUser({
|
|
@@ -1341,10 +1395,7 @@ var Auth = class {
|
|
|
1341
1395
|
}
|
|
1342
1396
|
return { data: response, error: null };
|
|
1343
1397
|
} catch (error) {
|
|
1344
|
-
return wrapError(
|
|
1345
|
-
error,
|
|
1346
|
-
"An unexpected error occurred while updating user profile"
|
|
1347
|
-
);
|
|
1398
|
+
return wrapError(error, "An unexpected error occurred while updating user profile");
|
|
1348
1399
|
}
|
|
1349
1400
|
}
|
|
1350
1401
|
// ============================================================================
|
|
@@ -1357,10 +1408,7 @@ var Auth = class {
|
|
|
1357
1408
|
});
|
|
1358
1409
|
return { data: response, error: null };
|
|
1359
1410
|
} catch (error) {
|
|
1360
|
-
return wrapError(
|
|
1361
|
-
error,
|
|
1362
|
-
"An unexpected error occurred while sending verification email"
|
|
1363
|
-
);
|
|
1411
|
+
return wrapError(error, "An unexpected error occurred while sending verification email");
|
|
1364
1412
|
}
|
|
1365
1413
|
}
|
|
1366
1414
|
async verifyEmail(request) {
|
|
@@ -1376,10 +1424,7 @@ var Auth = class {
|
|
|
1376
1424
|
}
|
|
1377
1425
|
return { data: response, error: null };
|
|
1378
1426
|
} catch (error) {
|
|
1379
|
-
return wrapError(
|
|
1380
|
-
error,
|
|
1381
|
-
"An unexpected error occurred while verifying email"
|
|
1382
|
-
);
|
|
1427
|
+
return wrapError(error, "An unexpected error occurred while verifying email");
|
|
1383
1428
|
}
|
|
1384
1429
|
}
|
|
1385
1430
|
// ============================================================================
|
|
@@ -1392,10 +1437,7 @@ var Auth = class {
|
|
|
1392
1437
|
});
|
|
1393
1438
|
return { data: response, error: null };
|
|
1394
1439
|
} catch (error) {
|
|
1395
|
-
return wrapError(
|
|
1396
|
-
error,
|
|
1397
|
-
"An unexpected error occurred while sending password reset email"
|
|
1398
|
-
);
|
|
1440
|
+
return wrapError(error, "An unexpected error occurred while sending password reset email");
|
|
1399
1441
|
}
|
|
1400
1442
|
}
|
|
1401
1443
|
async exchangeResetPasswordToken(request) {
|
|
@@ -1407,10 +1449,7 @@ var Auth = class {
|
|
|
1407
1449
|
);
|
|
1408
1450
|
return { data: response, error: null };
|
|
1409
1451
|
} catch (error) {
|
|
1410
|
-
return wrapError(
|
|
1411
|
-
error,
|
|
1412
|
-
"An unexpected error occurred while verifying reset code"
|
|
1413
|
-
);
|
|
1452
|
+
return wrapError(error, "An unexpected error occurred while verifying reset code");
|
|
1414
1453
|
}
|
|
1415
1454
|
}
|
|
1416
1455
|
async resetPassword(request) {
|
|
@@ -1422,10 +1461,7 @@ var Auth = class {
|
|
|
1422
1461
|
);
|
|
1423
1462
|
return { data: response, error: null };
|
|
1424
1463
|
} catch (error) {
|
|
1425
|
-
return wrapError(
|
|
1426
|
-
error,
|
|
1427
|
-
"An unexpected error occurred while resetting password"
|
|
1428
|
-
);
|
|
1464
|
+
return wrapError(error, "An unexpected error occurred while resetting password");
|
|
1429
1465
|
}
|
|
1430
1466
|
}
|
|
1431
1467
|
// ============================================================================
|
|
@@ -1433,16 +1469,12 @@ var Auth = class {
|
|
|
1433
1469
|
// ============================================================================
|
|
1434
1470
|
async getPublicAuthConfig() {
|
|
1435
1471
|
try {
|
|
1436
|
-
const response = await this.http.get(
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
);
|
|
1472
|
+
const response = await this.http.get("/api/auth/public-config", {
|
|
1473
|
+
skipAuthRefresh: true
|
|
1474
|
+
});
|
|
1440
1475
|
return { data: response, error: null };
|
|
1441
1476
|
} catch (error) {
|
|
1442
|
-
return wrapError(
|
|
1443
|
-
error,
|
|
1444
|
-
"An unexpected error occurred while fetching auth configuration"
|
|
1445
|
-
);
|
|
1477
|
+
return wrapError(error, "An unexpected error occurred while fetching auth configuration");
|
|
1446
1478
|
}
|
|
1447
1479
|
}
|
|
1448
1480
|
};
|
|
@@ -1600,11 +1632,7 @@ var StorageBucket = class {
|
|
|
1600
1632
|
} catch (error) {
|
|
1601
1633
|
return {
|
|
1602
1634
|
data: null,
|
|
1603
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1604
|
-
"Upload failed",
|
|
1605
|
-
500,
|
|
1606
|
-
"STORAGE_ERROR"
|
|
1607
|
-
)
|
|
1635
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Upload failed", 500, "STORAGE_ERROR")
|
|
1608
1636
|
};
|
|
1609
1637
|
}
|
|
1610
1638
|
}
|
|
@@ -1650,11 +1678,7 @@ var StorageBucket = class {
|
|
|
1650
1678
|
} catch (error) {
|
|
1651
1679
|
return {
|
|
1652
1680
|
data: null,
|
|
1653
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1654
|
-
"Upload failed",
|
|
1655
|
-
500,
|
|
1656
|
-
"STORAGE_ERROR"
|
|
1657
|
-
)
|
|
1681
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Upload failed", 500, "STORAGE_ERROR")
|
|
1658
1682
|
};
|
|
1659
1683
|
}
|
|
1660
1684
|
}
|
|
@@ -1682,13 +1706,10 @@ var StorageBucket = class {
|
|
|
1682
1706
|
);
|
|
1683
1707
|
}
|
|
1684
1708
|
if (strategy.confirmRequired && strategy.confirmUrl) {
|
|
1685
|
-
const confirmResponse = await this.http.post(
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
contentType: file.type || "application/octet-stream"
|
|
1690
|
-
}
|
|
1691
|
-
);
|
|
1709
|
+
const confirmResponse = await this.http.post(strategy.confirmUrl, {
|
|
1710
|
+
size: file.size,
|
|
1711
|
+
contentType: file.type || "application/octet-stream"
|
|
1712
|
+
});
|
|
1692
1713
|
return { data: confirmResponse, error: null };
|
|
1693
1714
|
}
|
|
1694
1715
|
return {
|
|
@@ -1703,11 +1724,7 @@ var StorageBucket = class {
|
|
|
1703
1724
|
error: null
|
|
1704
1725
|
};
|
|
1705
1726
|
} catch (error) {
|
|
1706
|
-
throw error instanceof InsForgeError ? error : new InsForgeError(
|
|
1707
|
-
"Presigned upload failed",
|
|
1708
|
-
500,
|
|
1709
|
-
"STORAGE_ERROR"
|
|
1710
|
-
);
|
|
1727
|
+
throw error instanceof InsForgeError ? error : new InsForgeError("Presigned upload failed", 500, "STORAGE_ERROR");
|
|
1711
1728
|
}
|
|
1712
1729
|
}
|
|
1713
1730
|
/**
|
|
@@ -1761,11 +1778,7 @@ var StorageBucket = class {
|
|
|
1761
1778
|
} catch (error) {
|
|
1762
1779
|
return {
|
|
1763
1780
|
data: null,
|
|
1764
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1765
|
-
"Download failed",
|
|
1766
|
-
500,
|
|
1767
|
-
"STORAGE_ERROR"
|
|
1768
|
-
)
|
|
1781
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Download failed", 500, "STORAGE_ERROR")
|
|
1769
1782
|
};
|
|
1770
1783
|
}
|
|
1771
1784
|
}
|
|
@@ -1800,7 +1813,9 @@ var StorageBucket = class {
|
|
|
1800
1813
|
} catch (error) {
|
|
1801
1814
|
const status = error instanceof InsForgeError ? error.statusCode : void 0;
|
|
1802
1815
|
const isMissingRoute = (status === 404 || status === 405) && !(error instanceof InsForgeError && error.error === "STORAGE_NOT_FOUND");
|
|
1803
|
-
if (!isMissingRoute)
|
|
1816
|
+
if (!isMissingRoute) {
|
|
1817
|
+
throw error;
|
|
1818
|
+
}
|
|
1804
1819
|
return await this.http.post(
|
|
1805
1820
|
`/api/storage/buckets/${this.bucketName}/objects/${encoded}/download-strategy`,
|
|
1806
1821
|
{ expiresIn }
|
|
@@ -1876,10 +1891,18 @@ var StorageBucket = class {
|
|
|
1876
1891
|
async list(options) {
|
|
1877
1892
|
try {
|
|
1878
1893
|
const params = {};
|
|
1879
|
-
if (options?.prefix)
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
if (options?.
|
|
1894
|
+
if (options?.prefix) {
|
|
1895
|
+
params.prefix = options.prefix;
|
|
1896
|
+
}
|
|
1897
|
+
if (options?.search) {
|
|
1898
|
+
params.search = options.search;
|
|
1899
|
+
}
|
|
1900
|
+
if (options?.limit) {
|
|
1901
|
+
params.limit = options.limit.toString();
|
|
1902
|
+
}
|
|
1903
|
+
if (options?.offset) {
|
|
1904
|
+
params.offset = options.offset.toString();
|
|
1905
|
+
}
|
|
1883
1906
|
const response = await this.http.get(
|
|
1884
1907
|
`/api/storage/buckets/${this.bucketName}/objects`,
|
|
1885
1908
|
{ params }
|
|
@@ -1888,11 +1911,7 @@ var StorageBucket = class {
|
|
|
1888
1911
|
} catch (error) {
|
|
1889
1912
|
return {
|
|
1890
1913
|
data: null,
|
|
1891
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1892
|
-
"List failed",
|
|
1893
|
-
500,
|
|
1894
|
-
"STORAGE_ERROR"
|
|
1895
|
-
)
|
|
1914
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("List failed", 500, "STORAGE_ERROR")
|
|
1896
1915
|
};
|
|
1897
1916
|
}
|
|
1898
1917
|
}
|
|
@@ -1909,11 +1928,7 @@ var StorageBucket = class {
|
|
|
1909
1928
|
} catch (error) {
|
|
1910
1929
|
return {
|
|
1911
1930
|
data: null,
|
|
1912
|
-
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
1913
|
-
"Delete failed",
|
|
1914
|
-
500,
|
|
1915
|
-
"STORAGE_ERROR"
|
|
1916
|
-
)
|
|
1931
|
+
error: error instanceof InsForgeError ? error : new InsForgeError("Delete failed", 500, "STORAGE_ERROR")
|
|
1917
1932
|
};
|
|
1918
1933
|
}
|
|
1919
1934
|
}
|
|
@@ -2035,14 +2050,11 @@ var ChatCompletions = class {
|
|
|
2035
2050
|
if (params.stream) {
|
|
2036
2051
|
const headers = this.http.getHeaders();
|
|
2037
2052
|
headers["Content-Type"] = "application/json";
|
|
2038
|
-
const response2 = await this.http.fetch(
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
body: JSON.stringify(backendParams)
|
|
2044
|
-
}
|
|
2045
|
-
);
|
|
2053
|
+
const response2 = await this.http.fetch(`${this.http.baseUrl}/api/ai/chat/completion`, {
|
|
2054
|
+
method: "POST",
|
|
2055
|
+
headers,
|
|
2056
|
+
body: JSON.stringify(backendParams)
|
|
2057
|
+
});
|
|
2046
2058
|
if (!response2.ok) {
|
|
2047
2059
|
const error = await response2.json();
|
|
2048
2060
|
throw new Error(error.error || "Stream request failed");
|
|
@@ -2090,7 +2102,9 @@ var ChatCompletions = class {
|
|
|
2090
2102
|
try {
|
|
2091
2103
|
while (true) {
|
|
2092
2104
|
const { done, value } = await reader.read();
|
|
2093
|
-
if (done)
|
|
2105
|
+
if (done) {
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2094
2108
|
buffer += decoder.decode(value, { stream: true });
|
|
2095
2109
|
const lines = buffer.split("\n");
|
|
2096
2110
|
buffer = lines.pop() || "";
|
|
@@ -2138,7 +2152,7 @@ var ChatCompletions = class {
|
|
|
2138
2152
|
reader.releaseLock();
|
|
2139
2153
|
return;
|
|
2140
2154
|
}
|
|
2141
|
-
} catch
|
|
2155
|
+
} catch {
|
|
2142
2156
|
console.warn("Failed to parse SSE data:", dataStr);
|
|
2143
2157
|
}
|
|
2144
2158
|
}
|
|
@@ -2191,10 +2205,7 @@ var Embeddings = class {
|
|
|
2191
2205
|
* ```
|
|
2192
2206
|
*/
|
|
2193
2207
|
async create(params) {
|
|
2194
|
-
const response = await this.http.post(
|
|
2195
|
-
"/api/ai/embeddings",
|
|
2196
|
-
params
|
|
2197
|
-
);
|
|
2208
|
+
const response = await this.http.post("/api/ai/embeddings", params);
|
|
2198
2209
|
return {
|
|
2199
2210
|
object: response.object,
|
|
2200
2211
|
data: response.data,
|
|
@@ -2280,7 +2291,9 @@ var Functions = class _Functions {
|
|
|
2280
2291
|
static deriveSubhostingUrl(baseUrl) {
|
|
2281
2292
|
try {
|
|
2282
2293
|
const { hostname } = new URL(baseUrl);
|
|
2283
|
-
if (!hostname.endsWith(".insforge.app"))
|
|
2294
|
+
if (!hostname.endsWith(".insforge.app")) {
|
|
2295
|
+
return void 0;
|
|
2296
|
+
}
|
|
2284
2297
|
const appKey = hostname.split(".")[0];
|
|
2285
2298
|
return `https://${appKey}.functions.insforge.app`;
|
|
2286
2299
|
} catch {
|
|
@@ -2326,7 +2339,9 @@ var Functions = class _Functions {
|
|
|
2326
2339
|
const data = await parseResponse(res);
|
|
2327
2340
|
return { data, error: null };
|
|
2328
2341
|
} catch (error) {
|
|
2329
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2342
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2343
|
+
throw error;
|
|
2344
|
+
}
|
|
2330
2345
|
return {
|
|
2331
2346
|
data: null,
|
|
2332
2347
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2345,7 +2360,9 @@ var Functions = class _Functions {
|
|
|
2345
2360
|
});
|
|
2346
2361
|
return { data, error: null };
|
|
2347
2362
|
} catch (error) {
|
|
2348
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2363
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2364
|
+
throw error;
|
|
2365
|
+
}
|
|
2349
2366
|
if (error instanceof InsForgeError && error.statusCode === 404) {
|
|
2350
2367
|
} else {
|
|
2351
2368
|
return {
|
|
@@ -2364,7 +2381,9 @@ var Functions = class _Functions {
|
|
|
2364
2381
|
const data = await this.http.request(method, path, { body, headers });
|
|
2365
2382
|
return { data, error: null };
|
|
2366
2383
|
} catch (error) {
|
|
2367
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2384
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2385
|
+
throw error;
|
|
2386
|
+
}
|
|
2368
2387
|
return {
|
|
2369
2388
|
data: null,
|
|
2370
2389
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2379,32 +2398,37 @@ var Functions = class _Functions {
|
|
|
2379
2398
|
|
|
2380
2399
|
// src/modules/realtime.ts
|
|
2381
2400
|
var CONNECT_TIMEOUT = 1e4;
|
|
2401
|
+
var SUBSCRIBE_TIMEOUT = 1e4;
|
|
2382
2402
|
var Realtime = class {
|
|
2383
|
-
constructor(baseUrl, tokenManager, anonKey) {
|
|
2384
|
-
this.socket = null;
|
|
2385
|
-
this.connectPromise = null;
|
|
2386
|
-
this.subscribedChannels = /* @__PURE__ */ new Set();
|
|
2387
|
-
this.eventListeners = /* @__PURE__ */ new Map();
|
|
2403
|
+
constructor(baseUrl, tokenManager, anonKey, getValidAccessToken = async () => tokenManager.getAccessToken()) {
|
|
2388
2404
|
this.baseUrl = baseUrl;
|
|
2389
2405
|
this.tokenManager = tokenManager;
|
|
2390
2406
|
this.anonKey = anonKey;
|
|
2391
|
-
this.
|
|
2407
|
+
this.getValidAccessToken = getValidAccessToken;
|
|
2408
|
+
this.socket = null;
|
|
2409
|
+
this.connectPromise = null;
|
|
2410
|
+
this.connectionAttempt = null;
|
|
2411
|
+
this.nextConnectionAttemptId = 0;
|
|
2412
|
+
this.subscriptions = /* @__PURE__ */ new Map();
|
|
2413
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
2414
|
+
this.tokenManager.onAuthStateChange((event) => {
|
|
2415
|
+
if (event !== AuthChangeEvent.TOKEN_REFRESHED) {
|
|
2416
|
+
this.reconnectForAuthChange();
|
|
2417
|
+
}
|
|
2418
|
+
});
|
|
2392
2419
|
}
|
|
2393
2420
|
notifyListeners(event, payload) {
|
|
2394
|
-
const
|
|
2395
|
-
if (!listeners) return;
|
|
2396
|
-
for (const cb of listeners) {
|
|
2421
|
+
for (const callback of this.eventListeners.get(event) ?? []) {
|
|
2397
2422
|
try {
|
|
2398
|
-
|
|
2399
|
-
} catch (
|
|
2400
|
-
console.error(`Error in ${event} callback:`,
|
|
2423
|
+
callback(payload);
|
|
2424
|
+
} catch (error) {
|
|
2425
|
+
console.error(`Error in ${event} callback:`, error);
|
|
2401
2426
|
}
|
|
2402
2427
|
}
|
|
2403
2428
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
*/
|
|
2429
|
+
async getHandshakeToken() {
|
|
2430
|
+
return await this.getValidAccessToken() ?? this.anonKey ?? null;
|
|
2431
|
+
}
|
|
2408
2432
|
connect() {
|
|
2409
2433
|
if (this.socket?.connected) {
|
|
2410
2434
|
return Promise.resolve();
|
|
@@ -2412,210 +2436,324 @@ var Realtime = class {
|
|
|
2412
2436
|
if (this.connectPromise) {
|
|
2413
2437
|
return this.connectPromise;
|
|
2414
2438
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
}
|
|
2431
|
-
};
|
|
2432
|
-
timeoutId = setTimeout(() => {
|
|
2433
|
-
if (initialConnection) {
|
|
2434
|
-
initialConnection = false;
|
|
2435
|
-
this.connectPromise = null;
|
|
2436
|
-
this.socket?.disconnect();
|
|
2437
|
-
this.socket = null;
|
|
2438
|
-
reject(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`));
|
|
2439
|
-
}
|
|
2440
|
-
}, CONNECT_TIMEOUT);
|
|
2441
|
-
this.socket.on("connect", () => {
|
|
2442
|
-
cleanup();
|
|
2443
|
-
for (const channel of this.subscribedChannels) {
|
|
2444
|
-
this.socket.emit("realtime:subscribe", { channel });
|
|
2445
|
-
}
|
|
2446
|
-
this.notifyListeners("connect");
|
|
2447
|
-
if (initialConnection) {
|
|
2448
|
-
initialConnection = false;
|
|
2449
|
-
this.connectPromise = null;
|
|
2450
|
-
resolve();
|
|
2451
|
-
}
|
|
2452
|
-
});
|
|
2453
|
-
this.socket.on("connect_error", (error) => {
|
|
2454
|
-
cleanup();
|
|
2455
|
-
this.notifyListeners("connect_error", error);
|
|
2456
|
-
if (initialConnection) {
|
|
2457
|
-
initialConnection = false;
|
|
2458
|
-
this.connectPromise = null;
|
|
2459
|
-
reject(error);
|
|
2460
|
-
}
|
|
2461
|
-
});
|
|
2462
|
-
this.socket.on("disconnect", (reason) => {
|
|
2463
|
-
this.notifyListeners("disconnect", reason);
|
|
2464
|
-
});
|
|
2465
|
-
this.socket.on("realtime:error", (error) => {
|
|
2466
|
-
this.notifyListeners("error", error);
|
|
2467
|
-
});
|
|
2468
|
-
this.socket.onAny((event, message) => {
|
|
2469
|
-
if (event === "realtime:error") return;
|
|
2470
|
-
this.notifyListeners(event, message);
|
|
2471
|
-
});
|
|
2439
|
+
const attemptId = ++this.nextConnectionAttemptId;
|
|
2440
|
+
const connection = (async () => {
|
|
2441
|
+
const { io } = await import("socket.io-client");
|
|
2442
|
+
if (attemptId !== this.nextConnectionAttemptId) {
|
|
2443
|
+
throw new Error("Connection cancelled");
|
|
2444
|
+
}
|
|
2445
|
+
await new Promise((resolve, reject) => {
|
|
2446
|
+
const socket = io(this.baseUrl, {
|
|
2447
|
+
transports: ["websocket"],
|
|
2448
|
+
auth: (callback) => {
|
|
2449
|
+
void this.getHandshakeToken().then(
|
|
2450
|
+
(token) => callback(token ? { token } : {}),
|
|
2451
|
+
() => callback({})
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2472
2454
|
});
|
|
2473
|
-
|
|
2455
|
+
this.socket = socket;
|
|
2456
|
+
let initialConnection = true;
|
|
2457
|
+
let timeoutId = null;
|
|
2458
|
+
const clearConnectTimeout = () => {
|
|
2459
|
+
if (timeoutId) {
|
|
2460
|
+
clearTimeout(timeoutId);
|
|
2461
|
+
timeoutId = null;
|
|
2462
|
+
}
|
|
2463
|
+
};
|
|
2464
|
+
const dispose = () => {
|
|
2465
|
+
clearConnectTimeout();
|
|
2466
|
+
socket.off("connect", onConnect);
|
|
2467
|
+
socket.off("connect_error", onConnectError);
|
|
2468
|
+
socket.off("disconnect", onDisconnect);
|
|
2469
|
+
socket.off("realtime:error", onRealtimeError);
|
|
2470
|
+
socket.offAny(onAny);
|
|
2471
|
+
socket.disconnect();
|
|
2472
|
+
if (this.socket === socket) {
|
|
2473
|
+
this.socket = null;
|
|
2474
|
+
}
|
|
2475
|
+
if (this.connectionAttempt?.id === attemptId) {
|
|
2476
|
+
this.connectionAttempt = null;
|
|
2477
|
+
}
|
|
2478
|
+
};
|
|
2479
|
+
const fail = (error) => {
|
|
2480
|
+
if (!initialConnection) {
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
initialConnection = false;
|
|
2484
|
+
dispose();
|
|
2485
|
+
reject(error);
|
|
2486
|
+
};
|
|
2487
|
+
const onConnect = () => {
|
|
2488
|
+
if (this.socket !== socket) {
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
clearConnectTimeout();
|
|
2492
|
+
this.resubscribeChannels();
|
|
2493
|
+
this.notifyListeners("connect");
|
|
2494
|
+
if (initialConnection) {
|
|
2495
|
+
initialConnection = false;
|
|
2496
|
+
if (this.connectionAttempt?.id === attemptId) {
|
|
2497
|
+
this.connectionAttempt = null;
|
|
2498
|
+
}
|
|
2499
|
+
resolve();
|
|
2500
|
+
}
|
|
2501
|
+
};
|
|
2502
|
+
const onConnectError = (error) => {
|
|
2503
|
+
clearConnectTimeout();
|
|
2504
|
+
this.notifyListeners("connect_error", error);
|
|
2505
|
+
if (initialConnection) {
|
|
2506
|
+
fail(error);
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
const onDisconnect = (reason) => {
|
|
2510
|
+
this.handleDisconnect(reason);
|
|
2511
|
+
};
|
|
2512
|
+
const onRealtimeError = (error) => {
|
|
2513
|
+
this.notifyListeners("error", error);
|
|
2514
|
+
};
|
|
2515
|
+
const onAny = (event, message) => {
|
|
2516
|
+
if (event === "realtime:error") {
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2519
|
+
this.applyPresenceEvent(event, message);
|
|
2520
|
+
this.notifyListeners(event, message);
|
|
2521
|
+
};
|
|
2522
|
+
this.connectionAttempt = { id: attemptId, socket, cancel: fail };
|
|
2523
|
+
socket.on("connect", onConnect);
|
|
2524
|
+
socket.on("connect_error", onConnectError);
|
|
2525
|
+
socket.on("disconnect", onDisconnect);
|
|
2526
|
+
socket.on("realtime:error", onRealtimeError);
|
|
2527
|
+
socket.onAny(onAny);
|
|
2528
|
+
timeoutId = setTimeout(
|
|
2529
|
+
() => fail(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`)),
|
|
2530
|
+
CONNECT_TIMEOUT
|
|
2531
|
+
);
|
|
2532
|
+
});
|
|
2533
|
+
})();
|
|
2534
|
+
const trackedConnection = connection.finally(() => {
|
|
2535
|
+
if (this.connectPromise === trackedConnection) {
|
|
2474
2536
|
this.connectPromise = null;
|
|
2475
|
-
throw error;
|
|
2476
2537
|
}
|
|
2477
|
-
})
|
|
2478
|
-
|
|
2538
|
+
});
|
|
2539
|
+
this.connectPromise = trackedConnection;
|
|
2540
|
+
return trackedConnection;
|
|
2479
2541
|
}
|
|
2480
|
-
/**
|
|
2481
|
-
* Disconnect from the realtime server
|
|
2482
|
-
*/
|
|
2483
2542
|
disconnect() {
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2543
|
+
this.nextConnectionAttemptId++;
|
|
2544
|
+
this.connectionAttempt?.cancel(new Error("Disconnected"));
|
|
2545
|
+
this.socket?.disconnect();
|
|
2546
|
+
this.socket = null;
|
|
2547
|
+
this.connectPromise = null;
|
|
2548
|
+
for (const subscription of this.subscriptions.values()) {
|
|
2549
|
+
this.settleSubscription(
|
|
2550
|
+
subscription,
|
|
2551
|
+
{
|
|
2552
|
+
ok: false,
|
|
2553
|
+
channel: subscription.channel,
|
|
2554
|
+
error: { code: "DISCONNECTED", message: "Disconnected" }
|
|
2555
|
+
},
|
|
2556
|
+
false
|
|
2557
|
+
);
|
|
2487
2558
|
}
|
|
2488
|
-
this.
|
|
2559
|
+
this.subscriptions.clear();
|
|
2489
2560
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
onTokenChange() {
|
|
2496
|
-
const token = this.tokenManager.getAccessToken() ?? this.anonKey;
|
|
2497
|
-
if (this.socket) {
|
|
2498
|
-
this.socket.auth = token ? { token } : {};
|
|
2561
|
+
reconnectForAuthChange() {
|
|
2562
|
+
for (const subscription of this.subscriptions.values()) {
|
|
2563
|
+
if (subscription.status === "rejected") {
|
|
2564
|
+
subscription.status = "pending";
|
|
2565
|
+
}
|
|
2499
2566
|
}
|
|
2500
|
-
if (this.socket
|
|
2501
|
-
|
|
2502
|
-
|
|
2567
|
+
if (!this.socket) {
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
this.socket.disconnect();
|
|
2571
|
+
this.socket.connect();
|
|
2572
|
+
}
|
|
2573
|
+
handleDisconnect(reason) {
|
|
2574
|
+
for (const subscription of this.subscriptions.values()) {
|
|
2575
|
+
if (subscription.status === "rejected") {
|
|
2576
|
+
continue;
|
|
2577
|
+
}
|
|
2578
|
+
subscription.status = "pending";
|
|
2579
|
+
this.settleSubscription(
|
|
2580
|
+
subscription,
|
|
2581
|
+
{
|
|
2582
|
+
ok: false,
|
|
2583
|
+
channel: subscription.channel,
|
|
2584
|
+
error: { code: "DISCONNECTED", message: "Connection lost before subscription completed" }
|
|
2585
|
+
},
|
|
2586
|
+
true
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
this.notifyListeners("disconnect", reason);
|
|
2590
|
+
}
|
|
2591
|
+
resubscribeChannels() {
|
|
2592
|
+
for (const [channel, subscription] of this.subscriptions) {
|
|
2593
|
+
if (subscription.status === "pending") {
|
|
2594
|
+
this.requestSubscription(channel, subscription);
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
requestSubscription(channel, subscription) {
|
|
2599
|
+
if (subscription.pending) {
|
|
2600
|
+
return subscription.pending;
|
|
2601
|
+
}
|
|
2602
|
+
const socket = this.socket;
|
|
2603
|
+
if (!socket?.connected) {
|
|
2604
|
+
return Promise.resolve({
|
|
2605
|
+
ok: false,
|
|
2606
|
+
channel,
|
|
2607
|
+
error: { code: "CONNECTION_FAILED", message: "Not connected to realtime server" }
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
subscription.status = "pending";
|
|
2611
|
+
const epoch = ++subscription.epoch;
|
|
2612
|
+
let timeoutId;
|
|
2613
|
+
subscription.pending = new Promise((resolve) => {
|
|
2614
|
+
subscription.settlePending = (response) => {
|
|
2615
|
+
if (timeoutId) {
|
|
2616
|
+
clearTimeout(timeoutId);
|
|
2617
|
+
}
|
|
2618
|
+
subscription.pending = void 0;
|
|
2619
|
+
subscription.settlePending = void 0;
|
|
2620
|
+
resolve(response);
|
|
2621
|
+
};
|
|
2622
|
+
timeoutId = setTimeout(() => {
|
|
2623
|
+
if (this.subscriptions.get(channel) === subscription && subscription.epoch === epoch) {
|
|
2624
|
+
this.settleSubscription(
|
|
2625
|
+
subscription,
|
|
2626
|
+
{
|
|
2627
|
+
ok: false,
|
|
2628
|
+
channel,
|
|
2629
|
+
error: {
|
|
2630
|
+
code: "SUBSCRIBE_TIMEOUT",
|
|
2631
|
+
message: "Subscription acknowledgement timed out"
|
|
2632
|
+
}
|
|
2633
|
+
},
|
|
2634
|
+
true
|
|
2635
|
+
);
|
|
2636
|
+
}
|
|
2637
|
+
}, SUBSCRIBE_TIMEOUT);
|
|
2638
|
+
socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
2639
|
+
if (this.subscriptions.get(channel) !== subscription || subscription.epoch !== epoch) {
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
if (response.ok) {
|
|
2643
|
+
subscription.status = "subscribed";
|
|
2644
|
+
subscription.members = new Map(
|
|
2645
|
+
response.presence.members.map((member) => [member.presenceId, member])
|
|
2646
|
+
);
|
|
2647
|
+
} else {
|
|
2648
|
+
subscription.status = "rejected";
|
|
2649
|
+
subscription.members.clear();
|
|
2650
|
+
}
|
|
2651
|
+
this.settleSubscription(subscription, response, false);
|
|
2652
|
+
});
|
|
2653
|
+
});
|
|
2654
|
+
return subscription.pending;
|
|
2655
|
+
}
|
|
2656
|
+
settleSubscription(subscription, response, incrementEpoch) {
|
|
2657
|
+
if (incrementEpoch) {
|
|
2658
|
+
subscription.epoch++;
|
|
2659
|
+
}
|
|
2660
|
+
subscription.settlePending?.(response);
|
|
2661
|
+
}
|
|
2662
|
+
applyPresenceEvent(event, message) {
|
|
2663
|
+
if (event !== "presence:join" && event !== "presence:leave") {
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
const presenceEvent = message;
|
|
2667
|
+
const channel = presenceEvent.meta?.channel;
|
|
2668
|
+
const member = presenceEvent.member;
|
|
2669
|
+
if (!channel || !member) {
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
2672
|
+
const subscription = this.subscriptions.get(channel);
|
|
2673
|
+
if (!subscription) {
|
|
2674
|
+
return;
|
|
2675
|
+
}
|
|
2676
|
+
if (event === "presence:join") {
|
|
2677
|
+
subscription.members.set(member.presenceId, member);
|
|
2678
|
+
} else {
|
|
2679
|
+
subscription.members.delete(member.presenceId);
|
|
2503
2680
|
}
|
|
2504
2681
|
}
|
|
2505
|
-
/**
|
|
2506
|
-
* Check if connected to the realtime server
|
|
2507
|
-
*/
|
|
2508
2682
|
get isConnected() {
|
|
2509
2683
|
return this.socket?.connected ?? false;
|
|
2510
2684
|
}
|
|
2511
|
-
/**
|
|
2512
|
-
* Get the current connection state
|
|
2513
|
-
*/
|
|
2514
2685
|
get connectionState() {
|
|
2515
|
-
if (!this.socket)
|
|
2516
|
-
|
|
2517
|
-
|
|
2686
|
+
if (!this.socket) {
|
|
2687
|
+
return "disconnected";
|
|
2688
|
+
}
|
|
2689
|
+
return this.socket.connected ? "connected" : "connecting";
|
|
2518
2690
|
}
|
|
2519
|
-
/**
|
|
2520
|
-
* Get the socket ID (if connected)
|
|
2521
|
-
*/
|
|
2522
2691
|
get socketId() {
|
|
2523
2692
|
return this.socket?.id;
|
|
2524
2693
|
}
|
|
2525
|
-
/**
|
|
2526
|
-
* Subscribe to a channel
|
|
2527
|
-
*
|
|
2528
|
-
* Automatically connects if not already connected.
|
|
2529
|
-
*
|
|
2530
|
-
* @param channel - Channel name (e.g., 'orders:123', 'broadcast')
|
|
2531
|
-
* @returns Promise with the subscription response
|
|
2532
|
-
*/
|
|
2533
2694
|
async subscribe(channel) {
|
|
2534
|
-
|
|
2535
|
-
|
|
2695
|
+
let subscription = this.subscriptions.get(channel);
|
|
2696
|
+
if (subscription) {
|
|
2697
|
+
if (subscription.pending) {
|
|
2698
|
+
return subscription.pending;
|
|
2699
|
+
}
|
|
2700
|
+
if (subscription.status === "subscribed") {
|
|
2701
|
+
return { ok: true, channel, presence: { members: [...subscription.members.values()] } };
|
|
2702
|
+
}
|
|
2703
|
+
} else {
|
|
2704
|
+
subscription = { channel, epoch: 0, status: "pending", members: /* @__PURE__ */ new Map() };
|
|
2705
|
+
this.subscriptions.set(channel, subscription);
|
|
2536
2706
|
}
|
|
2537
2707
|
if (!this.socket?.connected) {
|
|
2538
2708
|
try {
|
|
2539
2709
|
await this.connect();
|
|
2540
2710
|
} catch (error) {
|
|
2711
|
+
if (this.subscriptions.get(channel) === subscription) {
|
|
2712
|
+
this.subscriptions.delete(channel);
|
|
2713
|
+
}
|
|
2541
2714
|
const message = error instanceof Error ? error.message : "Connection failed";
|
|
2542
2715
|
return { ok: false, channel, error: { code: "CONNECTION_FAILED", message } };
|
|
2543
2716
|
}
|
|
2544
2717
|
}
|
|
2545
|
-
return
|
|
2546
|
-
this.socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
2547
|
-
if (response.ok) {
|
|
2548
|
-
this.subscribedChannels.add(channel);
|
|
2549
|
-
}
|
|
2550
|
-
resolve(response);
|
|
2551
|
-
});
|
|
2552
|
-
});
|
|
2718
|
+
return subscription.pending ?? this.requestSubscription(channel, subscription);
|
|
2553
2719
|
}
|
|
2554
|
-
/**
|
|
2555
|
-
* Unsubscribe from a channel (fire-and-forget)
|
|
2556
|
-
*
|
|
2557
|
-
* @param channel - Channel name to unsubscribe from
|
|
2558
|
-
*/
|
|
2559
2720
|
unsubscribe(channel) {
|
|
2560
|
-
this.
|
|
2721
|
+
const subscription = this.subscriptions.get(channel);
|
|
2722
|
+
if (!subscription) {
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
this.subscriptions.delete(channel);
|
|
2726
|
+
this.settleSubscription(
|
|
2727
|
+
subscription,
|
|
2728
|
+
{
|
|
2729
|
+
ok: false,
|
|
2730
|
+
channel,
|
|
2731
|
+
error: { code: "SUBSCRIPTION_CANCELLED", message: "Subscription cancelled" }
|
|
2732
|
+
},
|
|
2733
|
+
true
|
|
2734
|
+
);
|
|
2561
2735
|
if (this.socket?.connected) {
|
|
2562
2736
|
this.socket.emit("realtime:unsubscribe", { channel });
|
|
2563
2737
|
}
|
|
2564
2738
|
}
|
|
2565
|
-
/**
|
|
2566
|
-
* Publish a message to a channel
|
|
2567
|
-
*
|
|
2568
|
-
* @param channel - Channel name
|
|
2569
|
-
* @param event - Event name
|
|
2570
|
-
* @param payload - Message payload
|
|
2571
|
-
*/
|
|
2572
2739
|
async publish(channel, event, payload) {
|
|
2573
2740
|
if (!this.socket?.connected) {
|
|
2574
2741
|
throw new Error("Not connected to realtime server. Call connect() first.");
|
|
2575
2742
|
}
|
|
2576
2743
|
this.socket.emit("realtime:publish", { channel, event, payload });
|
|
2577
2744
|
}
|
|
2578
|
-
/**
|
|
2579
|
-
* Listen for events
|
|
2580
|
-
*
|
|
2581
|
-
* Reserved event names:
|
|
2582
|
-
* - 'connect' - Fired when connected to the server
|
|
2583
|
-
* - 'connect_error' - Fired when connection fails (payload: Error)
|
|
2584
|
-
* - 'disconnect' - Fired when disconnected (payload: reason string)
|
|
2585
|
-
* - 'error' - Fired when a realtime error occurs (payload: RealtimeErrorPayload)
|
|
2586
|
-
*
|
|
2587
|
-
* All other events receive a `SocketMessage` payload with metadata.
|
|
2588
|
-
*
|
|
2589
|
-
* @param event - Event name to listen for
|
|
2590
|
-
* @param callback - Callback function when event is received
|
|
2591
|
-
*/
|
|
2592
2745
|
on(event, callback) {
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
this.eventListeners.get(event).add(callback);
|
|
2746
|
+
const listeners = this.eventListeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
2747
|
+
listeners.add(callback);
|
|
2748
|
+
this.eventListeners.set(event, listeners);
|
|
2597
2749
|
}
|
|
2598
|
-
/**
|
|
2599
|
-
* Remove a listener for a specific event
|
|
2600
|
-
*
|
|
2601
|
-
* @param event - Event name
|
|
2602
|
-
* @param callback - The callback function to remove
|
|
2603
|
-
*/
|
|
2604
2750
|
off(event, callback) {
|
|
2605
2751
|
const listeners = this.eventListeners.get(event);
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
this.eventListeners.delete(event);
|
|
2610
|
-
}
|
|
2752
|
+
listeners?.delete(callback);
|
|
2753
|
+
if (listeners?.size === 0) {
|
|
2754
|
+
this.eventListeners.delete(event);
|
|
2611
2755
|
}
|
|
2612
2756
|
}
|
|
2613
|
-
/**
|
|
2614
|
-
* Listen for an event only once, then automatically remove the listener
|
|
2615
|
-
*
|
|
2616
|
-
* @param event - Event name to listen for
|
|
2617
|
-
* @param callback - Callback function when event is received
|
|
2618
|
-
*/
|
|
2619
2757
|
once(event, callback) {
|
|
2620
2758
|
const wrapper = (payload) => {
|
|
2621
2759
|
this.off(event, wrapper);
|
|
@@ -2623,13 +2761,11 @@ var Realtime = class {
|
|
|
2623
2761
|
};
|
|
2624
2762
|
this.on(event, wrapper);
|
|
2625
2763
|
}
|
|
2626
|
-
/**
|
|
2627
|
-
* Get all currently subscribed channels
|
|
2628
|
-
*
|
|
2629
|
-
* @returns Array of channel names
|
|
2630
|
-
*/
|
|
2631
2764
|
getSubscribedChannels() {
|
|
2632
|
-
return
|
|
2765
|
+
return [...this.subscriptions.values()].filter((subscription) => subscription.status === "subscribed").map((subscription) => subscription.channel);
|
|
2766
|
+
}
|
|
2767
|
+
getPresenceState(channel) {
|
|
2768
|
+
return [...this.subscriptions.get(channel)?.members.values() ?? []];
|
|
2633
2769
|
}
|
|
2634
2770
|
};
|
|
2635
2771
|
|
|
@@ -2644,13 +2780,12 @@ var Emails = class {
|
|
|
2644
2780
|
*/
|
|
2645
2781
|
async send(options) {
|
|
2646
2782
|
try {
|
|
2647
|
-
const data = await this.http.post(
|
|
2648
|
-
"/api/email/send-raw",
|
|
2649
|
-
options
|
|
2650
|
-
);
|
|
2783
|
+
const data = await this.http.post("/api/email/send-raw", options);
|
|
2651
2784
|
return { data, error: null };
|
|
2652
2785
|
} catch (error) {
|
|
2653
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
2786
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2787
|
+
throw error;
|
|
2788
|
+
}
|
|
2654
2789
|
return {
|
|
2655
2790
|
data: null,
|
|
2656
2791
|
error: error instanceof InsForgeError ? error : new InsForgeError(
|
|
@@ -2733,10 +2868,7 @@ var RazorpayPayments = class {
|
|
|
2733
2868
|
);
|
|
2734
2869
|
return { data, error: null };
|
|
2735
2870
|
} catch (error) {
|
|
2736
|
-
return wrapError(
|
|
2737
|
-
error,
|
|
2738
|
-
"Razorpay order creation failed"
|
|
2739
|
-
);
|
|
2871
|
+
return wrapError(error, "Razorpay order creation failed");
|
|
2740
2872
|
}
|
|
2741
2873
|
}
|
|
2742
2874
|
async verifyOrder(environment, request) {
|
|
@@ -2747,10 +2879,7 @@ var RazorpayPayments = class {
|
|
|
2747
2879
|
);
|
|
2748
2880
|
return { data, error: null };
|
|
2749
2881
|
} catch (error) {
|
|
2750
|
-
return wrapError(
|
|
2751
|
-
error,
|
|
2752
|
-
"Razorpay order verification failed"
|
|
2753
|
-
);
|
|
2882
|
+
return wrapError(error, "Razorpay order verification failed");
|
|
2754
2883
|
}
|
|
2755
2884
|
}
|
|
2756
2885
|
async createSubscription(environment, request) {
|
|
@@ -2859,7 +2988,8 @@ var InsForgeClient = class {
|
|
|
2859
2988
|
this.realtime = new Realtime(
|
|
2860
2989
|
this.http.baseUrl,
|
|
2861
2990
|
this.tokenManager,
|
|
2862
|
-
config.anonKey
|
|
2991
|
+
config.anonKey,
|
|
2992
|
+
() => this.http.getValidAccessToken()
|
|
2863
2993
|
);
|
|
2864
2994
|
this.emails = new Emails(this.http);
|
|
2865
2995
|
this.payments = new Payments(this.http);
|
|
@@ -2879,32 +3009,35 @@ var InsForgeClient = class {
|
|
|
2879
3009
|
/**
|
|
2880
3010
|
* Set the access token used by every SDK surface. Updates both the HTTP
|
|
2881
3011
|
* client (database / storage / functions / AI / emails) and the realtime
|
|
2882
|
-
* token manager
|
|
2883
|
-
*
|
|
3012
|
+
* token manager. Pass `null` to sign out. By default a token replacement is
|
|
3013
|
+
* treated as a sign-in boundary and reconnects realtime. Pass
|
|
3014
|
+
* `AuthChangeEvent.TOKEN_REFRESHED` for a same-identity refresh to preserve a live socket; the
|
|
3015
|
+
* refreshed token is then used at the next handshake.
|
|
2884
3016
|
*
|
|
2885
3017
|
* Use this when an external auth provider (Better Auth, Clerk, Auth0,
|
|
2886
3018
|
* WorkOS, Kinde, Stytch, …) issues the JWT and you need to keep the
|
|
2887
3019
|
* long-lived InsForge client in sync. Without this, you'd have to call
|
|
2888
3020
|
* `client.getHttpClient().setAuthToken(token)` AND reach into the private
|
|
2889
|
-
*
|
|
2890
|
-
* forgetting the second one silently breaks realtime auth.
|
|
3021
|
+
* realtime token manager separately.
|
|
2891
3022
|
*
|
|
2892
3023
|
* @example
|
|
2893
3024
|
* ```typescript
|
|
3025
|
+
* import { AuthChangeEvent } from '@insforge/sdk';
|
|
3026
|
+
*
|
|
2894
3027
|
* // Refresh a third-party-issued JWT periodically
|
|
2895
3028
|
* const { token } = await fetch('/api/insforge-token').then((r) => r.json());
|
|
2896
|
-
* client.setAccessToken(token);
|
|
3029
|
+
* client.setAccessToken(token, AuthChangeEvent.TOKEN_REFRESHED);
|
|
2897
3030
|
*
|
|
2898
3031
|
* // Sign-out
|
|
2899
3032
|
* client.setAccessToken(null);
|
|
2900
3033
|
* ```
|
|
2901
3034
|
*/
|
|
2902
|
-
setAccessToken(token) {
|
|
3035
|
+
setAccessToken(token, event = AuthChangeEvent.SIGNED_IN) {
|
|
2903
3036
|
this.http.setAuthToken(token);
|
|
2904
3037
|
if (token === null) {
|
|
2905
3038
|
this.tokenManager.clearSession();
|
|
2906
3039
|
} else {
|
|
2907
|
-
this.tokenManager.setAccessToken(token);
|
|
3040
|
+
this.tokenManager.setAccessToken(token, event);
|
|
2908
3041
|
}
|
|
2909
3042
|
}
|
|
2910
3043
|
/**
|
|
@@ -2938,6 +3071,7 @@ var src_default = InsForgeClient;
|
|
|
2938
3071
|
0 && (module.exports = {
|
|
2939
3072
|
AI,
|
|
2940
3073
|
Auth,
|
|
3074
|
+
AuthChangeEvent,
|
|
2941
3075
|
Database,
|
|
2942
3076
|
Emails,
|
|
2943
3077
|
Functions,
|
|
@@ -2949,7 +3083,6 @@ var src_default = InsForgeClient;
|
|
|
2949
3083
|
Realtime,
|
|
2950
3084
|
Storage,
|
|
2951
3085
|
StorageBucket,
|
|
2952
|
-
TokenManager,
|
|
2953
3086
|
createAdminClient,
|
|
2954
3087
|
createClient
|
|
2955
3088
|
});
|