@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 +10 -5
- package/dist/index.cjs +308 -103
- package/dist/index.d.cts +611 -72
- package/dist/index.d.ts +611 -72
- package/dist/index.js +306 -104
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,42 @@ declare function isAuraError(error: unknown): error is AuraError;
|
|
|
41
41
|
|
|
42
42
|
interface Meta {
|
|
43
43
|
total?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Numéro de page, quand le backend pagine par page (services non-CRUD).
|
|
46
|
+
* Absent sur le CRUD, qui pagine par `limit`/`offset` ou par curseur.
|
|
47
|
+
*/
|
|
48
|
+
page?: number;
|
|
49
|
+
/**
|
|
50
|
+
* A27 — limite **réellement appliquée** par le serveur à cette réponse, plafond compris.
|
|
51
|
+
*
|
|
52
|
+
* C'est le seul moyen de détecter une troncature : une lecture sans `.limit()` est bornée
|
|
53
|
+
* (1000 lignes sur le CRUD aura-db, 1000 objets sur un listing storage), et une `.limit(n)`
|
|
54
|
+
* au-delà du plafond est rabattue en silence. Le champ était rendu par le backend mais
|
|
55
|
+
* **absent de ce type** : le plafond restait donc invisible côté TypeScript alors que la
|
|
56
|
+
* donnée était dans la réponse.
|
|
57
|
+
*
|
|
58
|
+
* Motif de détection : `data.length >= meta.per_page` ⇒ il y a probablement une suite —
|
|
59
|
+
* enchaînez avec `.range()`/`.offset()`, ou `cursor()`/`meta.next_cursor` là où il existe.
|
|
60
|
+
*
|
|
61
|
+
* ⚠️ N15 — **N'EST PAS ÉMIS SUR LE CHEMIN PostgREST**, c.-à-d. sur le CRUD d'un projet
|
|
62
|
+
* PostgreSQL, qui est le moteur NOMINAL. Le champ n'existe que sur les chemins qui
|
|
63
|
+
* connaissent la limite qu'ils ont eux-mêmes appliquée : aura-db natif (MongoDB, ou
|
|
64
|
+
* PostgreSQL avec PostgREST désactivé), listings storage, analytics notifications.
|
|
65
|
+
*
|
|
66
|
+
* Ce n'est pas un oubli du gateway : PostgREST ne rend que `Content-Range` (`0-4/*`), qui
|
|
67
|
+
* décrit les lignes **rendues**, jamais la limite appliquée. Mesuré en réel (PostgREST
|
|
68
|
+
* v12.2.3, `db-max-rows=5`, table de 10 lignes) : une requête `limit=50` rabattue à 5
|
|
69
|
+
* produit exactement le même en-tête qu'une table qui ne contient que 5 lignes. Émettre
|
|
70
|
+
* `per_page = limit demandé` serait donc faux précisément dans le cas de troncature que ce
|
|
71
|
+
* champ sert à détecter — le gateway s'abstient plutôt que de mentir.
|
|
72
|
+
*
|
|
73
|
+
* Sur ce chemin, le motif de détection qui FONCTIONNE est le comptage explicite :
|
|
74
|
+
* demandez `count: 'exact'` (`.count('exact')`) et comparez `data.length` à `meta.total`
|
|
75
|
+
* (`data.length < meta.total` ⇒ il reste des lignes) — mesuré : `{"total":10}` pour
|
|
76
|
+
* 5 lignes rendues. Sans `count`, `meta` vaut `{}` et **aucune** troncature n'est
|
|
77
|
+
* détectable côté client.
|
|
78
|
+
*/
|
|
79
|
+
per_page?: number;
|
|
44
80
|
/**
|
|
45
81
|
* Curseur opaque de pagination keyset (voir `QueryBuilder.cursor()`, `@aurabase/db`).
|
|
46
82
|
* SPÉCIFIQUE AURA-DB : n'est émis QUE sur le chemin aura-db natif (MongoDB, ou
|
|
@@ -197,8 +233,51 @@ interface MagicLinkRequest {
|
|
|
197
233
|
redirect_url?: string;
|
|
198
234
|
}
|
|
199
235
|
interface OAuthSignInOptions {
|
|
236
|
+
/**
|
|
237
|
+
* URL d'atterrissage après la connexion (parité Supabase).
|
|
238
|
+
*
|
|
239
|
+
* **Connexion aboutie** — le navigateur est redirigé sur cette URL MÊME, augmentée du seul
|
|
240
|
+
* fragment `#access_token=…&refresh_token=…&expires_in=…&token_type=bearer&user=…`.
|
|
241
|
+
* Aucun segment n'est ajouté.
|
|
242
|
+
*
|
|
243
|
+
* **⚠️ EXCEPTION — utilisateur avec MFA actif** : la connexion n'est pas encore aboutie, il
|
|
244
|
+
* reste un défi à relever. L'atterrissage est alors le sous-chemin dédié
|
|
245
|
+
* `{redirectTo}/oauth/mfa#mfa_token=…` (et `{site_url}/oauth/mfa#mfa_token=…` si `redirectTo`
|
|
246
|
+
* est omise). C'est délibéré : `#mfa_token` n'est pas une session, et l'envoyer sur la page
|
|
247
|
+
* d'accueil de l'application y déposerait un jeton que rien n'attend. Votre application DOIT
|
|
248
|
+
* donc servir une page sur `{redirectTo}/oauth/mfa` : elle y lit `mfa_token` dans le
|
|
249
|
+
* fragment, demande le code TOTP, puis appelle `mfaVerify({ mfa_token, code })`.
|
|
250
|
+
* `detectSessionInUrl` reconnaît ce fragment et émet `MFA_CHALLENGE` avec `mfaToken`
|
|
251
|
+
* (aucune session n'est posée). Le sous-chemin reste sous l'origine et le préfixe de chemin
|
|
252
|
+
* validés : il n'échappe pas à l'allowlist.
|
|
253
|
+
*
|
|
254
|
+
* Contraintes (N25) :
|
|
255
|
+
* - elle doit figurer dans l'allowlist `redirect_urls` du projet — c'est l'URL
|
|
256
|
+
* validée, et c'est exactement celle qui sera servie ;
|
|
257
|
+
* - elle ne doit PAS contenir de fragment (`#…`) : le fragment final porte les
|
|
258
|
+
* jetons et une URL ne peut pas en avoir deux — le démarrage répond `400`.
|
|
259
|
+
*
|
|
260
|
+
* **Divergence Supabase assumée** : une `redirectTo` HORS allowlist fait échouer le
|
|
261
|
+
* démarrage (`400`). Supabase, lui, l'ignore en silence et retombe sur le Site URL.
|
|
262
|
+
* Aurabase préfère l'erreur explicite — une redirection silencieuse vers une autre
|
|
263
|
+
* destination que celle demandée est indétectable côté application.
|
|
264
|
+
*
|
|
265
|
+
* Omise, le retour se fait sur `{site_url du projet}/oauth/callback` (convention
|
|
266
|
+
* historique d'Aurabase, inchangée : `site_url` est une base, pas une page).
|
|
267
|
+
*/
|
|
200
268
|
redirectTo?: string;
|
|
201
269
|
scopes?: string;
|
|
270
|
+
/**
|
|
271
|
+
* Paramètres d'expérience de connexion transmis au provider (`prompt`, `login_hint`,
|
|
272
|
+
* `display`, `ui_locales`, `max_age`, `acr_values`, `claims_locales`, `access_type`,
|
|
273
|
+
* `include_granted_scopes`, `hd`).
|
|
274
|
+
*
|
|
275
|
+
* Toute autre clé est **refusée** (`error.code === 'invalid_oauth_query_param'`),
|
|
276
|
+
* jamais transmise ni ignorée en silence : les paramètres du protocole (`state`,
|
|
277
|
+
* `nonce`, `client_id`, `scope`, `code_challenge`…) sont calculés par le serveur, et
|
|
278
|
+
* ceux de la requête (`project_id`, `redirect_uri`, `scopes`) ont leur option dédiée.
|
|
279
|
+
* Le backend applique le même contrat et répond `400` — cf. `signInWithOAuth`.
|
|
280
|
+
*/
|
|
202
281
|
queryParams?: Record<string, string>;
|
|
203
282
|
/**
|
|
204
283
|
* Active le flux PKCE (génère + stocke un `code_verifier`, envoie
|
|
@@ -213,8 +292,16 @@ interface PasswordResetRequest {
|
|
|
213
292
|
email: string;
|
|
214
293
|
}
|
|
215
294
|
interface UpdateUserRequest {
|
|
295
|
+
/**
|
|
296
|
+
* Nouvelle adresse email.
|
|
297
|
+
*
|
|
298
|
+
* **Ne change PAS l'email immédiatement** (A24) : le serveur envoie un lien de
|
|
299
|
+
* confirmation à la NOUVELLE adresse et répond `200` avec l'utilisateur **inchangé**
|
|
300
|
+
* — l'ancien email est donc normal dans la réponse. Le changement ne prend effet
|
|
301
|
+
* qu'une fois le lien suivi, via `verifyOtp({ type: 'email_change', token })`.
|
|
302
|
+
* Tant que la confirmation n'a pas eu lieu, `user.email` reste l'ancienne valeur.
|
|
303
|
+
*/
|
|
216
304
|
email?: string;
|
|
217
|
-
phone?: string;
|
|
218
305
|
password?: string;
|
|
219
306
|
current_password?: string;
|
|
220
307
|
data?: Record<string, unknown>;
|
|
@@ -297,6 +384,146 @@ interface SchemaEnum {
|
|
|
297
384
|
name: string;
|
|
298
385
|
values: string[];
|
|
299
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Rôles acceptés dans `grant_execute_to` à la CRÉATION d'une fonction.
|
|
389
|
+
*
|
|
390
|
+
* Miroir exact de l'allowlist backend `ALLOWED_ROLES` (`format_target_roles`,
|
|
391
|
+
* `services/aura-db/src/handlers/schema.rs`) : toute autre valeur est refusée en 400 —
|
|
392
|
+
* y compris `PUBLIC` et le rôle propriétaire `tenant_<uuid>` que la LECTURE
|
|
393
|
+
* (`PgFunctionInfo.grant_execute_to`) peut pourtant rendre. Union fermée à dessein :
|
|
394
|
+
* une faute de frappe se voit à la compilation plutôt qu'en 400 à l'exécution.
|
|
395
|
+
*/
|
|
396
|
+
type PgExecutableRole = 'anon' | 'authenticated' | 'service_role' | 'aura_anon' | 'aura_authenticated' | 'aura_service_role';
|
|
397
|
+
/**
|
|
398
|
+
* Corps de `POST /v1/db/functions` — miroir de `CreateFunctionRequest`.
|
|
399
|
+
*
|
|
400
|
+
* Les défauts de sécurité du backend sont documentés champ par champ ci-dessous : ils
|
|
401
|
+
* sont SÛRS (aucune élévation, aucun droit d'exécution) tant qu'on ne les change pas.
|
|
402
|
+
*/
|
|
403
|
+
interface CreatePgFunctionRequest {
|
|
404
|
+
/**
|
|
405
|
+
* Nom de la fonction, pris TEL QUEL (la casse est conservée : le DDL le pose entre
|
|
406
|
+
* guillemets). Le backend REFUSE — il n'assainit pas en silence — tout nom qui n'est
|
|
407
|
+
* pas `[A-Za-z0-9_]+` : `calc-total` rend 400, il ne devient pas `calctotal`.
|
|
408
|
+
*/
|
|
409
|
+
name: string;
|
|
410
|
+
/**
|
|
411
|
+
* Arguments, dans l'ordre. Même forme que ceux rendus par `GET /schema`
|
|
412
|
+
* (`SchemaFunctionArg`) — le backend désérialise les deux depuis la MÊME structure Rust
|
|
413
|
+
* `FunctionArg { name, data_type, mode }`.
|
|
414
|
+
*
|
|
415
|
+
* Les trois champs sont REQUIS (aucun `serde(default)` côté serveur : un `mode` absent
|
|
416
|
+
* fait échouer la désérialisation). `mode` doit valoir `'IN'` — le seul mode accepté à
|
|
417
|
+
* la création aujourd'hui ; `OUT`/`INOUT`/`VARIADIC` se lisent mais ne s'écrivent pas.
|
|
418
|
+
* `data_type` passe par une ALLOWLIST de formes (`text`, `uuid[]`, `numeric(10,2)`,
|
|
419
|
+
* `timestamp with time zone`…), pas par une denylist.
|
|
420
|
+
*/
|
|
421
|
+
args: SchemaFunctionArg[];
|
|
422
|
+
/** Type de retour, même allowlist que `args[].data_type`, `setof x` compris. */
|
|
423
|
+
returns: string;
|
|
424
|
+
/** `plv8` n'est jamais installé : le backend n'accepte que ces deux langages. */
|
|
425
|
+
language: 'plpgsql' | 'sql';
|
|
426
|
+
/**
|
|
427
|
+
* Corps de la fonction. Soumis à la MÊME denylist d'isolation multi-tenant que
|
|
428
|
+
* `sql()` (cf. son docstring), en version PLUS stricte : ni `EXECUTE` (SQL dynamique),
|
|
429
|
+
* ni bascule de RLS — un corps est persistant et potentiellement GRANTable à `anon`.
|
|
430
|
+
*/
|
|
431
|
+
body: string;
|
|
432
|
+
/**
|
|
433
|
+
* ⚠️ Défaut SÛR : `'invoker'` quand le champ est omis — la fonction s'exécute avec les
|
|
434
|
+
* droits de son APPELANT, aucune élévation de privilège implicite. `'definer'` la fait
|
|
435
|
+
* tourner avec ceux de son propriétaire (rôle admin du tenant) : à ne poser qu'en
|
|
436
|
+
* connaissance de cause. La casse est repliée côté serveur.
|
|
437
|
+
*/
|
|
438
|
+
security?: 'invoker' | 'definer';
|
|
439
|
+
/**
|
|
440
|
+
* ⚠️ Défaut SÛR : AUCUN GRANT quand le champ est omis. Postgres accorde `EXECUTE` à
|
|
441
|
+
* `PUBLIC` sur toute fonction neuve (piège connu) ; le backend le RÉVOQUE
|
|
442
|
+
* systématiquement, de `PUBLIC` comme de tous les rôles de l'allowlist, AVANT
|
|
443
|
+
* d'appliquer ce champ. Une fonction créée sans `grant_execute_to` existe donc mais
|
|
444
|
+
* n'est appelable par personne — pas même en `rpc()` — tant qu'un GRANT explicite
|
|
445
|
+
* n'est pas posé. C'est voulu : un `CREATE OR REPLACE` qui RESTREINT restreint
|
|
446
|
+
* réellement, il n'hérite pas des droits de la version précédente.
|
|
447
|
+
*/
|
|
448
|
+
grant_execute_to?: PgExecutableRole[];
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Une fonction du schéma du projet, telle que la rend `GET /v1/db/functions`
|
|
452
|
+
* — miroir de `FunctionInfo`.
|
|
453
|
+
*
|
|
454
|
+
* Les quatre premiers champs sont historiques ; `args`, `security` et `grant_execute_to`
|
|
455
|
+
* ont été ajoutés côté serveur (la liste taisait jusque-là ce que la création écrivait
|
|
456
|
+
* pourtant réellement).
|
|
457
|
+
*/
|
|
458
|
+
interface PgFunctionInfo {
|
|
459
|
+
name: string;
|
|
460
|
+
/** Type de retour CANONIQUE Postgres : `text`, `text[]`, `SETOF uuid`, `TABLE(x integer)`. */
|
|
461
|
+
return_type: string;
|
|
462
|
+
/** Langage en MAJUSCULES, tel que le rend le catalogue (`SQL`, `PLPGSQL`). */
|
|
463
|
+
language: string;
|
|
464
|
+
/** Corps de la fonction. */
|
|
465
|
+
body: string;
|
|
466
|
+
/** `[]` si la fonction n'a aucun argument. `name` peut être vide (argument anonyme). */
|
|
467
|
+
args: SchemaFunctionArg[];
|
|
468
|
+
/**
|
|
469
|
+
* `'INVOKER'` ou `'DEFINER'` — MAJUSCULES à la lecture, alors que l'écriture attend des
|
|
470
|
+
* minuscules. Typé `string` et non une union fermée : c'est une valeur de fil, un
|
|
471
|
+
* affichage ne doit pas se croire exhaustif.
|
|
472
|
+
*/
|
|
473
|
+
security: string;
|
|
474
|
+
/**
|
|
475
|
+
* Rôles portant `EXECUTE`, triés. Peut contenir `'PUBLIC'` et le rôle propriétaire
|
|
476
|
+
* (`tenant_<uuid>`), que Postgres matérialise dans l'ACL.
|
|
477
|
+
*
|
|
478
|
+
* ⚠️ LECTURE D'AUDIT UNIQUEMENT — ces valeurs ne sont PAS valides en écriture (cf.
|
|
479
|
+
* `PgExecutableRole`). Ne jamais les réinjecter telles quelles dans un
|
|
480
|
+
* `createFunction()`.
|
|
481
|
+
*/
|
|
482
|
+
grant_execute_to: string[];
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Corps de `POST /v1/db/:table/triggers` — miroir de `CreateTriggerRequest`.
|
|
486
|
+
*
|
|
487
|
+
* Le trigger BRANCHE une fonction déjà créée (`createFunction()` avec
|
|
488
|
+
* `returns: 'trigger'`) ; il n'en crée aucune. Les trois identifiants (`name`,
|
|
489
|
+
* `function_name`, et la table) sont pris tels quels ou refusés — jamais assainis en
|
|
490
|
+
* silence.
|
|
491
|
+
*/
|
|
492
|
+
interface CreatePgTriggerRequest {
|
|
493
|
+
name: string;
|
|
494
|
+
timing: 'before' | 'after' | 'instead_of';
|
|
495
|
+
/**
|
|
496
|
+
* Au moins un événement, sans DOUBLON (le backend refuse `['insert','insert']` — que
|
|
497
|
+
* Postgres rejetterait de toute façon). `truncate` exige `for_each: 'statement'`.
|
|
498
|
+
*/
|
|
499
|
+
events: Array<'insert' | 'update' | 'delete' | 'truncate'>;
|
|
500
|
+
/** Fonction du MÊME schéma, appelée sans argument (`EXECUTE FUNCTION fn()`). */
|
|
501
|
+
function_name: string;
|
|
502
|
+
for_each: 'row' | 'statement';
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Un trigger tel que le rend `GET /v1/db/:table/triggers`.
|
|
506
|
+
*
|
|
507
|
+
* ⚠️ La source est `information_schema.triggers`, qui rend UNE LIGNE PAR ÉVÉNEMENT : un
|
|
508
|
+
* trigger déclaré `INSERT OR UPDATE` ressort en DEUX entrées de même `name`, l'une avec
|
|
509
|
+
* `events: 'INSERT'`, l'autre `'UPDATE'`. Regrouper par `name` si l'affichage doit
|
|
510
|
+
* refléter le trigger et non ses événements.
|
|
511
|
+
*/
|
|
512
|
+
interface PgTriggerInfo {
|
|
513
|
+
name: string;
|
|
514
|
+
table_name: string;
|
|
515
|
+
/** `'BEFORE'`, `'AFTER'` ou `'INSTEAD OF'` — MAJUSCULES, contrairement à l'écriture. */
|
|
516
|
+
timing: string;
|
|
517
|
+
/** UN SEUL événement par entrée (`'INSERT'`, `'UPDATE'`, …) — voir la note ci-dessus. */
|
|
518
|
+
events: string;
|
|
519
|
+
/**
|
|
520
|
+
* ⚠️ Ce n'est PAS un nom de fonction mais l'`action_statement` complet du catalogue,
|
|
521
|
+
* p. ex. `EXECUTE FUNCTION project_xxx.set_updated_at()`. Le nom d'alias vient du
|
|
522
|
+
* serveur ; il est conservé tel quel plutôt que corrigé côté client, pour que ce type
|
|
523
|
+
* reste un miroir exact de la réponse.
|
|
524
|
+
*/
|
|
525
|
+
function_name: string;
|
|
526
|
+
}
|
|
300
527
|
/**
|
|
301
528
|
* Options de transformation d'image (vocabulaire façon Supabase) appliquées
|
|
302
529
|
* côté rendu backend (`GET /v1/storage/{pid}/{bucket}/{key}?width=&height=&
|
|
@@ -353,7 +580,19 @@ interface FileEntry {
|
|
|
353
580
|
content_type: string;
|
|
354
581
|
etag?: string | null;
|
|
355
582
|
last_modified?: string | null;
|
|
356
|
-
|
|
583
|
+
/**
|
|
584
|
+
* ⚠️ TOUJOURS `{}` sur le retour de `list()` (C1).
|
|
585
|
+
*
|
|
586
|
+
* `ListObjectsV2` ne renvoie pas les métadonnées d'objet : le backend pose un objet vide
|
|
587
|
+
* (`storage/s3.rs`) et n'enrichit ensuite que `content_type` depuis la base
|
|
588
|
+
* (`handlers/manage.rs`). Le champ était déclaré NON optionnel, ce qui laissait écrire
|
|
589
|
+
* `files.find(f => f.metadata.owner === 'x')` — une recherche qui ne matche jamais, sans
|
|
590
|
+
* erreur ni erreur de type.
|
|
591
|
+
*
|
|
592
|
+
* Pour lire les métadonnées réelles d'un objet, appelez `getMetadata(bucket, key)`, qui
|
|
593
|
+
* fait un `HEAD` et les renvoie peuplées.
|
|
594
|
+
*/
|
|
595
|
+
metadata?: Record<string, unknown>;
|
|
357
596
|
}
|
|
358
597
|
/** Métadonnées d'un objet storage — forme réelle retournée par getMetadata() */
|
|
359
598
|
interface ObjectMeta {
|
|
@@ -386,9 +625,20 @@ interface ServerEventMsg {
|
|
|
386
625
|
interface ServerPongMsg {
|
|
387
626
|
type: 'pong';
|
|
388
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* Erreur serveur.
|
|
630
|
+
*
|
|
631
|
+
* `channel` (B9) distingue une erreur PROPRE À UN CANAL d'une erreur GLOBALE de session :
|
|
632
|
+
* - présent → l'erreur ne concerne QUE ce canal (refus RLS/write policy, canal hors projet,
|
|
633
|
+
* quota de canaux, join/s, présence sans abonnement, retard de messages). Le SDK ne
|
|
634
|
+
* notifie que lui ; si le canal est inconnu du client, personne n'est notifié.
|
|
635
|
+
* - absent → erreur globale (heartbeat, token de session, message illisible, quota de
|
|
636
|
+
* violations) : tous les canaux sont légitimement concernés.
|
|
637
|
+
*/
|
|
389
638
|
interface ServerErrorMsg {
|
|
390
639
|
type: 'error';
|
|
391
640
|
message: string;
|
|
641
|
+
channel?: string;
|
|
392
642
|
}
|
|
393
643
|
interface ServerTokenRefreshedMsg {
|
|
394
644
|
type: 'token_refreshed';
|
|
@@ -480,6 +730,40 @@ interface PostgresChangesFilter {
|
|
|
480
730
|
interface BroadcastFilter {
|
|
481
731
|
event: string;
|
|
482
732
|
}
|
|
733
|
+
/**
|
|
734
|
+
* Résultat de `AuraChannel.send()` — un statut TYPÉ par cause.
|
|
735
|
+
*
|
|
736
|
+
* Avant C18/N24, trois échecs sans rapport partageaient la même valeur `'error'` :
|
|
737
|
+
* l'appelant ne pouvait ni les distinguer ni réagir différemment. Chaque cause a
|
|
738
|
+
* désormais son propre code, exhaustivement vérifiable par TypeScript.
|
|
739
|
+
*
|
|
740
|
+
* - `'ok'`
|
|
741
|
+
* — sans `ack` : le message a été **remis au transport** (aucune confirmation
|
|
742
|
+
* serveur n'est demandée dans ce mode) ;
|
|
743
|
+
* — avec `ack` : le serveur a répondu `broadcast_ack` (confirmation de bout en bout).
|
|
744
|
+
* - `'timeout'` — `ack` demandé sur un canal abonné, mais aucun `broadcast_ack` reçu
|
|
745
|
+
* dans les 5 s. Le message est parti ; c'est la réponse qui manque (lien coupé, accusé
|
|
746
|
+
* perdu, serveur saturé). Ce statut ne recouvre QUE cette cause : le cas « le serveur a
|
|
747
|
+
* refusé l'écriture faute d'abonnement » se résout désormais tout de suite en
|
|
748
|
+
* `'requires_subscription'` (C18), au lieu d'être maquillé en attente expirée.
|
|
749
|
+
* - `'not_connected'` — le message n'a **pas quitté le process** : aucun transport lié
|
|
750
|
+
* (canal jamais connecté) ou socket non-OPEN (`RealtimeService._send` jette
|
|
751
|
+
* silencieusement ce qu'il ne peut pas émettre). À retenter après reconnexion.
|
|
752
|
+
* - `'requires_subscription'` — le SERVEUR n'a **pas d'abonnement** pour ce canal : rien
|
|
753
|
+
* n'a été émis. Trois chemins y mènent :
|
|
754
|
+
* 1. `.subscribe()` n'a jamais été appelé ;
|
|
755
|
+
* 2. il a été appelé mais le serveur l'a **refusé** (`Accès refusé au canal privé (RLS
|
|
756
|
+
* policy)`, `private_only`, quota de canaux…) — le canal a alors reçu `CHANNEL_ERROR` ;
|
|
757
|
+
* 3. l'abonnement a été **révoqué** ensuite (`Accès révoqué au channel '…' après token
|
|
758
|
+
* refresh`), ou le serveur a répondu `Non abonné au canal '…'` à une écriture.
|
|
759
|
+
* Le serveur exige l'abonnement avant toute écriture (`handlers/websocket.rs`, bras
|
|
760
|
+
* `ClientMessage::Broadcast` : sans entrée d'abonnement, aucune policy RLS n'est
|
|
761
|
+
* consultable pour ce canal — laisser passer serait fail-open). C'est le modèle de
|
|
762
|
+
* Phoenix, dont dérive le protocole Supabase Realtime : on rejoint un topic avant d'y
|
|
763
|
+
* pousser quoi que ce soit. Appelez `.subscribe()` — et vérifiez le statut qu'il rend.
|
|
764
|
+
* - `'error'` — l'envoi lui-même a levé une exception (sérialisation, transport).
|
|
765
|
+
*/
|
|
766
|
+
type BroadcastSendStatus = 'ok' | 'timeout' | 'not_connected' | 'requires_subscription' | 'error';
|
|
483
767
|
/** Config pour presence */
|
|
484
768
|
interface PresenceFilter {
|
|
485
769
|
event: 'sync' | 'join' | 'leave';
|
|
@@ -489,7 +773,18 @@ interface ChannelConfig {
|
|
|
489
773
|
private?: boolean;
|
|
490
774
|
/** Configuration broadcast pour ce channel */
|
|
491
775
|
broadcast?: {
|
|
492
|
-
/**
|
|
776
|
+
/**
|
|
777
|
+
* Si `true`, le serveur accuse réception (`broadcast_ack`) des `send()` émis sur ce
|
|
778
|
+
* canal **une fois qu'il est abonné**, et `send()` ne résout qu'à cet accusé.
|
|
779
|
+
*
|
|
780
|
+
* **Prérequis — `.subscribe()` d'abord.** L'option `ack` voyage dans le message
|
|
781
|
+
* `subscribe` (`SubscribeConfig.broadcast.ack`) et le serveur ne la relit que dans la
|
|
782
|
+
* subscription de la connexion (`handlers/websocket.rs`, bras `Broadcast` :
|
|
783
|
+
* `subscriptions.get(&channel)` → `entry.config.broadcast.ack`). Sans abonnement, il
|
|
784
|
+
* n'a aucune config à consulter et **n'émettra jamais** d'accusé : `send()` sort donc
|
|
785
|
+
* immédiatement en `'requires_subscription'`, sans rien émettre ni attendre en vain
|
|
786
|
+
* (cf. {@link BroadcastSendStatus}).
|
|
787
|
+
*/
|
|
493
788
|
ack?: boolean;
|
|
494
789
|
/** Si true, le client reçoit ses propres broadcasts (self-echo) */
|
|
495
790
|
self?: boolean;
|
|
@@ -528,6 +823,31 @@ interface ChatResponse {
|
|
|
528
823
|
content: string;
|
|
529
824
|
input_tokens?: number;
|
|
530
825
|
output_tokens?: number;
|
|
826
|
+
/**
|
|
827
|
+
* Part des tokens de sortie consommée par le raisonnement du modèle
|
|
828
|
+
* (sous-ensemble d'`output_tokens`). Absent si le provider ne la rapporte pas.
|
|
829
|
+
*/
|
|
830
|
+
reasoning_tokens?: number;
|
|
831
|
+
/**
|
|
832
|
+
* Motif d'arrêt NORMALISÉ par le serveur : `'stop' | 'length' | 'content_filter' |
|
|
833
|
+
* 'tool_use' | 'other'`.
|
|
834
|
+
*
|
|
835
|
+
* **A25** — sans lui, une réponse coupée par `max_tokens` (`'length'`) est indiscernable
|
|
836
|
+
* d'une réponse complète (`'stop'`) : le texte arrive tronqué, sans erreur ni signal. Le
|
|
837
|
+
* serveur le calcule et le renvoie déjà (`services/aura-ai/src/handlers/chat.rs`), et le
|
|
838
|
+
* chemin streamé le typait ; seul `chat()` le masquait.
|
|
839
|
+
*
|
|
840
|
+
* Le champ reste optionnel : un provider qui ne rapporte pas de motif, ou un serveur
|
|
841
|
+
* antérieur, renverra `undefined`.
|
|
842
|
+
*/
|
|
843
|
+
finish_reason?: string;
|
|
844
|
+
/** Provider ayant effectivement répondu (`'openai'`, `'anthropic'`, `'google'`…). */
|
|
845
|
+
provider?: string;
|
|
846
|
+
/**
|
|
847
|
+
* `true` si une bascule de fallback a eu lieu — le provider effectif n'est pas celui
|
|
848
|
+
* demandé. Permet de tracer une dégradation silencieuse de qualité ou de coût.
|
|
849
|
+
*/
|
|
850
|
+
fallback_used?: boolean;
|
|
531
851
|
}
|
|
532
852
|
interface EmbedRequest {
|
|
533
853
|
text: string;
|
|
@@ -639,10 +959,30 @@ interface Notification {
|
|
|
639
959
|
}
|
|
640
960
|
interface SendNotificationRequest {
|
|
641
961
|
channel: 'email' | 'sms' | 'push' | 'webhook';
|
|
962
|
+
/** Adresse email, numéro E.164, jeton d'appareil ou URL `https://`, selon le canal. */
|
|
642
963
|
recipient: string;
|
|
964
|
+
/** Corps du message. Ignoré quand `template_id` est fourni. */
|
|
643
965
|
body?: string;
|
|
966
|
+
/**
|
|
967
|
+
* ⚠️ Sémantique dépendante du canal (A28) — ce n'est pas partout un « sujet » :
|
|
968
|
+
* - `email` → en-tête `Subject` du message. Absent ⇒ `"Notification Aurabase"` ;
|
|
969
|
+
* - `push` → **titre** de la notification affichée. Absent ⇒ `"Notification"` ;
|
|
970
|
+
* - `webhook` → champ **`"event"`** du corps JSON POSTé. Absent ⇒ `"notification"` ; il
|
|
971
|
+
* participe en outre à la clé d'idempotence de livraison ;
|
|
972
|
+
* - `sms` → **ignoré** (Twilio ne reçoit que `To`, `From` et `Body`).
|
|
973
|
+
*
|
|
974
|
+
* Avec `template_id` : à la différence de `body`, ce champ n'est PAS ignoré — un sujet non
|
|
975
|
+
* vide **surcharge** celui du template et est interpolé avec les mêmes `variables` ; vide ou
|
|
976
|
+
* absent, le sujet du template s'applique.
|
|
977
|
+
*/
|
|
644
978
|
subject?: string;
|
|
979
|
+
/** Le `body` fourni est ignoré : c'est le corps du template qui est rendu. */
|
|
645
980
|
template_id?: string;
|
|
981
|
+
/**
|
|
982
|
+
* Valeurs interpolées dans `body`/`subject` (`{{ var }}`), avec ou sans `template_id`.
|
|
983
|
+
* Une variable manquante refuse l'envoi (400). Les variables se fournissent ICI, jamais
|
|
984
|
+
* en déclaration sur le template.
|
|
985
|
+
*/
|
|
646
986
|
variables?: Record<string, unknown>;
|
|
647
987
|
metadata?: Record<string, unknown>;
|
|
648
988
|
idempotency_key?: string;
|
|
@@ -658,11 +998,30 @@ interface BatchSendResponse {
|
|
|
658
998
|
}
|
|
659
999
|
interface NotificationTemplate {
|
|
660
1000
|
id: string;
|
|
1001
|
+
/**
|
|
1002
|
+
* N53 — le serveur renvoie ce champ sur TOUTES les routes template (`POST /templates`,
|
|
1003
|
+
* `GET /templates`, `GET /templates/{id}`, `PATCH /templates/{id}`) : il est sérialisé
|
|
1004
|
+
* depuis `NotificationTemplate` côté Rust. Il manquait ici — miroir exact de C24, où un
|
|
1005
|
+
* champ DÉCLARÉ (`variables`) n'existait pas côté serveur ; ici un champ qui EXISTE
|
|
1006
|
+
* n'était pas déclaré, donc invisible aux appelants typés.
|
|
1007
|
+
*/
|
|
1008
|
+
project_id: string;
|
|
661
1009
|
name: string;
|
|
1010
|
+
/**
|
|
1011
|
+
* Le canal est FIXÉ à la création : il conditionne le rendu (échappement HTML pour
|
|
1012
|
+
* `email`, texte brut sinon) et la validation à l'envoi. Le serveur refuse de le modifier
|
|
1013
|
+
* (400) — créez un autre template pour un autre canal.
|
|
1014
|
+
*/
|
|
662
1015
|
channel: 'email' | 'sms' | 'push' | 'webhook';
|
|
1016
|
+
/**
|
|
1017
|
+
* ⚠️ Sémantique dépendante du canal (A28) :
|
|
1018
|
+
* - `email` → en-tête Subject du message ;
|
|
1019
|
+
* - `push` → **titre** de la notification ;
|
|
1020
|
+
* - `webhook` → champ **`"event"`** du corps JSON envoyé au destinataire ;
|
|
1021
|
+
* - `sms` → **ignoré** (un SMS n'a pas de sujet).
|
|
1022
|
+
*/
|
|
663
1023
|
subject?: string | null;
|
|
664
1024
|
body: string;
|
|
665
|
-
variables?: string[];
|
|
666
1025
|
created_at: string;
|
|
667
1026
|
updated_at: string;
|
|
668
1027
|
}
|
|
@@ -841,7 +1200,6 @@ interface AuraClientOptions {
|
|
|
841
1200
|
*/
|
|
842
1201
|
detectSessionInUrl?: boolean;
|
|
843
1202
|
};
|
|
844
|
-
apiKey?: string;
|
|
845
1203
|
fetch?: typeof globalThis.fetch;
|
|
846
1204
|
/**
|
|
847
1205
|
* SEC-F5 : si une clé `service_role` (`aura_sk_…`) est fournie en contexte NAVIGATEUR
|
|
@@ -885,8 +1243,7 @@ interface IdentityInfo {
|
|
|
885
1243
|
auto_linked: boolean;
|
|
886
1244
|
created_at: string;
|
|
887
1245
|
}
|
|
888
|
-
|
|
889
|
-
}
|
|
1246
|
+
type AdminUser = User;
|
|
890
1247
|
interface AuditLogEntry {
|
|
891
1248
|
id: string;
|
|
892
1249
|
user_id?: string | null;
|
|
@@ -916,6 +1273,24 @@ interface AdminSessionInfo {
|
|
|
916
1273
|
expires_at: string;
|
|
917
1274
|
last_used_at: string;
|
|
918
1275
|
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Valeur d'un provider dans `oauth_providers`.
|
|
1278
|
+
*
|
|
1279
|
+
* Le backend stocke ce champ en JSONB libre et l'interprète de DEUX façons :
|
|
1280
|
+
* - un booléen — simple interrupteur (défaut du schéma : `{"github": true, "google": true}`) ;
|
|
1281
|
+
* - un objet de configuration — `client_id`/`client_secret`/`scopes` pour un provider intégré,
|
|
1282
|
+
* ou `discovery_url`+`client_id`+`client_secret` sous une clé `oidc:<nom>` pour un OIDC
|
|
1283
|
+
* personnalisé (cf. `load_oidc_config`, services/aura-auth/src/handlers/oauth.rs).
|
|
1284
|
+
*/
|
|
1285
|
+
type OAuthProviderSetting = boolean | {
|
|
1286
|
+
enabled?: boolean;
|
|
1287
|
+
client_id?: string;
|
|
1288
|
+
client_secret?: string;
|
|
1289
|
+
scopes?: string[];
|
|
1290
|
+
/** OIDC personnalisé (clé `oidc:<nom>`) : endpoint de découverte OpenID Connect. */
|
|
1291
|
+
discovery_url?: string;
|
|
1292
|
+
[key: string]: unknown;
|
|
1293
|
+
};
|
|
919
1294
|
interface AuthSettings {
|
|
920
1295
|
allow_signup: boolean;
|
|
921
1296
|
email_confirmation_required: boolean;
|
|
@@ -925,11 +1300,7 @@ interface AuthSettings {
|
|
|
925
1300
|
jwt_expiry_seconds: number;
|
|
926
1301
|
refresh_token_expiry_days: number;
|
|
927
1302
|
site_url?: string | null;
|
|
928
|
-
oauth_providers: Record<string,
|
|
929
|
-
enabled?: boolean;
|
|
930
|
-
client_id?: string;
|
|
931
|
-
client_secret?: string;
|
|
932
|
-
}>;
|
|
1303
|
+
oauth_providers: Record<string, OAuthProviderSetting>;
|
|
933
1304
|
new_device_alerts_enabled: boolean;
|
|
934
1305
|
webhook_url?: string | null;
|
|
935
1306
|
smtp_host?: string | null;
|
|
@@ -953,13 +1324,142 @@ interface AuthSettings {
|
|
|
953
1324
|
hook_send_email_url?: string | null;
|
|
954
1325
|
hook_mfa_verification_url?: string | null;
|
|
955
1326
|
hook_password_verification_url?: string | null;
|
|
1327
|
+
/** Hook BLOQUANT avant inscription : peut refuser le signup. */
|
|
1328
|
+
hook_before_signup_url?: string | null;
|
|
1329
|
+
/** Hook de notification après inscription (fire-and-forget). */
|
|
1330
|
+
hook_after_signup_url?: string | null;
|
|
1331
|
+
/** Hook d'enrichissement des claims du JWT à chaque émission de jeton. */
|
|
1332
|
+
hook_custom_access_token_url?: string | null;
|
|
1333
|
+
/**
|
|
1334
|
+
* Le secret HMAC qui SIGNE tous les hooks est posé (write-only : jamais relu en clair).
|
|
1335
|
+
* Sans lui, aucun hook n'est signé — le destinataire ne peut pas authentifier l'appel.
|
|
1336
|
+
*/
|
|
1337
|
+
hook_secret_set?: boolean;
|
|
956
1338
|
auto_link_by_email: boolean;
|
|
1339
|
+
password_require_uppercase: boolean;
|
|
1340
|
+
password_require_number: boolean;
|
|
1341
|
+
password_require_special: boolean;
|
|
957
1342
|
revoke_on_new_device: boolean;
|
|
958
1343
|
enforce_ip_binding: boolean;
|
|
959
1344
|
allowed_email_domains: string[];
|
|
960
1345
|
blocked_email_domains: string[];
|
|
1346
|
+
rate_limit_login_per_hour: number;
|
|
1347
|
+
rate_limit_emails_per_hour: number;
|
|
1348
|
+
rate_limit_signup_per_hour: number;
|
|
961
1349
|
updated_at: string;
|
|
962
1350
|
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Champs RÉELLEMENT acceptés en écriture par `PUT /v1/auth/{project_id}/admin/settings`.
|
|
1353
|
+
*
|
|
1354
|
+
* Miroir exact de `UpdateSettingsRequest` (services/aura-auth/src/handlers/settings.rs) —
|
|
1355
|
+
* un test anti-divergence relit la struct Rust et compare. Ne PAS y ajouter un champ que
|
|
1356
|
+
* le backend ne désérialise pas : `UpdateSettingsRequest` porte `deny_unknown_fields`,
|
|
1357
|
+
* l'appel entier serait REFUSÉ (`400`, le champ fautif est nommé).
|
|
1358
|
+
*/
|
|
1359
|
+
declare const AUTH_SETTINGS_WRITABLE_FIELDS: readonly ["allow_signup", "email_confirmation_required", "password_min_length", "mfa_enabled", "allow_anonymous_users", "jwt_expiry_seconds", "refresh_token_expiry_days", "site_url", "oauth_providers", "new_device_alerts_enabled", "webhook_url", "webhook_secret", "smtp_host", "smtp_port", "smtp_username", "smtp_password", "smtp_from_email", "smtp_from_name", "smtp_clear", "oauth_credentials", "oauth_credentials_clear", "captcha_enabled", "captcha_provider", "captcha_site_key", "captcha_secret_key", "password_check_hibp", "sms_provider", "sms_api_key", "sms_api_secret", "sms_from_number", "sms_clear", "redirect_urls", "session_inactivity_timeout_seconds", "hook_send_sms_url", "hook_send_email_url", "hook_mfa_verification_url", "hook_password_verification_url", "auto_link_by_email", "rate_limit_login_per_hour", "rate_limit_emails_per_hour", "rate_limit_signup_per_hour", "revoke_on_new_device", "enforce_ip_binding", "allowed_email_domains", "blocked_email_domains", "password_require_uppercase", "password_require_number", "password_require_special", "hook_before_signup_url", "hook_after_signup_url", "hook_custom_access_token_url", "hook_secret"];
|
|
1360
|
+
/**
|
|
1361
|
+
* Corps de `authAdmin.updateSettings()`.
|
|
1362
|
+
*
|
|
1363
|
+
* Distinct de `Partial<AuthSettings>` À DESSEIN, dans les deux sens :
|
|
1364
|
+
* - il AJOUTE les champs write-only que la vue de lecture ne peut pas porter (secrets
|
|
1365
|
+
* `webhook_secret`/`smtp_password`/`captcha_secret_key`/`sms_api_key`/`sms_api_secret`,
|
|
1366
|
+
* drapeaux d'effacement `smtp_clear`/`sms_clear`/`oauth_credentials_clear`,
|
|
1367
|
+
* `oauth_credentials` en clair) ;
|
|
1368
|
+
* - il RETIRE les projections de lecture (`smtp_password_set`, `captcha_secret_key_set`,
|
|
1369
|
+
* `sms_api_key_set`, `hook_secret_set`, `updated_at`) : le backend les REFUSE.
|
|
1370
|
+
*
|
|
1371
|
+
* Ces deux refus sont ceux du SERVEUR, pas seulement du compilateur (C26, rectifié) :
|
|
1372
|
+
* - champ inconnu → `400` nommant le champ (`deny_unknown_fields` sur `UpdateSettingsRequest`) ;
|
|
1373
|
+
* - `null` explicite → `400` : la mise à jour est un `COALESCE($n, colonne)`, donc `null`
|
|
1374
|
+
* ne remet RIEN à zéro — il ne ferait rien. Pour effacer, utiliser les drapeaux `*_clear`
|
|
1375
|
+
* (SMTP/SMS/OAuth) ou la valeur vide (`""`, `[]`) selon le champ.
|
|
1376
|
+
*
|
|
1377
|
+
* Conséquence pratique : ne PAS renvoyer tel quel un objet lu par `getSettings()`
|
|
1378
|
+
* (`{...settings, mfa_enabled: true}`) — il porte `updated_at` et les `*_set`, l'appel serait
|
|
1379
|
+
* refusé. N'envoyez que les champs à modifier.
|
|
1380
|
+
*/
|
|
1381
|
+
interface AuthSettingsUpdate {
|
|
1382
|
+
allow_signup?: boolean;
|
|
1383
|
+
email_confirmation_required?: boolean;
|
|
1384
|
+
/** Entre 8 et 128 (refusé hors bornes par le backend). */
|
|
1385
|
+
password_min_length?: number;
|
|
1386
|
+
mfa_enabled?: boolean;
|
|
1387
|
+
allow_anonymous_users?: boolean;
|
|
1388
|
+
/** Entre 300 et 3600 secondes. */
|
|
1389
|
+
jwt_expiry_seconds?: number;
|
|
1390
|
+
/** Entre 1 et 90 jours. */
|
|
1391
|
+
refresh_token_expiry_days?: number;
|
|
1392
|
+
site_url?: string;
|
|
1393
|
+
oauth_providers?: Record<string, OAuthProviderSetting>;
|
|
1394
|
+
new_device_alerts_enabled?: boolean;
|
|
1395
|
+
webhook_url?: string;
|
|
1396
|
+
/** Write-only : jamais relu par `getSettings()`. */
|
|
1397
|
+
webhook_secret?: string;
|
|
1398
|
+
smtp_host?: string;
|
|
1399
|
+
smtp_port?: number;
|
|
1400
|
+
smtp_username?: string;
|
|
1401
|
+
/** Write-only ; `getSettings()` n'expose que `smtp_password_set`. */
|
|
1402
|
+
smtp_password?: string;
|
|
1403
|
+
smtp_from_email?: string;
|
|
1404
|
+
smtp_from_name?: string;
|
|
1405
|
+
/** `true` = efface TOUTE la config SMTP du projet (retour au SMTP global). */
|
|
1406
|
+
smtp_clear?: boolean;
|
|
1407
|
+
/** Fusion partielle ; `getSettings()` ne les renvoie que MASQUÉS. */
|
|
1408
|
+
oauth_credentials?: Record<string, Record<string, unknown>>;
|
|
1409
|
+
/** `true` = efface tous les identifiants OAuth du projet (retour aux variables globales). */
|
|
1410
|
+
oauth_credentials_clear?: boolean;
|
|
1411
|
+
captcha_enabled?: boolean;
|
|
1412
|
+
captcha_provider?: string;
|
|
1413
|
+
captcha_site_key?: string;
|
|
1414
|
+
/** Write-only ; `getSettings()` n'expose que `captcha_secret_key_set`. */
|
|
1415
|
+
captcha_secret_key?: string;
|
|
1416
|
+
password_check_hibp?: boolean;
|
|
1417
|
+
sms_provider?: string;
|
|
1418
|
+
/** Write-only ; `getSettings()` n'expose que `sms_api_key_set`. */
|
|
1419
|
+
sms_api_key?: string;
|
|
1420
|
+
/** Write-only : jamais relu. */
|
|
1421
|
+
sms_api_secret?: string;
|
|
1422
|
+
sms_from_number?: string;
|
|
1423
|
+
/** `true` = efface toute la config SMS. */
|
|
1424
|
+
sms_clear?: boolean;
|
|
1425
|
+
redirect_urls?: string[];
|
|
1426
|
+
/** 0 (désactivé) ou >= 300 secondes. */
|
|
1427
|
+
session_inactivity_timeout_seconds?: number;
|
|
1428
|
+
hook_send_sms_url?: string;
|
|
1429
|
+
hook_send_email_url?: string;
|
|
1430
|
+
hook_mfa_verification_url?: string;
|
|
1431
|
+
hook_password_verification_url?: string;
|
|
1432
|
+
/** Hook BLOQUANT avant inscription : un refus du hook annule le signup. */
|
|
1433
|
+
hook_before_signup_url?: string;
|
|
1434
|
+
/** Hook de notification après inscription (fire-and-forget). */
|
|
1435
|
+
hook_after_signup_url?: string;
|
|
1436
|
+
/** Hook d'enrichissement des claims du JWT (fusionnés dans `app_metadata`). */
|
|
1437
|
+
hook_custom_access_token_url?: string;
|
|
1438
|
+
/**
|
|
1439
|
+
* Secret HMAC signant TOUS les hooks (en-tête `X-Aurabase-Signature`).
|
|
1440
|
+
*
|
|
1441
|
+
* Write-only ; `getSettings()` n'expose que `hook_secret_set`. Tant qu'il est absent,
|
|
1442
|
+
* les hooks partent NON SIGNÉS : le destinataire ne peut pas distinguer un appel
|
|
1443
|
+
* Aurabase d'un appel forgé par un tiers.
|
|
1444
|
+
*/
|
|
1445
|
+
hook_secret?: string;
|
|
1446
|
+
auto_link_by_email?: boolean;
|
|
1447
|
+
/** Exige au moins une majuscule (inscription + changement de mot de passe). */
|
|
1448
|
+
password_require_uppercase?: boolean;
|
|
1449
|
+
/** Exige au moins un chiffre. */
|
|
1450
|
+
password_require_number?: boolean;
|
|
1451
|
+
/** Exige au moins un caractère spécial. */
|
|
1452
|
+
password_require_special?: boolean;
|
|
1453
|
+
rate_limit_login_per_hour?: number;
|
|
1454
|
+
rate_limit_emails_per_hour?: number;
|
|
1455
|
+
rate_limit_signup_per_hour?: number;
|
|
1456
|
+
revoke_on_new_device?: boolean;
|
|
1457
|
+
enforce_ip_binding?: boolean;
|
|
1458
|
+
/** Remplacement complet (sémantique de liste, pas de fusion). */
|
|
1459
|
+
allowed_email_domains?: string[];
|
|
1460
|
+
/** Remplacement complet. */
|
|
1461
|
+
blocked_email_domains?: string[];
|
|
1462
|
+
}
|
|
963
1463
|
interface EmailTemplateUpdate {
|
|
964
1464
|
subject: string;
|
|
965
1465
|
html_body: string;
|
|
@@ -1068,8 +1568,12 @@ declare class BaseAuthStore {
|
|
|
1068
1568
|
* dépendance core → meta (les services n'importent plus la classe concrète).
|
|
1069
1569
|
*/
|
|
1070
1570
|
interface AuraClientLike {
|
|
1571
|
+
/**
|
|
1572
|
+
* URL du projet (`https://<slug>.aurabase.cloud`). C'est elle — et elle seule — qui
|
|
1573
|
+
* désigne le projet : la passerelle résout le tenant depuis l'hôte et injecte le
|
|
1574
|
+
* `project_id` dans le chemin. Aucun identifiant de projet ne transite côté client.
|
|
1575
|
+
*/
|
|
1071
1576
|
readonly baseUrl: string;
|
|
1072
|
-
readonly projectId: string;
|
|
1073
1577
|
readonly apiKey: string | null;
|
|
1074
1578
|
readonly fetchFn: typeof globalThis.fetch;
|
|
1075
1579
|
readonly authStore: BaseAuthStore;
|
|
@@ -1098,6 +1602,44 @@ interface AuraClientLike {
|
|
|
1098
1602
|
readonly debug?: boolean;
|
|
1099
1603
|
}
|
|
1100
1604
|
|
|
1605
|
+
/**
|
|
1606
|
+
* Abstract base class for all service modules.
|
|
1607
|
+
* Provides the shared HTTP helper and URL builder.
|
|
1608
|
+
*/
|
|
1609
|
+
declare abstract class BaseService {
|
|
1610
|
+
protected readonly client: AuraClientLike;
|
|
1611
|
+
constructor(client: AuraClientLike);
|
|
1612
|
+
/** Build a full URL for the given path. */
|
|
1613
|
+
protected url(path: string): string;
|
|
1614
|
+
/** Convenience HTTP helper that delegates to fetchWithAuth. */
|
|
1615
|
+
protected request<T>(method: string, path: string, body?: unknown, options?: {
|
|
1616
|
+
headers?: Record<string, string>;
|
|
1617
|
+
signal?: AbortSignal;
|
|
1618
|
+
skipAuthRefresh?: boolean;
|
|
1619
|
+
/** Override explicite du Bearer (LOT D) — valide un token arbitraire. */
|
|
1620
|
+
authToken?: string;
|
|
1621
|
+
}): Promise<AuraResponse<T>>;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
/**
|
|
1625
|
+
* Parse a raw Cookie header string into key-value pairs.
|
|
1626
|
+
*/
|
|
1627
|
+
declare function parseCookieHeader(header: string): Record<string, string>;
|
|
1628
|
+
interface SerializeCookieOptions {
|
|
1629
|
+
path?: string;
|
|
1630
|
+
domain?: string;
|
|
1631
|
+
maxAge?: number;
|
|
1632
|
+
secure?: boolean;
|
|
1633
|
+
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
1634
|
+
httpOnly?: boolean;
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Serialize a cookie name + value + options into a Set-Cookie header value.
|
|
1638
|
+
*/
|
|
1639
|
+
declare function serializeCookieHeader(name: string, value: string, options?: SerializeCookieOptions): string;
|
|
1640
|
+
|
|
1641
|
+
/** Timeout par défaut d'une requête HTTP (borne un serveur qui « hang »). */
|
|
1642
|
+
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
1101
1643
|
/** Point d'entrée unique du refresh, dédupliqué par client (single-flight). */
|
|
1102
1644
|
declare function refreshOnce(client: AuraClientLike): Promise<AuraResponse<TokenPair>>;
|
|
1103
1645
|
interface FetchInit {
|
|
@@ -1128,23 +1670,43 @@ interface FetchInit {
|
|
|
1128
1670
|
declare function fetchWithAuth<T>(url: string, init: FetchInit, client: AuraClientLike): Promise<AuraResponse<T>>;
|
|
1129
1671
|
|
|
1130
1672
|
/**
|
|
1131
|
-
*
|
|
1132
|
-
*
|
|
1673
|
+
* Primitives de résilience réseau (module pur, sans I/O).
|
|
1674
|
+
* `Math.random`/`Date.now` sont injectables pour des tests déterministes.
|
|
1133
1675
|
*/
|
|
1134
|
-
declare
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1676
|
+
declare const RETRYABLE_STATUS: Set<number>;
|
|
1677
|
+
declare const RETRYABLE_METHODS: Set<string>;
|
|
1678
|
+
declare const DEFAULT_MAX_RETRIES = 3;
|
|
1679
|
+
declare function isRetryableStatus(status: number): boolean;
|
|
1680
|
+
declare function isRetryableMethod(method: string): boolean;
|
|
1681
|
+
/** Full jitter : random(0, min(BASE·2^attempt, CAP)). `attempt` commence à 0. */
|
|
1682
|
+
declare function computeBackoff(attempt: number, rand?: () => number): number;
|
|
1683
|
+
/** Parse un header `Retry-After` (secondes OU HTTP-date) → délai en ms, ou null. */
|
|
1684
|
+
declare function parseRetryAfter(header: string | null, now?: number): number | null;
|
|
1685
|
+
/** `sleep(ms)` qui résout tôt (sans rejeter) si `signal` s'annule. */
|
|
1686
|
+
declare function abortableSleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
1687
|
+
/** True si l'erreur `fetch` est une annulation utilisateur (jamais rejouée). */
|
|
1688
|
+
declare function isAbortError(err: unknown): boolean;
|
|
1689
|
+
|
|
1690
|
+
/**
|
|
1691
|
+
* Portée d'un client, dérivée de son URL de projet.
|
|
1692
|
+
*
|
|
1693
|
+
* Chaque projet est adressé par son PROPRE hôte (`https://<slug>.aurabase.cloud`), et le
|
|
1694
|
+
* SDK ne connaît plus d'identifiant de projet : l'URL est la seule chose qui distingue
|
|
1695
|
+
* deux projets. Tout ce qui devait auparavant être « scopé projet » côté client — clé
|
|
1696
|
+
* BroadcastChannel de synchro multi-onglets, `code_verifier` PKCE, marqueur d'initiation
|
|
1697
|
+
* OAuth, nom de cookie SSR, nom de verrou de refresh — se scope donc par cette portée.
|
|
1698
|
+
*
|
|
1699
|
+
* Ce n'est PAS cosmétique : une page (`app.example.com`) peut parfaitement instancier deux
|
|
1700
|
+
* clients pointant sur deux projets. Sans portée, leurs clés de stockage se marcheraient
|
|
1701
|
+
* dessus dans le MÊME `localStorage`, celui de la page.
|
|
1702
|
+
*
|
|
1703
|
+
* Le résultat est réduit à `[a-z0-9-]` : il sert aussi de nom de cookie, et un nom de
|
|
1704
|
+
* cookie est un `token` RFC 6265 — un `:` de port (`localhost:8080`) y est interdit.
|
|
1705
|
+
*/
|
|
1706
|
+
declare function clientScope(baseUrl: string): string;
|
|
1707
|
+
|
|
1708
|
+
/** JSON.stringify avec support des bigint (sérialisés en string). */
|
|
1709
|
+
declare function serializeBody(body: unknown): string;
|
|
1148
1710
|
|
|
1149
1711
|
interface CookieOptions {
|
|
1150
1712
|
path?: string;
|
|
@@ -1156,21 +1718,36 @@ interface CookieOptions {
|
|
|
1156
1718
|
}
|
|
1157
1719
|
/**
|
|
1158
1720
|
* Auth store backed by `localStorage` (browser) with cookie helpers for SSR.
|
|
1721
|
+
*
|
|
1722
|
+
* `key` est OBLIGATOIRE, et c'est le correctif K3 : la session est le sixième état
|
|
1723
|
+
* local du SDK, et le seul qui n'était pas scopé par projet. Un nom par défaut
|
|
1724
|
+
* constant faisait que deux clients pointant sur deux projets dans la MÊME page
|
|
1725
|
+
* écrivaient sous la même clé du MÊME `localStorage` (celui de la page, pas celui du
|
|
1726
|
+
* projet) et s'écrasaient mutuellement. Le nom canonique est
|
|
1727
|
+
* `aura-${clientScope(baseUrl)}-auth` — celui que posent `AuraClient` (navigateur) et
|
|
1728
|
+
* `createServerClient` (cookie SSR), et la même convention que les cinq autres états
|
|
1729
|
+
* (`aura-<portée>-pkce-code-verifier`, `-oauth-implicit-initiated`, canal de synchro,
|
|
1730
|
+
* verrou de refresh). Voir `packages/core/src/scope.ts`.
|
|
1159
1731
|
*/
|
|
1160
1732
|
declare class LocalAuthStore extends BaseAuthStore {
|
|
1161
1733
|
/** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
|
|
1162
1734
|
readonly persistent = true;
|
|
1163
1735
|
private readonly key;
|
|
1164
|
-
|
|
1736
|
+
/**
|
|
1737
|
+
* @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
|
|
1738
|
+
* `aura-${clientScope(baseUrl)}-auth`.
|
|
1739
|
+
*/
|
|
1740
|
+
constructor(key: string);
|
|
1165
1741
|
save(token: string, refreshToken: string, user: User): void;
|
|
1166
1742
|
clear(): void;
|
|
1167
1743
|
/**
|
|
1168
1744
|
* Supprime de localStorage tous les artefacts d'auth éphémères sur disque :
|
|
1169
|
-
* - `code_verifier` PKCE (`aura-<
|
|
1170
|
-
* - marqueur d'initiation OAuth implicit (`aura-<
|
|
1745
|
+
* - `code_verifier` PKCE (`aura-<portée>-pkce-code-verifier`),
|
|
1746
|
+
* - marqueur d'initiation OAuth implicit (`aura-<portée>-oauth-implicit-initiated`,
|
|
1171
1747
|
* SEC-F3). Un `signOut`/`clear()` ne doit laisser aucun résidu réutilisable (parité
|
|
1172
1748
|
* `@supabase/ssr` : ces artefacts sont éphémères). On itère les clés préfixées pour
|
|
1173
|
-
* couvrir un store partagé entre plusieurs projets, sans dépendre
|
|
1749
|
+
* couvrir un store partagé entre plusieurs projets, sans dépendre de la portée courante
|
|
1750
|
+
* (`clientScope`, dérivée de l'URL du projet).
|
|
1174
1751
|
*/
|
|
1175
1752
|
private _removePkceVerifiers;
|
|
1176
1753
|
setItem(key: string, value: string): void;
|
|
@@ -1190,42 +1767,4 @@ declare class LocalAuthStore extends BaseAuthStore {
|
|
|
1190
1767
|
private _removeStorage;
|
|
1191
1768
|
}
|
|
1192
1769
|
|
|
1193
|
-
|
|
1194
|
-
declare function serializeBody(body: unknown): string;
|
|
1195
|
-
|
|
1196
|
-
/**
|
|
1197
|
-
* Parse a raw Cookie header string into key-value pairs.
|
|
1198
|
-
*/
|
|
1199
|
-
declare function parseCookieHeader(header: string): Record<string, string>;
|
|
1200
|
-
interface SerializeCookieOptions {
|
|
1201
|
-
path?: string;
|
|
1202
|
-
domain?: string;
|
|
1203
|
-
maxAge?: number;
|
|
1204
|
-
secure?: boolean;
|
|
1205
|
-
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
1206
|
-
httpOnly?: boolean;
|
|
1207
|
-
}
|
|
1208
|
-
/**
|
|
1209
|
-
* Serialize a cookie name + value + options into a Set-Cookie header value.
|
|
1210
|
-
*/
|
|
1211
|
-
declare function serializeCookieHeader(name: string, value: string, options?: SerializeCookieOptions): string;
|
|
1212
|
-
|
|
1213
|
-
/**
|
|
1214
|
-
* Primitives de résilience réseau (module pur, sans I/O).
|
|
1215
|
-
* `Math.random`/`Date.now` sont injectables pour des tests déterministes.
|
|
1216
|
-
*/
|
|
1217
|
-
declare const RETRYABLE_STATUS: Set<number>;
|
|
1218
|
-
declare const RETRYABLE_METHODS: Set<string>;
|
|
1219
|
-
declare const DEFAULT_MAX_RETRIES = 3;
|
|
1220
|
-
declare function isRetryableStatus(status: number): boolean;
|
|
1221
|
-
declare function isRetryableMethod(method: string): boolean;
|
|
1222
|
-
/** Full jitter : random(0, min(BASE·2^attempt, CAP)). `attempt` commence à 0. */
|
|
1223
|
-
declare function computeBackoff(attempt: number, rand?: () => number): number;
|
|
1224
|
-
/** Parse un header `Retry-After` (secondes OU HTTP-date) → délai en ms, ou null. */
|
|
1225
|
-
declare function parseRetryAfter(header: string | null, now?: number): number | null;
|
|
1226
|
-
/** `sleep(ms)` qui résout tôt (sans rejeter) si `signal` s'annule. */
|
|
1227
|
-
declare function abortableSleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
1228
|
-
/** True si l'erreur `fetch` est une annulation utilisateur (jamais rejouée). */
|
|
1229
|
-
declare function isAbortError(err: unknown): boolean;
|
|
1230
|
-
|
|
1231
|
-
export { type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CronJob, DEFAULT_MAX_RETRIES, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationStats, type NotificationTemplate, type NotificationTemplatePreview, type OAuthSignInOptions, type ObjectMeta, type PasswordResetRequest, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeEvent, type RealtimeListenType, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationRequest, type SerializeCookieOptions, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type SessionInfo, type SignInRequest, type SignUpRequest, type StorageBucket, type SubscribeCallback, type TokenPair, type UpdateUserRequest, type UploadResult, type User, type UserStats, abortableSleep, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|
|
1770
|
+
export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, 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 CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationStats, type NotificationTemplate, type NotificationTemplatePreview, type 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, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeEvent, type RealtimeListenType, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationRequest, type SerializeCookieOptions, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type SessionInfo, type SignInRequest, type SignUpRequest, type StorageBucket, type SubscribeCallback, type TokenPair, type UpdateUserRequest, type UploadResult, type User, type UserStats, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
|