# Vimine (navigateur)

> Référence de la classe Vimine de @vimine/sdk — connexion, session, appels à l’API, stockage.

URL: https://developers.vimine.network/fr/sdk/browser/

```js
import { Vimine } from '@vimine/sdk';
const vimine = new Vimine(options);
```

`Vimine` gère **Se connecter avec Vimine** (OAuth 2.1 + PKCE), la session du membre et les appels à l’API membre.
Une instance par client ; créez-la une fois et réutilisez-la.

## Options

| Option | Type | Par défaut | |
|---|---|---|---|
| `clientId` | `string` | — | votre `vmc_…` (obligatoire) |
| `redirectUri` | `string` | — | la page qui appelle `handleRedirect()`, enregistrée pour le client (obligatoire) |
| `scopes` | `Scope[]` | `['profile:read', 'membership:read']` | autorisations demandées par `signIn()` |
| `storage` | `'session' \| 'local' \| 'memory' \| TokenStorage` | `'session'` | où vivent la session et la connexion en cours |
| `storageKey` | `string` | `vimine:<clientId>:` | préfixe des clés dans le stockage |
| `locale` | `Locale` | `'it'` | langue des pages de connexion et de consentement |
| `apiUrl` | `string` | `https://api.vimine.network` | origine de l’API |
| `appUrl` | `string` | `https://app.vimine.network` | origine des pages de connexion |
| `fetch` | `typeof fetch` | `globalThis.fetch` | pour les tests et les polyfills |
| `navigate` | `(url) => void` | `top.location.assign` | comment quitter la page |

Le constructeur lève une `VimineError` (`invalid_options`) sans `clientId` ou sans `redirectUri`.

## Connexion

### `signIn(options?)`

Envoie la **fenêtre principale** vers la page de connexion et de consentement. Appelez-la depuis un geste de
l’utilisateur (un clic). Depuis une iframe d’une autre origine, la navigation est refusée (`framed`) : la page de
connexion n’est jamais affichée dans un cadre.

| Option | |
|---|---|
| `scopes` | autorisations de cette connexion |
| `prompt` | `'login'` (identifiants toujours) ou `'consent'` (consentement toujours) |
| `returnTo` | votre propre état, conservé localement et renvoyé par `handleRedirect()` — il n’est pas envoyé |

### `createSignInUrl(options?)`

Construit l’URL d’autorisation et mémorise la connexion en cours (vérificateur PKCE et `state`) sans naviguer. Utile
quand vous affichez le lien vous-même.

### `hasRedirectParams(url?)`

`true` quand l’URL (par défaut : la page courante) porte les paramètres d’un retour (`code`+`state` ou `error`).

### `handleRedirect({ url?, cleanUrl? })`

Sur la page de callback : vérifie le `state`, échange le code contre des jetons et enregistre la session. Renvoie
`{ session, returnTo }`, ou `null` quand l’URL ne porte aucun paramètre de connexion. Avec `cleanUrl` (par défaut
`true`), elle retire `code`, `state` et `error` de la barre d’adresse.

Erreurs (`VimineError.code`) : `access_denied` et les autres erreurs OAuth quand le membre n’a pas donné son
autorisation ; `no_flow` quand aucune connexion n’a été lancée depuis ce navigateur (stockage vide ou expiré) ;
`state_mismatch` quand le retour ne correspond pas ; `invalid_grant` quand le code a expiré ou a déjà été utilisé.

## Session

### `getSession()` · `isSignedIn()`

La session enregistrée, ou `null`.

```ts
interface Session {
    accessToken: string;
    refreshToken: string | null;
    expiresAt: number;            // ms, absolu
    scope: Scope[];
    sub: string | null;           // l’identifiant du membre pour votre association (vms_…)
    mode?: 'live' | 'test';       // 'test' pour les sessions d’un client de test
}
```

### `getAccessToken()`

Un jeton d’accès valable, rafraîchi quand il expire dans les 30 secondes. Lève `not_signed_in` sans session.

### `refresh()`

Fait tourner la session avec le jeton de rafraîchissement (une seule requête même en cas d’appels concurrents). Quand le
jeton de rafraîchissement n’est plus utilisable, la session est effacée et l’erreur relancée.

### `signOut({ everywhere?, postLogoutRedirectUri?, state? })`

Révoque le jeton de rafraîchissement (RFC 7009, au mieux) et oublie la session. Avec `everywhere: true`, elle envoie
ensuite le navigateur vers le `/oauth/logout` de l’application Vimine, qui met fin à la session du membre et revient sur
`postLogoutRedirectUri` (par défaut `redirectUri` ; elle doit se trouver sur une origine enregistrée).

### `onChange(listener)`

`listener(session | null)` à chaque connexion, rafraîchissement et déconnexion ; avec `storage: 'local'`, aussi pour
ceux survenus dans d’autres onglets. Renvoie la fonction de désabonnement.

## API membre

### `request(path, init?)`

Un appel authentifié : `Authorization: Bearer`, JSON en entrée et en sortie, erreurs sous forme de `VimineError`, un
nouvel essai après un rafraîchissement quand la réponse est `401`. `init.idempotencyKey` devient l’en-tête
`Idempotency-Key`.

```js
const events = await vimine.request('/v1/me/membership/events?limit=5');
```

### `me`

| | Scope | |
|---|---|---|
| `me.get()` | `profile:read` | le profil (`Me`), avec `codicefiscale_check` : comment la personne est identifiée (`null` = pas encore) |
| `me.update(patch)` | `profile:write` | téléphone, genre, et l’identification : date, lieu et pays de naissance (`birth_country`), code fiscal — facultatif, peut être ajouté plus tard. Un code en désaccord répond `422` avec une entrée `codicefiscale_mismatch` ; quand elle est `confirmable`, la personne le confirme avec `codicefiscale_confirmed: true` ([guide](https://developers.vimine.network/fr/guides/members-without-cf/)) |

### `membership`

| | Scope | |
|---|---|---|
| `membership.get()` | `membership:read` | la carte : `status`, `verdict`, `expires_at`, `statuto`, `card` |
| `membership.request(body, { idempotencyKey? })` | `membership:request` | demande d’adhésion ; une clé d’idempotence est générée si elle est absente. `422 identification_required` quand le profil n’est pas identifié : `err.errors` nomme ce qui manque, à compléter avec `me.update()` |
| `membership.events({ limit?, cursor? })` | `membership:read` | historique des états, paginé |
| `membership.statuto()` | `membership:read` | le statuto en vigueur et l’acceptation du membre |
| `membership.acceptStatuto({ version, sha256? })` | `statuto:accept` | enregistre l’acceptation |
| `membership.card()` | `membership:read` | le jeton de la carte (QR) et sa fenêtre ; `409 not_active` si l’adhésion n’est pas active |
| `membership.renew({ version, sha256? })` | `POST /v1/me/membership/renew` | renouvellement par le membre : immédiat quand il est gratuit et sans approbation, sinon une demande au secrétariat |
| `membership.setNetworkOptIn(bool)` | `PATCH /v1/me/membership/network-opt-in` | consentement au Network (`network:manage`) |
| `membership.googleWalletUrl()` | `GET /v1/me/membership/wallet/google` | un lien « Enregistrer dans Google Wallet » ; `409 not_active` si l’adhésion n’est pas active |
| `membership.cardSvg()` | `GET /v1/me/membership/card.svg` | la carte en SVG (avec le QR) ; `409 not_active` si l’adhésion n’est pas active |
| `membership.emailCard()` | `POST /v1/me/membership/card/email` | envoie sa carte au membre par e-mail |

### `events`

Les événements publiés de l’association, y compris ceux réservés aux membres (`events:read`). Les événements publics
n’exigent pas de connexion : `vimine.public.events`.

| | |
|---|---|
| `events.list({ filter?, from?, to?, limit? })` | `filter` : `upcoming` (par défaut), `past`, `cancelled`, `all` |
| `events.get(slug)` | contenu, images, types d’inscription avec les places restantes |
| `events.eligibility(slug)` | le membre peut-il s’inscrire ? par type, avec les raisons (`membership_required`, `sold_out`, …) |

### `registrations`

Les inscriptions du membre (`registrations:read` / `registrations:write`). Voir le guide [Événements et inscriptions](https://developers.vimine.network/fr/guides/events/).

| | |
|---|---|
| `registrations.create(slug, { registration_type, note?, quantity? }, opts?)` | type gratuit → `confirmed` avec le `token` du QR ; avec contribution → `reserved` avec `payment_instructions` ; `422 not_eligible` avec les raisons ; `Idempotency-Key` générée |
| `registrations.list({ filter? })` · `registrations.get(id)` | les inscriptions du membre (`filter: 'upcoming'`) |
| `registrations.cancel(id)` | annule dans la fenêtre de l’événement (`409 window_closed` ensuite) |
| `registrations.tokenPayload(id)` | le contenu du QR d’une inscription confirmée, ou `null` |

### `connections`

| | |
|---|---|
| `connections.list()` | les applications que le membre a autorisées dans cette association |
| `connections.revoke(clientId)` | retire le consentement à une application (ses sessions prennent fin) |

### `public`

Les endpoints publics, sans identifiants : voir [`publicApi`](https://developers.vimine.network/fr/sdk/public/).

## Stockage

| `storage` | Où | Quand |
|---|---|---|
| `'session'` (par défaut) | `sessionStorage` : par onglet, effacé à sa fermeture | le choix par défaut le plus sûr pour un client public |
| `'local'` | `localStorage` : partagé entre les onglets, survit aux redémarrages ; la déconnexion se propage aux autres onglets | applications où le membre s’attend à rester connecté |
| `'memory'` | en mémoire seulement | SSR, tests, bornes |
| `TokenStorage` | un objet à vous `{ get, set, remove }` | Node, stockages chiffrés, applications natives |

Là où le stockage du navigateur n’est pas disponible (navigation privée, `about:blank`), le SDK se replie sur la
mémoire. `MemoryStorage` est exporté.

## Utilitaires

- `createPkce()` → `{ verifier, challenge, method: 'S256' }`, `createVerifier()`, `createChallenge(verifier)`,
  `createState()`, `base64url(bytes)` — PKCE et `state` sur WebCrypto, si vous construisez le flux à la main.
- `DEFAULT_API_URL`, `DEFAULT_APP_URL`.
- `publicApi(options?)` — voir [API publique](https://developers.vimine.network/fr/sdk/public/).
- `VimineError` — voir [Erreurs](https://developers.vimine.network/fr/sdk/errors/).
