@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/dist/index.js CHANGED
@@ -31,6 +31,70 @@ function isAuraError(error) {
31
31
  return error instanceof AuraError || typeof error === "object" && error !== null && error.__isAuraError === true;
32
32
  }
33
33
 
34
+ // src/retry.ts
35
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
36
+ var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
37
+ var DEFAULT_MAX_RETRIES = 3;
38
+ var BASE_MS = 500;
39
+ var CAP_MS = 1e4;
40
+ function isRetryableStatus(status) {
41
+ return RETRYABLE_STATUS.has(status);
42
+ }
43
+ function isRetryableMethod(method) {
44
+ return RETRYABLE_METHODS.has(method.toUpperCase());
45
+ }
46
+ function computeBackoff(attempt, rand = Math.random) {
47
+ const bound = Math.min(BASE_MS * 2 ** attempt, CAP_MS);
48
+ return Math.floor(rand() * bound);
49
+ }
50
+ function parseRetryAfter(header, now = Date.now()) {
51
+ if (header === null) return null;
52
+ const trimmed = header.trim();
53
+ if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10) * 1e3;
54
+ const dateMs = Date.parse(trimmed);
55
+ if (Number.isNaN(dateMs)) return null;
56
+ return Math.max(0, dateMs - now);
57
+ }
58
+ function abortableSleep(ms, signal) {
59
+ return new Promise((resolve) => {
60
+ if (signal?.aborted) {
61
+ resolve();
62
+ return;
63
+ }
64
+ const onAbort = () => {
65
+ clearTimeout(timer);
66
+ resolve();
67
+ };
68
+ const timer = setTimeout(() => {
69
+ signal?.removeEventListener("abort", onAbort);
70
+ resolve();
71
+ }, ms);
72
+ signal?.addEventListener("abort", onAbort, { once: true });
73
+ });
74
+ }
75
+ function isAbortError(err) {
76
+ return typeof err === "object" && err !== null && err.name === "AbortError";
77
+ }
78
+
79
+ // src/scope.ts
80
+ function clientScope(baseUrl) {
81
+ let host;
82
+ try {
83
+ host = new URL(baseUrl).host;
84
+ } catch {
85
+ host = baseUrl;
86
+ }
87
+ return host.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "default";
88
+ }
89
+
90
+ // src/serialize.ts
91
+ function serializeBody(body) {
92
+ return JSON.stringify(
93
+ body,
94
+ (_key, value) => typeof value === "bigint" ? value.toString() : value
95
+ );
96
+ }
97
+
34
98
  // src/types.ts
35
99
  function redactUrl(u) {
36
100
  const cut = u.search(/[?#]/);
@@ -50,6 +114,60 @@ function emitAuraEvent(onEvent, debug, event) {
50
114
  } catch {
51
115
  }
52
116
  }
117
+ var AUTH_SETTINGS_WRITABLE_FIELDS = [
118
+ "allow_signup",
119
+ "email_confirmation_required",
120
+ "password_min_length",
121
+ "mfa_enabled",
122
+ "allow_anonymous_users",
123
+ "jwt_expiry_seconds",
124
+ "refresh_token_expiry_days",
125
+ "site_url",
126
+ "oauth_providers",
127
+ "new_device_alerts_enabled",
128
+ "webhook_url",
129
+ "webhook_secret",
130
+ "smtp_host",
131
+ "smtp_port",
132
+ "smtp_username",
133
+ "smtp_password",
134
+ "smtp_from_email",
135
+ "smtp_from_name",
136
+ "smtp_clear",
137
+ "oauth_credentials",
138
+ "oauth_credentials_clear",
139
+ "captcha_enabled",
140
+ "captcha_provider",
141
+ "captcha_site_key",
142
+ "captcha_secret_key",
143
+ "password_check_hibp",
144
+ "sms_provider",
145
+ "sms_api_key",
146
+ "sms_api_secret",
147
+ "sms_from_number",
148
+ "sms_clear",
149
+ "redirect_urls",
150
+ "session_inactivity_timeout_seconds",
151
+ "hook_send_sms_url",
152
+ "hook_send_email_url",
153
+ "hook_mfa_verification_url",
154
+ "hook_password_verification_url",
155
+ "auto_link_by_email",
156
+ "rate_limit_login_per_hour",
157
+ "rate_limit_emails_per_hour",
158
+ "rate_limit_signup_per_hour",
159
+ "revoke_on_new_device",
160
+ "enforce_ip_binding",
161
+ "allowed_email_domains",
162
+ "blocked_email_domains",
163
+ "password_require_uppercase",
164
+ "password_require_number",
165
+ "password_require_special",
166
+ "hook_before_signup_url",
167
+ "hook_after_signup_url",
168
+ "hook_custom_access_token_url",
169
+ "hook_secret"
170
+ ];
53
171
 
54
172
  // src/stores/BaseAuthStore.ts
55
173
  function decodeJwtPayload(token) {
@@ -170,59 +288,6 @@ var BaseAuthStore = class {
170
288
  }
171
289
  };
172
290
 
173
- // src/retry.ts
174
- var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
175
- var RETRYABLE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
176
- var DEFAULT_MAX_RETRIES = 3;
177
- var BASE_MS = 500;
178
- var CAP_MS = 1e4;
179
- function isRetryableStatus(status) {
180
- return RETRYABLE_STATUS.has(status);
181
- }
182
- function isRetryableMethod(method) {
183
- return RETRYABLE_METHODS.has(method.toUpperCase());
184
- }
185
- function computeBackoff(attempt, rand = Math.random) {
186
- const bound = Math.min(BASE_MS * 2 ** attempt, CAP_MS);
187
- return Math.floor(rand() * bound);
188
- }
189
- function parseRetryAfter(header, now = Date.now()) {
190
- if (header === null) return null;
191
- const trimmed = header.trim();
192
- if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10) * 1e3;
193
- const dateMs = Date.parse(trimmed);
194
- if (Number.isNaN(dateMs)) return null;
195
- return Math.max(0, dateMs - now);
196
- }
197
- function abortableSleep(ms, signal) {
198
- return new Promise((resolve) => {
199
- if (signal?.aborted) {
200
- resolve();
201
- return;
202
- }
203
- const onAbort = () => {
204
- clearTimeout(timer);
205
- resolve();
206
- };
207
- const timer = setTimeout(() => {
208
- signal?.removeEventListener("abort", onAbort);
209
- resolve();
210
- }, ms);
211
- signal?.addEventListener("abort", onAbort, { once: true });
212
- });
213
- }
214
- function isAbortError(err) {
215
- return typeof err === "object" && err !== null && err.name === "AbortError";
216
- }
217
-
218
- // src/serialize.ts
219
- function serializeBody(body) {
220
- return JSON.stringify(
221
- body,
222
- (_key, value) => typeof value === "bigint" ? value.toString() : value
223
- );
224
- }
225
-
226
291
  // src/fetch.ts
227
292
  var REFRESH_MARGIN_SEC = 30;
228
293
  var RETRY_AFTER_CAP_MS = 6e4;
@@ -233,6 +298,56 @@ function unwrapEnvelope(body) {
233
298
  }
234
299
  return body;
235
300
  }
301
+ async function readTextBounded(res, timeoutMs) {
302
+ const body = res.body;
303
+ if (!body || typeof body.getReader !== "function" || !(timeoutMs > 0)) {
304
+ try {
305
+ return { ok: true, text: await res.text() };
306
+ } catch (err) {
307
+ return {
308
+ ok: false,
309
+ timedOut: false,
310
+ message: err instanceof Error ? err.message : "read failed"
311
+ };
312
+ }
313
+ }
314
+ let timer;
315
+ const TIMED_OUT = /* @__PURE__ */ Symbol("body-timeout");
316
+ try {
317
+ const reader = body.getReader();
318
+ const decoder = new TextDecoder();
319
+ let out = "";
320
+ const deadline = new Promise((resolve) => {
321
+ timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
322
+ timer.unref?.();
323
+ });
324
+ for (; ; ) {
325
+ const step = await Promise.race([reader.read(), deadline]);
326
+ if (step === TIMED_OUT) {
327
+ await reader.cancel().catch(() => {
328
+ });
329
+ return {
330
+ ok: false,
331
+ timedOut: true,
332
+ message: `Response body not fully received within ${timeoutMs}ms`
333
+ };
334
+ }
335
+ if (step.done) {
336
+ out += decoder.decode();
337
+ return { ok: true, text: out };
338
+ }
339
+ if (step.value) out += decoder.decode(step.value, { stream: true });
340
+ }
341
+ } catch (err) {
342
+ return {
343
+ ok: false,
344
+ timedOut: false,
345
+ message: err instanceof Error ? err.message : "read failed"
346
+ };
347
+ } finally {
348
+ if (timer) clearTimeout(timer);
349
+ }
350
+ }
236
351
  var inflightRefreshes = /* @__PURE__ */ new WeakMap();
237
352
  async function doRefresh(client) {
238
353
  const store = client.authStore;
@@ -240,32 +355,68 @@ async function doRefresh(client) {
240
355
  if (!rt) {
241
356
  return {
242
357
  data: null,
243
- error: new AuraError(0, { error: { code: "no_session", message: "No refresh token available" } })
358
+ error: new AuraError(0, {
359
+ error: { code: "no_session", message: "No refresh token available" }
360
+ })
244
361
  };
245
362
  }
246
363
  const epoch = store.removalEpoch;
247
364
  const headers = { "Content-Type": "application/json" };
248
365
  if (client.apiKey) headers["apikey"] = client.apiKey;
366
+ const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
367
+ let timedOut = false;
368
+ let refreshTimer;
369
+ let refreshSignal;
370
+ if (timeoutMs > 0 && typeof AbortController !== "undefined") {
371
+ const ctrl = new AbortController();
372
+ refreshTimer = setTimeout(() => {
373
+ timedOut = true;
374
+ ctrl.abort();
375
+ }, timeoutMs);
376
+ refreshTimer.unref?.();
377
+ refreshSignal = ctrl.signal;
378
+ }
379
+ const clearRefreshTimer = () => {
380
+ if (refreshTimer) {
381
+ clearTimeout(refreshTimer);
382
+ refreshTimer = void 0;
383
+ }
384
+ };
249
385
  let res;
250
386
  try {
251
- res = await client.fetchFn(`${client.baseUrl}/v1/auth/${client.projectId}/refresh`, {
387
+ res = await client.fetchFn(`${client.baseUrl}/v1/auth/refresh`, {
252
388
  method: "POST",
253
389
  headers,
254
- body: serializeBody({ refresh_token: rt })
390
+ body: serializeBody({ refresh_token: rt }),
391
+ signal: refreshSignal
255
392
  });
256
393
  } catch (err) {
394
+ clearRefreshTimer();
395
+ if (timedOut) {
396
+ emitAuraEvent(client.onEvent, client.debug, {
397
+ kind: "timeout",
398
+ url: `${client.baseUrl}/v1/auth/refresh`
399
+ });
400
+ }
257
401
  return {
258
402
  data: null,
259
403
  error: new AuraError(0, {
260
- error: { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
404
+ error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : {
405
+ code: "network_error",
406
+ message: err instanceof Error ? err.message : "Network request failed"
407
+ }
261
408
  })
262
409
  };
263
410
  }
411
+ clearRefreshTimer();
264
412
  if (!res.ok) {
265
413
  let body2 = null;
266
- try {
267
- body2 = await res.json();
268
- } catch {
414
+ const errRead = await readTextBounded(res, timeoutMs);
415
+ if (errRead.ok && errRead.text.trim() !== "") {
416
+ try {
417
+ body2 = JSON.parse(errRead.text);
418
+ } catch {
419
+ }
269
420
  }
270
421
  const code = typeof body2?.error?.code === "string" ? body2.error.code : null;
271
422
  const definitivelyRejected = res.status === 403 || res.status === 401 && code === "REFRESH_TOKEN_REJECTED";
@@ -277,12 +428,29 @@ async function doRefresh(client) {
277
428
  return { data: null, error: new AuraError(res.status, body2) };
278
429
  }
279
430
  let body;
431
+ const okRead = await readTextBounded(res, timeoutMs);
432
+ if (!okRead.ok) {
433
+ if (okRead.timedOut) {
434
+ emitAuraEvent(client.onEvent, client.debug, {
435
+ kind: "timeout",
436
+ url: `${client.baseUrl}/v1/auth/refresh`
437
+ });
438
+ }
439
+ return {
440
+ data: null,
441
+ error: new AuraError(0, {
442
+ error: okRead.timedOut ? { code: "timeout", message: `Refresh body not received within ${timeoutMs}ms` } : { code: "invalid_response", message: `Invalid refresh response (${okRead.message})` }
443
+ })
444
+ };
445
+ }
280
446
  try {
281
- body = await res.json();
447
+ body = JSON.parse(okRead.text);
282
448
  } catch {
283
449
  return {
284
450
  data: null,
285
- error: new AuraError(0, { error: { code: "invalid_response", message: "Invalid refresh response (non-JSON body)" } })
451
+ error: new AuraError(0, {
452
+ error: { code: "invalid_response", message: "Invalid refresh response (non-JSON body)" }
453
+ })
286
454
  };
287
455
  }
288
456
  const data = unwrapEnvelope(body);
@@ -294,7 +462,9 @@ async function doRefresh(client) {
294
462
  });
295
463
  return {
296
464
  data: null,
297
- error: new AuraError(409, { error: { code: "refresh_discarded", message: "Session changed during refresh" } })
465
+ error: new AuraError(409, {
466
+ error: { code: "refresh_discarded", message: "Session changed during refresh" }
467
+ })
298
468
  };
299
469
  }
300
470
  store.save(data.access_token, data.refresh_token, data.user);
@@ -303,7 +473,9 @@ async function doRefresh(client) {
303
473
  }
304
474
  return {
305
475
  data: null,
306
- error: new AuraError(0, { error: { code: "invalid_response", message: "Invalid refresh response" } })
476
+ error: new AuraError(0, {
477
+ error: { code: "invalid_response", message: "Invalid refresh response" }
478
+ })
307
479
  };
308
480
  }
309
481
  function withRefreshLock(client, fn) {
@@ -314,7 +486,7 @@ function withRefreshLock(client, fn) {
314
486
  return fn();
315
487
  }
316
488
  const request = locks.request;
317
- const lockName = `aura-refresh-${client.projectId}`;
489
+ const lockName = `aura-refresh-${clientScope(client.baseUrl)}`;
318
490
  const timeoutMs = (client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS) || DEFAULT_REQUEST_TIMEOUT_MS;
319
491
  let timer;
320
492
  const options = {};
@@ -371,6 +543,7 @@ async function fetchWithAuth(url, init, client) {
371
543
  const isFormData = typeof FormData !== "undefined" && init.body instanceof FormData;
372
544
  const timeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
373
545
  const applyTimeout = timeoutMs > 0 && init.signal === void 0 && !isFormData;
546
+ const bodyTimeoutMs = init.signal === void 0 ? timeoutMs : 0;
374
547
  let attempt = 0;
375
548
  for (; ; ) {
376
549
  const headers = { ...init.headers };
@@ -429,7 +602,10 @@ async function fetchWithAuth(url, init, client) {
429
602
  return {
430
603
  data: null,
431
604
  error: new AuraError(0, {
432
- error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : { code: "network_error", message: err instanceof Error ? err.message : "Network request failed" }
605
+ error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : {
606
+ code: "network_error",
607
+ message: err instanceof Error ? err.message : "Network request failed"
608
+ }
433
609
  })
434
610
  };
435
611
  }
@@ -442,7 +618,12 @@ async function fetchWithAuth(url, init, client) {
442
618
  await res.body?.cancel().catch(() => {
443
619
  });
444
620
  const nextAttempt = attempt + 1;
445
- emitAuraEvent(client.onEvent, client.debug, { kind: "retry", attempt: nextAttempt, status: res.status, url });
621
+ emitAuraEvent(client.onEvent, client.debug, {
622
+ kind: "retry",
623
+ attempt: nextAttempt,
624
+ status: res.status,
625
+ url
626
+ });
446
627
  await abortableSleep(delay, init.signal);
447
628
  attempt = nextAttempt;
448
629
  continue;
@@ -460,26 +641,42 @@ async function fetchWithAuth(url, init, client) {
460
641
  }
461
642
  if (!res.ok) {
462
643
  let body2 = null;
463
- try {
464
- body2 = await res.json();
465
- } catch {
644
+ const errRead = await readTextBounded(res, bodyTimeoutMs);
645
+ if (errRead.ok && errRead.text.trim() !== "") {
646
+ try {
647
+ body2 = JSON.parse(errRead.text);
648
+ } catch {
649
+ }
650
+ } else if (!errRead.ok && errRead.timedOut) {
651
+ emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
466
652
  }
467
653
  return { data: null, error: new AuraError(res.status, body2) };
468
654
  }
469
- let raw;
470
- try {
471
- raw = await res.text();
472
- } catch (err) {
655
+ const read = await readTextBounded(res, bodyTimeoutMs);
656
+ if (!read.ok) {
657
+ if (read.timedOut) {
658
+ emitAuraEvent(client.onEvent, client.debug, { kind: "timeout", url });
659
+ return {
660
+ data: null,
661
+ error: new AuraError(0, {
662
+ error: {
663
+ code: "timeout",
664
+ message: `Response body not fully received within ${bodyTimeoutMs}ms (HTTP ${res.status})`
665
+ }
666
+ })
667
+ };
668
+ }
473
669
  return {
474
670
  data: null,
475
671
  error: new AuraError(0, {
476
672
  error: {
477
673
  code: "invalid_response",
478
- message: `Unreadable response body (HTTP ${res.status}): ${err instanceof Error ? err.message : "read failed"}`
674
+ message: `Unreadable response body (HTTP ${res.status}): ${read.message}`
479
675
  }
480
676
  })
481
677
  };
482
678
  }
679
+ const raw = read.text;
483
680
  if (raw.trim() === "") {
484
681
  return { data: null, error: null };
485
682
  }
@@ -532,12 +729,40 @@ var BaseService = class {
532
729
  }
533
730
  };
534
731
 
732
+ // src/cookie-utils.ts
733
+ function parseCookieHeader(header) {
734
+ const result = {};
735
+ if (!header) return result;
736
+ for (const pair of header.split(";")) {
737
+ const idx = pair.indexOf("=");
738
+ if (idx === -1) continue;
739
+ const key = pair.slice(0, idx).trim();
740
+ const value = pair.slice(idx + 1).trim();
741
+ if (key) result[key] = decodeURIComponent(value);
742
+ }
743
+ return result;
744
+ }
745
+ function serializeCookieHeader(name, value, options = {}) {
746
+ const parts = [`${name}=${encodeURIComponent(value)}`];
747
+ if (options.path) parts.push(`Path=${options.path}`);
748
+ if (options.domain) parts.push(`Domain=${options.domain}`);
749
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
750
+ if (options.secure) parts.push("Secure");
751
+ if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
752
+ if (options.httpOnly) parts.push("HttpOnly");
753
+ return parts.join("; ");
754
+ }
755
+
535
756
  // src/stores/LocalAuthStore.ts
536
757
  var LocalAuthStore = class extends BaseAuthStore {
537
758
  /** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
538
759
  persistent = true;
539
760
  key;
540
- constructor(key = "aurabase_auth") {
761
+ /**
762
+ * @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
763
+ * `aura-${clientScope(baseUrl)}-auth`.
764
+ */
765
+ constructor(key) {
541
766
  super();
542
767
  this.key = key;
543
768
  this._loadFromStorage();
@@ -554,11 +779,12 @@ var LocalAuthStore = class extends BaseAuthStore {
554
779
  }
555
780
  /**
556
781
  * Supprime de localStorage tous les artefacts d'auth éphémères sur disque :
557
- * - `code_verifier` PKCE (`aura-<projectId>-pkce-code-verifier`),
558
- * - marqueur d'initiation OAuth implicit (`aura-<projectId>-oauth-implicit-initiated`,
782
+ * - `code_verifier` PKCE (`aura-<portée>-pkce-code-verifier`),
783
+ * - marqueur d'initiation OAuth implicit (`aura-<portée>-oauth-implicit-initiated`,
559
784
  * SEC-F3). Un `signOut`/`clear()` ne doit laisser aucun résidu réutilisable (parité
560
785
  * `@supabase/ssr` : ces artefacts sont éphémères). On itère les clés préfixées pour
561
- * couvrir un store partagé entre plusieurs projets, sans dépendre du `projectId` courant.
786
+ * couvrir un store partagé entre plusieurs projets, sans dépendre de la portée courante
787
+ * (`clientScope`, dérivée de l'URL du projet).
562
788
  */
563
789
  _removePkceVerifiers() {
564
790
  if (typeof localStorage === "undefined") return;
@@ -679,28 +905,4 @@ var LocalAuthStore = class extends BaseAuthStore {
679
905
  }
680
906
  };
681
907
 
682
- // src/cookie-utils.ts
683
- function parseCookieHeader(header) {
684
- const result = {};
685
- if (!header) return result;
686
- for (const pair of header.split(";")) {
687
- const idx = pair.indexOf("=");
688
- if (idx === -1) continue;
689
- const key = pair.slice(0, idx).trim();
690
- const value = pair.slice(idx + 1).trim();
691
- if (key) result[key] = decodeURIComponent(value);
692
- }
693
- return result;
694
- }
695
- function serializeCookieHeader(name, value, options = {}) {
696
- const parts = [`${name}=${encodeURIComponent(value)}`];
697
- if (options.path) parts.push(`Path=${options.path}`);
698
- if (options.domain) parts.push(`Domain=${options.domain}`);
699
- if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
700
- if (options.secure) parts.push("Secure");
701
- if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
702
- if (options.httpOnly) parts.push("HttpOnly");
703
- return parts.join("; ");
704
- }
705
-
706
- export { AuraError, BaseAuthStore, BaseService, DEFAULT_MAX_RETRIES, LocalAuthStore, RETRYABLE_METHODS, RETRYABLE_STATUS, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
908
+ export { AUTH_SETTINGS_WRITABLE_FIELDS, AuraError, BaseAuthStore, BaseService, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, LocalAuthStore, RETRYABLE_METHODS, RETRYABLE_STATUS, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurabase/core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Cœur du SDK Aurabase : fetch résilient, erreurs, stores auth, types de réponse (infrastructure interne)",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -45,6 +45,7 @@
45
45
  "core"
46
46
  ],
47
47
  "devDependencies": {
48
+ "@types/node": "20.14.15",
48
49
  "tsup": "^8.0.0",
49
50
  "typescript": "^5.5.0",
50
51
  "vitest": "^4.1.6"
@@ -53,6 +54,7 @@
53
54
  "build": "tsup",
54
55
  "test": "vitest run && tsc --noEmit -p tsconfig.test-d.json",
55
56
  "test:types": "tsc --noEmit -p tsconfig.test-d.json",
56
- "typecheck": "tsc --noEmit"
57
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
58
+ "typecheck:tests": "tsc --noEmit -p tsconfig.test.json"
57
59
  }
58
60
  }