@aurabase/core 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,7 +113,7 @@ class WidgetsService extends BaseService {
113
113
  }
114
114
 
115
115
  private get prefix(): string {
116
- return `/v1/widgets/${this.client.projectId}`
116
+ return '/v1/widgets'
117
117
  }
118
118
 
119
119
  async list(): Promise<AuraResponse<Widget[]>> {
@@ -132,16 +132,21 @@ class WidgetsService extends BaseService {
132
132
 
133
133
  `BaseAuthStore` est en mémoire (event-emitter `onChange`, stockage clé/valeur générique pour le PKCE `code_verifier`). `LocalAuthStore` l'étend avec persistance `localStorage` et des helpers cookie pour le rendu serveur.
134
134
 
135
+ Son argument `key` est **obligatoire** et doit être SCOPÉ par projet : deux clients pointant sur deux projets dans la même page partagent un seul `localStorage` (celui de la page), et une clé constante les ferait s'écraser. Le nom canonique est `aura-${clientScope(baseUrl)}-auth` — celui que posent déjà `AuraClient` et `createServerClient`.
136
+
135
137
  ```ts
136
- import { LocalAuthStore } from '@aurabase/core'
138
+ import { LocalAuthStore, clientScope } from '@aurabase/core'
139
+
140
+ const PROJET = 'https://mon-projet.aurabase.cloud'
141
+ const CLE = `aura-${clientScope(PROJET)}-auth`
137
142
 
138
143
  function hydrateFromCookieHeader(cookieHeader: string) {
139
- const store = new LocalAuthStore('aurabase_auth')
144
+ const store = new LocalAuthStore(CLE)
140
145
  store.loadFromCookie(cookieHeader) // hydrate depuis l'en-tête Cookie de la requête entrante
141
146
  return store
142
147
  }
143
148
 
144
- const store = hydrateFromCookieHeader('aurabase_auth=%7B%22token%22%3Anull%7D')
149
+ const store = hydrateFromCookieHeader(`${CLE}=%7B%22token%22%3Anull%7D`)
145
150
 
146
151
  // Après une mutation de session, sérialiser l'état courant en Set-Cookie.
147
152
  const setCookieValue = store.exportToCookie({ path: '/', secure: true, sameSite: 'Lax', httpOnly: true })
@@ -163,7 +168,7 @@ Cette couche (`fetchWithAuth`, `refreshOnce`) est exportée mais reste un détai
163
168
 
164
169
  - Package d'infrastructure : pas d'API « produit ». Pour construire une application, utilisez le méta-package [`aurabase`](../aurabase), qui assemble tous les services derrière un seul client.
165
170
  - `BaseAuthStore` est en mémoire — l'état est perdu au reload ; `LocalAuthStore` nécessite un `localStorage` disponible (sinon retombe silencieusement sur le comportement en mémoire, sauf via les helpers cookie explicites).
166
- - `AuraClientLike` est un contrat minimal (`baseUrl`, `projectId`, `apiKey`, `fetchFn`, `authStore`, `auth.refreshSession`, `retryConfig?`, `requestTimeoutMs?`) : un client maison doit l'implémenter intégralement pour rester compatible avec `BaseService`.
171
+ - `AuraClientLike` est un contrat minimal (`baseUrl`, `apiKey`, `fetchFn`, `authStore`, `auth.refreshSession`, `retryConfig?`, `requestTimeoutMs?`) : un client maison doit l'implémenter intégralement pour rester compatible avec `BaseService`.
167
172
  - La couche fetch bas niveau (`fetchWithAuth`/`refreshOnce`) est un détail d'implémentation interne, pas une API publique stabilisée.
168
173
 
169
174
  ## Documentation
package/dist/index.cjs CHANGED
@@ -33,6 +33,70 @@ function isAuraError(error) {
33
33
  return error instanceof AuraError || typeof error === "object" && error !== null && error.__isAuraError === true;
34
34
  }
35
35
 
36
+ // src/retry.ts
37
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
38
+ var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
39
+ var DEFAULT_MAX_RETRIES = 3;
40
+ var BASE_MS = 500;
41
+ var CAP_MS = 1e4;
42
+ function isRetryableStatus(status) {
43
+ return RETRYABLE_STATUS.has(status);
44
+ }
45
+ function isRetryableMethod(method) {
46
+ return RETRYABLE_METHODS.has(method.toUpperCase());
47
+ }
48
+ function computeBackoff(attempt, rand = Math.random) {
49
+ const bound = Math.min(BASE_MS * 2 ** attempt, CAP_MS);
50
+ return Math.floor(rand() * bound);
51
+ }
52
+ function parseRetryAfter(header, now = Date.now()) {
53
+ if (header === null) return null;
54
+ const trimmed = header.trim();
55
+ if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10) * 1e3;
56
+ const dateMs = Date.parse(trimmed);
57
+ if (Number.isNaN(dateMs)) return null;
58
+ return Math.max(0, dateMs - now);
59
+ }
60
+ function abortableSleep(ms, signal) {
61
+ return new Promise((resolve) => {
62
+ if (signal?.aborted) {
63
+ resolve();
64
+ return;
65
+ }
66
+ const onAbort = () => {
67
+ clearTimeout(timer);
68
+ resolve();
69
+ };
70
+ const timer = setTimeout(() => {
71
+ signal?.removeEventListener("abort", onAbort);
72
+ resolve();
73
+ }, ms);
74
+ signal?.addEventListener("abort", onAbort, { once: true });
75
+ });
76
+ }
77
+ function isAbortError(err) {
78
+ return typeof err === "object" && err !== null && err.name === "AbortError";
79
+ }
80
+
81
+ // src/scope.ts
82
+ function clientScope(baseUrl) {
83
+ let host;
84
+ try {
85
+ host = new URL(baseUrl).host;
86
+ } catch {
87
+ host = baseUrl;
88
+ }
89
+ return host.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "default";
90
+ }
91
+
92
+ // src/serialize.ts
93
+ function serializeBody(body) {
94
+ return JSON.stringify(
95
+ body,
96
+ (_key, value) => typeof value === "bigint" ? value.toString() : value
97
+ );
98
+ }
99
+
36
100
  // src/types.ts
37
101
  function redactUrl(u) {
38
102
  const cut = u.search(/[?#]/);
@@ -52,6 +116,60 @@ function emitAuraEvent(onEvent, debug, event) {
52
116
  } catch {
53
117
  }
54
118
  }
119
+ var AUTH_SETTINGS_WRITABLE_FIELDS = [
120
+ "allow_signup",
121
+ "email_confirmation_required",
122
+ "password_min_length",
123
+ "mfa_enabled",
124
+ "allow_anonymous_users",
125
+ "jwt_expiry_seconds",
126
+ "refresh_token_expiry_days",
127
+ "site_url",
128
+ "oauth_providers",
129
+ "new_device_alerts_enabled",
130
+ "webhook_url",
131
+ "webhook_secret",
132
+ "smtp_host",
133
+ "smtp_port",
134
+ "smtp_username",
135
+ "smtp_password",
136
+ "smtp_from_email",
137
+ "smtp_from_name",
138
+ "smtp_clear",
139
+ "oauth_credentials",
140
+ "oauth_credentials_clear",
141
+ "captcha_enabled",
142
+ "captcha_provider",
143
+ "captcha_site_key",
144
+ "captcha_secret_key",
145
+ "password_check_hibp",
146
+ "sms_provider",
147
+ "sms_api_key",
148
+ "sms_api_secret",
149
+ "sms_from_number",
150
+ "sms_clear",
151
+ "redirect_urls",
152
+ "session_inactivity_timeout_seconds",
153
+ "hook_send_sms_url",
154
+ "hook_send_email_url",
155
+ "hook_mfa_verification_url",
156
+ "hook_password_verification_url",
157
+ "auto_link_by_email",
158
+ "rate_limit_login_per_hour",
159
+ "rate_limit_emails_per_hour",
160
+ "rate_limit_signup_per_hour",
161
+ "revoke_on_new_device",
162
+ "enforce_ip_binding",
163
+ "allowed_email_domains",
164
+ "blocked_email_domains",
165
+ "password_require_uppercase",
166
+ "password_require_number",
167
+ "password_require_special",
168
+ "hook_before_signup_url",
169
+ "hook_after_signup_url",
170
+ "hook_custom_access_token_url",
171
+ "hook_secret"
172
+ ];
55
173
 
56
174
  // src/stores/BaseAuthStore.ts
57
175
  function decodeJwtPayload(token) {
@@ -172,59 +290,6 @@ var BaseAuthStore = class {
172
290
  }
173
291
  };
174
292
 
175
- // src/retry.ts
176
- var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
177
- var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
178
- var DEFAULT_MAX_RETRIES = 3;
179
- var BASE_MS = 500;
180
- var CAP_MS = 1e4;
181
- function isRetryableStatus(status) {
182
- return RETRYABLE_STATUS.has(status);
183
- }
184
- function isRetryableMethod(method) {
185
- return RETRYABLE_METHODS.has(method.toUpperCase());
186
- }
187
- function computeBackoff(attempt, rand = Math.random) {
188
- const bound = Math.min(BASE_MS * 2 ** attempt, CAP_MS);
189
- return Math.floor(rand() * bound);
190
- }
191
- function parseRetryAfter(header, now = Date.now()) {
192
- if (header === null) return null;
193
- const trimmed = header.trim();
194
- if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10) * 1e3;
195
- const dateMs = Date.parse(trimmed);
196
- if (Number.isNaN(dateMs)) return null;
197
- return Math.max(0, dateMs - now);
198
- }
199
- function abortableSleep(ms, signal) {
200
- return new Promise((resolve) => {
201
- if (signal?.aborted) {
202
- resolve();
203
- return;
204
- }
205
- const onAbort = () => {
206
- clearTimeout(timer);
207
- resolve();
208
- };
209
- const timer = setTimeout(() => {
210
- signal?.removeEventListener("abort", onAbort);
211
- resolve();
212
- }, ms);
213
- signal?.addEventListener("abort", onAbort, { once: true });
214
- });
215
- }
216
- function isAbortError(err) {
217
- return typeof err === "object" && err !== null && err.name === "AbortError";
218
- }
219
-
220
- // src/serialize.ts
221
- function serializeBody(body) {
222
- return JSON.stringify(
223
- body,
224
- (_key, value) => typeof value === "bigint" ? value.toString() : value
225
- );
226
- }
227
-
228
293
  // src/fetch.ts
229
294
  var REFRESH_MARGIN_SEC = 30;
230
295
  var RETRY_AFTER_CAP_MS = 6e4;
@@ -235,6 +300,56 @@ function unwrapEnvelope(body) {
235
300
  }
236
301
  return body;
237
302
  }
303
+ async function readTextBounded(res, timeoutMs) {
304
+ const body = res.body;
305
+ if (!body || typeof body.getReader !== "function" || !(timeoutMs > 0)) {
306
+ try {
307
+ return { ok: true, text: await res.text() };
308
+ } catch (err) {
309
+ return {
310
+ ok: false,
311
+ timedOut: false,
312
+ message: err instanceof Error ? err.message : "read failed"
313
+ };
314
+ }
315
+ }
316
+ let timer;
317
+ const TIMED_OUT = /* @__PURE__ */ Symbol("body-timeout");
318
+ try {
319
+ const reader = body.getReader();
320
+ const decoder = new TextDecoder();
321
+ let out = "";
322
+ const deadline = new Promise((resolve) => {
323
+ timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
324
+ timer.unref?.();
325
+ });
326
+ for (; ; ) {
327
+ const step = await Promise.race([reader.read(), deadline]);
328
+ if (step === TIMED_OUT) {
329
+ await reader.cancel().catch(() => {
330
+ });
331
+ return {
332
+ ok: false,
333
+ timedOut: true,
334
+ message: `Response body not fully received within ${timeoutMs}ms`
335
+ };
336
+ }
337
+ if (step.done) {
338
+ out += decoder.decode();
339
+ return { ok: true, text: out };
340
+ }
341
+ if (step.value) out += decoder.decode(step.value, { stream: true });
342
+ }
343
+ } catch (err) {
344
+ return {
345
+ ok: false,
346
+ timedOut: false,
347
+ message: err instanceof Error ? err.message : "read failed"
348
+ };
349
+ } finally {
350
+ if (timer) clearTimeout(timer);
351
+ }
352
+ }
238
353
  var inflightRefreshes = /* @__PURE__ */ new WeakMap();
239
354
  async function doRefresh(client) {
240
355
  const store = client.authStore;
@@ -242,32 +357,68 @@ async function doRefresh(client) {
242
357
  if (!rt) {
243
358
  return {
244
359
  data: null,
245
- error: new AuraError(0, { error: { code: "no_session", message: "No refresh token available" } })
360
+ error: new AuraError(0, {
361
+ error: { code: "no_session", message: "No refresh token available" }
362
+ })
246
363
  };
247
364
  }
248
365
  const epoch = store.removalEpoch;
249
366
  const headers = { "Content-Type": "application/json" };
250
367
  if (client.apiKey) headers["apikey"] = client.apiKey;
368
+ const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
369
+ let timedOut = false;
370
+ let refreshTimer;
371
+ let refreshSignal;
372
+ if (timeoutMs > 0 && typeof AbortController !== "undefined") {
373
+ const ctrl = new AbortController();
374
+ refreshTimer = setTimeout(() => {
375
+ timedOut = true;
376
+ ctrl.abort();
377
+ }, timeoutMs);
378
+ refreshTimer.unref?.();
379
+ refreshSignal = ctrl.signal;
380
+ }
381
+ const clearRefreshTimer = () => {
382
+ if (refreshTimer) {
383
+ clearTimeout(refreshTimer);
384
+ refreshTimer = void 0;
385
+ }
386
+ };
251
387
  let res;
252
388
  try {
253
- res = await client.fetchFn(`${client.baseUrl}/v1/auth/${client.projectId}/refresh`, {
389
+ res = await client.fetchFn(`${client.baseUrl}/v1/auth/refresh`, {
254
390
  method: "POST",
255
391
  headers,
256
- body: serializeBody({ refresh_token: rt })
392
+ body: serializeBody({ refresh_token: rt }),
393
+ signal: refreshSignal
257
394
  });
258
395
  } catch (err) {
396
+ clearRefreshTimer();
397
+ if (timedOut) {
398
+ emitAuraEvent(client.onEvent, client.debug, {
399
+ kind: "timeout",
400
+ url: `${client.baseUrl}/v1/auth/refresh`
401
+ });
402
+ }
259
403
  return {
260
404
  data: null,
261
405
  error: new AuraError(0, {
262
- error: { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
406
+ error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : {
407
+ code: "network_error",
408
+ message: err instanceof Error ? err.message : "Network request failed"
409
+ }
263
410
  })
264
411
  };
265
412
  }
413
+ clearRefreshTimer();
266
414
  if (!res.ok) {
267
415
  let body2 = null;
268
- try {
269
- body2 = await res.json();
270
- } catch {
416
+ const errRead = await readTextBounded(res, timeoutMs);
417
+ if (errRead.ok && errRead.text.trim() !== "") {
418
+ try {
419
+ body2 = JSON.parse(errRead.text);
420
+ } catch {
421
+ }
271
422
  }
272
423
  const code = typeof body2?.error?.code === "string" ? body2.error.code : null;
273
424
  const definitivelyRejected = res.status === 403 || res.status === 401 && code === "REFRESH_TOKEN_REJECTED";
@@ -279,12 +430,29 @@ async function doRefresh(client) {
279
430
  return { data: null, error: new AuraError(res.status, body2) };
280
431
  }
281
432
  let body;
433
+ const okRead = await readTextBounded(res, timeoutMs);
434
+ if (!okRead.ok) {
435
+ if (okRead.timedOut) {
436
+ emitAuraEvent(client.onEvent, client.debug, {
437
+ kind: "timeout",
438
+ url: `${client.baseUrl}/v1/auth/refresh`
439
+ });
440
+ }
441
+ return {
442
+ data: null,
443
+ error: new AuraError(0, {
444
+ error: okRead.timedOut ? { code: "timeout", message: `Refresh body not received within ${timeoutMs}ms` } : { code: "invalid_response", message: `Invalid refresh response (${okRead.message})` }
445
+ })
446
+ };
447
+ }
282
448
  try {
283
- body = await res.json();
449
+ body = JSON.parse(okRead.text);
284
450
  } catch {
285
451
  return {
286
452
  data: null,
287
- error: new AuraError(0, { error: { code: "invalid_response", message: "Invalid refresh response (non-JSON body)" } })
453
+ error: new AuraError(0, {
454
+ error: { code: "invalid_response", message: "Invalid refresh response (non-JSON body)" }
455
+ })
288
456
  };
289
457
  }
290
458
  const data = unwrapEnvelope(body);
@@ -296,7 +464,9 @@ async function doRefresh(client) {
296
464
  });
297
465
  return {
298
466
  data: null,
299
- error: new AuraError(409, { error: { code: "refresh_discarded", message: "Session changed during refresh" } })
467
+ error: new AuraError(409, {
468
+ error: { code: "refresh_discarded", message: "Session changed during refresh" }
469
+ })
300
470
  };
301
471
  }
302
472
  store.save(data.access_token, data.refresh_token, data.user);
@@ -305,7 +475,9 @@ async function doRefresh(client) {
305
475
  }
306
476
  return {
307
477
  data: null,
308
- error: new AuraError(0, { error: { code: "invalid_response", message: "Invalid refresh response" } })
478
+ error: new AuraError(0, {
479
+ error: { code: "invalid_response", message: "Invalid refresh response" }
480
+ })
309
481
  };
310
482
  }
311
483
  function withRefreshLock(client, fn) {
@@ -316,7 +488,7 @@ function withRefreshLock(client, fn) {
316
488
  return fn();
317
489
  }
318
490
  const request = locks.request;
319
- const lockName = `aura-refresh-${client.projectId}`;
491
+ const lockName = `aura-refresh-${clientScope(client.baseUrl)}`;
320
492
  const timeoutMs = (client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS) || DEFAULT_REQUEST_TIMEOUT_MS;
321
493
  let timer;
322
494
  const options = {};
@@ -373,6 +545,7 @@ async function fetchWithAuth(url, init, client) {
373
545
  const isFormData = typeof FormData !== "undefined" && init.body instanceof FormData;
374
546
  const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
375
547
  const applyTimeout = timeoutMs > 0 && init.signal === void 0 && !isFormData;
548
+ const bodyTimeoutMs = init.signal === void 0 ? timeoutMs : 0;
376
549
  let attempt = 0;
377
550
  for (; ; ) {
378
551
  const headers = { ...init.headers };
@@ -431,7 +604,10 @@ async function fetchWithAuth(url, init, client) {
431
604
  return {
432
605
  data: null,
433
606
  error: new AuraError(0, {
434
- error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
607
+ error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : {
608
+ code: "network_error",
609
+ message: err instanceof Error ? err.message : "Network request failed"
610
+ }
435
611
  })
436
612
  };
437
613
  }
@@ -444,7 +620,12 @@ async function fetchWithAuth(url, init, client) {
444
620
  await res.body?.cancel().catch(() => {
445
621
  });
446
622
  const nextAttempt = attempt + 1;
447
- emitAuraEvent(client.onEvent, client.debug, { kind: "retry", attempt: nextAttempt, status: res.status, url });
623
+ emitAuraEvent(client.onEvent, client.debug, {
624
+ kind: "retry",
625
+ attempt: nextAttempt,
626
+ status: res.status,
627
+ url
628
+ });
448
629
  await abortableSleep(delay, init.signal);
449
630
  attempt = nextAttempt;
450
631
  continue;
@@ -462,26 +643,42 @@ async function fetchWithAuth(url, init, client) {
462
643
  }
463
644
  if (!res.ok) {
464
645
  let body2 = null;
465
- try {
466
- body2 = await res.json();
467
- } catch {
646
+ const errRead = await readTextBounded(res, bodyTimeoutMs);
647
+ if (errRead.ok && errRead.text.trim() !== "") {
648
+ try {
649
+ body2 = JSON.parse(errRead.text);
650
+ } catch {
651
+ }
652
+ } else if (!errRead.ok && errRead.timedOut) {
653
+ emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
468
654
  }
469
655
  return { data: null, error: new AuraError(res.status, body2) };
470
656
  }
471
- let raw;
472
- try {
473
- raw = await res.text();
474
- } catch (err) {
657
+ const read = await readTextBounded(res, bodyTimeoutMs);
658
+ if (!read.ok) {
659
+ if (read.timedOut) {
660
+ emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
661
+ return {
662
+ data: null,
663
+ error: new AuraError(0, {
664
+ error: {
665
+ code: "timeout",
666
+ message: `Response body not fully received within ${bodyTimeoutMs}ms (HTTP ${res.status})`
667
+ }
668
+ })
669
+ };
670
+ }
475
671
  return {
476
672
  data: null,
477
673
  error: new AuraError(0, {
478
674
  error: {
479
675
  code: "invalid_response",
480
- message: `Unreadable response body (HTTP ${res.status}): ${err instanceof Error ? err.message : "read failed"}`
676
+ message: `Unreadable response body (HTTP ${res.status}): ${read.message}`
481
677
  }
482
678
  })
483
679
  };
484
680
  }
681
+ const raw = read.text;
485
682
  if (raw.trim() === "") {
486
683
  return { data: null, error: null };
487
684
  }
@@ -534,12 +731,40 @@ var BaseService = class {
534
731
  }
535
732
  };
536
733
 
734
+ // src/cookie-utils.ts
735
+ function parseCookieHeader(header) {
736
+ const result = {};
737
+ if (!header) return result;
738
+ for (const pair of header.split(";")) {
739
+ const idx = pair.indexOf("=");
740
+ if (idx === -1) continue;
741
+ const key = pair.slice(0, idx).trim();
742
+ const value = pair.slice(idx + 1).trim();
743
+ if (key) result[key] = decodeURIComponent(value);
744
+ }
745
+ return result;
746
+ }
747
+ function serializeCookieHeader(name, value, options = {}) {
748
+ const parts = [`${name}=${encodeURIComponent(value)}`];
749
+ if (options.path) parts.push(`Path=${options.path}`);
750
+ if (options.domain) parts.push(`Domain=${options.domain}`);
751
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
752
+ if (options.secure) parts.push("Secure");
753
+ if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
754
+ if (options.httpOnly) parts.push("HttpOnly");
755
+ return parts.join("; ");
756
+ }
757
+
537
758
  // src/stores/LocalAuthStore.ts
538
759
  var LocalAuthStore = class extends BaseAuthStore {
539
760
  /** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
540
761
  persistent = true;
541
762
  key;
542
- constructor(key = "aurabase_auth") {
763
+ /**
764
+ * @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
765
+ * `aura-${clientScope(baseUrl)}-auth`.
766
+ */
767
+ constructor(key) {
543
768
  super();
544
769
  this.key = key;
545
770
  this._loadFromStorage();
@@ -556,11 +781,12 @@ var LocalAuthStore = class extends BaseAuthStore {
556
781
  }
557
782
  /**
558
783
  * Supprime de localStorage tous les artefacts d'auth éphémères sur disque :
559
- * - `code_verifier` PKCE (`aura-<projectId>-pkce-code-verifier`),
560
- * - marqueur d'initiation OAuth implicit (`aura-<projectId>-oauth-implicit-initiated`,
784
+ * - `code_verifier` PKCE (`aura-<portée>-pkce-code-verifier`),
785
+ * - marqueur d'initiation OAuth implicit (`aura-<portée>-oauth-implicit-initiated`,
561
786
  * SEC-F3). Un `signOut`/`clear()` ne doit laisser aucun résidu réutilisable (parité
562
787
  * `@supabase/ssr` : ces artefacts sont éphémères). On itère les clés préfixées pour
563
- * couvrir un store partagé entre plusieurs projets, sans dépendre du `projectId` courant.
788
+ * couvrir un store partagé entre plusieurs projets, sans dépendre de la portée courante
789
+ * (`clientScope`, dérivée de l'URL du projet).
564
790
  */
565
791
  _removePkceVerifiers() {
566
792
  if (typeof localStorage === "undefined") return;
@@ -681,38 +907,17 @@ var LocalAuthStore = class extends BaseAuthStore {
681
907
  }
682
908
  };
683
909
 
684
- // src/cookie-utils.ts
685
- function parseCookieHeader(header) {
686
- const result = {};
687
- if (!header) return result;
688
- for (const pair of header.split(";")) {
689
- const idx = pair.indexOf("=");
690
- if (idx === -1) continue;
691
- const key = pair.slice(0, idx).trim();
692
- const value = pair.slice(idx + 1).trim();
693
- if (key) result[key] = decodeURIComponent(value);
694
- }
695
- return result;
696
- }
697
- function serializeCookieHeader(name, value, options = {}) {
698
- const parts = [`${name}=${encodeURIComponent(value)}`];
699
- if (options.path) parts.push(`Path=${options.path}`);
700
- if (options.domain) parts.push(`Domain=${options.domain}`);
701
- if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
702
- if (options.secure) parts.push("Secure");
703
- if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
704
- if (options.httpOnly) parts.push("HttpOnly");
705
- return parts.join("; ");
706
- }
707
-
910
+ exports.AUTH_SETTINGS_WRITABLE_FIELDS = AUTH_SETTINGS_WRITABLE_FIELDS;
708
911
  exports.AuraError = AuraError;
709
912
  exports.BaseAuthStore = BaseAuthStore;
710
913
  exports.BaseService = BaseService;
711
914
  exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES;
915
+ exports.DEFAULT_REQUEST_TIMEOUT_MS = DEFAULT_REQUEST_TIMEOUT_MS;
712
916
  exports.LocalAuthStore = LocalAuthStore;
713
917
  exports.RETRYABLE_METHODS = RETRYABLE_METHODS;
714
918
  exports.RETRYABLE_STATUS = RETRYABLE_STATUS;
715
919
  exports.abortableSleep = abortableSleep;
920
+ exports.clientScope = clientScope;
716
921
  exports.computeBackoff = computeBackoff;
717
922
  exports.emitAuraEvent = emitAuraEvent;
718
923
  exports.fetchWithAuth = fetchWithAuth;