@aurabase/core 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +128 -14
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +128 -15
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -235,6 +235,56 @@ function unwrapEnvelope(body) {
|
|
|
235
235
|
}
|
|
236
236
|
return body;
|
|
237
237
|
}
|
|
238
|
+
async function readTextBounded(res, timeoutMs) {
|
|
239
|
+
const body = res.body;
|
|
240
|
+
if (!body || typeof body.getReader !== "function" || !(timeoutMs > 0)) {
|
|
241
|
+
try {
|
|
242
|
+
return { ok: true, text: await res.text() };
|
|
243
|
+
} catch (err) {
|
|
244
|
+
return {
|
|
245
|
+
ok: false,
|
|
246
|
+
timedOut: false,
|
|
247
|
+
message: err instanceof Error ? err.message : "read failed"
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
let timer;
|
|
252
|
+
const TIMED_OUT = /* @__PURE__ */ Symbol("body-timeout");
|
|
253
|
+
try {
|
|
254
|
+
const reader = body.getReader();
|
|
255
|
+
const decoder = new TextDecoder();
|
|
256
|
+
let out = "";
|
|
257
|
+
const deadline = new Promise((resolve) => {
|
|
258
|
+
timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
|
|
259
|
+
timer.unref?.();
|
|
260
|
+
});
|
|
261
|
+
for (; ; ) {
|
|
262
|
+
const step = await Promise.race([reader.read(), deadline]);
|
|
263
|
+
if (step === TIMED_OUT) {
|
|
264
|
+
await reader.cancel().catch(() => {
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
ok: false,
|
|
268
|
+
timedOut: true,
|
|
269
|
+
message: `Response body not fully received within ${timeoutMs}ms`
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (step.done) {
|
|
273
|
+
out += decoder.decode();
|
|
274
|
+
return { ok: true, text: out };
|
|
275
|
+
}
|
|
276
|
+
if (step.value) out += decoder.decode(step.value, { stream: true });
|
|
277
|
+
}
|
|
278
|
+
} catch (err) {
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
timedOut: false,
|
|
282
|
+
message: err instanceof Error ? err.message : "read failed"
|
|
283
|
+
};
|
|
284
|
+
} finally {
|
|
285
|
+
if (timer) clearTimeout(timer);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
238
288
|
var inflightRefreshes = /* @__PURE__ */ new WeakMap();
|
|
239
289
|
async function doRefresh(client) {
|
|
240
290
|
const store = client.authStore;
|
|
@@ -248,26 +298,57 @@ async function doRefresh(client) {
|
|
|
248
298
|
const epoch = store.removalEpoch;
|
|
249
299
|
const headers = { "Content-Type": "application/json" };
|
|
250
300
|
if (client.apiKey) headers["apikey"] = client.apiKey;
|
|
301
|
+
const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
302
|
+
let timedOut = false;
|
|
303
|
+
let refreshTimer;
|
|
304
|
+
let refreshSignal;
|
|
305
|
+
if (timeoutMs > 0 && typeof AbortController !== "undefined") {
|
|
306
|
+
const ctrl = new AbortController();
|
|
307
|
+
refreshTimer = setTimeout(() => {
|
|
308
|
+
timedOut = true;
|
|
309
|
+
ctrl.abort();
|
|
310
|
+
}, timeoutMs);
|
|
311
|
+
refreshTimer.unref?.();
|
|
312
|
+
refreshSignal = ctrl.signal;
|
|
313
|
+
}
|
|
314
|
+
const clearRefreshTimer = () => {
|
|
315
|
+
if (refreshTimer) {
|
|
316
|
+
clearTimeout(refreshTimer);
|
|
317
|
+
refreshTimer = void 0;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
251
320
|
let res;
|
|
252
321
|
try {
|
|
253
322
|
res = await client.fetchFn(`${client.baseUrl}/v1/auth/${client.projectId}/refresh`, {
|
|
254
323
|
method: "POST",
|
|
255
324
|
headers,
|
|
256
|
-
body: serializeBody({ refresh_token: rt })
|
|
325
|
+
body: serializeBody({ refresh_token: rt }),
|
|
326
|
+
signal: refreshSignal
|
|
257
327
|
});
|
|
258
328
|
} catch (err) {
|
|
329
|
+
clearRefreshTimer();
|
|
330
|
+
if (timedOut) {
|
|
331
|
+
emitAuraEvent(client.onEvent, client.debug, {
|
|
332
|
+
kind: "timeout",
|
|
333
|
+
url: `${client.baseUrl}/v1/auth/${client.projectId}/refresh`
|
|
334
|
+
});
|
|
335
|
+
}
|
|
259
336
|
return {
|
|
260
337
|
data: null,
|
|
261
338
|
error: new AuraError(0, {
|
|
262
|
-
error: { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
|
|
339
|
+
error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
|
|
263
340
|
})
|
|
264
341
|
};
|
|
265
342
|
}
|
|
343
|
+
clearRefreshTimer();
|
|
266
344
|
if (!res.ok) {
|
|
267
345
|
let body2 = null;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
346
|
+
const errRead = await readTextBounded(res, timeoutMs);
|
|
347
|
+
if (errRead.ok && errRead.text.trim() !== "") {
|
|
348
|
+
try {
|
|
349
|
+
body2 = JSON.parse(errRead.text);
|
|
350
|
+
} catch {
|
|
351
|
+
}
|
|
271
352
|
}
|
|
272
353
|
const code = typeof body2?.error?.code === "string" ? body2.error.code : null;
|
|
273
354
|
const definitivelyRejected = res.status === 403 || res.status === 401 && code === "REFRESH_TOKEN_REJECTED";
|
|
@@ -279,8 +360,23 @@ async function doRefresh(client) {
|
|
|
279
360
|
return { data: null, error: new AuraError(res.status, body2) };
|
|
280
361
|
}
|
|
281
362
|
let body;
|
|
363
|
+
const okRead = await readTextBounded(res, timeoutMs);
|
|
364
|
+
if (!okRead.ok) {
|
|
365
|
+
if (okRead.timedOut) {
|
|
366
|
+
emitAuraEvent(client.onEvent, client.debug, {
|
|
367
|
+
kind: "timeout",
|
|
368
|
+
url: `${client.baseUrl}/v1/auth/${client.projectId}/refresh`
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
data: null,
|
|
373
|
+
error: new AuraError(0, {
|
|
374
|
+
error: okRead.timedOut ? { code: "timeout", message: `Refresh body not received within ${timeoutMs}ms` } : { code: "invalid_response", message: `Invalid refresh response (${okRead.message})` }
|
|
375
|
+
})
|
|
376
|
+
};
|
|
377
|
+
}
|
|
282
378
|
try {
|
|
283
|
-
body =
|
|
379
|
+
body = JSON.parse(okRead.text);
|
|
284
380
|
} catch {
|
|
285
381
|
return {
|
|
286
382
|
data: null,
|
|
@@ -373,6 +469,7 @@ async function fetchWithAuth(url, init, client) {
|
|
|
373
469
|
const isFormData = typeof FormData !== "undefined" && init.body instanceof FormData;
|
|
374
470
|
const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
375
471
|
const applyTimeout = timeoutMs > 0 && init.signal === void 0 && !isFormData;
|
|
472
|
+
const bodyTimeoutMs = init.signal === void 0 ? timeoutMs : 0;
|
|
376
473
|
let attempt = 0;
|
|
377
474
|
for (; ; ) {
|
|
378
475
|
const headers = { ...init.headers };
|
|
@@ -462,26 +559,42 @@ async function fetchWithAuth(url, init, client) {
|
|
|
462
559
|
}
|
|
463
560
|
if (!res.ok) {
|
|
464
561
|
let body2 = null;
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
562
|
+
const errRead = await readTextBounded(res, bodyTimeoutMs);
|
|
563
|
+
if (errRead.ok && errRead.text.trim() !== "") {
|
|
564
|
+
try {
|
|
565
|
+
body2 = JSON.parse(errRead.text);
|
|
566
|
+
} catch {
|
|
567
|
+
}
|
|
568
|
+
} else if (!errRead.ok && errRead.timedOut) {
|
|
569
|
+
emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
|
|
468
570
|
}
|
|
469
571
|
return { data: null, error: new AuraError(res.status, body2) };
|
|
470
572
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
573
|
+
const read = await readTextBounded(res, bodyTimeoutMs);
|
|
574
|
+
if (!read.ok) {
|
|
575
|
+
if (read.timedOut) {
|
|
576
|
+
emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
|
|
577
|
+
return {
|
|
578
|
+
data: null,
|
|
579
|
+
error: new AuraError(0, {
|
|
580
|
+
error: {
|
|
581
|
+
code: "timeout",
|
|
582
|
+
message: `Response body not fully received within ${bodyTimeoutMs}ms (HTTP ${res.status})`
|
|
583
|
+
}
|
|
584
|
+
})
|
|
585
|
+
};
|
|
586
|
+
}
|
|
475
587
|
return {
|
|
476
588
|
data: null,
|
|
477
589
|
error: new AuraError(0, {
|
|
478
590
|
error: {
|
|
479
591
|
code: "invalid_response",
|
|
480
|
-
message: `Unreadable response body (HTTP ${res.status}): ${
|
|
592
|
+
message: `Unreadable response body (HTTP ${res.status}): ${read.message}`
|
|
481
593
|
}
|
|
482
594
|
})
|
|
483
595
|
};
|
|
484
596
|
}
|
|
597
|
+
const raw = read.text;
|
|
485
598
|
if (raw.trim() === "") {
|
|
486
599
|
return { data: null, error: null };
|
|
487
600
|
}
|
|
@@ -709,6 +822,7 @@ exports.AuraError = AuraError;
|
|
|
709
822
|
exports.BaseAuthStore = BaseAuthStore;
|
|
710
823
|
exports.BaseService = BaseService;
|
|
711
824
|
exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES;
|
|
825
|
+
exports.DEFAULT_REQUEST_TIMEOUT_MS = DEFAULT_REQUEST_TIMEOUT_MS;
|
|
712
826
|
exports.LocalAuthStore = LocalAuthStore;
|
|
713
827
|
exports.RETRYABLE_METHODS = RETRYABLE_METHODS;
|
|
714
828
|
exports.RETRYABLE_STATUS = RETRYABLE_STATUS;
|
package/dist/index.d.cts
CHANGED
|
@@ -1098,6 +1098,8 @@ interface AuraClientLike {
|
|
|
1098
1098
|
readonly debug?: boolean;
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
|
+
/** Timeout par défaut d'une requête HTTP (borne un serveur qui « hang »). */
|
|
1102
|
+
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
1101
1103
|
/** Point d'entrée unique du refresh, dédupliqué par client (single-flight). */
|
|
1102
1104
|
declare function refreshOnce(client: AuraClientLike): Promise<AuraResponse<TokenPair>>;
|
|
1103
1105
|
interface FetchInit {
|
|
@@ -1228,4 +1230,4 @@ declare function abortableSleep(ms: number, signal?: AbortSignal): Promise<void>
|
|
|
1228
1230
|
/** True si l'erreur `fetch` est une annulation utilisateur (jamais rejouée). */
|
|
1229
1231
|
declare function isAbortError(err: unknown): boolean;
|
|
1230
1232
|
|
|
1231
|
-
export { type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CronJob, DEFAULT_MAX_RETRIES, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationStats, type NotificationTemplate, type NotificationTemplatePreview, type OAuthSignInOptions, type ObjectMeta, type PasswordResetRequest, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeEvent, type RealtimeListenType, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationRequest, type SerializeCookieOptions, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type SessionInfo, type SignInRequest, type SignUpRequest, type StorageBucket, type SubscribeCallback, type TokenPair, type UpdateUserRequest, type UploadResult, type User, type UserStats, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
|
1233
|
+
export { type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationStats, type NotificationTemplate, type NotificationTemplatePreview, type OAuthSignInOptions, type ObjectMeta, type PasswordResetRequest, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeEvent, type RealtimeListenType, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationRequest, type SerializeCookieOptions, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type SessionInfo, type SignInRequest, type SignUpRequest, type StorageBucket, type SubscribeCallback, type TokenPair, type UpdateUserRequest, type UploadResult, type User, type UserStats, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
package/dist/index.d.ts
CHANGED
|
@@ -1098,6 +1098,8 @@ interface AuraClientLike {
|
|
|
1098
1098
|
readonly debug?: boolean;
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
|
+
/** Timeout par défaut d'une requête HTTP (borne un serveur qui « hang »). */
|
|
1102
|
+
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
1101
1103
|
/** Point d'entrée unique du refresh, dédupliqué par client (single-flight). */
|
|
1102
1104
|
declare function refreshOnce(client: AuraClientLike): Promise<AuraResponse<TokenPair>>;
|
|
1103
1105
|
interface FetchInit {
|
|
@@ -1228,4 +1230,4 @@ declare function abortableSleep(ms: number, signal?: AbortSignal): Promise<void>
|
|
|
1228
1230
|
/** True si l'erreur `fetch` est une annulation utilisateur (jamais rejouée). */
|
|
1229
1231
|
declare function isAbortError(err: unknown): boolean;
|
|
1230
1232
|
|
|
1231
|
-
export { type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CronJob, DEFAULT_MAX_RETRIES, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationStats, type NotificationTemplate, type NotificationTemplatePreview, type OAuthSignInOptions, type ObjectMeta, type PasswordResetRequest, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeEvent, type RealtimeListenType, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationRequest, type SerializeCookieOptions, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type SessionInfo, type SignInRequest, type SignUpRequest, type StorageBucket, type SubscribeCallback, type TokenPair, type UpdateUserRequest, type UploadResult, type User, type UserStats, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
|
1233
|
+
export { type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationStats, type NotificationTemplate, type NotificationTemplatePreview, type OAuthSignInOptions, type ObjectMeta, type PasswordResetRequest, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeEvent, type RealtimeListenType, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationRequest, type SerializeCookieOptions, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type SessionInfo, type SignInRequest, type SignUpRequest, type StorageBucket, type SubscribeCallback, type TokenPair, type UpdateUserRequest, type UploadResult, type User, type UserStats, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
package/dist/index.js
CHANGED
|
@@ -233,6 +233,56 @@ function unwrapEnvelope(body) {
|
|
|
233
233
|
}
|
|
234
234
|
return body;
|
|
235
235
|
}
|
|
236
|
+
async function readTextBounded(res, timeoutMs) {
|
|
237
|
+
const body = res.body;
|
|
238
|
+
if (!body || typeof body.getReader !== "function" || !(timeoutMs > 0)) {
|
|
239
|
+
try {
|
|
240
|
+
return { ok: true, text: await res.text() };
|
|
241
|
+
} catch (err) {
|
|
242
|
+
return {
|
|
243
|
+
ok: false,
|
|
244
|
+
timedOut: false,
|
|
245
|
+
message: err instanceof Error ? err.message : "read failed"
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
let timer;
|
|
250
|
+
const TIMED_OUT = /* @__PURE__ */ Symbol("body-timeout");
|
|
251
|
+
try {
|
|
252
|
+
const reader = body.getReader();
|
|
253
|
+
const decoder = new TextDecoder();
|
|
254
|
+
let out = "";
|
|
255
|
+
const deadline = new Promise((resolve) => {
|
|
256
|
+
timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
|
|
257
|
+
timer.unref?.();
|
|
258
|
+
});
|
|
259
|
+
for (; ; ) {
|
|
260
|
+
const step = await Promise.race([reader.read(), deadline]);
|
|
261
|
+
if (step === TIMED_OUT) {
|
|
262
|
+
await reader.cancel().catch(() => {
|
|
263
|
+
});
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
timedOut: true,
|
|
267
|
+
message: `Response body not fully received within ${timeoutMs}ms`
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (step.done) {
|
|
271
|
+
out += decoder.decode();
|
|
272
|
+
return { ok: true, text: out };
|
|
273
|
+
}
|
|
274
|
+
if (step.value) out += decoder.decode(step.value, { stream: true });
|
|
275
|
+
}
|
|
276
|
+
} catch (err) {
|
|
277
|
+
return {
|
|
278
|
+
ok: false,
|
|
279
|
+
timedOut: false,
|
|
280
|
+
message: err instanceof Error ? err.message : "read failed"
|
|
281
|
+
};
|
|
282
|
+
} finally {
|
|
283
|
+
if (timer) clearTimeout(timer);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
236
286
|
var inflightRefreshes = /* @__PURE__ */ new WeakMap();
|
|
237
287
|
async function doRefresh(client) {
|
|
238
288
|
const store = client.authStore;
|
|
@@ -246,26 +296,57 @@ async function doRefresh(client) {
|
|
|
246
296
|
const epoch = store.removalEpoch;
|
|
247
297
|
const headers = { "Content-Type": "application/json" };
|
|
248
298
|
if (client.apiKey) headers["apikey"] = client.apiKey;
|
|
299
|
+
const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
300
|
+
let timedOut = false;
|
|
301
|
+
let refreshTimer;
|
|
302
|
+
let refreshSignal;
|
|
303
|
+
if (timeoutMs > 0 && typeof AbortController !== "undefined") {
|
|
304
|
+
const ctrl = new AbortController();
|
|
305
|
+
refreshTimer = setTimeout(() => {
|
|
306
|
+
timedOut = true;
|
|
307
|
+
ctrl.abort();
|
|
308
|
+
}, timeoutMs);
|
|
309
|
+
refreshTimer.unref?.();
|
|
310
|
+
refreshSignal = ctrl.signal;
|
|
311
|
+
}
|
|
312
|
+
const clearRefreshTimer = () => {
|
|
313
|
+
if (refreshTimer) {
|
|
314
|
+
clearTimeout(refreshTimer);
|
|
315
|
+
refreshTimer = void 0;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
249
318
|
let res;
|
|
250
319
|
try {
|
|
251
320
|
res = await client.fetchFn(`${client.baseUrl}/v1/auth/${client.projectId}/refresh`, {
|
|
252
321
|
method: "POST",
|
|
253
322
|
headers,
|
|
254
|
-
body: serializeBody({ refresh_token: rt })
|
|
323
|
+
body: serializeBody({ refresh_token: rt }),
|
|
324
|
+
signal: refreshSignal
|
|
255
325
|
});
|
|
256
326
|
} catch (err) {
|
|
327
|
+
clearRefreshTimer();
|
|
328
|
+
if (timedOut) {
|
|
329
|
+
emitAuraEvent(client.onEvent, client.debug, {
|
|
330
|
+
kind: "timeout",
|
|
331
|
+
url: `${client.baseUrl}/v1/auth/${client.projectId}/refresh`
|
|
332
|
+
});
|
|
333
|
+
}
|
|
257
334
|
return {
|
|
258
335
|
data: null,
|
|
259
336
|
error: new AuraError(0, {
|
|
260
|
-
error: { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
|
|
337
|
+
error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
|
|
261
338
|
})
|
|
262
339
|
};
|
|
263
340
|
}
|
|
341
|
+
clearRefreshTimer();
|
|
264
342
|
if (!res.ok) {
|
|
265
343
|
let body2 = null;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
344
|
+
const errRead = await readTextBounded(res, timeoutMs);
|
|
345
|
+
if (errRead.ok && errRead.text.trim() !== "") {
|
|
346
|
+
try {
|
|
347
|
+
body2 = JSON.parse(errRead.text);
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
269
350
|
}
|
|
270
351
|
const code = typeof body2?.error?.code === "string" ? body2.error.code : null;
|
|
271
352
|
const definitivelyRejected = res.status === 403 || res.status === 401 && code === "REFRESH_TOKEN_REJECTED";
|
|
@@ -277,8 +358,23 @@ async function doRefresh(client) {
|
|
|
277
358
|
return { data: null, error: new AuraError(res.status, body2) };
|
|
278
359
|
}
|
|
279
360
|
let body;
|
|
361
|
+
const okRead = await readTextBounded(res, timeoutMs);
|
|
362
|
+
if (!okRead.ok) {
|
|
363
|
+
if (okRead.timedOut) {
|
|
364
|
+
emitAuraEvent(client.onEvent, client.debug, {
|
|
365
|
+
kind: "timeout",
|
|
366
|
+
url: `${client.baseUrl}/v1/auth/${client.projectId}/refresh`
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
data: null,
|
|
371
|
+
error: new AuraError(0, {
|
|
372
|
+
error: okRead.timedOut ? { code: "timeout", message: `Refresh body not received within ${timeoutMs}ms` } : { code: "invalid_response", message: `Invalid refresh response (${okRead.message})` }
|
|
373
|
+
})
|
|
374
|
+
};
|
|
375
|
+
}
|
|
280
376
|
try {
|
|
281
|
-
body =
|
|
377
|
+
body = JSON.parse(okRead.text);
|
|
282
378
|
} catch {
|
|
283
379
|
return {
|
|
284
380
|
data: null,
|
|
@@ -371,6 +467,7 @@ async function fetchWithAuth(url, init, client) {
|
|
|
371
467
|
const isFormData = typeof FormData !== "undefined" && init.body instanceof FormData;
|
|
372
468
|
const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
373
469
|
const applyTimeout = timeoutMs > 0 && init.signal === void 0 && !isFormData;
|
|
470
|
+
const bodyTimeoutMs = init.signal === void 0 ? timeoutMs : 0;
|
|
374
471
|
let attempt = 0;
|
|
375
472
|
for (; ; ) {
|
|
376
473
|
const headers = { ...init.headers };
|
|
@@ -460,26 +557,42 @@ async function fetchWithAuth(url, init, client) {
|
|
|
460
557
|
}
|
|
461
558
|
if (!res.ok) {
|
|
462
559
|
let body2 = null;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
560
|
+
const errRead = await readTextBounded(res, bodyTimeoutMs);
|
|
561
|
+
if (errRead.ok && errRead.text.trim() !== "") {
|
|
562
|
+
try {
|
|
563
|
+
body2 = JSON.parse(errRead.text);
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
} else if (!errRead.ok && errRead.timedOut) {
|
|
567
|
+
emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
|
|
466
568
|
}
|
|
467
569
|
return { data: null, error: new AuraError(res.status, body2) };
|
|
468
570
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
571
|
+
const read = await readTextBounded(res, bodyTimeoutMs);
|
|
572
|
+
if (!read.ok) {
|
|
573
|
+
if (read.timedOut) {
|
|
574
|
+
emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
|
|
575
|
+
return {
|
|
576
|
+
data: null,
|
|
577
|
+
error: new AuraError(0, {
|
|
578
|
+
error: {
|
|
579
|
+
code: "timeout",
|
|
580
|
+
message: `Response body not fully received within ${bodyTimeoutMs}ms (HTTP ${res.status})`
|
|
581
|
+
}
|
|
582
|
+
})
|
|
583
|
+
};
|
|
584
|
+
}
|
|
473
585
|
return {
|
|
474
586
|
data: null,
|
|
475
587
|
error: new AuraError(0, {
|
|
476
588
|
error: {
|
|
477
589
|
code: "invalid_response",
|
|
478
|
-
message: `Unreadable response body (HTTP ${res.status}): ${
|
|
590
|
+
message: `Unreadable response body (HTTP ${res.status}): ${read.message}`
|
|
479
591
|
}
|
|
480
592
|
})
|
|
481
593
|
};
|
|
482
594
|
}
|
|
595
|
+
const raw = read.text;
|
|
483
596
|
if (raw.trim() === "") {
|
|
484
597
|
return { data: null, error: null };
|
|
485
598
|
}
|
|
@@ -703,4 +816,4 @@ function serializeCookieHeader(name, value, options = {}) {
|
|
|
703
816
|
return parts.join("; ");
|
|
704
817
|
}
|
|
705
818
|
|
|
706
|
-
export { AuraError, BaseAuthStore, BaseService, DEFAULT_MAX_RETRIES, LocalAuthStore, RETRYABLE_METHODS, RETRYABLE_STATUS, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
|
819
|
+
export { AuraError, BaseAuthStore, BaseService, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, LocalAuthStore, RETRYABLE_METHODS, RETRYABLE_STATUS, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurabase/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Cœur du SDK Aurabase : fetch résilient, erreurs, stores auth, types de réponse (infrastructure interne)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"core"
|
|
46
46
|
],
|
|
47
47
|
"devDependencies": {
|
|
48
|
+
"@types/node": "20.14.15",
|
|
48
49
|
"tsup": "^8.0.0",
|
|
49
50
|
"typescript": "^5.5.0",
|
|
50
51
|
"vitest": "^4.1.6"
|
|
@@ -53,6 +54,7 @@
|
|
|
53
54
|
"build": "tsup",
|
|
54
55
|
"test": "vitest run && tsc --noEmit -p tsconfig.test-d.json",
|
|
55
56
|
"test:types": "tsc --noEmit -p tsconfig.test-d.json",
|
|
56
|
-
"typecheck": "tsc --noEmit"
|
|
57
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
|
|
58
|
+
"typecheck:tests": "tsc --noEmit -p tsconfig.test.json"
|
|
57
59
|
}
|
|
58
60
|
}
|