@41devs/naya-id-angular 0.1.1 → 0.4.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
@@ -1,30 +1,23 @@
1
- # Naya ID — SDK Angular
1
+ # @41devs/naya-id-angular
2
2
 
3
- SDK Angular officiel de **Naya ID**, la vérification d'identité (eKYC) clé en
4
- main pour l'Afrique de l'Ouest (zone CEDEAO). Votre application appelle **une
5
- seule méthode** ; l'interface Naya s'ouvre en plein écran, prend en charge la
6
- capture du document, la détection de vivacité et la décision, puis vous rend le
7
- résultat et rend la main à votre application.
3
+ SDK Angular officiel de **[Naya ID](https://naya.41devs.com)** — vérification d'identité (eKYC) clé en main pour l'Afrique de l'Ouest.
8
4
 
9
- C'est le pendant web du [SDK Flutter](https://pub.dev/packages/naya_id) : même
10
- parcours, mêmes écrans, même contrat d'API.
5
+ Un service injectable, un appel : consentement, capture guidée du document, selfie avec **défi de vivacité imposé par le serveur**, confirmation des données lues, décision expliquée — puis un `NayaResult` typé.
11
6
 
12
- - **npm** : [`@41devs/naya-id-angular`](https://www.npmjs.com/package/@41devs/naya-id-angular)
13
- - **Démo en ligne** : https://naya-id-demo.surge.sh
7
+ **Pays** : Bénin, Côte d'Ivoire, Sénégal, Mali, Burkina Faso, Togo.
8
+ **Documents** : CNI (CEDEAO), CIP (Bénin), passeport, permis de conduire.
9
+ **Langues** : français, anglais, portugais.
14
10
 
15
- ---
11
+ ## Comment c'est construit
16
12
 
17
- ## Prérequis
13
+ Le parcours est celui du SDK navigateur [`@41devs/naya-id-js`](https://www.npmjs.com/package/@41devs/naya-id-js) : **une seule implémentation**, partagée avec les intégrations sans framework. Cette bibliothèque y ajoute ce qu'attend une application Angular :
18
14
 
19
- | Élément | Détail |
20
- |---|---|
21
- | **Angular** | 20 minimum. |
22
- | **Clé API Naya** | Une clé `naya_live_…` ou `naya_test_…` (créée dans le dashboard Naya). |
23
- | **Entreprise vérifiée (KYB)** | La société propriétaire de la clé doit être **KYB-vérifiée**, sinon l'API renvoie `403`. |
24
- | **HTTPS** | La caméra (`getUserMedia`) exige un **contexte sécurisé** : `https://` en production, ou `http://localhost` en développement. Sur `http://` distant, la caméra ne démarre pas. |
25
- | **CORS** | L'origine de votre application doit être autorisée côté API Naya (liste `ALLOWED_ORIGINS`). |
15
+ - **injection** — un service `providedIn: 'root'`, testable et remplaçable ;
16
+ - **chargement différé** — le SDK n'entre pas dans le bundle initial (voir plus bas) ;
17
+ - **zone Angular** — le parcours tourne hors zone, sans quoi le flux caméra déclencherait une détection de changements à chaque trame ; vos rappels y sont ramenés, donc signaux et vues se mettent à jour normalement ;
18
+ - **rendu serveur** — sans `document`, le service rend une erreur typée au lieu de lever.
26
19
 
27
- ---
20
+ Conséquence utile : les correctifs et les nouveautés du parcours arrivent chez vous au prochain `npm update`, sans publication de cette bibliothèque.
28
21
 
29
22
  ## Installation
30
23
 
@@ -32,221 +25,119 @@ parcours, mêmes écrans, même contrat d'API.
32
25
  npm install @41devs/naya-id-angular
33
26
  ```
34
27
 
35
- Le paquet tire automatiquement `@mediapipe/tasks-vision` (détection de vivacité).
36
- Aucune configuration Angular supplémentaire : le SDK est *standalone*, il
37
- s'injecte directement.
28
+ `@41devs/naya-id-js` est installé automatiquement. Angular 19 ou 20.
38
29
 
39
- ---
30
+ ## Utilisation
40
31
 
41
- ## Démarrage rapide
32
+ ### Session ouverte par votre backend (production)
42
33
 
43
- Injectez `NayaIdService` et appelez `startVerification`. La promesse se résout
44
- quand l'utilisateur ressort du parcours, avec le résultat.
34
+ Votre serveur ouvre la vérification avec sa clé d'API — qui ne quitte jamais le serveur — puis ne confie au navigateur que `verificationId` et `sessionToken`.
45
35
 
46
36
  ```ts
47
- import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
37
+ import { Component, inject, signal } from '@angular/core';
48
38
  import { NayaIdService, type NayaResult } from '@41devs/naya-id-angular';
49
39
 
50
- @Component({
51
- selector: 'app-verify',
52
- changeDetection: ChangeDetectionStrategy.OnPush,
53
- template: `
54
- <button (click)="verifier()" [disabled]="enCours()">
55
- {{ enCours() ? 'Vérification…' : 'Vérifier mon identité' }}
56
- </button>
57
- `,
58
- })
59
- export class VerifyComponent {
40
+ @Component({ /* … */ })
41
+ export class KycComponent {
60
42
  private readonly naya = inject(NayaIdService);
61
- readonly enCours = signal(false);
62
-
63
- async verifier(): Promise<void> {
64
- this.enCours.set(true);
65
- try {
66
- const result: NayaResult = await this.naya.startVerification({
67
- auth: { apiKey: 'naya_live_xxx' },
68
- config: {
69
- country: 'BJ', // pré-sélectionne le pays
70
- callbackUrl: 'https://votre-backend.com/naya/hook', // notification serveur (optionnel)
71
- },
72
- });
73
-
74
- switch (result.status) {
75
- case 'approved':
76
- // Identité vérifiée. result.ocrData contient les champs extraits.
77
- console.log('Approuvé', result.verificationId, result.ocrData);
78
- break;
79
- case 'review':
80
- // Revue manuelle : décision finale notifiée plus tard via callbackUrl.
81
- break;
82
- case 'rejected':
83
- // Refus (result.note explique pourquoi).
84
- break;
85
- case 'cancelled':
86
- // L'utilisateur a fermé le parcours avant la fin.
87
- break;
88
- case 'error':
89
- // Problème technique : result.errorMessage.
90
- console.error(result.errorMessage);
91
- break;
92
- }
93
- } finally {
94
- this.enCours.set(false);
95
- }
43
+ readonly result = signal<NayaResult | null>(null);
44
+
45
+ async verify(session: { verificationId: string; sessionToken: string }) {
46
+ this.result.set(
47
+ await this.naya.startVerification({
48
+ auth: session,
49
+ config: { locale: 'fr' },
50
+ }),
51
+ );
96
52
  }
97
53
  }
98
54
  ```
99
55
 
100
- `startVerification` **ne rejette jamais** : une erreur réseau ou une annulation
101
- revient toujours sous forme de `NayaResult` (`status: 'error'` ou `'cancelled'`).
102
-
103
- ---
104
-
105
- ## Le parcours vu par l'utilisateur
106
-
107
- Une fois `startVerification` appelée, l'interface Naya enchaîne seule :
108
-
109
- 1. **Bienvenue** — écran d'accueil.
110
- 2. **Sélection** — pays et type de document (sautée si `country` + un seul `documentTypes`).
111
- 3. **Capture du document** — recto, puis verso pour la CNI (bande MRZ). Cadre vert quand l'image est nette ; l'utilisateur déclenche.
112
- 4. **Vivacité** — selfie avec défi actif : regarder droit → cligner → tourner la tête.
113
- 5. **Traitement** — OCR, biométrie, décision côté serveur.
114
- 6. **Résultat** — approuvé / en revue / rejeté, puis retour à votre application.
115
-
116
- ---
117
-
118
- ## Configuration (`config`)
119
-
120
- Tous les champs sont optionnels — le SDK a des défauts raisonnables (tous pays,
121
- tous documents, français).
122
-
123
- | Champ | Type | Défaut | Rôle |
124
- |---|---|---|---|
125
- | `country` | `'BJ'\|'CI'\|'SN'\|'ML'\|'BF'\|'TG'` | — | Pré-sélectionne le pays ; l'utilisateur ne le choisit plus. |
126
- | `documentTypes` | `('cni'\|'passeport'\|'permis')[]` | les trois | Types proposés. Un seul + `country` défini ⇒ écran de sélection sauté. |
127
- | `countries` | `NayaCountryCode[]` | les six | Pays proposés quand `country` n'est pas fixé. |
128
- | `locale` | `'fr' \| 'en'` | `'fr'` | Langue de l'interface. |
129
- | `callbackUrl` | `string` | — | URL HTTPS notifiée par le serveur Naya à chaque décision (voir plus bas). |
130
- | `maxAttempts` | `number` | `2` | Nombre d'essais autorisés après un rejet (bouton « Réessayer »). |
131
- | `theme` | `{ primaryColor?, accentColor? }` | navy / ambre | Surcharge des couleurs de marque. |
132
- | `clientLogoUrl` | `string` | — | Logo affiché à côté de « Propulsé par Naya ID ». |
133
- | `livenessAssets` | `{ wasmBaseUrl?, modelUrl? }` | CDN public | Auto-hébergement MediaPipe (CSP stricte — voir plus bas). |
134
- | `baseUrl` | `string` | API de prod | À ne changer que pour un environnement de test. |
135
-
136
- ---
137
-
138
- ## Résultat (`NayaResult`)
139
-
140
- | Champ | Type | Présent quand |
141
- |---|---|---|
142
- | `status` | `'approved'\|'review'\|'rejected'\|'cancelled'\|'error'` | toujours |
143
- | `verificationId` | `string` | dès qu'une session a été créée — à conserver pour rapprocher notifications et dashboard |
144
- | `riskScore` | `number` (0-100) | après traitement — score de confiance agrégé |
145
- | `matchScore` | `number` (0-100) | concordance visage document ↔ selfie |
146
- | `authenticityScore` | `number` (0-100) | intégrité du document |
147
- | `livenessScore` | `number` (0-100) | vivacité |
148
- | `ocrConfidence` | `number` (0-100) | confiance de l'OCR |
149
- | `ocrData` | `NayaOcrData` | champs extraits (nom, prénoms, date de naissance, n° document, NPI…) |
150
- | `note` | `string` | note d'analyse du pipeline |
151
- | `errorMessage` | `string` | uniquement si `status === 'error'` |
152
-
153
- ---
154
-
155
- ## Authentification
156
-
157
- Un seul des deux champs de `auth` est à renseigner :
158
-
159
- | Mode | Usage |
160
- |---|---|
161
- | `apiKey` | Simple, mais la clé est **lisible dans le navigateur** (outils de dev). À réserver aux démos et aux intégrations où le front est maîtrisé. |
162
- | `sessionToken` | Jeton éphémère obtenu par **votre backend**, limité à une vérification. **À privilégier en production** : la clé ne quitte jamais votre serveur. |
56
+ ### Clé de test (démo)
163
57
 
164
58
  ```ts
165
- // Production : votre backend échange sa clé contre un jeton de session, que
166
- // vous passez au SDK.
167
59
  await this.naya.startVerification({
168
- auth: { sessionToken: jetonObtenuDeVotreBackend },
169
- config: { country: 'BJ' },
60
+ auth: { apiKey: 'naya_test_xxx' },
61
+ config: { country: 'BJ', documentTypes: ['cni'] },
170
62
  });
171
63
  ```
172
64
 
173
- Passer de l'un à l'autre ne change rien au reste de l'intégration.
65
+ Une clé **live** utilisée depuis un navigateur doit déclarer ses origines dans le dashboard, sinon l'API répond `403`. Préférez la session serveur.
66
+
67
+ ### Instance réutilisable
174
68
 
175
- ---
69
+ ```ts
70
+ const verification = await this.naya.create({ auth, config });
71
+ verification.on('step', (e) => console.log(e));
72
+ const result = await verification.open();
73
+ verification.destroy();
74
+ ```
176
75
 
177
- ## Notifications serveur (`callbackUrl`)
76
+ ### Résultat
178
77
 
179
- En passant `callbackUrl`, votre backend est notifié à chaque décision, sans
180
- interroger l'API. Deux événements possibles par session :
181
- `verification.completed` (décision automatique) puis, le cas échéant,
182
- `verification.reviewed` (après revue d'un analyste). Les envois sont signés
183
- (`X-Naya-Signature`, HMAC-SHA256). Détails côté documentation de l'API.
78
+ `startVerification` **ne rejette jamais** : annulation et erreurs reviennent dans le `NayaResult`.
79
+
80
+ ```ts
81
+ switch (result.status) {
82
+ case 'approved': break; // result.riskScore, result.ocrData…
83
+ case 'review': break; // décision finale notifiée sur callbackUrl
84
+ case 'rejected': break; // result.reasonCodes
85
+ case 'cancelled': break;
86
+ case 'error': break; // result.errorCode, result.requestId
87
+ }
88
+ ```
184
89
 
185
- ---
90
+ Ne vous fiez jamais au résultat rendu par le navigateur pour décider : seul le webhook (ou `GET /verifications/:id` avec votre clé) fait foi.
186
91
 
187
- ## Détection de vivacité
92
+ ## Configuration
188
93
 
189
- La vivacité s'exécute **sur l'appareil** via [MediaPipe Face Landmarker]. Par
190
- défaut, le modèle et son binaire WASM sont chargés depuis un CDN public —
191
- aucune configuration requise. L'analyse est **locale** : la photo n'est
192
- transmise qu'à la fin du défi.
94
+ `config` est le `NayaConfig` du SDK navigateur — pays, documents, langue, consentement, confirmation des données lues, capture, vivacité, relais QR, thème, libellés surchargeables, télémétrie, `onEvent`. La liste complète et à jour est dans la [documentation du SDK navigateur](https://www.npmjs.com/package/@41devs/naya-id-js).
193
95
 
194
- Pour une application à **CSP stricte** (hôtes externes bloqués), auto-hébergez
195
- ces ressources et indiquez leurs URL :
96
+ Utile côté Angular :
196
97
 
197
98
  ```ts
198
99
  config: {
199
- country: 'BJ',
200
- livenessAssets: {
201
- wasmBaseUrl: '/assets/naya/wasm', // dossier wasm/ de @mediapipe/tasks-vision
202
- modelUrl: '/assets/naya/face_landmarker.task', // modèle MediaPipe
203
- },
100
+ container: this.host.nativeElement, // monte le parcours dans votre page
101
+ theme: { primaryColor: '#172062', mode: 'auto', radius: 'soft' },
102
+ strings: { welcomeTitle: 'Vérifions votre identité' },
103
+ onEvent: (e) => this.analytics.track(e.type), // rappelé dans la zone Angular
204
104
  }
205
105
  ```
206
106
 
207
- Copiez le dossier `wasm/` du paquet `@mediapipe/tasks-vision` et le modèle
208
- `face_landmarker.task` dans les `assets` de votre application.
209
-
210
- [MediaPipe Face Landmarker]: https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker
211
-
212
- ---
107
+ ## Pourquoi la bibliothèque n'exporte que des types
213
108
 
214
- ## Contraintes navigateur
109
+ `public-api.ts` réexporte les **types** du SDK navigateur, jamais de valeurs. Une constante ou une fonction réexportée placerait le SDK dans le graphe statique du module, et donc dans le bundle **initial** de votre application : mesuré sur l'application de démonstration, **308 ko au lieu de 154 ko**, soit 39 ko de transfert supplémentaires chargés par tous vos visiteurs, y compris ceux qui ne lancent jamais de vérification.
215
110
 
216
- - **Contexte sécurisé obligatoire** pour la caméra : `https://` ou `http://localhost`.
217
- - Testé sur Chrome et Safari (desktop et iOS). Les webviews in-app (Instagram,
218
- Facebook…) restreignent souvent `getUserMedia` — orientez l'utilisateur vers
219
- le navigateur système si la caméra ne démarre pas.
220
- - L'interface s'ouvre en superposition plein écran et fige le défilement de la
221
- page tant que le parcours est ouvert.
111
+ Les types disparaissent à la compilation : ils ne coûtent rien. Pour les rares valeurs utiles à un hôte, le service expose des accesseurs asynchrones :
222
112
 
223
- ---
113
+ ```ts
114
+ const countries = await this.naya.countries(); // sélecteur maison
115
+ const support = await this.naya.isSupported(); // avant d'ouvrir
116
+ ```
224
117
 
225
- ## Dépannage
118
+ Dans la plupart des cas vous n'en avez pas besoin : `config` accepte directement les codes (`country: 'BJ'`), typés par `NayaCountryCode`.
226
119
 
227
- | Symptôme | Cause probable |
228
- |---|---|
229
- | `status: 'error'`, message « clé API invalide » (401) | Clé absente/révoquée, ou coquille. |
230
- | `status: 'error'`, « KYB… » (403) | La société de la clé n'est pas KYB-vérifiée. |
231
- | `status: 'error'`, « CORS » | L'origine de votre app n'est pas dans `ALLOWED_ORIGINS` côté API. |
232
- | La caméra ne démarre pas | Page servie en `http://` distant (contexte non sécurisé), permission refusée, ou webview in-app. |
233
- | Score de vivacité anormalement bas | Selfie de mauvaise qualité (contre-jour, reflets d'écran) ou caméra défectueuse — l'anti-fraude serveur le pénalise. |
120
+ ## Rendu serveur (SSR)
234
121
 
235
- ---
122
+ Le service détecte l'absence de navigateur (`PLATFORM_ID`) et rend un `NayaResult` en erreur `unsupported_browser` au lieu de lever. Le SDK n'est jamais importé côté serveur, puisque l'import est différé au premier lancement.
236
123
 
237
- ## Développement (ce dépôt)
124
+ ## Vérifier la compatibilité du navigateur
238
125
 
126
+ ```ts
127
+ const s = await this.naya.isSupported();
128
+ if (!s.ok) {
129
+ // s.code : in_app_browser | insecure_context | unsupported_browser
130
+ }
239
131
  ```
240
- projects/
241
- naya-id/ — la librairie publiée (@41devs/naya-id-angular)
242
- demo/ — application de démonstration
243
- ```
244
132
 
245
- - Construire la librairie : `ng build naya-id`
246
- - Lancer la démo : `ng serve demo`
133
+ ## Démonstration
134
+
135
+ `projects/demo` contient une application avec tous les réglages du parcours.
247
136
 
248
- ---
137
+ ```bash
138
+ npm start
139
+ ```
249
140
 
250
141
  ## Licence
251
142
 
252
- Propriété de 41Devs. Voir le fichier `LICENSE`.
143
+ Voir `LICENSE`.
@@ -1,2 +1,198 @@
1
- export { b as NAYA_COUNTRIES, f as NAYA_DOCUMENT_TYPES, a as NayaApiError, g as NayaIdService, c as countryLabel } from './41devs-naya-id-angular-41devs-naya-id-angular-B7t92Ogc.mjs';
1
+ import * as i0 from '@angular/core';
2
+ import { inject, NgZone, PLATFORM_ID, Injectable } from '@angular/core';
3
+ import { isPlatformBrowser } from '@angular/common';
4
+
5
+ /**
6
+ * Point d'entrée du SDK Naya ID pour Angular.
7
+ *
8
+ * ```ts
9
+ * const result = await this.naya.startVerification({
10
+ * auth: { verificationId, sessionToken },
11
+ * config: { locale: 'fr' },
12
+ * });
13
+ * ```
14
+ *
15
+ * Le parcours (consentement, capture, vivacité, décision) est celui du SDK
16
+ * navigateur `@41devs/naya-id-js` : une seule implémentation, partagée avec
17
+ * les intégrations sans framework. Cette bibliothèque y ajoute ce qu'attend
18
+ * une application Angular :
19
+ *
20
+ * • **injection** — un service `providedIn: 'root'`, testable et
21
+ * remplaçable ;
22
+ * • **chargement différé** — le SDK n'entre pas dans le bundle initial ;
23
+ * il est importé au premier lancement ;
24
+ * • **zone** — le parcours tourne hors de la zone Angular (le flux caméra
25
+ * déclencherait sinon une détection de changements par trame), et les
26
+ * rappels de l'hôte y sont ramenés pour que signaux et vues se mettent
27
+ * à jour normalement ;
28
+ * • **rendu serveur** — sans `document`, le service rend une erreur typée
29
+ * au lieu de lever.
30
+ */
31
+ class NayaIdService {
32
+ zone = inject(NgZone);
33
+ platformId = inject(PLATFORM_ID);
34
+ /** Chargé une seule fois, au premier usage. */
35
+ sdk;
36
+ load() {
37
+ return (this.sdk ??= import('@41devs/naya-id-js'));
38
+ }
39
+ get isBrowser() {
40
+ return isPlatformBrowser(this.platformId) && typeof document !== 'undefined';
41
+ }
42
+ serverSideResult() {
43
+ return {
44
+ status: 'error',
45
+ errorCode: 'unsupported_browser',
46
+ errorMessage: 'Le SDK Naya nécessite un environnement navigateur.',
47
+ };
48
+ }
49
+ /**
50
+ * Enveloppe la configuration de l'hôte pour ramener ses rappels dans la
51
+ * zone Angular. Sans cela, un `onEvent` qui écrit dans un signal ne
52
+ * déclencherait aucun rafraîchissement : le parcours tourne hors zone.
53
+ */
54
+ withZone(config) {
55
+ const onEvent = config?.onEvent;
56
+ if (!onEvent)
57
+ return config;
58
+ return {
59
+ ...config,
60
+ onEvent: (event) => this.zone.run(() => onEvent(event)),
61
+ };
62
+ }
63
+ /**
64
+ * Lance le parcours de vérification d'identité.
65
+ *
66
+ * Authentification, au choix :
67
+ * • `auth.verificationId` + `auth.sessionToken`, rendus à votre backend
68
+ * par `POST /verifications/start` — à privilégier en production, la
69
+ * clé ne quitte pas le serveur ;
70
+ * • `auth.apiKey` — démos et fronts maîtrisés. Une clé live doit
71
+ * déclarer ses origines dans le dashboard.
72
+ *
73
+ * **Ne rejette jamais** : une erreur ou une annulation revient sous forme
74
+ * de `NayaResult` (`status: 'error'` avec `errorCode`, ou `'cancelled'`).
75
+ */
76
+ async startVerification(params) {
77
+ if (!this.isBrowser)
78
+ return this.serverSideResult();
79
+ const sdk = await this.load();
80
+ return this.zone.runOutsideAngular(() => sdk.startVerification({ ...params, config: this.withZone(params.config) }));
81
+ }
82
+ /**
83
+ * Instance réutilisable, pour piloter l'ouverture et la fermeture depuis
84
+ * un composant et suivre les événements du parcours.
85
+ */
86
+ async create(params) {
87
+ if (!this.isBrowser) {
88
+ const result = this.serverSideResult();
89
+ return {
90
+ open: async () => result,
91
+ close: () => { },
92
+ on: () => () => { },
93
+ destroy: () => { },
94
+ get isOpen() {
95
+ return false;
96
+ },
97
+ };
98
+ }
99
+ const sdk = await this.load();
100
+ const instance = this.zone.runOutsideAngular(() => sdk.create({ ...params, config: this.withZone(params.config) }));
101
+ return {
102
+ open: () => this.zone.runOutsideAngular(() => instance.open()),
103
+ close: () => instance.close(),
104
+ on: (event, cb) => instance.on(event, (payload) => this.zone.run(() => cb(payload))),
105
+ destroy: () => instance.destroy(),
106
+ get isOpen() {
107
+ return instance.isOpen;
108
+ },
109
+ };
110
+ }
111
+ /**
112
+ * Vérifie que le navigateur peut exécuter le parcours (caméra, contexte
113
+ * sécurisé, navigateur intégré) avant de l'ouvrir.
114
+ */
115
+ async isSupported() {
116
+ if (!this.isBrowser) {
117
+ // Rendu serveur : aucune capacité n'est mesurable, on rend un état
118
+ // complet et négatif plutôt que de lever.
119
+ return {
120
+ ok: false,
121
+ code: 'unsupported_browser',
122
+ reason: 'Aucun environnement navigateur.',
123
+ secureContext: false,
124
+ mediaDevices: false,
125
+ webAssembly: false,
126
+ webGL: false,
127
+ inAppBrowser: false,
128
+ inAppBrowserName: null,
129
+ mobile: false,
130
+ ios: false,
131
+ android: false,
132
+ deviceClass: 'desktop',
133
+ };
134
+ }
135
+ const sdk = await this.load();
136
+ return sdk.isSupported();
137
+ }
138
+ /** Version du SDK navigateur effectivement chargé. */
139
+ async version() {
140
+ const sdk = await this.load();
141
+ return sdk.version;
142
+ }
143
+ /**
144
+ * Pays couverts, avec leur libellé — pour un sélecteur affiché par l'hôte
145
+ * avant d'ouvrir le parcours.
146
+ *
147
+ * Accesseur asynchrone, et non constante réexportée : une valeur importée
148
+ * statiquement depuis cette bibliothèque entraînerait tout le SDK
149
+ * navigateur dans le bundle initial de votre application. Vous n'en avez
150
+ * besoin que si vous construisez votre propre sélection ; sinon `config`
151
+ * accepte directement les codes (`country: 'BJ'`), typés par
152
+ * `NayaCountryCode`.
153
+ */
154
+ async countries() {
155
+ const sdk = await this.load();
156
+ return sdk.NAYA_COUNTRIES;
157
+ }
158
+ /** Types de pièces acceptés. Même remarque que [countries]. */
159
+ async documentTypes() {
160
+ const sdk = await this.load();
161
+ return sdk.NAYA_DOCUMENT_TYPES;
162
+ }
163
+ /** Libellé lisible d'un code pays. */
164
+ async countryLabel(code) {
165
+ const sdk = await this.load();
166
+ return sdk.countryLabel(code);
167
+ }
168
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NayaIdService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
169
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NayaIdService, providedIn: 'root' });
170
+ }
171
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NayaIdService, decorators: [{
172
+ type: Injectable,
173
+ args: [{ providedIn: 'root' }]
174
+ }] });
175
+
176
+ /*
177
+ * Surface publique du SDK Naya ID — Angular.
178
+ *
179
+ * Le service d'entrée est propre à Angular ; les modèles sont **réexportés
180
+ * depuis `@41devs/naya-id-js`** plutôt que redéclarés. Les deux SDK ne
181
+ * peuvent donc pas diverger : une option ajoutée au parcours navigateur est
182
+ * disponible ici au prochain `npm update`, sans publication de cette
183
+ * bibliothèque.
184
+ *
185
+ * ⚠️ **Types uniquement.** Réexporter une valeur (une constante, une
186
+ * fonction) placerait le SDK navigateur dans le graphe statique du module,
187
+ * et donc dans le bundle **initial** de l'application cliente — 154 ko qui
188
+ * ne servent qu'au lancement du parcours. Les types disparaissent à la
189
+ * compilation ; le SDK reste chargé à la demande. Les rares valeurs utiles
190
+ * à un hôte sont exposées par des accesseurs asynchrones du service.
191
+ */
192
+
193
+ /**
194
+ * Generated bundle index. Do not edit.
195
+ */
196
+
197
+ export { NayaIdService };
2
198
  //# sourceMappingURL=41devs-naya-id-angular.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"41devs-naya-id-angular.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"41devs-naya-id-angular.mjs","sources":["../../../projects/naya-id/src/lib/naya-id.service.ts","../../../projects/naya-id/src/public-api.ts","../../../projects/naya-id/src/41devs-naya-id-angular.ts"],"sourcesContent":["import { Injectable, NgZone, PLATFORM_ID, inject } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport type {\n NayaConfig,\n NayaCountryCode,\n NayaDocumentType,\n NayaEvent,\n NayaResult,\n NayaSupport,\n StartVerificationParams,\n} from '@41devs/naya-id-js';\n\n/**\n * Parcours réutilisable : une instance qu'on ouvre, ferme et écoute.\n * Miroir de `create()` du SDK Naya, avec les rappels ramenés dans la zone\n * Angular.\n */\nexport interface NayaVerification {\n /** Monte l'interface et résout à la fermeture. Un seul `open` à la fois. */\n open(): Promise<NayaResult>;\n /** Ferme le parcours (résultat `cancelled`). */\n close(): void;\n /** Abonne un écouteur ; rend la fonction de désabonnement. */\n on(\n event: NayaEvent['type'] | 'result',\n cb: (payload: NayaEvent | NayaResult) => void,\n ): () => void;\n /** Ferme et libère les écouteurs. */\n destroy(): void;\n readonly isOpen: boolean;\n}\n\n/**\n * Point d'entrée du SDK Naya ID pour Angular.\n *\n * ```ts\n * const result = await this.naya.startVerification({\n * auth: { verificationId, sessionToken },\n * config: { locale: 'fr' },\n * });\n * ```\n *\n * Le parcours (consentement, capture, vivacité, décision) est celui du SDK\n * navigateur `@41devs/naya-id-js` : une seule implémentation, partagée avec\n * les intégrations sans framework. Cette bibliothèque y ajoute ce qu'attend\n * une application Angular :\n *\n * • **injection** — un service `providedIn: 'root'`, testable et\n * remplaçable ;\n * • **chargement différé** — le SDK n'entre pas dans le bundle initial ;\n * il est importé au premier lancement ;\n * • **zone** — le parcours tourne hors de la zone Angular (le flux caméra\n * déclencherait sinon une détection de changements par trame), et les\n * rappels de l'hôte y sont ramenés pour que signaux et vues se mettent\n * à jour normalement ;\n * • **rendu serveur** — sans `document`, le service rend une erreur typée\n * au lieu de lever.\n */\n@Injectable({ providedIn: 'root' })\nexport class NayaIdService {\n private readonly zone = inject(NgZone);\n private readonly platformId = inject(PLATFORM_ID);\n\n /** Chargé une seule fois, au premier usage. */\n private sdk?: Promise<typeof import('@41devs/naya-id-js')>;\n\n private load(): Promise<typeof import('@41devs/naya-id-js')> {\n return (this.sdk ??= import('@41devs/naya-id-js'));\n }\n\n private get isBrowser(): boolean {\n return isPlatformBrowser(this.platformId) && typeof document !== 'undefined';\n }\n\n private serverSideResult(): NayaResult {\n return {\n status: 'error',\n errorCode: 'unsupported_browser',\n errorMessage: 'Le SDK Naya nécessite un environnement navigateur.',\n };\n }\n\n /**\n * Enveloppe la configuration de l'hôte pour ramener ses rappels dans la\n * zone Angular. Sans cela, un `onEvent` qui écrit dans un signal ne\n * déclencherait aucun rafraîchissement : le parcours tourne hors zone.\n */\n private withZone(config?: NayaConfig): NayaConfig | undefined {\n const onEvent = config?.onEvent;\n if (!onEvent) return config;\n return {\n ...config,\n onEvent: (event) => this.zone.run(() => onEvent(event)),\n };\n }\n\n /**\n * Lance le parcours de vérification d'identité.\n *\n * Authentification, au choix :\n * • `auth.verificationId` + `auth.sessionToken`, rendus à votre backend\n * par `POST /verifications/start` — à privilégier en production, la\n * clé ne quitte pas le serveur ;\n * • `auth.apiKey` — démos et fronts maîtrisés. Une clé live doit\n * déclarer ses origines dans le dashboard.\n *\n * **Ne rejette jamais** : une erreur ou une annulation revient sous forme\n * de `NayaResult` (`status: 'error'` avec `errorCode`, ou `'cancelled'`).\n */\n async startVerification(params: StartVerificationParams): Promise<NayaResult> {\n if (!this.isBrowser) return this.serverSideResult();\n const sdk = await this.load();\n return this.zone.runOutsideAngular(() =>\n sdk.startVerification({ ...params, config: this.withZone(params.config) }),\n );\n }\n\n /**\n * Instance réutilisable, pour piloter l'ouverture et la fermeture depuis\n * un composant et suivre les événements du parcours.\n */\n async create(params: StartVerificationParams): Promise<NayaVerification> {\n if (!this.isBrowser) {\n const result = this.serverSideResult();\n return {\n open: async () => result,\n close: () => {},\n on: () => () => {},\n destroy: () => {},\n get isOpen() {\n return false;\n },\n };\n }\n const sdk = await this.load();\n const instance = this.zone.runOutsideAngular(() =>\n sdk.create({ ...params, config: this.withZone(params.config) }),\n );\n return {\n open: () => this.zone.runOutsideAngular(() => instance.open()),\n close: () => instance.close(),\n on: (event, cb) => instance.on(event, (payload) => this.zone.run(() => cb(payload))),\n destroy: () => instance.destroy(),\n get isOpen() {\n return instance.isOpen;\n },\n };\n }\n\n /**\n * Vérifie que le navigateur peut exécuter le parcours (caméra, contexte\n * sécurisé, navigateur intégré) avant de l'ouvrir.\n */\n async isSupported(): Promise<NayaSupport> {\n if (!this.isBrowser) {\n // Rendu serveur : aucune capacité n'est mesurable, on rend un état\n // complet et négatif plutôt que de lever.\n return {\n ok: false,\n code: 'unsupported_browser',\n reason: 'Aucun environnement navigateur.',\n secureContext: false,\n mediaDevices: false,\n webAssembly: false,\n webGL: false,\n inAppBrowser: false,\n inAppBrowserName: null,\n mobile: false,\n ios: false,\n android: false,\n deviceClass: 'desktop',\n };\n }\n const sdk = await this.load();\n return sdk.isSupported();\n }\n\n /** Version du SDK navigateur effectivement chargé. */\n async version(): Promise<string> {\n const sdk = await this.load();\n return sdk.version;\n }\n\n /**\n * Pays couverts, avec leur libellé — pour un sélecteur affiché par l'hôte\n * avant d'ouvrir le parcours.\n *\n * Accesseur asynchrone, et non constante réexportée : une valeur importée\n * statiquement depuis cette bibliothèque entraînerait tout le SDK\n * navigateur dans le bundle initial de votre application. Vous n'en avez\n * besoin que si vous construisez votre propre sélection ; sinon `config`\n * accepte directement les codes (`country: 'BJ'`), typés par\n * `NayaCountryCode`.\n */\n async countries(): Promise<readonly { code: NayaCountryCode; label: string }[]> {\n const sdk = await this.load();\n return sdk.NAYA_COUNTRIES;\n }\n\n /** Types de pièces acceptés. Même remarque que [countries]. */\n async documentTypes(): Promise<readonly NayaDocumentType[]> {\n const sdk = await this.load();\n return sdk.NAYA_DOCUMENT_TYPES;\n }\n\n /** Libellé lisible d'un code pays. */\n async countryLabel(code: NayaCountryCode): Promise<string> {\n const sdk = await this.load();\n return sdk.countryLabel(code);\n }\n}\n","/*\n * Surface publique du SDK Naya ID — Angular.\n *\n * Le service d'entrée est propre à Angular ; les modèles sont **réexportés\n * depuis `@41devs/naya-id-js`** plutôt que redéclarés. Les deux SDK ne\n * peuvent donc pas diverger : une option ajoutée au parcours navigateur est\n * disponible ici au prochain `npm update`, sans publication de cette\n * bibliothèque.\n *\n * ⚠️ **Types uniquement.** Réexporter une valeur (une constante, une\n * fonction) placerait le SDK navigateur dans le graphe statique du module,\n * et donc dans le bundle **initial** de l'application cliente — 154 ko qui\n * ne servent qu'au lancement du parcours. Les types disparaissent à la\n * compilation ; le SDK reste chargé à la demande. Les rares valeurs utiles\n * à un hôte sont exposées par des accesseurs asynchrones du service.\n */\n\nexport { NayaIdService, type NayaVerification } from './lib/naya-id.service';\n\nexport type {\n NayaAuth,\n NayaCaptureOptions,\n NayaConfig,\n NayaConsentOptions,\n NayaCountryCode,\n NayaDocumentType,\n NayaDocumentTypeMismatchPolicy,\n NayaErrorCode,\n NayaEvent,\n NayaEventName,\n NayaHandoffOptions,\n NayaLivenessAssets,\n NayaLivenessOptions,\n NayaLocale,\n NayaOcrData,\n NayaResult,\n NayaStatus,\n NayaSupport,\n NayaThemeOverrides,\n ResolvedNayaConfig,\n StartVerificationParams,\n} from '@41devs/naya-id-js';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}