@mostajs/face 1.3.6 → 1.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.
@@ -0,0 +1,33 @@
1
+ /* @mostajs/face — helper navigateur autonome (window.MostaFace).
2
+ * À charger APRÈS browser/face-api.js (global `faceapi`). Sur le modèle de
3
+ * @mostajs/qrpanel/browser-scan, mais face-api reste un FICHIER SÉPARÉ vendu dans
4
+ * le module (browser/face-api.js), non concaténé. Aucune dépendance CDN ni build.
5
+ * @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later */
6
+ (function () {
7
+ if (typeof faceapi === 'undefined') { console.warn('[MostaFace] browser/face-api.js doit être chargé avant mosta-face.js'); return; }
8
+ var loaded = false;
9
+ async function loadModels(uri) {
10
+ if (loaded) return;
11
+ uri = uri || '/static/models/face-api';
12
+ await faceapi.nets.tinyFaceDetector.loadFromUri(uri);
13
+ await faceapi.nets.faceLandmark68Net.loadFromUri(uri);
14
+ await faceapi.nets.faceRecognitionNet.loadFromUri(uri);
15
+ loaded = true;
16
+ }
17
+ async function startCamera(video, opts) {
18
+ var stream = await navigator.mediaDevices.getUserMedia({ video: Object.assign({ facingMode: 'user', width: 480, height: 360 }, opts || {}) });
19
+ video.srcObject = stream; await video.play(); return stream;
20
+ }
21
+ async function detect(video) {
22
+ return faceapi.detectSingleFace(video, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceDescriptor();
23
+ }
24
+ async function captureDescriptor(video) { var d = await detect(video); return d ? Array.from(d.descriptor) : null; }
25
+ function snapshot(video, q) {
26
+ var c = document.createElement('canvas'); c.width = video.videoWidth || 480; c.height = video.videoHeight || 360;
27
+ c.getContext('2d').drawImage(video, 0, 0, c.width, c.height); return c.toDataURL('image/jpeg', q || 0.7);
28
+ }
29
+ window.MostaFace = {
30
+ loadModels: loadModels, startCamera: startCamera, detect: detect,
31
+ captureDescriptor: captureDescriptor, snapshot: snapshot, isLoaded: function () { return loaded; },
32
+ };
33
+ })();
package/llms.txt ADDED
@@ -0,0 +1,118 @@
1
+ # @mostajs/face — fiche LLM
2
+ > Reconnaissance faciale réutilisable : détection, extraction de descripteur 128-D, matching 1:N, hooks React et factories de routes API.
3
+
4
+ - Version: 1.3.7 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
5
+ - Chemin: mostajs/mosta-face · Statut audit: complet (dist/)
6
+
7
+ ## RÔLE
8
+ Module de reconnaissance faciale de l'écosystème mostajs, bâti sur @vladmandic/face-api. Détecte les visages, extrait le descripteur facial 128 dimensions, compare des descripteurs (distance euclidienne) et effectue du matching 1:N contre une liste de candidats. Fournit des hooks React (accès caméra, détection en boucle), un composant FaceDetector, et des factories de routes API Next.js (recognize, detect). Module 100% autonome : aucune dépendance sur `@mostajs/orm` ni un autre package @mostajs/*. La détection s'exécute côté client (browser) ; le serveur ne fait que le gating de permission et le matching.
9
+
10
+ ## INSTALLATION
11
+ npm i @mostajs/face
12
+ # Puis copier les modèles face-api.js dans public/models/face-api/ :
13
+ # tiny_face_detector, face_landmark_68, face_recognition (fichiers .bin + .json
14
+ # depuis node_modules/@vladmandic/face-api/model/)
15
+
16
+ ## EXPORTS
17
+ Point d'entrée racine (`.`) :
18
+ - Core face-api: loadModels, isLoaded, detectFace, detectAllFaces, extractDescriptor
19
+ - Matching: compareFaces, findMatch, findAllMatches
20
+ - Utils: descriptorToArray, arrayToDescriptor, isValidDescriptor, drawDetection
21
+ - Hooks: useCamera, useFaceDetection
22
+ - API factories: createRecognizeHandler, createDetectHandler
23
+ - Composant: FaceDetector (default)
24
+ - Menu: faceMenuContribution
25
+ - Constante: DEFAULT_FACE_SETTINGS
26
+ - Types: MostaFaceConfig, FaceDetectionResult, FaceMatchResult, FaceDescriptor, FaceSettings, FaceDetectorProps, FaceCandidate, RecognizeHandlerConfig, DetectHandlerConfig
27
+
28
+ ## EXPORTS PAR SOUS-CHEMIN
29
+ - `./lib/face-api` — loadModels, isLoaded, detectFace, detectAllFaces, extractDescriptor
30
+ - `./lib/face-matcher` — compareFaces, findMatch, findAllMatches
31
+ - `./lib/face-utils` — descriptorToArray, arrayToDescriptor, isValidDescriptor, drawDetection
32
+ - `./lib/menu` — faceMenuContribution
33
+ - `./hooks/useCamera` — useCamera
34
+ - `./hooks/useFaceDetection` — useFaceDetection
35
+ - `./components/FaceDetector` — composant React (default)
36
+ - `./api/recognize.route` — createRecognizeHandler ; types FaceCandidate, RecognizeHandlerConfig
37
+ - `./api/detect.route` — createDetectHandler ; type DetectHandlerConfig
38
+ - `./types` — tous les types + DEFAULT_FACE_SETTINGS
39
+ - `./register` — register(registry)
40
+ - **ASSETS NAVIGATEUR AUTONOMES** (sur le modèle de `@mostajs/qrpanel/browser-scan`, **sans CDN ni build**) — face-api est **vendu comme fichier source du module** (PAS bundlé/concaténé) :
41
+ - `./browser/face-api.js` — source @vladmandic/face-api (global `faceapi`), copié dans le module (LICENSE: `browser/face-api.LICENSE`).
42
+ - `./browser/mosta-face.js` — helper `window.MostaFace`, fichier SÉPARÉ, à charger APRÈS face-api.js.
43
+ - Chargement app : `<script src="/static/face-api.js"></script><script src="/static/mosta-face.js"></script>` + servir les modèles sous `/static/models/face-api/`.
44
+ - `window.MostaFace`: `loadModels(uri?)`, `startCamera(video, opts?)`, `detect(video)`, `captureDescriptor(video) -> number[]|null`, `snapshot(video, q?) -> dataURL`, `isLoaded()`.
45
+
46
+ ## API — SIGNATURES
47
+ loadModels(config?: MostaFaceConfig): Promise<void> // charge face-api.js + 3 modèles
48
+ isLoaded(): boolean
49
+ detectFace(input: HTMLVideoElement|HTMLCanvasElement, config?): Promise<WithFaceLandmarks|null>
50
+ detectAllFaces(input, config?): Promise<WithFaceLandmarks[]>
51
+ extractDescriptor(input, config?): Promise<Float32Array|null> // descripteur 128-D
52
+ compareFaces(d1: FaceDescriptor, d2: FaceDescriptor): number // distance euclidienne
53
+ findMatch<T extends {faceDescriptor:number[]}>(descriptor, candidates: T[], threshold?): FaceMatchResult<T>|null
54
+ findAllMatches<T extends {faceDescriptor:number[]}>(descriptor, candidates, threshold?): FaceMatchResult<T>[] // trié par distance
55
+ descriptorToArray(d: Float32Array): number[] · arrayToDescriptor(arr: number[]): Float32Array
56
+ isValidDescriptor(data: unknown): boolean // vrai si 128 éléments
57
+ drawDetection(canvas, detection|null, videoWidth, videoHeight): void
58
+ useCamera(options?: { facingMode?, width?, height?, autoStart? }): { videoRef, stream, status, error, start, stop, switchCamera }
59
+ useFaceDetection(videoRef, options?: { interval?, autoStart?, extractDescriptor?, config? }): { detection, descriptor, status, modelsLoaded, start, stop }
60
+ createRecognizeHandler(config: RecognizeHandlerConfig): { POST } // POST /api/face/recognize
61
+ createDetectHandler(config?: DetectHandlerConfig): { POST } // POST /api/face/detect (placeholder, gating)
62
+
63
+ ## TYPES CLÉS
64
+ MostaFaceConfig { modelsPath?('/models/face-api'), scoreThreshold?(0.5), inputSize?(320), matchThreshold?(0.6) }
65
+ FaceDescriptor = Float32Array | number[]
66
+ FaceDetectionResult { detection:{ score, box:{x,y,width,height} }, landmarks? }
67
+ FaceMatchResult<T> { match: T, distance: number }
68
+ FaceSettings { faceRecognitionEnabled, faceRecognitionThreshold(0.6), faceRequireForCapture, faceAutoVerify }
69
+ DEFAULT_FACE_SETTINGS — valeurs par défaut de FaceSettings
70
+ FaceCandidate { id, faceDescriptor: number[], [key: string]: unknown }
71
+ RecognizeHandlerConfig { getCandidates()=>Promise<FaceCandidate[]>, checkAuth?(req)=>Promise<Response|null>, getThreshold?()=>Promise<number>, isEnabled?()=>Promise<boolean>, publicFields?:string[] }
72
+ DetectHandlerConfig { checkAuth?(req)=>Promise<Response|null>, isEnabled?()=>Promise<boolean> }
73
+ CameraStatus = 'idle'|'requesting'|'active'|'denied'|'error'
74
+ DetectionStatus = 'loading'|'detecting'|'detected'|'noFace'
75
+
76
+ ## PATTERN
77
+ ```ts
78
+ // Client — détection + extraction
79
+ import { loadModels, extractDescriptor, descriptorToArray } from '@mostajs/face'
80
+ await loadModels({ modelsPath: '/models/face-api' })
81
+ const d = await extractDescriptor(videoEl) // Float32Array 128-D
82
+ const stored = descriptorToArray(d) // number[] pour DB/JSON
83
+
84
+ // Client — hooks React
85
+ import { useCamera, useFaceDetection } from '@mostajs/face'
86
+ const { videoRef, start } = useCamera({ autoStart: true })
87
+ const { descriptor, status } = useFaceDetection(videoRef, { extractDescriptor: true })
88
+
89
+ // Matching 1:N
90
+ import { findMatch } from '@mostajs/face'
91
+ const result = findMatch(descriptor, candidates, 0.6) // null si rien sous le seuil
92
+
93
+ // Serveur — route Next.js
94
+ import { createRecognizeHandler } from '@mostajs/face/api/recognize.route'
95
+ export const { POST } = createRecognizeHandler({
96
+ getCandidates: async () => clientRepo.findAll({ status:'active' }, { select:['firstName','faceDescriptor'] }),
97
+ getThreshold: async () => 0.6,
98
+ })
99
+ ```
100
+
101
+ ## DÉPEND DE
102
+ - @vladmandic/face-api (dependency) — moteur de détection/reconnaissance.
103
+ - Aucun module @mostajs/* requis (module 100% autonome).
104
+ - Peers : react >=18 ; @mostajs/menu (optionnel), @mostajs/socle (>=2.0.0, pour register).
105
+
106
+ ## PIÈGES
107
+ - Les modèles face-api.js NE sont PAS embarqués : il faut copier manuellement les 3 modèles (tiny_face_detector, face_landmark_68, face_recognition) dans `public/models/face-api/` (ou modelsPath custom) sinon loadModels échoue.
108
+ - loadModels doit être appelé (et terminé) avant detectFace/extractDescriptor — vérifier isLoaded() ou attendre la promesse.
109
+ - La détection s'exécute CÔTÉ CLIENT (browser, via HTMLVideoElement/HTMLCanvasElement) ; createDetectHandler est un placeholder de gating, il ne détecte pas de visage côté serveur.
110
+ - Le matching utilise la distance euclidienne : seuil PLUS BAS = PLUS strict (défaut 0.6). findMatch renvoie null si aucun candidat n'est sous le seuil.
111
+ - Les descripteurs se stockent en `number[]` (descriptorToArray) pour JSON/DB et se reconvertissent en Float32Array (arrayToDescriptor) avant comparaison.
112
+ - Un descripteur valide a exactement 128 éléments (isValidDescriptor).
113
+ - createRecognizeHandler n'authentifie pas par défaut : fournir une callback `checkAuth` retournant une Response pour refuser, ou null pour laisser passer ; par défaut publicFields exclut faceDescriptor de la réponse.
114
+
115
+ ## RÉFÉRENCES
116
+ - README.md (racine du module) — quick start, intégration complète, API reference
117
+ - face.wire.json, wire.json — déclaration de câblage
118
+ - LICENSE (AGPL-3.0-or-later)
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@mostajs/face",
3
- "version": "1.3.6",
3
+ "version": "1.4.0",
4
4
  "description": "Reusable face recognition module — detection, descriptor extraction, 1:N matching",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
- "license": "MIT",
6
+ "license": "AGPL-3.0-or-later",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "exports": {
@@ -69,14 +69,18 @@
69
69
  "types": "./dist/register.d.ts",
70
70
  "import": "./dist/register.js",
71
71
  "default": "./dist/register.js"
72
- }
72
+ },
73
+ "./browser/face-api.js": "./browser/face-api.js",
74
+ "./browser/mosta-face.js": "./browser/mosta-face.js"
73
75
  },
74
76
  "files": [
75
77
  "dist",
76
78
  "wire.json",
77
79
  "face.wire.json",
78
80
  "LICENSE",
79
- "README.md"
81
+ "README.md",
82
+ "llms.txt",
83
+ "browser"
80
84
  ],
81
85
  "keywords": [
82
86
  "face-recognition",
@@ -94,10 +98,6 @@
94
98
  "engines": {
95
99
  "node": ">=18.0.0"
96
100
  },
97
- "scripts": {
98
- "build": "tsc",
99
- "prepublishOnly": "npm run build"
100
- },
101
101
  "dependencies": {
102
102
  "@vladmandic/face-api": "^1.7.0"
103
103
  },
@@ -117,5 +117,8 @@
117
117
  "@mostajs/menu": {
118
118
  "optional": true
119
119
  }
120
+ },
121
+ "scripts": {
122
+ "build": "tsc"
120
123
  }
121
- }
124
+ }