@aurabase/core 0.6.0 → 0.6.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/README.md CHANGED
@@ -149,7 +149,7 @@ function hydrateFromCookieHeader(cookieHeader: string) {
149
149
  const store = hydrateFromCookieHeader(`${CLE}=%7B%22token%22%3Anull%7D`)
150
150
 
151
151
  // Après une mutation de session, sérialiser l'état courant en Set-Cookie.
152
- const setCookieValue = store.exportToCookie({ path: '/', secure: true, sameSite: 'Lax', httpOnly: true })
152
+ const setCookieValue = store.exportToCookie({ path: '/', secure: true, sameSite: 'lax', httpOnly: true })
153
153
  ```
154
154
 
155
155
  `store.persistent` indique si le store survit à un redirect pleine page (`false` pour `BaseAuthStore`, `true` pour `LocalAuthStore`) — c'est ce flag que le flux OAuth PKCE utilise pour décider s'il peut stocker un `code_verifier` de façon fiable.
package/dist/index.cjs CHANGED
@@ -1,15 +1,51 @@
1
1
  'use strict';
2
2
 
3
3
  // src/AuraError.ts
4
+ var LEGACY_LOWERCASE_CODES = /* @__PURE__ */ new Set([
5
+ "NO_SESSION",
6
+ "TIMEOUT",
7
+ "NETWORK_ERROR",
8
+ "INVALID_RESPONSE",
9
+ "REFRESH_DISCARDED",
10
+ "UNVERIFIABLE_TOKEN",
11
+ "NO_CODE_VERIFIER",
12
+ "PKCE_GENERATION_FAILED",
13
+ "MISSING_EMAIL",
14
+ "JWKS_FETCH_FAILED",
15
+ "SIGNATURE_INVALID",
16
+ "TOKEN_EXPIRED",
17
+ "TOKEN_NOT_YET_VALID",
18
+ "INVALID_JWT",
19
+ "UNSUPPORTED_ALG",
20
+ "NO_MATCHING_KEY",
21
+ "INVALID_ISSUER",
22
+ "INVALID_AUDIENCE",
23
+ "INVALID_PATH",
24
+ "INVALID_CREDENTIALS",
25
+ "WEAK_PASSWORD",
26
+ "EMAIL_ALREADY_IN_USE",
27
+ "USER_NOT_FOUND",
28
+ "SESSION_EXPIRED",
29
+ "INVALID_OTP",
30
+ "SESSION_REVOKED"
31
+ ]);
32
+ function normalizeErrorCode(code) {
33
+ return code.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
34
+ }
4
35
  var AuraError = class _AuraError extends Error {
5
36
  status;
6
37
  code;
7
38
  details;
8
39
  retryAfter;
40
+ /**
41
+ * Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
42
+ */
43
+ hint;
9
44
  /** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
10
45
  __isAuraError = true;
11
46
  constructor(status, body, headers) {
12
- let rawMessage = body?.error?.message ?? body?.message ?? (status === 429 ? "Trop de requ\xEAtes, veuillez patienter avant de r\xE9essayer." : `HTTP ${status}`);
47
+ const serverMessage = body?.error?.message ?? body?.message;
48
+ let rawMessage = serverMessage ?? (status === 429 ? "Too many requests, please wait before retrying." : `HTTP ${status}`);
13
49
  let retryAfterSec;
14
50
  if (headers) {
15
51
  const getHeader = (name) => {
@@ -24,8 +60,8 @@ var AuraError = class _AuraError extends Error {
24
60
  const sec = parseInt(ra, 10);
25
61
  if (!isNaN(sec)) {
26
62
  retryAfterSec = sec;
27
- if (status === 429) {
28
- rawMessage = `Trop de requ\xEAtes, veuillez patienter ${sec} seconde${sec > 1 ? "s" : ""} avant de r\xE9essayer.`;
63
+ if (status === 429 && !serverMessage) {
64
+ rawMessage = `Too many requests, please wait ${sec} second${sec > 1 ? "s" : ""} before retrying.`;
29
65
  }
30
66
  }
31
67
  }
@@ -37,30 +73,47 @@ var AuraError = class _AuraError extends Error {
37
73
  if (retryAfterSec !== void 0) {
38
74
  this.retryAfter = retryAfterSec;
39
75
  }
40
- const rawCode = body?.error?.code;
76
+ const hint = body?.error?.hint;
77
+ if (typeof hint === "string" && hint) {
78
+ this.hint = hint;
79
+ }
80
+ const rawCode = typeof body?.error?.code === "string" ? body.error.code : void 0;
81
+ const canonical = rawCode ? normalizeErrorCode(rawCode) : void 0;
41
82
  const lowerMsg = rawMessage.toLowerCase();
42
- const isGenericHttpCode = typeof rawCode !== "string" || rawCode === "UNAUTHORIZED" || rawCode === "BAD_REQUEST" || rawCode === "CONFLICT" || rawCode === "RATE_LIMITED" || rawCode.startsWith("http_");
43
- if (!isGenericHttpCode) {
44
- this.code = rawCode;
45
- } else if (status === 429 || rawCode === "RATE_LIMITED" || rawCode === "http_429") {
46
- this.code = "rate_limit_exceeded";
83
+ if (canonical === "USER_ALREADY_EXISTS") {
84
+ this.code = "EMAIL_ALREADY_IN_USE";
85
+ } else if (canonical === "TOKEN_EXPIRED" && status === 401) {
86
+ this.code = "SESSION_EXPIRED";
87
+ } else if (canonical === "RATE_LIMIT_EXCEEDED") {
88
+ this.code = "RATE_LIMITED";
89
+ } else if (canonical && !isGenericCode(canonical)) {
90
+ this.code = canonical;
91
+ } else if (status === 429) {
92
+ this.code = "RATE_LIMITED";
47
93
  } else if (status === 401 && (lowerMsg.includes("identifiants") || lowerMsg.includes("credentials") || lowerMsg.includes("mot de passe incorrect"))) {
48
- this.code = "invalid_credentials";
49
- } else if (status === 401 && lowerMsg.includes("r\xE9voqu\xE9e")) {
50
- this.code = "session_revoked";
51
- } else if (status === 401 && lowerMsg.includes("expir\xE9")) {
52
- this.code = "session_expired";
94
+ this.code = "INVALID_CREDENTIALS";
95
+ } else if (status === 401 && (lowerMsg.includes("r\xE9voqu\xE9e") || lowerMsg.includes("revoked"))) {
96
+ this.code = "SESSION_REVOKED";
97
+ } else if (status === 401 && (lowerMsg.includes("expir\xE9") || lowerMsg.includes("expired"))) {
98
+ this.code = "SESSION_EXPIRED";
53
99
  } else if (status === 400 && (lowerMsg.includes("mot de passe") || lowerMsg.includes("password"))) {
54
- this.code = "weak_password";
100
+ this.code = "WEAK_PASSWORD";
55
101
  } else if (status === 409 && (lowerMsg.includes("d\xE9j\xE0 utilis\xE9") || lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
56
- this.code = "email_already_in_use";
57
- } else if (status === 404 && lowerMsg.includes("utilisateur")) {
58
- this.code = "user_not_found";
102
+ this.code = "EMAIL_ALREADY_IN_USE";
103
+ } else if (status === 404 && (lowerMsg.includes("utilisateur") || lowerMsg.includes("user not found"))) {
104
+ this.code = "USER_NOT_FOUND";
59
105
  } else {
60
- this.code = rawCode ?? `http_${status}`;
106
+ this.code = canonical ?? `HTTP_${status}`;
61
107
  }
62
108
  Object.setPrototypeOf(this, _AuraError.prototype);
63
109
  }
110
+ /**
111
+ * @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
112
+ * Utilisez `error.code`, toujours en SCREAMING_SNAKE.
113
+ */
114
+ get legacyCode() {
115
+ return LEGACY_LOWERCASE_CODES.has(this.code) ? this.code.toLowerCase() : this.code;
116
+ }
64
117
  get isNotFound() {
65
118
  return this.status === 404;
66
119
  }
@@ -73,7 +126,17 @@ var AuraError = class _AuraError extends Error {
73
126
  get isRateLimited() {
74
127
  return this.status === 429;
75
128
  }
129
+ /**
130
+ * Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
131
+ * métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
132
+ */
133
+ get isQuotaExceeded() {
134
+ return this.code === "QUOTA_EXCEEDED";
135
+ }
76
136
  };
137
+ function isGenericCode(code) {
138
+ return code === "UNAUTHORIZED" || code === "BAD_REQUEST" || code === "CONFLICT" || code.startsWith("HTTP_");
139
+ }
77
140
  function isAuraError(error) {
78
141
  return error instanceof AuraError || typeof error === "object" && error !== null && error.__isAuraError === true;
79
142
  }
@@ -338,6 +401,17 @@ var BaseAuthStore = class {
338
401
  // src/fetch.ts
339
402
  var REFRESH_MARGIN_SEC = 30;
340
403
  var RETRY_AFTER_CAP_MS = 6e4;
404
+ function networkErrorMessage(err, url) {
405
+ const cause = err?.cause;
406
+ const reason = cause?.code ?? cause?.message ?? (err instanceof Error ? err.message : "request failed");
407
+ let target = url;
408
+ try {
409
+ const u = new URL(url);
410
+ target = `${u.origin}${u.pathname}`;
411
+ } catch {
412
+ }
413
+ return `Cannot reach ${target} (${reason}). Check the project URL (NEXT_PUBLIC_AURABASE_URL) and your network.`;
414
+ }
341
415
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
342
416
  function unwrapEnvelope(body) {
343
417
  if (body !== null && typeof body === "object" && "data" in body) {
@@ -459,7 +533,7 @@ async function doRefresh(client) {
459
533
  error: new AuraError(0, {
460
534
  error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : {
461
535
  code: "network_error",
462
- message: err instanceof Error ? err.message : "Network request failed"
536
+ message: networkErrorMessage(err, `${client.baseUrl}/v1/auth/refresh`)
463
537
  }
464
538
  })
465
539
  };
@@ -660,7 +734,7 @@ async function fetchWithAuth(url, init, client) {
660
734
  error: new AuraError(0, {
661
735
  error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : {
662
736
  code: "network_error",
663
- message: err instanceof Error ? err.message : "Network request failed"
737
+ message: networkErrorMessage(err, url)
664
738
  }
665
739
  })
666
740
  };
@@ -693,7 +767,9 @@ async function fetchWithAuth(url, init, client) {
693
767
  }
694
768
  }
695
769
  if (res.status === 204) {
696
- return { data: null, error: null };
770
+ const totalFromCr2 = parseContentRangeTotal(res.headers.get("content-range"));
771
+ if (totalFromCr2 === void 0) return { data: null, error: null };
772
+ return { data: null, error: null, meta: { total: totalFromCr2 }, count: totalFromCr2 };
697
773
  }
698
774
  if (!res.ok) {
699
775
  let body2 = null;
@@ -807,13 +883,16 @@ function parseCookieHeader(header) {
807
883
  }
808
884
  return result;
809
885
  }
886
+ function sameSiteAttribute(v) {
887
+ return v.charAt(0).toUpperCase() + v.slice(1);
888
+ }
810
889
  function serializeCookieHeader(name, value, options = {}) {
811
890
  const parts = [`${name}=${encodeURIComponent(value)}`];
812
891
  if (options.path) parts.push(`Path=${options.path}`);
813
892
  if (options.domain) parts.push(`Domain=${options.domain}`);
814
893
  if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
815
894
  if (options.secure) parts.push("Secure");
816
- if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
895
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
817
896
  if (options.httpOnly) parts.push("HttpOnly");
818
897
  return parts.join("; ");
819
898
  }
@@ -933,7 +1012,7 @@ var LocalAuthStore = class extends BaseAuthStore {
933
1012
  if (options.domain) parts.push(`Domain=${options.domain}`);
934
1013
  if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
935
1014
  if (options.secure) parts.push("Secure");
936
- if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
1015
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
937
1016
  if (options.httpOnly) parts.push("HttpOnly");
938
1017
  return parts.join("; ");
939
1018
  }
package/dist/index.d.cts CHANGED
@@ -14,7 +14,7 @@
14
14
  * - storage/path.ts : invalid_path
15
15
  * - db/QueryBuilder.ts: PGRST116 (parité PostgREST : `.single()` sur 0/plusieurs lignes)
16
16
  */
17
- type AuraErrorCode = 'no_session' | 'timeout' | 'network_error' | 'invalid_response' | 'refresh_discarded' | 'unverifiable_token' | 'no_code_verifier' | 'pkce_generation_failed' | 'missing_email' | 'jwks_fetch_failed' | 'signature_invalid' | 'token_expired' | 'token_not_yet_valid' | 'invalid_jwt' | 'unsupported_alg' | 'no_matching_key' | 'invalid_issuer' | 'invalid_audience' | 'invalid_path' | 'PGRST116' | 'invalid_credentials' | 'weak_password' | 'email_already_in_use' | 'user_not_found' | 'rate_limit_exceeded' | 'session_expired' | 'session_revoked' | (string & {});
17
+ type AuraErrorCode = 'NO_SESSION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'INVALID_RESPONSE' | 'REFRESH_DISCARDED' | 'UNVERIFIABLE_TOKEN' | 'NO_CODE_VERIFIER' | 'PKCE_GENERATION_FAILED' | 'MISSING_EMAIL' | 'JWKS_FETCH_FAILED' | 'SIGNATURE_INVALID' | 'TOKEN_EXPIRED' | 'TOKEN_NOT_YET_VALID' | 'INVALID_JWT' | 'UNSUPPORTED_ALG' | 'NO_MATCHING_KEY' | 'INVALID_ISSUER' | 'INVALID_AUDIENCE' | 'INVALID_PATH' | 'PGRST116' | 'INVALID_CREDENTIALS' | 'WEAK_PASSWORD' | 'EMAIL_ALREADY_IN_USE' | 'USER_NOT_FOUND' | 'RATE_LIMITED' | 'SESSION_EXPIRED' | 'INVALID_OTP' | 'SESSION_REVOKED' | (string & {});
18
18
  /**
19
19
  * Structured error thrown/returned by the Aurabase SDK.
20
20
  * Mirrors the backend's `AuraError` JSON shape with semantic DX normalization.
@@ -24,6 +24,10 @@ declare class AuraError extends Error {
24
24
  readonly code: AuraErrorCode;
25
25
  readonly details: unknown;
26
26
  readonly retryAfter?: number;
27
+ /**
28
+ * Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
29
+ */
30
+ readonly hint?: string;
27
31
  /** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
28
32
  protected readonly __isAuraError = true;
29
33
  constructor(status: number, body?: {
@@ -33,10 +37,20 @@ declare class AuraError extends Error {
33
37
  };
34
38
  message?: string;
35
39
  } | null, headers?: Headers | Record<string, string | null | undefined>);
40
+ /**
41
+ * @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
42
+ * Utilisez `error.code`, toujours en SCREAMING_SNAKE.
43
+ */
44
+ get legacyCode(): string;
36
45
  get isNotFound(): boolean;
37
46
  get isUnauthorized(): boolean;
38
47
  get isForbidden(): boolean;
39
48
  get isRateLimited(): boolean;
49
+ /**
50
+ * Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
51
+ * métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
52
+ */
53
+ get isQuotaExceeded(): boolean;
40
54
  }
41
55
  declare function isAuraError(error: unknown): error is AuraError;
42
56
 
@@ -260,12 +274,18 @@ interface AuthChangePayload {
260
274
  interface SignUpRequest {
261
275
  email: string;
262
276
  password: string;
277
+ name?: string;
263
278
  captcha_token?: string;
279
+ options?: {
280
+ emailRedirectTo?: string;
281
+ data?: Record<string, unknown>;
282
+ };
264
283
  }
265
284
  interface SignInRequest {
266
285
  email: string;
267
286
  password: string;
268
287
  captcha_token?: string;
288
+ mfa?: 'required' | 'optional';
269
289
  }
270
290
  interface MagicLinkRequest {
271
291
  email: string;
@@ -329,6 +349,10 @@ interface OAuthSignInOptions {
329
349
  */
330
350
  flowType?: 'pkce' | 'implicit';
331
351
  }
352
+ interface OAuthSignInObjectOptions extends OAuthSignInOptions {
353
+ provider: string;
354
+ options?: OAuthSignInOptions;
355
+ }
332
356
  interface PasswordResetRequest {
333
357
  email: string;
334
358
  }
@@ -646,7 +670,7 @@ interface ObjectMeta {
646
670
  metadata: Record<string, string>;
647
671
  }
648
672
  /** Événement serveur brut reçu via WebSocket (union discriminée par type) */
649
- type ServerMessage = ServerSubscribedMsg | ServerEventMsg | ServerPongMsg | ServerErrorMsg | ServerTokenRefreshedMsg | ServerPresenceStateMsg | BroadcastAckMessage;
673
+ type ServerMessage = ServerSubscribedMsg | ServerEventMsg | ServerPongMsg | ServerErrorMsg | ServerTokenRefreshedMsg | ServerPresenceStateMsg | BroadcastAckMessage | ServerChannelRecoveredMsg;
650
674
  interface ServerSubscribedMsg {
651
675
  type: 'subscribed';
652
676
  channel: string;
@@ -689,6 +713,14 @@ interface ServerPresenceStateMsg {
689
713
  channel: string;
690
714
  presences: PresenceInfo[];
691
715
  }
716
+ /**
717
+ * L'écoute `postgres_changes` qui avait valu une erreur (`CHANNEL_ERROR`) à ce canal est rétablie :
718
+ * les changements reprennent. Le SDK repasse le canal en `SUBSCRIBED`.
719
+ */
720
+ interface ServerChannelRecoveredMsg {
721
+ type: 'channel_recovered';
722
+ channel: string;
723
+ }
692
724
  /** Événement realtime (CDC, broadcast, presence) */
693
725
  interface RealtimeEvent<T = Record<string, unknown>> {
694
726
  id: string;
@@ -741,6 +773,11 @@ interface RealtimePostgresUpdatePayload<T = Record<string, unknown>> extends Rea
741
773
  eventType: 'UPDATE';
742
774
  event: 'update';
743
775
  new: T;
776
+ /**
777
+ * Colonnes de **clé primaire** de l'ancienne ligne seulement (`{}` si la table n'en a pas), jamais
778
+ * ses autres valeurs : la sécurité au niveau des lignes n'est évaluée que sur la nouvelle ligne.
779
+ * Si la clé a changé, ce sont les valeurs d'avant.
780
+ */
744
781
  old: Partial<T>;
745
782
  }
746
783
  /** Payload enrichi pour un événement CDC PostgreSQL de suppression */
@@ -748,6 +785,7 @@ interface RealtimePostgresDeletePayload<T = Record<string, unknown>> extends Rea
748
785
  eventType: 'DELETE';
749
786
  event: 'delete';
750
787
  new: null;
788
+ /** Colonnes de **clé primaire** de la ligne supprimée seulement (`{}` si la table n'en a pas). */
751
789
  old: Partial<T> | null;
752
790
  }
753
791
  /** Union discriminée par eventType pour les événements CDC PostgreSQL */
@@ -803,12 +841,14 @@ interface PostgresChangesFilter {
803
841
  * (defense-in-depth) : il ne peut que sur-filtrer, jamais faire fuiter un
804
842
  * event que le serveur aurait dû exclure.
805
843
  *
844
+ * **INSERT / UPDATE** : le prédicat est évalué sur la **nouvelle ligne** (`record`).
845
+ *
806
846
  * **DELETE** : le prédicat est évalué sur l'**ancienne ligne** (`old_record`),
807
- * puisque le `record` d'un DELETE est vide côté CDC. Si votre table utilise
808
- * `REPLICA IDENTITY DEFAULT`, `old_record` ne contient que la clé primaire —
809
- * un filtre sur une autre colonne exclura systématiquement les DELETE, **sauf
810
- * `is.null`/`isdistinct`** qui traitent une colonne absente comme `NULL` et
811
- * peuvent donc matcher même sans cette colonne dans `old_record`.
847
+ * puisque le `record` d'un DELETE est vide côté CDC. `old_record` ne contient
848
+ * que la clé primaire (UPDATE comme DELETE) — un filtre sur une autre colonne
849
+ * exclura systématiquement les DELETE, **sauf `is.null`/`isdistinct`** qui
850
+ * traitent une colonne absente comme `NULL` et peuvent donc matcher même sans
851
+ * cette colonne dans `old_record`.
812
852
  */
813
853
  filter?: string;
814
854
  }
@@ -847,9 +887,13 @@ interface BroadcastFilter {
847
887
  * consultable pour ce canal — laisser passer serait fail-open). C'est le modèle de
848
888
  * Phoenix, dont dérive le protocole Supabase Realtime : on rejoint un topic avant d'y
849
889
  * pousser quoi que ce soit. Appelez `.subscribe()` — et vérifiez le statut qu'il rend.
890
+ * - `'invalid_payload'` — le `payload` porte lui-même une clé `event`. Le nom de l'événement
891
+ * voyage dans le payload du fil (`{ event, ...payload }`) : une clé `event` du payload
892
+ * l'écraserait et le message serait routé sous un autre nom chez les abonnés. Rien n'est
893
+ * émis ; renommez la clé (`eventName`, `kind`…).
850
894
  * - `'error'` — l'envoi lui-même a levé une exception (sérialisation, transport).
851
895
  */
852
- type BroadcastSendStatus = 'ok' | 'timeout' | 'not_connected' | 'requires_subscription' | 'error';
896
+ type BroadcastSendStatus = 'ok' | 'timeout' | 'not_connected' | 'requires_subscription' | 'invalid_payload' | 'error';
853
897
  /** Config pour presence */
854
898
  interface PresenceFilter {
855
899
  event: 'sync' | 'join' | 'leave';
@@ -1897,7 +1941,8 @@ interface SerializeCookieOptions {
1897
1941
  domain?: string;
1898
1942
  maxAge?: number;
1899
1943
  secure?: boolean;
1900
- sameSite?: 'Strict' | 'Lax' | 'None';
1944
+ /** Minuscules : forme acceptée par `ResponseCookie` de Next.js (`'lax' | 'strict' | 'none'`). */
1945
+ sameSite?: 'strict' | 'lax' | 'none';
1901
1946
  httpOnly?: boolean;
1902
1947
  }
1903
1948
  /**
@@ -1905,7 +1950,6 @@ interface SerializeCookieOptions {
1905
1950
  */
1906
1951
  declare function serializeCookieHeader(name: string, value: string, options?: SerializeCookieOptions): string;
1907
1952
 
1908
- /** Timeout par défaut d'une requête HTTP (borne un serveur qui « hang »). */
1909
1953
  declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
1910
1954
  /** Point d'entrée unique du refresh, dédupliqué par client (single-flight). */
1911
1955
  declare function refreshOnce(client: AuraClientLike): Promise<AuraResponse<TokenPair>>;
@@ -1980,7 +2024,7 @@ interface CookieOptions {
1980
2024
  domain?: string;
1981
2025
  maxAge?: number;
1982
2026
  secure?: boolean;
1983
- sameSite?: 'Strict' | 'Lax' | 'None';
2027
+ sameSite?: 'strict' | 'lax' | 'none';
1984
2028
  httpOnly?: boolean;
1985
2029
  }
1986
2030
  /**
@@ -2035,4 +2079,4 @@ declare class LocalAuthStore extends BaseAuthStore {
2035
2079
  private _removeStorage;
2036
2080
  }
2037
2081
 
2038
- 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 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 OAuthSignInOptions, type ObjectMeta, 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 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 };
2082
+ 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 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 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 };
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * - storage/path.ts : invalid_path
15
15
  * - db/QueryBuilder.ts: PGRST116 (parité PostgREST : `.single()` sur 0/plusieurs lignes)
16
16
  */
17
- type AuraErrorCode = 'no_session' | 'timeout' | 'network_error' | 'invalid_response' | 'refresh_discarded' | 'unverifiable_token' | 'no_code_verifier' | 'pkce_generation_failed' | 'missing_email' | 'jwks_fetch_failed' | 'signature_invalid' | 'token_expired' | 'token_not_yet_valid' | 'invalid_jwt' | 'unsupported_alg' | 'no_matching_key' | 'invalid_issuer' | 'invalid_audience' | 'invalid_path' | 'PGRST116' | 'invalid_credentials' | 'weak_password' | 'email_already_in_use' | 'user_not_found' | 'rate_limit_exceeded' | 'session_expired' | 'session_revoked' | (string & {});
17
+ type AuraErrorCode = 'NO_SESSION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'INVALID_RESPONSE' | 'REFRESH_DISCARDED' | 'UNVERIFIABLE_TOKEN' | 'NO_CODE_VERIFIER' | 'PKCE_GENERATION_FAILED' | 'MISSING_EMAIL' | 'JWKS_FETCH_FAILED' | 'SIGNATURE_INVALID' | 'TOKEN_EXPIRED' | 'TOKEN_NOT_YET_VALID' | 'INVALID_JWT' | 'UNSUPPORTED_ALG' | 'NO_MATCHING_KEY' | 'INVALID_ISSUER' | 'INVALID_AUDIENCE' | 'INVALID_PATH' | 'PGRST116' | 'INVALID_CREDENTIALS' | 'WEAK_PASSWORD' | 'EMAIL_ALREADY_IN_USE' | 'USER_NOT_FOUND' | 'RATE_LIMITED' | 'SESSION_EXPIRED' | 'INVALID_OTP' | 'SESSION_REVOKED' | (string & {});
18
18
  /**
19
19
  * Structured error thrown/returned by the Aurabase SDK.
20
20
  * Mirrors the backend's `AuraError` JSON shape with semantic DX normalization.
@@ -24,6 +24,10 @@ declare class AuraError extends Error {
24
24
  readonly code: AuraErrorCode;
25
25
  readonly details: unknown;
26
26
  readonly retryAfter?: number;
27
+ /**
28
+ * Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
29
+ */
30
+ readonly hint?: string;
27
31
  /** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
28
32
  protected readonly __isAuraError = true;
29
33
  constructor(status: number, body?: {
@@ -33,10 +37,20 @@ declare class AuraError extends Error {
33
37
  };
34
38
  message?: string;
35
39
  } | null, headers?: Headers | Record<string, string | null | undefined>);
40
+ /**
41
+ * @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
42
+ * Utilisez `error.code`, toujours en SCREAMING_SNAKE.
43
+ */
44
+ get legacyCode(): string;
36
45
  get isNotFound(): boolean;
37
46
  get isUnauthorized(): boolean;
38
47
  get isForbidden(): boolean;
39
48
  get isRateLimited(): boolean;
49
+ /**
50
+ * Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
51
+ * métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
52
+ */
53
+ get isQuotaExceeded(): boolean;
40
54
  }
41
55
  declare function isAuraError(error: unknown): error is AuraError;
42
56
 
@@ -260,12 +274,18 @@ interface AuthChangePayload {
260
274
  interface SignUpRequest {
261
275
  email: string;
262
276
  password: string;
277
+ name?: string;
263
278
  captcha_token?: string;
279
+ options?: {
280
+ emailRedirectTo?: string;
281
+ data?: Record<string, unknown>;
282
+ };
264
283
  }
265
284
  interface SignInRequest {
266
285
  email: string;
267
286
  password: string;
268
287
  captcha_token?: string;
288
+ mfa?: 'required' | 'optional';
269
289
  }
270
290
  interface MagicLinkRequest {
271
291
  email: string;
@@ -329,6 +349,10 @@ interface OAuthSignInOptions {
329
349
  */
330
350
  flowType?: 'pkce' | 'implicit';
331
351
  }
352
+ interface OAuthSignInObjectOptions extends OAuthSignInOptions {
353
+ provider: string;
354
+ options?: OAuthSignInOptions;
355
+ }
332
356
  interface PasswordResetRequest {
333
357
  email: string;
334
358
  }
@@ -646,7 +670,7 @@ interface ObjectMeta {
646
670
  metadata: Record<string, string>;
647
671
  }
648
672
  /** Événement serveur brut reçu via WebSocket (union discriminée par type) */
649
- type ServerMessage = ServerSubscribedMsg | ServerEventMsg | ServerPongMsg | ServerErrorMsg | ServerTokenRefreshedMsg | ServerPresenceStateMsg | BroadcastAckMessage;
673
+ type ServerMessage = ServerSubscribedMsg | ServerEventMsg | ServerPongMsg | ServerErrorMsg | ServerTokenRefreshedMsg | ServerPresenceStateMsg | BroadcastAckMessage | ServerChannelRecoveredMsg;
650
674
  interface ServerSubscribedMsg {
651
675
  type: 'subscribed';
652
676
  channel: string;
@@ -689,6 +713,14 @@ interface ServerPresenceStateMsg {
689
713
  channel: string;
690
714
  presences: PresenceInfo[];
691
715
  }
716
+ /**
717
+ * L'écoute `postgres_changes` qui avait valu une erreur (`CHANNEL_ERROR`) à ce canal est rétablie :
718
+ * les changements reprennent. Le SDK repasse le canal en `SUBSCRIBED`.
719
+ */
720
+ interface ServerChannelRecoveredMsg {
721
+ type: 'channel_recovered';
722
+ channel: string;
723
+ }
692
724
  /** Événement realtime (CDC, broadcast, presence) */
693
725
  interface RealtimeEvent<T = Record<string, unknown>> {
694
726
  id: string;
@@ -741,6 +773,11 @@ interface RealtimePostgresUpdatePayload<T = Record<string, unknown>> extends Rea
741
773
  eventType: 'UPDATE';
742
774
  event: 'update';
743
775
  new: T;
776
+ /**
777
+ * Colonnes de **clé primaire** de l'ancienne ligne seulement (`{}` si la table n'en a pas), jamais
778
+ * ses autres valeurs : la sécurité au niveau des lignes n'est évaluée que sur la nouvelle ligne.
779
+ * Si la clé a changé, ce sont les valeurs d'avant.
780
+ */
744
781
  old: Partial<T>;
745
782
  }
746
783
  /** Payload enrichi pour un événement CDC PostgreSQL de suppression */
@@ -748,6 +785,7 @@ interface RealtimePostgresDeletePayload<T = Record<string, unknown>> extends Rea
748
785
  eventType: 'DELETE';
749
786
  event: 'delete';
750
787
  new: null;
788
+ /** Colonnes de **clé primaire** de la ligne supprimée seulement (`{}` si la table n'en a pas). */
751
789
  old: Partial<T> | null;
752
790
  }
753
791
  /** Union discriminée par eventType pour les événements CDC PostgreSQL */
@@ -803,12 +841,14 @@ interface PostgresChangesFilter {
803
841
  * (defense-in-depth) : il ne peut que sur-filtrer, jamais faire fuiter un
804
842
  * event que le serveur aurait dû exclure.
805
843
  *
844
+ * **INSERT / UPDATE** : le prédicat est évalué sur la **nouvelle ligne** (`record`).
845
+ *
806
846
  * **DELETE** : le prédicat est évalué sur l'**ancienne ligne** (`old_record`),
807
- * puisque le `record` d'un DELETE est vide côté CDC. Si votre table utilise
808
- * `REPLICA IDENTITY DEFAULT`, `old_record` ne contient que la clé primaire —
809
- * un filtre sur une autre colonne exclura systématiquement les DELETE, **sauf
810
- * `is.null`/`isdistinct`** qui traitent une colonne absente comme `NULL` et
811
- * peuvent donc matcher même sans cette colonne dans `old_record`.
847
+ * puisque le `record` d'un DELETE est vide côté CDC. `old_record` ne contient
848
+ * que la clé primaire (UPDATE comme DELETE) — un filtre sur une autre colonne
849
+ * exclura systématiquement les DELETE, **sauf `is.null`/`isdistinct`** qui
850
+ * traitent une colonne absente comme `NULL` et peuvent donc matcher même sans
851
+ * cette colonne dans `old_record`.
812
852
  */
813
853
  filter?: string;
814
854
  }
@@ -847,9 +887,13 @@ interface BroadcastFilter {
847
887
  * consultable pour ce canal — laisser passer serait fail-open). C'est le modèle de
848
888
  * Phoenix, dont dérive le protocole Supabase Realtime : on rejoint un topic avant d'y
849
889
  * pousser quoi que ce soit. Appelez `.subscribe()` — et vérifiez le statut qu'il rend.
890
+ * - `'invalid_payload'` — le `payload` porte lui-même une clé `event`. Le nom de l'événement
891
+ * voyage dans le payload du fil (`{ event, ...payload }`) : une clé `event` du payload
892
+ * l'écraserait et le message serait routé sous un autre nom chez les abonnés. Rien n'est
893
+ * émis ; renommez la clé (`eventName`, `kind`…).
850
894
  * - `'error'` — l'envoi lui-même a levé une exception (sérialisation, transport).
851
895
  */
852
- type BroadcastSendStatus = 'ok' | 'timeout' | 'not_connected' | 'requires_subscription' | 'error';
896
+ type BroadcastSendStatus = 'ok' | 'timeout' | 'not_connected' | 'requires_subscription' | 'invalid_payload' | 'error';
853
897
  /** Config pour presence */
854
898
  interface PresenceFilter {
855
899
  event: 'sync' | 'join' | 'leave';
@@ -1897,7 +1941,8 @@ interface SerializeCookieOptions {
1897
1941
  domain?: string;
1898
1942
  maxAge?: number;
1899
1943
  secure?: boolean;
1900
- sameSite?: 'Strict' | 'Lax' | 'None';
1944
+ /** Minuscules : forme acceptée par `ResponseCookie` de Next.js (`'lax' | 'strict' | 'none'`). */
1945
+ sameSite?: 'strict' | 'lax' | 'none';
1901
1946
  httpOnly?: boolean;
1902
1947
  }
1903
1948
  /**
@@ -1905,7 +1950,6 @@ interface SerializeCookieOptions {
1905
1950
  */
1906
1951
  declare function serializeCookieHeader(name: string, value: string, options?: SerializeCookieOptions): string;
1907
1952
 
1908
- /** Timeout par défaut d'une requête HTTP (borne un serveur qui « hang »). */
1909
1953
  declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
1910
1954
  /** Point d'entrée unique du refresh, dédupliqué par client (single-flight). */
1911
1955
  declare function refreshOnce(client: AuraClientLike): Promise<AuraResponse<TokenPair>>;
@@ -1980,7 +2024,7 @@ interface CookieOptions {
1980
2024
  domain?: string;
1981
2025
  maxAge?: number;
1982
2026
  secure?: boolean;
1983
- sameSite?: 'Strict' | 'Lax' | 'None';
2027
+ sameSite?: 'strict' | 'lax' | 'none';
1984
2028
  httpOnly?: boolean;
1985
2029
  }
1986
2030
  /**
@@ -2035,4 +2079,4 @@ declare class LocalAuthStore extends BaseAuthStore {
2035
2079
  private _removeStorage;
2036
2080
  }
2037
2081
 
2038
- 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 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 OAuthSignInOptions, type ObjectMeta, 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 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 };
2082
+ 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 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 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 };
package/dist/index.js CHANGED
@@ -1,13 +1,49 @@
1
1
  // src/AuraError.ts
2
+ var LEGACY_LOWERCASE_CODES = /* @__PURE__ */ new Set([
3
+ "NO_SESSION",
4
+ "TIMEOUT",
5
+ "NETWORK_ERROR",
6
+ "INVALID_RESPONSE",
7
+ "REFRESH_DISCARDED",
8
+ "UNVERIFIABLE_TOKEN",
9
+ "NO_CODE_VERIFIER",
10
+ "PKCE_GENERATION_FAILED",
11
+ "MISSING_EMAIL",
12
+ "JWKS_FETCH_FAILED",
13
+ "SIGNATURE_INVALID",
14
+ "TOKEN_EXPIRED",
15
+ "TOKEN_NOT_YET_VALID",
16
+ "INVALID_JWT",
17
+ "UNSUPPORTED_ALG",
18
+ "NO_MATCHING_KEY",
19
+ "INVALID_ISSUER",
20
+ "INVALID_AUDIENCE",
21
+ "INVALID_PATH",
22
+ "INVALID_CREDENTIALS",
23
+ "WEAK_PASSWORD",
24
+ "EMAIL_ALREADY_IN_USE",
25
+ "USER_NOT_FOUND",
26
+ "SESSION_EXPIRED",
27
+ "INVALID_OTP",
28
+ "SESSION_REVOKED"
29
+ ]);
30
+ function normalizeErrorCode(code) {
31
+ return code.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
32
+ }
2
33
  var AuraError = class _AuraError extends Error {
3
34
  status;
4
35
  code;
5
36
  details;
6
37
  retryAfter;
38
+ /**
39
+ * Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
40
+ */
41
+ hint;
7
42
  /** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
8
43
  __isAuraError = true;
9
44
  constructor(status, body, headers) {
10
- let rawMessage = body?.error?.message ?? body?.message ?? (status === 429 ? "Trop de requ\xEAtes, veuillez patienter avant de r\xE9essayer." : `HTTP ${status}`);
45
+ const serverMessage = body?.error?.message ?? body?.message;
46
+ let rawMessage = serverMessage ?? (status === 429 ? "Too many requests, please wait before retrying." : `HTTP ${status}`);
11
47
  let retryAfterSec;
12
48
  if (headers) {
13
49
  const getHeader = (name) => {
@@ -22,8 +58,8 @@ var AuraError = class _AuraError extends Error {
22
58
  const sec = parseInt(ra, 10);
23
59
  if (!isNaN(sec)) {
24
60
  retryAfterSec = sec;
25
- if (status === 429) {
26
- rawMessage = `Trop de requ\xEAtes, veuillez patienter ${sec} seconde${sec > 1 ? "s" : ""} avant de r\xE9essayer.`;
61
+ if (status === 429 && !serverMessage) {
62
+ rawMessage = `Too many requests, please wait ${sec} second${sec > 1 ? "s" : ""} before retrying.`;
27
63
  }
28
64
  }
29
65
  }
@@ -35,30 +71,47 @@ var AuraError = class _AuraError extends Error {
35
71
  if (retryAfterSec !== void 0) {
36
72
  this.retryAfter = retryAfterSec;
37
73
  }
38
- const rawCode = body?.error?.code;
74
+ const hint = body?.error?.hint;
75
+ if (typeof hint === "string" && hint) {
76
+ this.hint = hint;
77
+ }
78
+ const rawCode = typeof body?.error?.code === "string" ? body.error.code : void 0;
79
+ const canonical = rawCode ? normalizeErrorCode(rawCode) : void 0;
39
80
  const lowerMsg = rawMessage.toLowerCase();
40
- const isGenericHttpCode = typeof rawCode !== "string" || rawCode === "UNAUTHORIZED" || rawCode === "BAD_REQUEST" || rawCode === "CONFLICT" || rawCode === "RATE_LIMITED" || rawCode.startsWith("http_");
41
- if (!isGenericHttpCode) {
42
- this.code = rawCode;
43
- } else if (status === 429 || rawCode === "RATE_LIMITED" || rawCode === "http_429") {
44
- this.code = "rate_limit_exceeded";
81
+ if (canonical === "USER_ALREADY_EXISTS") {
82
+ this.code = "EMAIL_ALREADY_IN_USE";
83
+ } else if (canonical === "TOKEN_EXPIRED" && status === 401) {
84
+ this.code = "SESSION_EXPIRED";
85
+ } else if (canonical === "RATE_LIMIT_EXCEEDED") {
86
+ this.code = "RATE_LIMITED";
87
+ } else if (canonical && !isGenericCode(canonical)) {
88
+ this.code = canonical;
89
+ } else if (status === 429) {
90
+ this.code = "RATE_LIMITED";
45
91
  } else if (status === 401 && (lowerMsg.includes("identifiants") || lowerMsg.includes("credentials") || lowerMsg.includes("mot de passe incorrect"))) {
46
- this.code = "invalid_credentials";
47
- } else if (status === 401 && lowerMsg.includes("r\xE9voqu\xE9e")) {
48
- this.code = "session_revoked";
49
- } else if (status === 401 && lowerMsg.includes("expir\xE9")) {
50
- this.code = "session_expired";
92
+ this.code = "INVALID_CREDENTIALS";
93
+ } else if (status === 401 && (lowerMsg.includes("r\xE9voqu\xE9e") || lowerMsg.includes("revoked"))) {
94
+ this.code = "SESSION_REVOKED";
95
+ } else if (status === 401 && (lowerMsg.includes("expir\xE9") || lowerMsg.includes("expired"))) {
96
+ this.code = "SESSION_EXPIRED";
51
97
  } else if (status === 400 && (lowerMsg.includes("mot de passe") || lowerMsg.includes("password"))) {
52
- this.code = "weak_password";
98
+ this.code = "WEAK_PASSWORD";
53
99
  } else if (status === 409 && (lowerMsg.includes("d\xE9j\xE0 utilis\xE9") || lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
54
- this.code = "email_already_in_use";
55
- } else if (status === 404 && lowerMsg.includes("utilisateur")) {
56
- this.code = "user_not_found";
100
+ this.code = "EMAIL_ALREADY_IN_USE";
101
+ } else if (status === 404 && (lowerMsg.includes("utilisateur") || lowerMsg.includes("user not found"))) {
102
+ this.code = "USER_NOT_FOUND";
57
103
  } else {
58
- this.code = rawCode ?? `http_${status}`;
104
+ this.code = canonical ?? `HTTP_${status}`;
59
105
  }
60
106
  Object.setPrototypeOf(this, _AuraError.prototype);
61
107
  }
108
+ /**
109
+ * @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
110
+ * Utilisez `error.code`, toujours en SCREAMING_SNAKE.
111
+ */
112
+ get legacyCode() {
113
+ return LEGACY_LOWERCASE_CODES.has(this.code) ? this.code.toLowerCase() : this.code;
114
+ }
62
115
  get isNotFound() {
63
116
  return this.status === 404;
64
117
  }
@@ -71,7 +124,17 @@ var AuraError = class _AuraError extends Error {
71
124
  get isRateLimited() {
72
125
  return this.status === 429;
73
126
  }
127
+ /**
128
+ * Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
129
+ * métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
130
+ */
131
+ get isQuotaExceeded() {
132
+ return this.code === "QUOTA_EXCEEDED";
133
+ }
74
134
  };
135
+ function isGenericCode(code) {
136
+ return code === "UNAUTHORIZED" || code === "BAD_REQUEST" || code === "CONFLICT" || code.startsWith("HTTP_");
137
+ }
75
138
  function isAuraError(error) {
76
139
  return error instanceof AuraError || typeof error === "object" && error !== null && error.__isAuraError === true;
77
140
  }
@@ -336,6 +399,17 @@ var BaseAuthStore = class {
336
399
  // src/fetch.ts
337
400
  var REFRESH_MARGIN_SEC = 30;
338
401
  var RETRY_AFTER_CAP_MS = 6e4;
402
+ function networkErrorMessage(err, url) {
403
+ const cause = err?.cause;
404
+ const reason = cause?.code ?? cause?.message ?? (err instanceof Error ? err.message : "request failed");
405
+ let target = url;
406
+ try {
407
+ const u = new URL(url);
408
+ target = `${u.origin}${u.pathname}`;
409
+ } catch {
410
+ }
411
+ return `Cannot reach ${target} (${reason}). Check the project URL (NEXT_PUBLIC_AURABASE_URL) and your network.`;
412
+ }
339
413
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
340
414
  function unwrapEnvelope(body) {
341
415
  if (body !== null && typeof body === "object" && "data" in body) {
@@ -457,7 +531,7 @@ async function doRefresh(client) {
457
531
  error: new AuraError(0, {
458
532
  error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : {
459
533
  code: "network_error",
460
- message: err instanceof Error ? err.message : "Network request failed"
534
+ message: networkErrorMessage(err, `${client.baseUrl}/v1/auth/refresh`)
461
535
  }
462
536
  })
463
537
  };
@@ -658,7 +732,7 @@ async function fetchWithAuth(url, init, client) {
658
732
  error: new AuraError(0, {
659
733
  error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : {
660
734
  code: "network_error",
661
- message: err instanceof Error ? err.message : "Network request failed"
735
+ message: networkErrorMessage(err, url)
662
736
  }
663
737
  })
664
738
  };
@@ -691,7 +765,9 @@ async function fetchWithAuth(url, init, client) {
691
765
  }
692
766
  }
693
767
  if (res.status === 204) {
694
- return { data: null, error: null };
768
+ const totalFromCr2 = parseContentRangeTotal(res.headers.get("content-range"));
769
+ if (totalFromCr2 === void 0) return { data: null, error: null };
770
+ return { data: null, error: null, meta: { total: totalFromCr2 }, count: totalFromCr2 };
695
771
  }
696
772
  if (!res.ok) {
697
773
  let body2 = null;
@@ -805,13 +881,16 @@ function parseCookieHeader(header) {
805
881
  }
806
882
  return result;
807
883
  }
884
+ function sameSiteAttribute(v) {
885
+ return v.charAt(0).toUpperCase() + v.slice(1);
886
+ }
808
887
  function serializeCookieHeader(name, value, options = {}) {
809
888
  const parts = [`${name}=${encodeURIComponent(value)}`];
810
889
  if (options.path) parts.push(`Path=${options.path}`);
811
890
  if (options.domain) parts.push(`Domain=${options.domain}`);
812
891
  if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
813
892
  if (options.secure) parts.push("Secure");
814
- if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
893
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
815
894
  if (options.httpOnly) parts.push("HttpOnly");
816
895
  return parts.join("; ");
817
896
  }
@@ -931,7 +1010,7 @@ var LocalAuthStore = class extends BaseAuthStore {
931
1010
  if (options.domain) parts.push(`Domain=${options.domain}`);
932
1011
  if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
933
1012
  if (options.secure) parts.push("Secure");
934
- if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
1013
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
935
1014
  if (options.httpOnly) parts.push("HttpOnly");
936
1015
  return parts.join("; ");
937
1016
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurabase/core",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
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",
@@ -31,12 +31,12 @@
31
31
  "author": "Aurabase",
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "git+https://github.com/daylami555/aurabase.git",
34
+ "url": "git+https://github.com/Aurabase-cloud/Aurabase.git",
35
35
  "directory": "aurabase-js/packages/core"
36
36
  },
37
37
  "homepage": "https://aurabase.cloud",
38
38
  "bugs": {
39
- "url": "https://github.com/daylami555/aurabase/issues"
39
+ "url": "https://github.com/Aurabase-cloud/Aurabase/issues"
40
40
  },
41
41
  "keywords": [
42
42
  "aurabase",