@aurabase/core 0.7.0 → 0.7.2
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 +178 -82
- package/dist/index.d.cts +62 -27
- package/dist/index.d.ts +62 -27
- package/dist/index.js +178 -82
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -76,6 +76,12 @@ var AuraError = class _AuraError extends Error {
|
|
|
76
76
|
const hint = body?.error?.hint;
|
|
77
77
|
if (typeof hint === "string" && hint) {
|
|
78
78
|
this.hint = hint;
|
|
79
|
+
} else {
|
|
80
|
+
const serverDetails = JSON.stringify(body ?? "");
|
|
81
|
+
const isJsonCastError = rawMessage.includes("invalid input syntax for type json") || serverDetails.includes("22P02") && serverDetails.includes("json");
|
|
82
|
+
if (isJsonCastError) {
|
|
83
|
+
this.hint = "To filter on a text value inside a JSON column, use the '->>' operator instead of '->' (e.g. .eq('metadata->>theme', 'dark')).";
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
86
|
const rawCode = typeof body?.error?.code === "string" ? body.error.code : void 0;
|
|
81
87
|
const canonical = rawCode ? normalizeErrorCode(rawCode) : void 0;
|
|
@@ -96,8 +102,12 @@ var AuraError = class _AuraError extends Error {
|
|
|
96
102
|
this.code = "SESSION_REVOKED";
|
|
97
103
|
} else if (status === 401 && lowerMsg.includes("expired")) {
|
|
98
104
|
this.code = "SESSION_EXPIRED";
|
|
99
|
-
} else if (status === 400 && lowerMsg.includes("password")) {
|
|
105
|
+
} else if (status === 400 && (lowerMsg.includes("current password") || lowerMsg.includes("old password") || lowerMsg.includes("password") && lowerMsg.includes("required"))) {
|
|
106
|
+
this.code = lowerMsg.includes("incorrect") || lowerMsg.includes("invalid") ? "INVALID_CURRENT_PASSWORD" : "CURRENT_PASSWORD_REQUIRED";
|
|
107
|
+
} else if (status === 400 && lowerMsg.includes("password") && (lowerMsg.includes("weak") || lowerMsg.includes("length") || lowerMsg.includes("strength") || lowerMsg.includes("character") || lowerMsg.includes("digit") || lowerMsg.includes("pwned") || lowerMsg.includes("too short") || lowerMsg.includes("too common"))) {
|
|
100
108
|
this.code = "WEAK_PASSWORD";
|
|
109
|
+
} else if (status === 400 && lowerMsg.includes("password")) {
|
|
110
|
+
this.code = "INVALID_PASSWORD";
|
|
101
111
|
} else if (status === 409 && (lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
|
|
102
112
|
this.code = "EMAIL_ALREADY_IN_USE";
|
|
103
113
|
} else if (status === 404 && lowerMsg.includes("user not found")) {
|
|
@@ -279,6 +289,33 @@ var AUTH_SETTINGS_WRITABLE_FIELDS = [
|
|
|
279
289
|
"hook_secret"
|
|
280
290
|
];
|
|
281
291
|
|
|
292
|
+
// src/cookie-utils.ts
|
|
293
|
+
function parseCookieHeader(header) {
|
|
294
|
+
const result = {};
|
|
295
|
+
if (!header) return result;
|
|
296
|
+
for (const pair of header.split(";")) {
|
|
297
|
+
const idx = pair.indexOf("=");
|
|
298
|
+
if (idx === -1) continue;
|
|
299
|
+
const key = pair.slice(0, idx).trim();
|
|
300
|
+
const value = pair.slice(idx + 1).trim();
|
|
301
|
+
if (key) result[key] = decodeURIComponent(value);
|
|
302
|
+
}
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
function sameSiteAttribute(v) {
|
|
306
|
+
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
307
|
+
}
|
|
308
|
+
function serializeCookieHeader(name, value, options = {}) {
|
|
309
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
310
|
+
if (options.path) parts.push(`Path=${options.path}`);
|
|
311
|
+
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
312
|
+
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
313
|
+
if (options.secure) parts.push("Secure");
|
|
314
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
315
|
+
if (options.httpOnly) parts.push("HttpOnly");
|
|
316
|
+
return parts.join("; ");
|
|
317
|
+
}
|
|
318
|
+
|
|
282
319
|
// src/stores/BaseAuthStore.ts
|
|
283
320
|
function decodeJwtPayload(token) {
|
|
284
321
|
try {
|
|
@@ -323,6 +360,7 @@ var BaseAuthStore = class {
|
|
|
323
360
|
* le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
|
|
324
361
|
*/
|
|
325
362
|
persistent = false;
|
|
363
|
+
key;
|
|
326
364
|
_token = null;
|
|
327
365
|
_refreshToken = null;
|
|
328
366
|
_user = null;
|
|
@@ -333,6 +371,9 @@ var BaseAuthStore = class {
|
|
|
333
371
|
/** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
|
|
334
372
|
_onEvent;
|
|
335
373
|
_debug = false;
|
|
374
|
+
constructor(key = "aura-auth") {
|
|
375
|
+
this.key = key;
|
|
376
|
+
}
|
|
336
377
|
// -- Getters ----------------------------------------------------------------
|
|
337
378
|
/** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
|
|
338
379
|
get removalEpoch() {
|
|
@@ -394,6 +435,91 @@ var BaseAuthStore = class {
|
|
|
394
435
|
_emitStorageBlocked(op) {
|
|
395
436
|
emitAuraEvent(this._onEvent, this._debug, { kind: "storage-blocked", op });
|
|
396
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Populate the store from a raw `Cookie` header string (server-side).
|
|
440
|
+
* Supports both single cookie `${this.key}=...` and numbered chunk cookies `${this.key}.0=...`, `${this.key}.1=...`
|
|
441
|
+
* automatically reassembled in order (AUTH-010).
|
|
442
|
+
*/
|
|
443
|
+
loadFromCookie(cookieString) {
|
|
444
|
+
if (!cookieString) return;
|
|
445
|
+
const entries = cookieString.split(";").map((c) => c.trim()).filter(Boolean);
|
|
446
|
+
let encodedData = null;
|
|
447
|
+
const prefix = `${this.key}=`;
|
|
448
|
+
const singleEntry = entries.find((c) => c.startsWith(prefix));
|
|
449
|
+
if (singleEntry) {
|
|
450
|
+
encodedData = singleEntry.slice(prefix.length);
|
|
451
|
+
} else {
|
|
452
|
+
const escapedKey = this.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
453
|
+
const chunkRegex = new RegExp(`^${escapedKey}\\.(\\d+)=(.*)$`);
|
|
454
|
+
const chunks = [];
|
|
455
|
+
for (const entry of entries) {
|
|
456
|
+
const match = entry.match(chunkRegex);
|
|
457
|
+
if (match) {
|
|
458
|
+
chunks.push({
|
|
459
|
+
index: parseInt(match[1], 10),
|
|
460
|
+
value: match[2]
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (chunks.length > 0) {
|
|
465
|
+
chunks.sort((a, b) => a.index - b.index);
|
|
466
|
+
encodedData = chunks.map((c) => c.value).join("");
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (!encodedData) return;
|
|
470
|
+
try {
|
|
471
|
+
const raw = decodeURIComponent(encodedData);
|
|
472
|
+
const parsed = JSON.parse(raw);
|
|
473
|
+
const user = parsed.token ? userFromAccessToken(parsed.token) : null;
|
|
474
|
+
if (parsed.token && parsed.refreshToken && user) {
|
|
475
|
+
this.save(parsed.token, parsed.refreshToken, user);
|
|
476
|
+
}
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Serialize the session tokens into an array of `Set-Cookie` header strings.
|
|
482
|
+
* If the payload exceeds `maxChunkSize` (default 3500 bytes), it is automatically split into
|
|
483
|
+
* numbered chunks `${key}.0`, `${key}.1`, etc. to avoid browser cookie size limits (AUTH-010).
|
|
484
|
+
*/
|
|
485
|
+
exportToCookies(options = {}, maxChunkSize = 3500) {
|
|
486
|
+
const data = {
|
|
487
|
+
token: this.token,
|
|
488
|
+
refreshToken: this.refreshToken
|
|
489
|
+
};
|
|
490
|
+
const encoded = encodeURIComponent(JSON.stringify(data));
|
|
491
|
+
const buildCookie = (name, value) => {
|
|
492
|
+
const parts = [`${name}=${value}`];
|
|
493
|
+
if (options.path) parts.push(`Path=${options.path}`);
|
|
494
|
+
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
495
|
+
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
496
|
+
if (options.secure) parts.push("Secure");
|
|
497
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
498
|
+
if (options.httpOnly) parts.push("HttpOnly");
|
|
499
|
+
return parts.join("; ");
|
|
500
|
+
};
|
|
501
|
+
const limit = Math.max(1, maxChunkSize);
|
|
502
|
+
if (encoded.length <= limit) {
|
|
503
|
+
return [buildCookie(this.key, encoded)];
|
|
504
|
+
}
|
|
505
|
+
const cookies = [];
|
|
506
|
+
let offset = 0;
|
|
507
|
+
let index = 0;
|
|
508
|
+
while (offset < encoded.length) {
|
|
509
|
+
const chunk = encoded.slice(offset, offset + limit);
|
|
510
|
+
cookies.push(buildCookie(`${this.key}.${index}`, chunk));
|
|
511
|
+
offset += limit;
|
|
512
|
+
index++;
|
|
513
|
+
}
|
|
514
|
+
return cookies;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Serialize the session tokens into a single `Set-Cookie` value.
|
|
518
|
+
* Note: If the token payload may exceed 3.5KB, use `exportToCookies()` instead for automatic chunking (AUTH-010).
|
|
519
|
+
*/
|
|
520
|
+
exportToCookie(options = {}) {
|
|
521
|
+
return this.exportToCookies(options, Infinity)[0];
|
|
522
|
+
}
|
|
397
523
|
// -- Event emitter ----------------------------------------------------------
|
|
398
524
|
/**
|
|
399
525
|
* Register a callback that fires whenever the auth state changes.
|
|
@@ -437,14 +563,42 @@ function unwrapEnvelope(body) {
|
|
|
437
563
|
}
|
|
438
564
|
return body;
|
|
439
565
|
}
|
|
566
|
+
function parseJsonBody(raw, parseBigIntAsString) {
|
|
567
|
+
if (parseBigIntAsString) {
|
|
568
|
+
const safe = raw.replace(/([:\[,]\s*)(-?\d{16,})(?=\s*[,}\]])/g, '$1"$2"');
|
|
569
|
+
return JSON.parse(safe);
|
|
570
|
+
}
|
|
571
|
+
return JSON.parse(raw);
|
|
572
|
+
}
|
|
440
573
|
function parseContentRangeTotal(headerVal) {
|
|
441
|
-
|
|
574
|
+
return parseContentRangeDetails(headerVal).total;
|
|
575
|
+
}
|
|
576
|
+
function parseContentRangeDetails(headerVal) {
|
|
577
|
+
if (!headerVal) return { total: void 0, hasMore: void 0 };
|
|
442
578
|
const parts = headerVal.split("/");
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
579
|
+
const totalStr = parts[1];
|
|
580
|
+
let total;
|
|
581
|
+
if (totalStr && totalStr !== "*") {
|
|
582
|
+
const n = parseInt(totalStr, 10);
|
|
583
|
+
if (!Number.isNaN(n)) total = n;
|
|
584
|
+
}
|
|
585
|
+
let hasMore;
|
|
586
|
+
const rangePart = parts[0];
|
|
587
|
+
if (rangePart) {
|
|
588
|
+
const rangeBounds = rangePart.split("-");
|
|
589
|
+
if (rangeBounds.length === 2) {
|
|
590
|
+
const start = parseInt(rangeBounds[0], 10);
|
|
591
|
+
const end = parseInt(rangeBounds[1], 10);
|
|
592
|
+
if (!Number.isNaN(start) && !Number.isNaN(end)) {
|
|
593
|
+
if (total !== void 0) {
|
|
594
|
+
hasMore = end + 1 < total;
|
|
595
|
+
} else if (end - start + 1 >= 1e3) {
|
|
596
|
+
hasMore = true;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
446
600
|
}
|
|
447
|
-
return
|
|
601
|
+
return { total, hasMore };
|
|
448
602
|
}
|
|
449
603
|
async function readTextBounded(res, timeoutMs) {
|
|
450
604
|
const body = res.body;
|
|
@@ -837,7 +991,7 @@ async function fetchWithAuth(url, init, client) {
|
|
|
837
991
|
}
|
|
838
992
|
let body;
|
|
839
993
|
try {
|
|
840
|
-
body =
|
|
994
|
+
body = parseJsonBody(raw, client.parseBigIntAsString === true);
|
|
841
995
|
} catch {
|
|
842
996
|
const ctRaw = res.headers.get("Content-Type") ?? "unknown";
|
|
843
997
|
const ct = ctRaw.length > 64 ? `${ctRaw.slice(0, 64)}\u2026` : ctRaw;
|
|
@@ -853,10 +1007,21 @@ async function fetchWithAuth(url, init, client) {
|
|
|
853
1007
|
}
|
|
854
1008
|
const unwrapped = unwrapEnvelope(body);
|
|
855
1009
|
const cr = res.headers.get("content-range") ?? res.headers.get("Content-Range");
|
|
856
|
-
const
|
|
1010
|
+
const crDetails = parseContentRangeDetails(cr);
|
|
1011
|
+
const totalFromCr = crDetails.total;
|
|
1012
|
+
const hasMore = crDetails.hasMore;
|
|
857
1013
|
const rawMeta = body !== null && typeof body === "object" && "meta" in body ? body.meta : void 0;
|
|
858
|
-
const meta = rawMeta !== void 0 ? rawMeta
|
|
1014
|
+
const meta = rawMeta !== void 0 ? { ...rawMeta, ...hasMore !== void 0 ? { has_more: hasMore } : {} } : totalFromCr !== void 0 || hasMore !== void 0 ? {
|
|
1015
|
+
...totalFromCr !== void 0 ? { total: totalFromCr } : {},
|
|
1016
|
+
...hasMore !== void 0 ? { has_more: hasMore } : {}
|
|
1017
|
+
} : void 0;
|
|
859
1018
|
const count = meta?.total !== void 0 ? meta.total : totalFromCr ?? null;
|
|
1019
|
+
const isDev = Boolean(client.debug) || typeof globalThis !== "undefined" && globalThis.process?.env?.NODE_ENV === "development";
|
|
1020
|
+
if (hasMore && isDev) {
|
|
1021
|
+
console.warn(
|
|
1022
|
+
"[Aurabase] Query result reached pagination limit of 1,000 rows. Use .range() to paginate further results."
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
860
1025
|
if (unwrapped === null) {
|
|
861
1026
|
return { data: null, error: null, meta, count };
|
|
862
1027
|
}
|
|
@@ -888,45 +1053,16 @@ var BaseService = class {
|
|
|
888
1053
|
}
|
|
889
1054
|
};
|
|
890
1055
|
|
|
891
|
-
// src/cookie-utils.ts
|
|
892
|
-
function parseCookieHeader(header) {
|
|
893
|
-
const result = {};
|
|
894
|
-
if (!header) return result;
|
|
895
|
-
for (const pair of header.split(";")) {
|
|
896
|
-
const idx = pair.indexOf("=");
|
|
897
|
-
if (idx === -1) continue;
|
|
898
|
-
const key = pair.slice(0, idx).trim();
|
|
899
|
-
const value = pair.slice(idx + 1).trim();
|
|
900
|
-
if (key) result[key] = decodeURIComponent(value);
|
|
901
|
-
}
|
|
902
|
-
return result;
|
|
903
|
-
}
|
|
904
|
-
function sameSiteAttribute(v) {
|
|
905
|
-
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
906
|
-
}
|
|
907
|
-
function serializeCookieHeader(name, value, options = {}) {
|
|
908
|
-
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
909
|
-
if (options.path) parts.push(`Path=${options.path}`);
|
|
910
|
-
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
911
|
-
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
912
|
-
if (options.secure) parts.push("Secure");
|
|
913
|
-
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
914
|
-
if (options.httpOnly) parts.push("HttpOnly");
|
|
915
|
-
return parts.join("; ");
|
|
916
|
-
}
|
|
917
|
-
|
|
918
1056
|
// src/stores/LocalAuthStore.ts
|
|
919
1057
|
var LocalAuthStore = class extends BaseAuthStore {
|
|
920
1058
|
/** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
|
|
921
1059
|
persistent = true;
|
|
922
|
-
key;
|
|
923
1060
|
/**
|
|
924
1061
|
* @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
|
|
925
1062
|
* `aura-${clientScope(baseUrl)}-auth`.
|
|
926
1063
|
*/
|
|
927
1064
|
constructor(key) {
|
|
928
|
-
super();
|
|
929
|
-
this.key = key;
|
|
1065
|
+
super(key);
|
|
930
1066
|
this._loadFromStorage();
|
|
931
1067
|
}
|
|
932
1068
|
// -- Overrides --------------------------------------------------------------
|
|
@@ -997,48 +1133,6 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
997
1133
|
this._emitStorageBlocked("removeItem");
|
|
998
1134
|
}
|
|
999
1135
|
}
|
|
1000
|
-
// -- Cookie helpers (SSR) ---------------------------------------------------
|
|
1001
|
-
/**
|
|
1002
|
-
* Populate the store from a raw `Cookie` header string (server-side).
|
|
1003
|
-
* Looks for the key matching `this.key`.
|
|
1004
|
-
*
|
|
1005
|
-
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
1006
|
-
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
1007
|
-
*/
|
|
1008
|
-
loadFromCookie(cookieString) {
|
|
1009
|
-
const prefix = `${this.key}=`;
|
|
1010
|
-
const entry = cookieString.split(";").map((c) => c.trim()).find((c) => c.startsWith(prefix));
|
|
1011
|
-
if (!entry) return;
|
|
1012
|
-
try {
|
|
1013
|
-
const raw = decodeURIComponent(entry.slice(prefix.length));
|
|
1014
|
-
const parsed = JSON.parse(raw);
|
|
1015
|
-
const user = parsed.token ? userFromAccessToken(parsed.token) : null;
|
|
1016
|
-
if (parsed.token && parsed.refreshToken && user) {
|
|
1017
|
-
super.save(parsed.token, parsed.refreshToken, user);
|
|
1018
|
-
}
|
|
1019
|
-
} catch {
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
/**
|
|
1023
|
-
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
1024
|
-
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
1025
|
-
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
1026
|
-
*/
|
|
1027
|
-
exportToCookie(options = {}) {
|
|
1028
|
-
const data = {
|
|
1029
|
-
token: this.token,
|
|
1030
|
-
refreshToken: this.refreshToken
|
|
1031
|
-
};
|
|
1032
|
-
const encoded = encodeURIComponent(JSON.stringify(data));
|
|
1033
|
-
const parts = [`${this.key}=${encoded}`];
|
|
1034
|
-
if (options.path) parts.push(`Path=${options.path}`);
|
|
1035
|
-
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
1036
|
-
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
1037
|
-
if (options.secure) parts.push("Secure");
|
|
1038
|
-
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
1039
|
-
if (options.httpOnly) parts.push("HttpOnly");
|
|
1040
|
-
return parts.join("; ");
|
|
1041
|
-
}
|
|
1042
1136
|
// -- Private ----------------------------------------------------------------
|
|
1043
1137
|
_getStorage() {
|
|
1044
1138
|
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
|
|
@@ -1051,7 +1145,9 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
1051
1145
|
const env = proc.env;
|
|
1052
1146
|
const isTest = !!(env?.VITEST || env?.NODE_ENV === "test" || env?.JEST_WORKER_ID);
|
|
1053
1147
|
const execArgv = proc.execArgv;
|
|
1054
|
-
const hasFlag = Array.isArray(execArgv) && execArgv.some(
|
|
1148
|
+
const hasFlag = Array.isArray(execArgv) && execArgv.some(
|
|
1149
|
+
(a) => typeof a === "string" && a.startsWith("--localstorage-file")
|
|
1150
|
+
);
|
|
1055
1151
|
if (isTest || hasFlag) {
|
|
1056
1152
|
try {
|
|
1057
1153
|
const s = g.localStorage;
|
package/dist/index.d.cts
CHANGED
|
@@ -102,6 +102,10 @@ interface Meta {
|
|
|
102
102
|
* échoue avec un 400 explicite plutôt que d'émettre ce champ.
|
|
103
103
|
*/
|
|
104
104
|
next_cursor?: string;
|
|
105
|
+
/**
|
|
106
|
+
* DB-005 : Indique si la requête a été tronquée par le serveur (ex: limite 1 000 lignes de PostgREST).
|
|
107
|
+
*/
|
|
108
|
+
has_more?: boolean;
|
|
105
109
|
}
|
|
106
110
|
/**
|
|
107
111
|
* Réponse en SUCCÈS porteuse de contenu. `error` y est `null` par construction.
|
|
@@ -203,6 +207,8 @@ interface User {
|
|
|
203
207
|
last_sign_in_at?: string | null;
|
|
204
208
|
}
|
|
205
209
|
interface Session {
|
|
210
|
+
/** Identifiant unique de la session (UUID ou 'sess_...'), dérivé du claim `session_id` du JWT. */
|
|
211
|
+
id?: string;
|
|
206
212
|
access_token: string;
|
|
207
213
|
refresh_token: string;
|
|
208
214
|
token_type: string;
|
|
@@ -267,6 +273,7 @@ interface SignUpRequest {
|
|
|
267
273
|
options?: {
|
|
268
274
|
emailRedirectTo?: string;
|
|
269
275
|
data?: Record<string, unknown>;
|
|
276
|
+
preventEmailEnumeration?: boolean;
|
|
270
277
|
};
|
|
271
278
|
}
|
|
272
279
|
interface SignInRequest {
|
|
@@ -369,14 +376,25 @@ interface MfaEnrollResponse {
|
|
|
369
376
|
* Pour une passkey, utilisez `auth.mfa.webauthn.verify`.
|
|
370
377
|
*/
|
|
371
378
|
type MfaVerifyRequest = {
|
|
372
|
-
mfa_token
|
|
379
|
+
mfa_token?: string;
|
|
380
|
+
mfaToken?: string;
|
|
381
|
+
challenge_id?: string;
|
|
382
|
+
challengeId?: string;
|
|
373
383
|
code: string;
|
|
374
384
|
} | {
|
|
375
|
-
mfa_token
|
|
385
|
+
mfa_token?: string;
|
|
386
|
+
mfaToken?: string;
|
|
387
|
+
challenge_id?: string;
|
|
388
|
+
challengeId?: string;
|
|
376
389
|
recovery_code: string;
|
|
390
|
+
recoveryCode?: string;
|
|
377
391
|
} | {
|
|
378
|
-
mfa_token
|
|
392
|
+
mfa_token?: string;
|
|
393
|
+
mfaToken?: string;
|
|
394
|
+
challenge_id?: string;
|
|
395
|
+
challengeId?: string;
|
|
379
396
|
sms_code: string;
|
|
397
|
+
smsCode?: string;
|
|
380
398
|
};
|
|
381
399
|
/** Facteur MFA utilisable pour relever un défi (`auth.mfa.factors`). */
|
|
382
400
|
type MfaFactorType = 'totp' | 'webauthn' | 'sms';
|
|
@@ -1558,6 +1576,12 @@ interface AuraClientOptions {
|
|
|
1558
1576
|
* Défaut `false`.
|
|
1559
1577
|
*/
|
|
1560
1578
|
debug?: boolean;
|
|
1579
|
+
/**
|
|
1580
|
+
* DB-006: Preserves 64-bit integer precision by parsing BIGINT values as strings
|
|
1581
|
+
* instead of JavaScript IEEE 754 floats when exceeding Number.MAX_SAFE_INTEGER (9007199254740991).
|
|
1582
|
+
* Default: false (native JSON.parse).
|
|
1583
|
+
*/
|
|
1584
|
+
parseBigIntAsString?: boolean;
|
|
1561
1585
|
}
|
|
1562
1586
|
interface IdentityInfo {
|
|
1563
1587
|
id: string;
|
|
@@ -1839,6 +1863,19 @@ type AuthChangeCallback = (token: string | null, user: User | null) => void;
|
|
|
1839
1863
|
* Check whether a JWT has expired (or will expire within `marginSec` seconds).
|
|
1840
1864
|
*/
|
|
1841
1865
|
declare function isTokenExpired(token: string, marginSec?: number): boolean;
|
|
1866
|
+
interface CookieOptions {
|
|
1867
|
+
path?: string;
|
|
1868
|
+
domain?: string;
|
|
1869
|
+
maxAge?: number;
|
|
1870
|
+
secure?: boolean;
|
|
1871
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
1872
|
+
httpOnly?: boolean;
|
|
1873
|
+
}
|
|
1874
|
+
interface StoredAuth {
|
|
1875
|
+
token: string | null;
|
|
1876
|
+
refreshToken: string | null;
|
|
1877
|
+
user: User | null;
|
|
1878
|
+
}
|
|
1842
1879
|
/**
|
|
1843
1880
|
* In-memory auth store with an event-emitter pattern.
|
|
1844
1881
|
* Can be extended (see LocalAuthStore) for persistence.
|
|
@@ -1850,6 +1887,7 @@ declare class BaseAuthStore {
|
|
|
1850
1887
|
* le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
|
|
1851
1888
|
*/
|
|
1852
1889
|
readonly persistent: boolean;
|
|
1890
|
+
protected readonly key: string;
|
|
1853
1891
|
private _token;
|
|
1854
1892
|
private _refreshToken;
|
|
1855
1893
|
private _user;
|
|
@@ -1860,6 +1898,7 @@ declare class BaseAuthStore {
|
|
|
1860
1898
|
/** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
|
|
1861
1899
|
private _onEvent?;
|
|
1862
1900
|
private _debug;
|
|
1901
|
+
constructor(key?: string);
|
|
1863
1902
|
/** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
|
|
1864
1903
|
get removalEpoch(): number;
|
|
1865
1904
|
get token(): string | null;
|
|
@@ -1881,6 +1920,23 @@ declare class BaseAuthStore {
|
|
|
1881
1920
|
setObservabilityHook(onEvent: ((event: AuraObservabilityEvent) => void) | undefined, debug: boolean | undefined): void;
|
|
1882
1921
|
/** Émet `{kind:'storage-blocked', op}` — ne throw jamais (voir `emitAuraEvent`). */
|
|
1883
1922
|
protected _emitStorageBlocked(op: string): void;
|
|
1923
|
+
/**
|
|
1924
|
+
* Populate the store from a raw `Cookie` header string (server-side).
|
|
1925
|
+
* Supports both single cookie `${this.key}=...` and numbered chunk cookies `${this.key}.0=...`, `${this.key}.1=...`
|
|
1926
|
+
* automatically reassembled in order (AUTH-010).
|
|
1927
|
+
*/
|
|
1928
|
+
loadFromCookie(cookieString: string): void;
|
|
1929
|
+
/**
|
|
1930
|
+
* Serialize the session tokens into an array of `Set-Cookie` header strings.
|
|
1931
|
+
* If the payload exceeds `maxChunkSize` (default 3500 bytes), it is automatically split into
|
|
1932
|
+
* numbered chunks `${key}.0`, `${key}.1`, etc. to avoid browser cookie size limits (AUTH-010).
|
|
1933
|
+
*/
|
|
1934
|
+
exportToCookies(options?: CookieOptions, maxChunkSize?: number): string[];
|
|
1935
|
+
/**
|
|
1936
|
+
* Serialize the session tokens into a single `Set-Cookie` value.
|
|
1937
|
+
* Note: If the token payload may exceed 3.5KB, use `exportToCookies()` instead for automatic chunking (AUTH-010).
|
|
1938
|
+
*/
|
|
1939
|
+
exportToCookie(options?: CookieOptions): string;
|
|
1884
1940
|
/**
|
|
1885
1941
|
* Register a callback that fires whenever the auth state changes.
|
|
1886
1942
|
* Returns an unsubscribe function.
|
|
@@ -1930,6 +1986,8 @@ interface AuraClientLike {
|
|
|
1930
1986
|
readonly onEvent?: (event: AuraObservabilityEvent) => void;
|
|
1931
1987
|
/** Si `true`, chaque `AuraObservabilityEvent` est aussi journalisé via `console.debug`. */
|
|
1932
1988
|
readonly debug?: boolean;
|
|
1989
|
+
/** DB-006: Preserves 64-bit integers as strings when true. */
|
|
1990
|
+
readonly parseBigIntAsString?: boolean;
|
|
1933
1991
|
}
|
|
1934
1992
|
|
|
1935
1993
|
/**
|
|
@@ -2038,14 +2096,6 @@ declare function clientScope(baseUrl: string): string;
|
|
|
2038
2096
|
/** JSON.stringify avec support des bigint (sérialisés en string). */
|
|
2039
2097
|
declare function serializeBody(body: unknown): string;
|
|
2040
2098
|
|
|
2041
|
-
interface CookieOptions {
|
|
2042
|
-
path?: string;
|
|
2043
|
-
domain?: string;
|
|
2044
|
-
maxAge?: number;
|
|
2045
|
-
secure?: boolean;
|
|
2046
|
-
sameSite?: 'strict' | 'lax' | 'none';
|
|
2047
|
-
httpOnly?: boolean;
|
|
2048
|
-
}
|
|
2049
2099
|
/**
|
|
2050
2100
|
* Auth store backed by `localStorage` (browser) with cookie helpers for SSR.
|
|
2051
2101
|
*
|
|
@@ -2062,7 +2112,6 @@ interface CookieOptions {
|
|
|
2062
2112
|
declare class LocalAuthStore extends BaseAuthStore {
|
|
2063
2113
|
/** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
|
|
2064
2114
|
readonly persistent = true;
|
|
2065
|
-
private readonly key;
|
|
2066
2115
|
/**
|
|
2067
2116
|
* @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
|
|
2068
2117
|
* `aura-${clientScope(baseUrl)}-auth`.
|
|
@@ -2083,24 +2132,10 @@ declare class LocalAuthStore extends BaseAuthStore {
|
|
|
2083
2132
|
setItem(key: string, value: string): void;
|
|
2084
2133
|
getItem(key: string): string | null;
|
|
2085
2134
|
removeItem(key: string): void;
|
|
2086
|
-
/**
|
|
2087
|
-
* Populate the store from a raw `Cookie` header string (server-side).
|
|
2088
|
-
* Looks for the key matching `this.key`.
|
|
2089
|
-
*
|
|
2090
|
-
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
2091
|
-
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
2092
|
-
*/
|
|
2093
|
-
loadFromCookie(cookieString: string): void;
|
|
2094
|
-
/**
|
|
2095
|
-
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
2096
|
-
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
2097
|
-
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
2098
|
-
*/
|
|
2099
|
-
exportToCookie(options?: CookieOptions): string;
|
|
2100
2135
|
private _getStorage;
|
|
2101
2136
|
private _loadFromStorage;
|
|
2102
2137
|
private _persist;
|
|
2103
2138
|
private _removeStorage;
|
|
2104
2139
|
}
|
|
2105
2140
|
|
|
2106
|
-
export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, 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, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, 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 MfaFactorType, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasskeyInfo, type PasskeyRegisterResult, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
|
2141
|
+
export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, 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, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CookieOptions, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, 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 MfaFactorType, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasskeyInfo, type PasskeyRegisterResult, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type StoredAuth, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
package/dist/index.d.ts
CHANGED
|
@@ -102,6 +102,10 @@ interface Meta {
|
|
|
102
102
|
* échoue avec un 400 explicite plutôt que d'émettre ce champ.
|
|
103
103
|
*/
|
|
104
104
|
next_cursor?: string;
|
|
105
|
+
/**
|
|
106
|
+
* DB-005 : Indique si la requête a été tronquée par le serveur (ex: limite 1 000 lignes de PostgREST).
|
|
107
|
+
*/
|
|
108
|
+
has_more?: boolean;
|
|
105
109
|
}
|
|
106
110
|
/**
|
|
107
111
|
* Réponse en SUCCÈS porteuse de contenu. `error` y est `null` par construction.
|
|
@@ -203,6 +207,8 @@ interface User {
|
|
|
203
207
|
last_sign_in_at?: string | null;
|
|
204
208
|
}
|
|
205
209
|
interface Session {
|
|
210
|
+
/** Identifiant unique de la session (UUID ou 'sess_...'), dérivé du claim `session_id` du JWT. */
|
|
211
|
+
id?: string;
|
|
206
212
|
access_token: string;
|
|
207
213
|
refresh_token: string;
|
|
208
214
|
token_type: string;
|
|
@@ -267,6 +273,7 @@ interface SignUpRequest {
|
|
|
267
273
|
options?: {
|
|
268
274
|
emailRedirectTo?: string;
|
|
269
275
|
data?: Record<string, unknown>;
|
|
276
|
+
preventEmailEnumeration?: boolean;
|
|
270
277
|
};
|
|
271
278
|
}
|
|
272
279
|
interface SignInRequest {
|
|
@@ -369,14 +376,25 @@ interface MfaEnrollResponse {
|
|
|
369
376
|
* Pour une passkey, utilisez `auth.mfa.webauthn.verify`.
|
|
370
377
|
*/
|
|
371
378
|
type MfaVerifyRequest = {
|
|
372
|
-
mfa_token
|
|
379
|
+
mfa_token?: string;
|
|
380
|
+
mfaToken?: string;
|
|
381
|
+
challenge_id?: string;
|
|
382
|
+
challengeId?: string;
|
|
373
383
|
code: string;
|
|
374
384
|
} | {
|
|
375
|
-
mfa_token
|
|
385
|
+
mfa_token?: string;
|
|
386
|
+
mfaToken?: string;
|
|
387
|
+
challenge_id?: string;
|
|
388
|
+
challengeId?: string;
|
|
376
389
|
recovery_code: string;
|
|
390
|
+
recoveryCode?: string;
|
|
377
391
|
} | {
|
|
378
|
-
mfa_token
|
|
392
|
+
mfa_token?: string;
|
|
393
|
+
mfaToken?: string;
|
|
394
|
+
challenge_id?: string;
|
|
395
|
+
challengeId?: string;
|
|
379
396
|
sms_code: string;
|
|
397
|
+
smsCode?: string;
|
|
380
398
|
};
|
|
381
399
|
/** Facteur MFA utilisable pour relever un défi (`auth.mfa.factors`). */
|
|
382
400
|
type MfaFactorType = 'totp' | 'webauthn' | 'sms';
|
|
@@ -1558,6 +1576,12 @@ interface AuraClientOptions {
|
|
|
1558
1576
|
* Défaut `false`.
|
|
1559
1577
|
*/
|
|
1560
1578
|
debug?: boolean;
|
|
1579
|
+
/**
|
|
1580
|
+
* DB-006: Preserves 64-bit integer precision by parsing BIGINT values as strings
|
|
1581
|
+
* instead of JavaScript IEEE 754 floats when exceeding Number.MAX_SAFE_INTEGER (9007199254740991).
|
|
1582
|
+
* Default: false (native JSON.parse).
|
|
1583
|
+
*/
|
|
1584
|
+
parseBigIntAsString?: boolean;
|
|
1561
1585
|
}
|
|
1562
1586
|
interface IdentityInfo {
|
|
1563
1587
|
id: string;
|
|
@@ -1839,6 +1863,19 @@ type AuthChangeCallback = (token: string | null, user: User | null) => void;
|
|
|
1839
1863
|
* Check whether a JWT has expired (or will expire within `marginSec` seconds).
|
|
1840
1864
|
*/
|
|
1841
1865
|
declare function isTokenExpired(token: string, marginSec?: number): boolean;
|
|
1866
|
+
interface CookieOptions {
|
|
1867
|
+
path?: string;
|
|
1868
|
+
domain?: string;
|
|
1869
|
+
maxAge?: number;
|
|
1870
|
+
secure?: boolean;
|
|
1871
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
1872
|
+
httpOnly?: boolean;
|
|
1873
|
+
}
|
|
1874
|
+
interface StoredAuth {
|
|
1875
|
+
token: string | null;
|
|
1876
|
+
refreshToken: string | null;
|
|
1877
|
+
user: User | null;
|
|
1878
|
+
}
|
|
1842
1879
|
/**
|
|
1843
1880
|
* In-memory auth store with an event-emitter pattern.
|
|
1844
1881
|
* Can be extended (see LocalAuthStore) for persistence.
|
|
@@ -1850,6 +1887,7 @@ declare class BaseAuthStore {
|
|
|
1850
1887
|
* le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
|
|
1851
1888
|
*/
|
|
1852
1889
|
readonly persistent: boolean;
|
|
1890
|
+
protected readonly key: string;
|
|
1853
1891
|
private _token;
|
|
1854
1892
|
private _refreshToken;
|
|
1855
1893
|
private _user;
|
|
@@ -1860,6 +1898,7 @@ declare class BaseAuthStore {
|
|
|
1860
1898
|
/** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
|
|
1861
1899
|
private _onEvent?;
|
|
1862
1900
|
private _debug;
|
|
1901
|
+
constructor(key?: string);
|
|
1863
1902
|
/** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
|
|
1864
1903
|
get removalEpoch(): number;
|
|
1865
1904
|
get token(): string | null;
|
|
@@ -1881,6 +1920,23 @@ declare class BaseAuthStore {
|
|
|
1881
1920
|
setObservabilityHook(onEvent: ((event: AuraObservabilityEvent) => void) | undefined, debug: boolean | undefined): void;
|
|
1882
1921
|
/** Émet `{kind:'storage-blocked', op}` — ne throw jamais (voir `emitAuraEvent`). */
|
|
1883
1922
|
protected _emitStorageBlocked(op: string): void;
|
|
1923
|
+
/**
|
|
1924
|
+
* Populate the store from a raw `Cookie` header string (server-side).
|
|
1925
|
+
* Supports both single cookie `${this.key}=...` and numbered chunk cookies `${this.key}.0=...`, `${this.key}.1=...`
|
|
1926
|
+
* automatically reassembled in order (AUTH-010).
|
|
1927
|
+
*/
|
|
1928
|
+
loadFromCookie(cookieString: string): void;
|
|
1929
|
+
/**
|
|
1930
|
+
* Serialize the session tokens into an array of `Set-Cookie` header strings.
|
|
1931
|
+
* If the payload exceeds `maxChunkSize` (default 3500 bytes), it is automatically split into
|
|
1932
|
+
* numbered chunks `${key}.0`, `${key}.1`, etc. to avoid browser cookie size limits (AUTH-010).
|
|
1933
|
+
*/
|
|
1934
|
+
exportToCookies(options?: CookieOptions, maxChunkSize?: number): string[];
|
|
1935
|
+
/**
|
|
1936
|
+
* Serialize the session tokens into a single `Set-Cookie` value.
|
|
1937
|
+
* Note: If the token payload may exceed 3.5KB, use `exportToCookies()` instead for automatic chunking (AUTH-010).
|
|
1938
|
+
*/
|
|
1939
|
+
exportToCookie(options?: CookieOptions): string;
|
|
1884
1940
|
/**
|
|
1885
1941
|
* Register a callback that fires whenever the auth state changes.
|
|
1886
1942
|
* Returns an unsubscribe function.
|
|
@@ -1930,6 +1986,8 @@ interface AuraClientLike {
|
|
|
1930
1986
|
readonly onEvent?: (event: AuraObservabilityEvent) => void;
|
|
1931
1987
|
/** Si `true`, chaque `AuraObservabilityEvent` est aussi journalisé via `console.debug`. */
|
|
1932
1988
|
readonly debug?: boolean;
|
|
1989
|
+
/** DB-006: Preserves 64-bit integers as strings when true. */
|
|
1990
|
+
readonly parseBigIntAsString?: boolean;
|
|
1933
1991
|
}
|
|
1934
1992
|
|
|
1935
1993
|
/**
|
|
@@ -2038,14 +2096,6 @@ declare function clientScope(baseUrl: string): string;
|
|
|
2038
2096
|
/** JSON.stringify avec support des bigint (sérialisés en string). */
|
|
2039
2097
|
declare function serializeBody(body: unknown): string;
|
|
2040
2098
|
|
|
2041
|
-
interface CookieOptions {
|
|
2042
|
-
path?: string;
|
|
2043
|
-
domain?: string;
|
|
2044
|
-
maxAge?: number;
|
|
2045
|
-
secure?: boolean;
|
|
2046
|
-
sameSite?: 'strict' | 'lax' | 'none';
|
|
2047
|
-
httpOnly?: boolean;
|
|
2048
|
-
}
|
|
2049
2099
|
/**
|
|
2050
2100
|
* Auth store backed by `localStorage` (browser) with cookie helpers for SSR.
|
|
2051
2101
|
*
|
|
@@ -2062,7 +2112,6 @@ interface CookieOptions {
|
|
|
2062
2112
|
declare class LocalAuthStore extends BaseAuthStore {
|
|
2063
2113
|
/** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
|
|
2064
2114
|
readonly persistent = true;
|
|
2065
|
-
private readonly key;
|
|
2066
2115
|
/**
|
|
2067
2116
|
* @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
|
|
2068
2117
|
* `aura-${clientScope(baseUrl)}-auth`.
|
|
@@ -2083,24 +2132,10 @@ declare class LocalAuthStore extends BaseAuthStore {
|
|
|
2083
2132
|
setItem(key: string, value: string): void;
|
|
2084
2133
|
getItem(key: string): string | null;
|
|
2085
2134
|
removeItem(key: string): void;
|
|
2086
|
-
/**
|
|
2087
|
-
* Populate the store from a raw `Cookie` header string (server-side).
|
|
2088
|
-
* Looks for the key matching `this.key`.
|
|
2089
|
-
*
|
|
2090
|
-
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
2091
|
-
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
2092
|
-
*/
|
|
2093
|
-
loadFromCookie(cookieString: string): void;
|
|
2094
|
-
/**
|
|
2095
|
-
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
2096
|
-
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
2097
|
-
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
2098
|
-
*/
|
|
2099
|
-
exportToCookie(options?: CookieOptions): string;
|
|
2100
2135
|
private _getStorage;
|
|
2101
2136
|
private _loadFromStorage;
|
|
2102
2137
|
private _persist;
|
|
2103
2138
|
private _removeStorage;
|
|
2104
2139
|
}
|
|
2105
2140
|
|
|
2106
|
-
export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, 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, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, 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 MfaFactorType, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasskeyInfo, type PasskeyRegisterResult, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
|
2141
|
+
export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, 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, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CookieOptions, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, 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 MfaFactorType, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasskeyInfo, type PasskeyRegisterResult, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type StoredAuth, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,12 @@ var AuraError = class _AuraError extends Error {
|
|
|
74
74
|
const hint = body?.error?.hint;
|
|
75
75
|
if (typeof hint === "string" && hint) {
|
|
76
76
|
this.hint = hint;
|
|
77
|
+
} else {
|
|
78
|
+
const serverDetails = JSON.stringify(body ?? "");
|
|
79
|
+
const isJsonCastError = rawMessage.includes("invalid input syntax for type json") || serverDetails.includes("22P02") && serverDetails.includes("json");
|
|
80
|
+
if (isJsonCastError) {
|
|
81
|
+
this.hint = "To filter on a text value inside a JSON column, use the '->>' operator instead of '->' (e.g. .eq('metadata->>theme', 'dark')).";
|
|
82
|
+
}
|
|
77
83
|
}
|
|
78
84
|
const rawCode = typeof body?.error?.code === "string" ? body.error.code : void 0;
|
|
79
85
|
const canonical = rawCode ? normalizeErrorCode(rawCode) : void 0;
|
|
@@ -94,8 +100,12 @@ var AuraError = class _AuraError extends Error {
|
|
|
94
100
|
this.code = "SESSION_REVOKED";
|
|
95
101
|
} else if (status === 401 && lowerMsg.includes("expired")) {
|
|
96
102
|
this.code = "SESSION_EXPIRED";
|
|
97
|
-
} else if (status === 400 && lowerMsg.includes("password")) {
|
|
103
|
+
} else if (status === 400 && (lowerMsg.includes("current password") || lowerMsg.includes("old password") || lowerMsg.includes("password") && lowerMsg.includes("required"))) {
|
|
104
|
+
this.code = lowerMsg.includes("incorrect") || lowerMsg.includes("invalid") ? "INVALID_CURRENT_PASSWORD" : "CURRENT_PASSWORD_REQUIRED";
|
|
105
|
+
} else if (status === 400 && lowerMsg.includes("password") && (lowerMsg.includes("weak") || lowerMsg.includes("length") || lowerMsg.includes("strength") || lowerMsg.includes("character") || lowerMsg.includes("digit") || lowerMsg.includes("pwned") || lowerMsg.includes("too short") || lowerMsg.includes("too common"))) {
|
|
98
106
|
this.code = "WEAK_PASSWORD";
|
|
107
|
+
} else if (status === 400 && lowerMsg.includes("password")) {
|
|
108
|
+
this.code = "INVALID_PASSWORD";
|
|
99
109
|
} else if (status === 409 && (lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
|
|
100
110
|
this.code = "EMAIL_ALREADY_IN_USE";
|
|
101
111
|
} else if (status === 404 && lowerMsg.includes("user not found")) {
|
|
@@ -277,6 +287,33 @@ var AUTH_SETTINGS_WRITABLE_FIELDS = [
|
|
|
277
287
|
"hook_secret"
|
|
278
288
|
];
|
|
279
289
|
|
|
290
|
+
// src/cookie-utils.ts
|
|
291
|
+
function parseCookieHeader(header) {
|
|
292
|
+
const result = {};
|
|
293
|
+
if (!header) return result;
|
|
294
|
+
for (const pair of header.split(";")) {
|
|
295
|
+
const idx = pair.indexOf("=");
|
|
296
|
+
if (idx === -1) continue;
|
|
297
|
+
const key = pair.slice(0, idx).trim();
|
|
298
|
+
const value = pair.slice(idx + 1).trim();
|
|
299
|
+
if (key) result[key] = decodeURIComponent(value);
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
function sameSiteAttribute(v) {
|
|
304
|
+
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
305
|
+
}
|
|
306
|
+
function serializeCookieHeader(name, value, options = {}) {
|
|
307
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
308
|
+
if (options.path) parts.push(`Path=${options.path}`);
|
|
309
|
+
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
310
|
+
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
311
|
+
if (options.secure) parts.push("Secure");
|
|
312
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
313
|
+
if (options.httpOnly) parts.push("HttpOnly");
|
|
314
|
+
return parts.join("; ");
|
|
315
|
+
}
|
|
316
|
+
|
|
280
317
|
// src/stores/BaseAuthStore.ts
|
|
281
318
|
function decodeJwtPayload(token) {
|
|
282
319
|
try {
|
|
@@ -321,6 +358,7 @@ var BaseAuthStore = class {
|
|
|
321
358
|
* le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
|
|
322
359
|
*/
|
|
323
360
|
persistent = false;
|
|
361
|
+
key;
|
|
324
362
|
_token = null;
|
|
325
363
|
_refreshToken = null;
|
|
326
364
|
_user = null;
|
|
@@ -331,6 +369,9 @@ var BaseAuthStore = class {
|
|
|
331
369
|
/** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
|
|
332
370
|
_onEvent;
|
|
333
371
|
_debug = false;
|
|
372
|
+
constructor(key = "aura-auth") {
|
|
373
|
+
this.key = key;
|
|
374
|
+
}
|
|
334
375
|
// -- Getters ----------------------------------------------------------------
|
|
335
376
|
/** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
|
|
336
377
|
get removalEpoch() {
|
|
@@ -392,6 +433,91 @@ var BaseAuthStore = class {
|
|
|
392
433
|
_emitStorageBlocked(op) {
|
|
393
434
|
emitAuraEvent(this._onEvent, this._debug, { kind: "storage-blocked", op });
|
|
394
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* Populate the store from a raw `Cookie` header string (server-side).
|
|
438
|
+
* Supports both single cookie `${this.key}=...` and numbered chunk cookies `${this.key}.0=...`, `${this.key}.1=...`
|
|
439
|
+
* automatically reassembled in order (AUTH-010).
|
|
440
|
+
*/
|
|
441
|
+
loadFromCookie(cookieString) {
|
|
442
|
+
if (!cookieString) return;
|
|
443
|
+
const entries = cookieString.split(";").map((c) => c.trim()).filter(Boolean);
|
|
444
|
+
let encodedData = null;
|
|
445
|
+
const prefix = `${this.key}=`;
|
|
446
|
+
const singleEntry = entries.find((c) => c.startsWith(prefix));
|
|
447
|
+
if (singleEntry) {
|
|
448
|
+
encodedData = singleEntry.slice(prefix.length);
|
|
449
|
+
} else {
|
|
450
|
+
const escapedKey = this.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
451
|
+
const chunkRegex = new RegExp(`^${escapedKey}\\.(\\d+)=(.*)$`);
|
|
452
|
+
const chunks = [];
|
|
453
|
+
for (const entry of entries) {
|
|
454
|
+
const match = entry.match(chunkRegex);
|
|
455
|
+
if (match) {
|
|
456
|
+
chunks.push({
|
|
457
|
+
index: parseInt(match[1], 10),
|
|
458
|
+
value: match[2]
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (chunks.length > 0) {
|
|
463
|
+
chunks.sort((a, b) => a.index - b.index);
|
|
464
|
+
encodedData = chunks.map((c) => c.value).join("");
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (!encodedData) return;
|
|
468
|
+
try {
|
|
469
|
+
const raw = decodeURIComponent(encodedData);
|
|
470
|
+
const parsed = JSON.parse(raw);
|
|
471
|
+
const user = parsed.token ? userFromAccessToken(parsed.token) : null;
|
|
472
|
+
if (parsed.token && parsed.refreshToken && user) {
|
|
473
|
+
this.save(parsed.token, parsed.refreshToken, user);
|
|
474
|
+
}
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Serialize the session tokens into an array of `Set-Cookie` header strings.
|
|
480
|
+
* If the payload exceeds `maxChunkSize` (default 3500 bytes), it is automatically split into
|
|
481
|
+
* numbered chunks `${key}.0`, `${key}.1`, etc. to avoid browser cookie size limits (AUTH-010).
|
|
482
|
+
*/
|
|
483
|
+
exportToCookies(options = {}, maxChunkSize = 3500) {
|
|
484
|
+
const data = {
|
|
485
|
+
token: this.token,
|
|
486
|
+
refreshToken: this.refreshToken
|
|
487
|
+
};
|
|
488
|
+
const encoded = encodeURIComponent(JSON.stringify(data));
|
|
489
|
+
const buildCookie = (name, value) => {
|
|
490
|
+
const parts = [`${name}=${value}`];
|
|
491
|
+
if (options.path) parts.push(`Path=${options.path}`);
|
|
492
|
+
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
493
|
+
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
494
|
+
if (options.secure) parts.push("Secure");
|
|
495
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
496
|
+
if (options.httpOnly) parts.push("HttpOnly");
|
|
497
|
+
return parts.join("; ");
|
|
498
|
+
};
|
|
499
|
+
const limit = Math.max(1, maxChunkSize);
|
|
500
|
+
if (encoded.length <= limit) {
|
|
501
|
+
return [buildCookie(this.key, encoded)];
|
|
502
|
+
}
|
|
503
|
+
const cookies = [];
|
|
504
|
+
let offset = 0;
|
|
505
|
+
let index = 0;
|
|
506
|
+
while (offset < encoded.length) {
|
|
507
|
+
const chunk = encoded.slice(offset, offset + limit);
|
|
508
|
+
cookies.push(buildCookie(`${this.key}.${index}`, chunk));
|
|
509
|
+
offset += limit;
|
|
510
|
+
index++;
|
|
511
|
+
}
|
|
512
|
+
return cookies;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Serialize the session tokens into a single `Set-Cookie` value.
|
|
516
|
+
* Note: If the token payload may exceed 3.5KB, use `exportToCookies()` instead for automatic chunking (AUTH-010).
|
|
517
|
+
*/
|
|
518
|
+
exportToCookie(options = {}) {
|
|
519
|
+
return this.exportToCookies(options, Infinity)[0];
|
|
520
|
+
}
|
|
395
521
|
// -- Event emitter ----------------------------------------------------------
|
|
396
522
|
/**
|
|
397
523
|
* Register a callback that fires whenever the auth state changes.
|
|
@@ -435,14 +561,42 @@ function unwrapEnvelope(body) {
|
|
|
435
561
|
}
|
|
436
562
|
return body;
|
|
437
563
|
}
|
|
564
|
+
function parseJsonBody(raw, parseBigIntAsString) {
|
|
565
|
+
if (parseBigIntAsString) {
|
|
566
|
+
const safe = raw.replace(/([:\[,]\s*)(-?\d{16,})(?=\s*[,}\]])/g, '$1"$2"');
|
|
567
|
+
return JSON.parse(safe);
|
|
568
|
+
}
|
|
569
|
+
return JSON.parse(raw);
|
|
570
|
+
}
|
|
438
571
|
function parseContentRangeTotal(headerVal) {
|
|
439
|
-
|
|
572
|
+
return parseContentRangeDetails(headerVal).total;
|
|
573
|
+
}
|
|
574
|
+
function parseContentRangeDetails(headerVal) {
|
|
575
|
+
if (!headerVal) return { total: void 0, hasMore: void 0 };
|
|
440
576
|
const parts = headerVal.split("/");
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
577
|
+
const totalStr = parts[1];
|
|
578
|
+
let total;
|
|
579
|
+
if (totalStr && totalStr !== "*") {
|
|
580
|
+
const n = parseInt(totalStr, 10);
|
|
581
|
+
if (!Number.isNaN(n)) total = n;
|
|
582
|
+
}
|
|
583
|
+
let hasMore;
|
|
584
|
+
const rangePart = parts[0];
|
|
585
|
+
if (rangePart) {
|
|
586
|
+
const rangeBounds = rangePart.split("-");
|
|
587
|
+
if (rangeBounds.length === 2) {
|
|
588
|
+
const start = parseInt(rangeBounds[0], 10);
|
|
589
|
+
const end = parseInt(rangeBounds[1], 10);
|
|
590
|
+
if (!Number.isNaN(start) && !Number.isNaN(end)) {
|
|
591
|
+
if (total !== void 0) {
|
|
592
|
+
hasMore = end + 1 < total;
|
|
593
|
+
} else if (end - start + 1 >= 1e3) {
|
|
594
|
+
hasMore = true;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
444
598
|
}
|
|
445
|
-
return
|
|
599
|
+
return { total, hasMore };
|
|
446
600
|
}
|
|
447
601
|
async function readTextBounded(res, timeoutMs) {
|
|
448
602
|
const body = res.body;
|
|
@@ -835,7 +989,7 @@ async function fetchWithAuth(url, init, client) {
|
|
|
835
989
|
}
|
|
836
990
|
let body;
|
|
837
991
|
try {
|
|
838
|
-
body =
|
|
992
|
+
body = parseJsonBody(raw, client.parseBigIntAsString === true);
|
|
839
993
|
} catch {
|
|
840
994
|
const ctRaw = res.headers.get("Content-Type") ?? "unknown";
|
|
841
995
|
const ct = ctRaw.length > 64 ? `${ctRaw.slice(0, 64)}\u2026` : ctRaw;
|
|
@@ -851,10 +1005,21 @@ async function fetchWithAuth(url, init, client) {
|
|
|
851
1005
|
}
|
|
852
1006
|
const unwrapped = unwrapEnvelope(body);
|
|
853
1007
|
const cr = res.headers.get("content-range") ?? res.headers.get("Content-Range");
|
|
854
|
-
const
|
|
1008
|
+
const crDetails = parseContentRangeDetails(cr);
|
|
1009
|
+
const totalFromCr = crDetails.total;
|
|
1010
|
+
const hasMore = crDetails.hasMore;
|
|
855
1011
|
const rawMeta = body !== null && typeof body === "object" && "meta" in body ? body.meta : void 0;
|
|
856
|
-
const meta = rawMeta !== void 0 ? rawMeta
|
|
1012
|
+
const meta = rawMeta !== void 0 ? { ...rawMeta, ...hasMore !== void 0 ? { has_more: hasMore } : {} } : totalFromCr !== void 0 || hasMore !== void 0 ? {
|
|
1013
|
+
...totalFromCr !== void 0 ? { total: totalFromCr } : {},
|
|
1014
|
+
...hasMore !== void 0 ? { has_more: hasMore } : {}
|
|
1015
|
+
} : void 0;
|
|
857
1016
|
const count = meta?.total !== void 0 ? meta.total : totalFromCr ?? null;
|
|
1017
|
+
const isDev = Boolean(client.debug) || typeof globalThis !== "undefined" && globalThis.process?.env?.NODE_ENV === "development";
|
|
1018
|
+
if (hasMore && isDev) {
|
|
1019
|
+
console.warn(
|
|
1020
|
+
"[Aurabase] Query result reached pagination limit of 1,000 rows. Use .range() to paginate further results."
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
858
1023
|
if (unwrapped === null) {
|
|
859
1024
|
return { data: null, error: null, meta, count };
|
|
860
1025
|
}
|
|
@@ -886,45 +1051,16 @@ var BaseService = class {
|
|
|
886
1051
|
}
|
|
887
1052
|
};
|
|
888
1053
|
|
|
889
|
-
// src/cookie-utils.ts
|
|
890
|
-
function parseCookieHeader(header) {
|
|
891
|
-
const result = {};
|
|
892
|
-
if (!header) return result;
|
|
893
|
-
for (const pair of header.split(";")) {
|
|
894
|
-
const idx = pair.indexOf("=");
|
|
895
|
-
if (idx === -1) continue;
|
|
896
|
-
const key = pair.slice(0, idx).trim();
|
|
897
|
-
const value = pair.slice(idx + 1).trim();
|
|
898
|
-
if (key) result[key] = decodeURIComponent(value);
|
|
899
|
-
}
|
|
900
|
-
return result;
|
|
901
|
-
}
|
|
902
|
-
function sameSiteAttribute(v) {
|
|
903
|
-
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
904
|
-
}
|
|
905
|
-
function serializeCookieHeader(name, value, options = {}) {
|
|
906
|
-
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
907
|
-
if (options.path) parts.push(`Path=${options.path}`);
|
|
908
|
-
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
909
|
-
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
910
|
-
if (options.secure) parts.push("Secure");
|
|
911
|
-
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
912
|
-
if (options.httpOnly) parts.push("HttpOnly");
|
|
913
|
-
return parts.join("; ");
|
|
914
|
-
}
|
|
915
|
-
|
|
916
1054
|
// src/stores/LocalAuthStore.ts
|
|
917
1055
|
var LocalAuthStore = class extends BaseAuthStore {
|
|
918
1056
|
/** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
|
|
919
1057
|
persistent = true;
|
|
920
|
-
key;
|
|
921
1058
|
/**
|
|
922
1059
|
* @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
|
|
923
1060
|
* `aura-${clientScope(baseUrl)}-auth`.
|
|
924
1061
|
*/
|
|
925
1062
|
constructor(key) {
|
|
926
|
-
super();
|
|
927
|
-
this.key = key;
|
|
1063
|
+
super(key);
|
|
928
1064
|
this._loadFromStorage();
|
|
929
1065
|
}
|
|
930
1066
|
// -- Overrides --------------------------------------------------------------
|
|
@@ -995,48 +1131,6 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
995
1131
|
this._emitStorageBlocked("removeItem");
|
|
996
1132
|
}
|
|
997
1133
|
}
|
|
998
|
-
// -- Cookie helpers (SSR) ---------------------------------------------------
|
|
999
|
-
/**
|
|
1000
|
-
* Populate the store from a raw `Cookie` header string (server-side).
|
|
1001
|
-
* Looks for the key matching `this.key`.
|
|
1002
|
-
*
|
|
1003
|
-
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
1004
|
-
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
1005
|
-
*/
|
|
1006
|
-
loadFromCookie(cookieString) {
|
|
1007
|
-
const prefix = `${this.key}=`;
|
|
1008
|
-
const entry = cookieString.split(";").map((c) => c.trim()).find((c) => c.startsWith(prefix));
|
|
1009
|
-
if (!entry) return;
|
|
1010
|
-
try {
|
|
1011
|
-
const raw = decodeURIComponent(entry.slice(prefix.length));
|
|
1012
|
-
const parsed = JSON.parse(raw);
|
|
1013
|
-
const user = parsed.token ? userFromAccessToken(parsed.token) : null;
|
|
1014
|
-
if (parsed.token && parsed.refreshToken && user) {
|
|
1015
|
-
super.save(parsed.token, parsed.refreshToken, user);
|
|
1016
|
-
}
|
|
1017
|
-
} catch {
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
/**
|
|
1021
|
-
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
1022
|
-
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
1023
|
-
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
1024
|
-
*/
|
|
1025
|
-
exportToCookie(options = {}) {
|
|
1026
|
-
const data = {
|
|
1027
|
-
token: this.token,
|
|
1028
|
-
refreshToken: this.refreshToken
|
|
1029
|
-
};
|
|
1030
|
-
const encoded = encodeURIComponent(JSON.stringify(data));
|
|
1031
|
-
const parts = [`${this.key}=${encoded}`];
|
|
1032
|
-
if (options.path) parts.push(`Path=${options.path}`);
|
|
1033
|
-
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
1034
|
-
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
1035
|
-
if (options.secure) parts.push("Secure");
|
|
1036
|
-
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
1037
|
-
if (options.httpOnly) parts.push("HttpOnly");
|
|
1038
|
-
return parts.join("; ");
|
|
1039
|
-
}
|
|
1040
1134
|
// -- Private ----------------------------------------------------------------
|
|
1041
1135
|
_getStorage() {
|
|
1042
1136
|
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
|
|
@@ -1049,7 +1143,9 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
1049
1143
|
const env = proc.env;
|
|
1050
1144
|
const isTest = !!(env?.VITEST || env?.NODE_ENV === "test" || env?.JEST_WORKER_ID);
|
|
1051
1145
|
const execArgv = proc.execArgv;
|
|
1052
|
-
const hasFlag = Array.isArray(execArgv) && execArgv.some(
|
|
1146
|
+
const hasFlag = Array.isArray(execArgv) && execArgv.some(
|
|
1147
|
+
(a) => typeof a === "string" && a.startsWith("--localstorage-file")
|
|
1148
|
+
);
|
|
1053
1149
|
if (isTest || hasFlag) {
|
|
1054
1150
|
try {
|
|
1055
1151
|
const s = g.localStorage;
|
package/package.json
CHANGED