alphavalid-sdk 0.0.25 → 0.1.1

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,10 +1,15 @@
1
1
  # alphavalid-sdk
2
2
 
3
- SDK frontend (framework-agnostic) para:
4
- - abrir câmera frontal
5
- - guiar posicionamento (overlay)
6
- - detectar rosto (MVP) com `face-api.js` (TinyFaceDetector)
7
- - capturar selfie em JPEG (Blob)
3
+ SDK frontend (framework-agnostic) para validação facial e liveness com captura de selfie em JPEG.
4
+
5
+ Principais recursos:
6
+ - câmera frontal com teardown correto
7
+ - overlay visual com guias e hints
8
+ - desafios de liveness configuráveis
9
+ - captura manual e auto-capture com gate de estabilidade
10
+ - preview com botões OK/Recapturar (SDK ou customizado)
11
+
12
+ ---
8
13
 
9
14
  ## Instalação
10
15
 
@@ -12,40 +17,31 @@ SDK frontend (framework-agnostic) para:
12
17
  npm i alphavalid-sdk
13
18
  ```
14
19
 
15
- ## Passo obrigatório: disponibilizar os models
16
-
17
- O `face-api.js` carrega os pesos do modelo via HTTP. Este pacote leva os arquivos em `models/`, e você deve copiá-los para os assets do seu app.
20
+ ---
18
21
 
19
- > Nota (Ionic/Angular + Vite dev-server): o shard do TinyFaceDetector é distribuído com extensão `.bin`
20
- > para evitar erros de `vite:import-analysis` em alguns pipelines.
22
+ ## Modelos (obrigatório)
21
23
 
22
- ### Ionic/Angular (recomendado) cópia automática no `postinstall`
24
+ O `face-api.js` carrega pesos via HTTP. Copie os models do pacote para a pasta pública do seu app.
23
25
 
24
- No `package.json` do **APP consumidor**, adicione:
26
+ ### CLI (recomendado)
25
27
 
26
- ```json
27
- {
28
- "scripts": {
29
- "postinstall": "node ./node_modules/alphavalid-sdk/scripts/copy-models.cjs ./src/assets/alphavalid-models"
30
- }
31
- }
28
+ ```bash
29
+ npx alphavalid-sdk setup
32
30
  ```
33
31
 
34
- Isso garante que, ao rodar `npm i`, os models sejam copiados para:
32
+ Ele detecta o tipo de projeto e copia:
33
+ - Angular: `src/assets/alphavalid-models` (runtime: `/assets/alphavalid-models`)
34
+ - Vite: `public/alphavalid-models` (runtime: `/alphavalid-models`)
35
35
 
36
- - `src/assets/alphavalid-models/` -> servidos em runtime como `/assets/alphavalid-models`
37
-
38
- > Se você estiver no Windows, pode precisar rodar esse script via `node` do mesmo jeito (sem `./`).
39
-
40
- ### Execução manual (alternativa)
36
+ ### Manual
41
37
 
42
38
  ```bash
43
- node ./node_modules/alphavalid-sdk/scripts/copy-models.cjs ./src/assets/alphavalid-models
39
+ node ./node_modules/alphavalid-sdk/scripts/copy-models.cjs ./public/alphavalid-models
44
40
  ```
45
41
 
46
- ## Uso
42
+ ---
47
43
 
48
- Crie um container (div) com tamanho definido (ex.: 320x320, ou 100% do seu layout) e passe no `start()`.
44
+ ## Uso minimo
49
45
 
50
46
  ```ts
51
47
  import { AlphaValid } from 'alphavalid-sdk';
@@ -53,117 +49,87 @@ import { AlphaValid } from 'alphavalid-sdk';
53
49
  const sdk = new AlphaValid();
54
50
 
55
51
  await sdk.start({
56
- container: document.getElementById('cameraContainer')!,
57
- // Angular/Ionic assets:
58
- modelsPath: '/assets/alphavalid-models',
59
- onReady: () => console.log('ready'),
60
- onFeedback: (msg) => console.log('feedback:', msg),
61
- onError: (err) => console.error('error:', err)
52
+ // se estiver no Angular/Ionic, use /assets/alphavalid-models
53
+ modelsPath: '/alphavalid-models',
54
+ onUserPreviewConfirm: (blob) => {
55
+ // enviar blob
56
+ }
62
57
  });
63
-
64
- // quando feedback retornar "Pronto para capturar"
65
- const blob = await sdk.capture();
66
58
  ```
67
59
 
68
- Para encerrar:
60
+ Para finalizar:
69
61
 
70
62
  ```ts
71
63
  await sdk.stop();
72
64
  ```
73
65
 
74
- ### Liveness / desafios (opcional)
75
-
76
- Você pode exigir que só seja considerado **válido olhando para frente**, e/ou definir uma sequência de desafios.
66
+ ---
77
67
 
78
- #### Importante (fluxo com challenges)
68
+ ## Preview (OK/Recapturar)
79
69
 
80
- - Quando `liveness.challenges` é informado, o SDK **não** força `lookForward` globalmente a cada frame.
81
- - Se você quiser `lookForward`, coloque como **um step explícito** (recomendado como último step).
82
-
83
- #### Ajuste de sensibilidade do lookForward
84
-
85
- Use `liveness.lookForwardTolerance` (0..1):
86
- - **maior = mais tolerante** (mais fácil passar)
87
- - se omitido, é derivado de `strictness`
70
+ O preview gerenciado pelo SDK funciona em `uiMode: 'Mobile'` e `userPreview: true`.
71
+ Por padrao o preview ja vem ligado.
88
72
 
89
73
  ```ts
90
74
  await sdk.start({
91
- // ...
92
- liveness: {
93
- strictness: 0.5,
94
- lookForwardTolerance: 0.75
75
+ uiMode: 'Mobile',
76
+ userPreview: true,
77
+ previewOkText: 'OK',
78
+ previewRetakeText: 'Tirar outra',
79
+ onUserPreviewConfirm: (blob) => {
80
+ // enviar blob
95
81
  }
96
82
  });
97
83
  ```
98
84
 
99
- Exemplo com sequência (cliente orquestra a ordem, SDK valida e controla o progresso):
85
+ Se voce quiser renderizar um preview customizado:
100
86
 
101
87
  ```ts
102
88
  await sdk.start({
103
- container: document.getElementById('cameraContainer')!,
104
- modelsPath: '/assets/alphavalid-models',
105
- liveness: {
106
- strictness: 0.5,
107
- lookForwardTolerance: 0.75,
108
- challenges: [
109
- { type: 'lookLeft' },
110
- { type: 'lookRight' },
111
- { type: 'lookUp' },
112
- { type: 'lookDown' },
113
- { type: 'blink' },
114
- { type: 'lookForward' } // recomendado como último
115
- ]
116
- },
117
- onStateChange: (state) => {
118
- console.log('step:', state.challenge?.current, state.challenge?.index, '/', state.challenge?.total);
89
+ onPreview: (blob, actions) => {
90
+ // abra seu modal, use actions.retake() e actions.confirm()
119
91
  }
120
92
  });
121
93
  ```
122
94
 
123
- #### Auto-capture (recomendado no cliente)
95
+ ---
124
96
 
125
- O SDK expõe `state.isReadyToCapture`. Para auto-capture sem duplicar disparos, use lock + debounce:
97
+ ## Personalizacao de botoes
126
98
 
127
99
  ```ts
128
- let lock = false;
129
- let timer: any = null;
130
-
131
100
  await sdk.start({
132
- // ...
133
- onStateChange: async (state) => {
134
- if (lock) return;
135
-
136
- if (state.isReadyToCapture) {
137
- if (timer) return;
138
- timer = setTimeout(async () => {
139
- timer = null;
140
- if (lock) return;
141
- if (!sdk.getState()?.isReadyToCapture) return;
142
-
143
- lock = true;
144
- try {
145
- const blob = await sdk.capture();
146
- // envie o blob pro backend...
147
- } finally {
148
- await sdk.stop();
149
- }
150
- }, 500); // 300-600ms costuma funcionar bem
151
- } else {
152
- if (timer) {
153
- clearTimeout(timer);
154
- timer = null;
155
- }
156
- }
157
- }
101
+ captureButton: {
102
+ enabled: true,
103
+ text: 'Capturar foto',
104
+ color: '#ff6b35'
105
+ },
106
+ previewOkText: 'Usar foto',
107
+ previewRetakeText: 'Tirar outra'
158
108
  });
159
109
  ```
160
110
 
161
- ## Observações
111
+ ---
112
+
113
+ ## API publica
114
+
115
+ - `new AlphaValid()`
116
+ - `start(options: AlphaValidStartOptions): Promise<void>`
117
+ - `stop(): Promise<void>`
118
+ - `capture(): Promise<Blob>`
119
+ - `getState(): AlphaValidState | null`
120
+ - `resetChallenges(): void`
121
+ - `setupModels(): Promise<string>` (importando `setupModels` do pacote)
122
+
123
+ ---
124
+
125
+ ## Documentacao completa
162
126
 
163
- - Este MVP faz **detecção** (tem 1 rosto? está centralizado? perto/longe?).
164
- - Não é reconhecimento facial (comparar com base de rostos).
127
+ Veja [alphavalid-sdk-docs.md](alphavalid-sdk-docs.md) para:
128
+ - todas as opcoes do SDK
129
+ - defaults oficiais
130
+ - exemplos completos
131
+ - ajustes de liveness e debug
132
+ - detalhes do estado e callbacks
165
133
 
166
- Observações sobre liveness:
167
- - Tudo aqui é **heurístico (MVP)**. Para liveness robusto, o ideal é um pipeline dedicado.
168
- - O desafio `blink` usa landmarks (modelo `faceLandmark68TinyNet`).
169
134
 
135
+ ### `stop(): Promise<void>`
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function detectProjectType() {
7
+ if (fs.existsSync('angular.json')) return 'angular';
8
+ if (fs.existsSync('vite.config.js') || fs.existsSync('vite.config.ts') || fs.existsSync('index.html') || fs.existsSync('public')) return 'vite';
9
+ return 'unknown';
10
+ }
11
+
12
+ function getTargetDir() {
13
+ const type = detectProjectType();
14
+ if (type === 'angular') return path.join('src', 'assets', 'alphavalid-models');
15
+ if (type === 'vite') return path.join('public', 'alphavalid-models');
16
+ return path.join('alphavalid-models');
17
+ }
18
+
19
+ function copyModels() {
20
+ const src = path.join(__dirname, '..', 'models');
21
+ const dest = path.resolve(process.cwd(), getTargetDir());
22
+ if (!fs.existsSync(src)) {
23
+ console.error('[AlphaValid] Pasta de models não encontrada:', src);
24
+ process.exit(1);
25
+ }
26
+ fs.mkdirSync(dest, { recursive: true });
27
+ for (const file of fs.readdirSync(src)) {
28
+ fs.copyFileSync(path.join(src, file), path.join(dest, file));
29
+ }
30
+ console.log(`[AlphaValid] Models copiados para ${dest}`);
31
+ }
32
+
33
+ // Após copiar os models, também copie o GIF de loader para um caminho padrão
34
+ // que funcione bem em Angular (src/assets/images) e Vite (public/images).
35
+ try {
36
+ const projectRoot = process.cwd();
37
+ const sdkRoot = path.join(__dirname, '..');
38
+ const loaderSrc = path.join(sdkRoot, 'demo', 'public', 'images', 'alphaloader.gif');
39
+
40
+ if (fs.existsSync(loaderSrc)) {
41
+ // Angular: src/assets/images
42
+ const angularImages = path.join(projectRoot, 'src', 'assets', 'images');
43
+ if (fs.existsSync(path.join(projectRoot, 'angular.json'))) {
44
+ fs.mkdirSync(angularImages, { recursive: true });
45
+ fs.copyFileSync(loaderSrc, path.join(angularImages, 'alphaloader.gif'));
46
+ console.log('[AlphaValid] Loader GIF copiado para', angularImages);
47
+ }
48
+
49
+ // Vite / SPA genérica: public/images
50
+ const viteImages = path.join(projectRoot, 'public', 'images');
51
+ if (fs.existsSync(path.join(projectRoot, 'vite.config.js')) || fs.existsSync(path.join(projectRoot, 'vite.config.ts')) || fs.existsSync(path.join(projectRoot, 'index.html'))) {
52
+ fs.mkdirSync(viteImages, { recursive: true });
53
+ fs.copyFileSync(loaderSrc, path.join(viteImages, 'alphaloader.gif'));
54
+ console.log('[AlphaValid] Loader GIF copiado para', viteImages);
55
+ }
56
+ }
57
+ } catch (e) {
58
+ // silencioso: se falhar, o app pode configurar loader.src manualmente
59
+ }
60
+
61
+ try {
62
+ copyModels();
63
+ } catch (e) {
64
+ console.error('[AlphaValid] Não foi possível copiar automaticamente os models.');
65
+ console.error('Execute: npx alphavalid-sdk setup');
66
+ process.exit(1);
67
+ }
@@ -0,0 +1,18 @@
1
+ //#region \0rolldown/runtime.js
2
+ var e = Object.create, t = Object.defineProperty, n = Object.getOwnPropertyDescriptor, r = Object.getOwnPropertyNames, i = Object.getPrototypeOf, a = Object.prototype.hasOwnProperty, o = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), s = (e, i, o, s) => {
3
+ if (i && typeof i == "object" || typeof i == "function") for (var c = r(i), l = 0, u = c.length, d; l < u; l++) d = c[l], !a.call(e, d) && d !== o && t(e, d, {
4
+ get: ((e) => i[e]).bind(null, d),
5
+ enumerable: !(s = n(i, d)) || s.enumerable
6
+ });
7
+ return e;
8
+ }, c = (n, r, a) => (a = n == null ? {} : e(i(n)), s(r || !n || !n.__esModule ? t(a, "default", {
9
+ value: n,
10
+ enumerable: !0
11
+ }) : a, n)), l = /* @__PURE__ */ ((e) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(e, { get: (e, t) => (typeof require < "u" ? require : e)[t] }) : e)(function(e) {
12
+ if (typeof require < "u") return require.apply(this, arguments);
13
+ throw Error("Calling `require` for \"" + e + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
14
+ }), u = /* @__PURE__ */ o(((e, t) => {
15
+ t.exports = {};
16
+ }));
17
+ //#endregion
18
+ export { l as n, c as r, u as t };