@mostajs/ticketing 2.1.0 → 3.0.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.
Files changed (69) hide show
  1. package/dist/api/detect.route.d.ts +19 -0
  2. package/dist/api/detect.route.d.ts.map +1 -0
  3. package/dist/api/detect.route.js +31 -0
  4. package/dist/api/detect.route.js.map +1 -0
  5. package/dist/api/recognize.route.d.ts +32 -0
  6. package/dist/api/recognize.route.d.ts.map +1 -0
  7. package/dist/api/recognize.route.js +86 -0
  8. package/dist/api/recognize.route.js.map +1 -0
  9. package/dist/components/FaceDetector.d.ts +28 -0
  10. package/dist/components/FaceDetector.d.ts.map +1 -0
  11. package/dist/components/FaceDetector.js +184 -0
  12. package/dist/components/FaceDetector.js.map +1 -0
  13. package/dist/components/ScanResultCard.d.ts +10 -0
  14. package/dist/components/ScanResultCard.d.ts.map +1 -0
  15. package/dist/components/ScanResultCard.js +63 -0
  16. package/dist/components/ScanResultCard.js.map +1 -0
  17. package/dist/components/ScannerView.d.ts +9 -0
  18. package/dist/components/ScannerView.d.ts.map +1 -0
  19. package/dist/components/ScannerView.js +62 -0
  20. package/dist/components/ScannerView.js.map +1 -0
  21. package/dist/hooks/useCamera.d.ts +18 -0
  22. package/dist/hooks/useCamera.d.ts.map +1 -0
  23. package/dist/hooks/useCamera.js +74 -0
  24. package/dist/hooks/useCamera.js.map +1 -0
  25. package/dist/hooks/useFaceDetection.d.ts +22 -0
  26. package/dist/hooks/useFaceDetection.d.ts.map +1 -0
  27. package/dist/hooks/useFaceDetection.js +62 -0
  28. package/dist/hooks/useFaceDetection.js.map +1 -0
  29. package/dist/hooks/useScan.d.ts +9 -0
  30. package/dist/hooks/useScan.d.ts.map +1 -0
  31. package/dist/hooks/useScan.js +101 -0
  32. package/dist/hooks/useScan.js.map +1 -0
  33. package/dist/index.d.ts +13 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +14 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/lib/audio.d.ts +10 -0
  38. package/dist/lib/audio.d.ts.map +1 -0
  39. package/dist/lib/audio.js +29 -0
  40. package/dist/lib/audio.js.map +1 -0
  41. package/dist/lib/face-api.d.ts +24 -0
  42. package/dist/lib/face-api.d.ts.map +1 -0
  43. package/dist/lib/face-api.js +66 -0
  44. package/dist/lib/face-api.js.map +1 -0
  45. package/dist/lib/face-matcher.d.ts +19 -0
  46. package/dist/lib/face-matcher.d.ts.map +1 -0
  47. package/dist/lib/face-matcher.js +53 -0
  48. package/dist/lib/face-matcher.js.map +1 -0
  49. package/dist/lib/face-utils.d.ts +21 -0
  50. package/dist/lib/face-utils.d.ts.map +1 -0
  51. package/dist/lib/face-utils.js +40 -0
  52. package/dist/lib/face-utils.js.map +1 -0
  53. package/dist/pages/ScanPage.d.ts +2 -0
  54. package/dist/pages/ScanPage.d.ts.map +1 -0
  55. package/dist/pages/ScanPage.js +32 -0
  56. package/dist/pages/ScanPage.js.map +1 -0
  57. package/dist/register.d.ts.map +1 -1
  58. package/dist/register.js +9 -3
  59. package/dist/register.js.map +1 -1
  60. package/dist/server.d.ts +4 -0
  61. package/dist/server.d.ts.map +1 -1
  62. package/dist/server.js +5 -2
  63. package/dist/server.js.map +1 -1
  64. package/dist/types/index.d.ts +115 -0
  65. package/dist/types/index.d.ts.map +1 -1
  66. package/dist/types/index.js +7 -2
  67. package/dist/types/index.js.map +1 -1
  68. package/i18n/fr/scan.json +25 -0
  69. package/package.json +82 -3
@@ -0,0 +1,18 @@
1
+ export type CameraStatus = 'idle' | 'requesting' | 'active' | 'denied' | 'error';
2
+ interface UseCameraOptions {
3
+ facingMode?: 'user' | 'environment';
4
+ width?: number;
5
+ height?: number;
6
+ autoStart?: boolean;
7
+ }
8
+ export declare function useCamera(options?: UseCameraOptions): {
9
+ videoRef: import("react").RefObject<HTMLVideoElement | null>;
10
+ stream: MediaStream | null;
11
+ status: CameraStatus;
12
+ error: string | null;
13
+ start: () => Promise<void>;
14
+ stop: () => void;
15
+ switchCamera: () => Promise<void>;
16
+ };
17
+ export {};
18
+ //# sourceMappingURL=useCamera.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCamera.d.ts","sourceRoot":"","sources":["../../hooks/useCamera.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEhF,UAAU,gBAAgB;IACxB,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CAAA;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB;;;;;;;;EAqEnD"}
@@ -0,0 +1,74 @@
1
+ // @mostajs/ticketing — useCamera hook (from @mostajs/face)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { useCallback, useEffect, useRef, useState } from 'react';
5
+ export function useCamera(options) {
6
+ const { facingMode = 'user', width = 640, height = 480, autoStart = false } = options || {};
7
+ const videoRef = useRef(null);
8
+ const [stream, setStream] = useState(null);
9
+ const [status, setStatus] = useState('idle');
10
+ const [error, setError] = useState(null);
11
+ const start = useCallback(async () => {
12
+ try {
13
+ setStatus('requesting');
14
+ setError(null);
15
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
16
+ video: { facingMode, width: { ideal: width }, height: { ideal: height } },
17
+ });
18
+ if (videoRef.current) {
19
+ videoRef.current.srcObject = mediaStream;
20
+ await videoRef.current.play();
21
+ }
22
+ setStream(mediaStream);
23
+ setStatus('active');
24
+ }
25
+ catch (err) {
26
+ if (err.name === 'NotAllowedError') {
27
+ setStatus('denied');
28
+ setError('Camera permission denied');
29
+ }
30
+ else {
31
+ setStatus('error');
32
+ setError(err.message || 'Camera error');
33
+ }
34
+ }
35
+ }, [facingMode, width, height]);
36
+ const stop = useCallback(() => {
37
+ if (stream) {
38
+ stream.getTracks().forEach((t) => t.stop());
39
+ setStream(null);
40
+ }
41
+ if (videoRef.current) {
42
+ videoRef.current.srcObject = null;
43
+ }
44
+ setStatus('idle');
45
+ }, [stream]);
46
+ const switchCamera = useCallback(async () => {
47
+ stop();
48
+ const newMode = facingMode === 'user' ? 'environment' : 'user';
49
+ try {
50
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
51
+ video: { facingMode: newMode, width: { ideal: width }, height: { ideal: height } },
52
+ });
53
+ if (videoRef.current) {
54
+ videoRef.current.srcObject = mediaStream;
55
+ await videoRef.current.play();
56
+ }
57
+ setStream(mediaStream);
58
+ setStatus('active');
59
+ }
60
+ catch (err) {
61
+ setStatus('error');
62
+ setError(err.message);
63
+ }
64
+ }, [stop, facingMode, width, height]);
65
+ useEffect(() => {
66
+ if (autoStart)
67
+ start();
68
+ return () => {
69
+ stream?.getTracks().forEach((t) => t.stop());
70
+ };
71
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
72
+ return { videoRef, stream, status, error, start, stop, switchCamera };
73
+ }
74
+ //# sourceMappingURL=useCamera.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCamera.js","sourceRoot":"","sources":["../../hooks/useCamera.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { MostaFaceConfig } from '../types';
2
+ export type DetectionStatus = 'loading' | 'detecting' | 'detected' | 'noFace';
3
+ interface UseFaceDetectionOptions {
4
+ /** Interval between detections in ms (default: 300) */
5
+ interval?: number;
6
+ /** Auto-start detection (default: true) */
7
+ autoStart?: boolean;
8
+ /** Also extract descriptor on each frame (default: false) */
9
+ extractDescriptor?: boolean;
10
+ /** Face-api config */
11
+ config?: MostaFaceConfig;
12
+ }
13
+ export declare function useFaceDetection(videoRef: React.RefObject<HTMLVideoElement | null>, options?: UseFaceDetectionOptions): {
14
+ detection: any;
15
+ descriptor: Float32Array<ArrayBufferLike> | null;
16
+ status: DetectionStatus;
17
+ modelsLoaded: boolean;
18
+ stop: () => void;
19
+ start: () => Promise<void>;
20
+ };
21
+ export {};
22
+ //# sourceMappingURL=useFaceDetection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFaceDetection.d.ts","sourceRoot":"","sources":["../../hooks/useFaceDetection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAA;AAE7E,UAAU,uBAAuB;IAC/B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,sBAAsB;IACtB,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,EAClD,OAAO,CAAC,EAAE,uBAAuB;;;;;;;EA6DlC"}
@@ -0,0 +1,62 @@
1
+ // @mostajs/ticketing — useFaceDetection hook (from @mostajs/face)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { useCallback, useEffect, useRef, useState } from 'react';
5
+ export function useFaceDetection(videoRef, options) {
6
+ const { interval = 300, autoStart = true, extractDescriptor: doExtract = false, config } = options || {};
7
+ const [detection, setDetection] = useState(null);
8
+ const [descriptor, setDescriptor] = useState(null);
9
+ const [status, setStatus] = useState('loading');
10
+ const [modelsLoaded, setModelsLoaded] = useState(false);
11
+ const running = useRef(false);
12
+ const timerRef = useRef(null);
13
+ const loadAndStart = useCallback(async () => {
14
+ const faceApi = await import('../lib/face-api');
15
+ await faceApi.loadModels(config);
16
+ setModelsLoaded(true);
17
+ setStatus('detecting');
18
+ running.current = true;
19
+ const detect = async () => {
20
+ if (!running.current || !videoRef.current)
21
+ return;
22
+ const video = videoRef.current;
23
+ if (video.readyState < 2) {
24
+ timerRef.current = setTimeout(detect, interval);
25
+ return;
26
+ }
27
+ const det = await faceApi.detectFace(video, config);
28
+ setDetection(det);
29
+ if (det) {
30
+ setStatus('detected');
31
+ if (doExtract) {
32
+ const desc = await faceApi.extractDescriptor(video, config);
33
+ setDescriptor(desc);
34
+ }
35
+ }
36
+ else {
37
+ setStatus('noFace');
38
+ setDescriptor(null);
39
+ }
40
+ if (running.current) {
41
+ timerRef.current = setTimeout(detect, interval);
42
+ }
43
+ };
44
+ detect();
45
+ }, [videoRef, interval, doExtract, config]);
46
+ useEffect(() => {
47
+ if (autoStart)
48
+ loadAndStart();
49
+ return () => {
50
+ running.current = false;
51
+ if (timerRef.current)
52
+ clearTimeout(timerRef.current);
53
+ };
54
+ }, [autoStart, loadAndStart]);
55
+ const stop = useCallback(() => {
56
+ running.current = false;
57
+ if (timerRef.current)
58
+ clearTimeout(timerRef.current);
59
+ }, []);
60
+ return { detection, descriptor, status, modelsLoaded, stop, start: loadAndStart };
61
+ }
62
+ //# sourceMappingURL=useFaceDetection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFaceDetection.js","sourceRoot":"","sources":["../../hooks/useFaceDetection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { UseScanOptions, UseScanReturn } from '../types/index';
2
+ /**
3
+ * Hook that manages QR code scanning lifecycle.
4
+ *
5
+ * Uses html5-qrcode under the hood. The scanner renders into a DOM element
6
+ * with id="qr-reader" — make sure this element exists in your component.
7
+ */
8
+ export declare function useScan(options?: UseScanOptions): UseScanReturn;
9
+ //# sourceMappingURL=useScan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScan.d.ts","sourceRoot":"","sources":["../../hooks/useScan.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAkB,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnF;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAsGnE"}
@@ -0,0 +1,101 @@
1
+ // @mostajs/ticketing — useScan hook (from @mostajs/scan)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { useState, useRef, useCallback, useEffect } from 'react';
5
+ import { playGranted, playDenied } from '../lib/audio';
6
+ /**
7
+ * Hook that manages QR code scanning lifecycle.
8
+ *
9
+ * Uses html5-qrcode under the hood. The scanner renders into a DOM element
10
+ * with id="qr-reader" — make sure this element exists in your component.
11
+ */
12
+ export function useScan(options = {}) {
13
+ const { apiEndpoint = '/api/scan', audioFeedback = true, soundFrequencies = [800, 300], onResult, onError, } = options;
14
+ const [scanning, setScanning] = useState(false);
15
+ const [processing, setProcessing] = useState(false);
16
+ const [result, setResult] = useState(null);
17
+ const scannerRef = useRef(null);
18
+ const processingRef = useRef(false);
19
+ const handleScan = useCallback(async (qrCode) => {
20
+ try {
21
+ const res = await fetch(apiEndpoint, {
22
+ method: 'POST',
23
+ headers: { 'Content-Type': 'application/json' },
24
+ body: JSON.stringify({ qrCode, scanMethod: 'webcam' }),
25
+ });
26
+ if (!res.ok) {
27
+ const err = await res.json().catch(() => null);
28
+ const msg = err?.error?.message || `Server error (${res.status})`;
29
+ const denied = { result: 'denied', reason: msg };
30
+ setResult(denied);
31
+ if (audioFeedback)
32
+ playDenied(soundFrequencies[1]);
33
+ onResult?.(denied);
34
+ return;
35
+ }
36
+ const json = await res.json();
37
+ const data = json.data;
38
+ setResult(data);
39
+ if (audioFeedback) {
40
+ if (data.result === 'granted')
41
+ playGranted(soundFrequencies[0]);
42
+ else
43
+ playDenied(soundFrequencies[1]);
44
+ }
45
+ onResult?.(data);
46
+ }
47
+ catch {
48
+ const denied = { result: 'denied', reason: 'Connection error' };
49
+ setResult(denied);
50
+ if (audioFeedback)
51
+ playDenied(soundFrequencies[1]);
52
+ onResult?.(denied);
53
+ }
54
+ }, [apiEndpoint, audioFeedback, soundFrequencies, onResult]);
55
+ const startScanner = useCallback(async () => {
56
+ setResult(null);
57
+ try {
58
+ const { Html5Qrcode } = await import('html5-qrcode');
59
+ const scanner = new Html5Qrcode('qr-reader');
60
+ scannerRef.current = scanner;
61
+ await scanner.start({ facingMode: 'environment' }, { fps: 10, qrbox: { width: 250, height: 250 } }, async (decodedText) => {
62
+ if (processingRef.current)
63
+ return;
64
+ processingRef.current = true;
65
+ setProcessing(true);
66
+ await scanner.stop();
67
+ setScanning(false);
68
+ await handleScan(decodedText);
69
+ processingRef.current = false;
70
+ setProcessing(false);
71
+ }, () => { });
72
+ setScanning(true);
73
+ }
74
+ catch (err) {
75
+ const msg = String(err?.message || err || '');
76
+ if (msg.includes('NotFound') || msg.includes('Requested device not found') || msg.includes('no camera')) {
77
+ onError?.('Camera not found. Check your camera connection.');
78
+ }
79
+ else if (msg.includes('NotAllowed') || msg.includes('Permission')) {
80
+ onError?.('Camera access denied. Allow camera access in browser settings.');
81
+ }
82
+ else {
83
+ onError?.(msg || 'Failed to start scanner');
84
+ }
85
+ }
86
+ }, [handleScan, onError]);
87
+ const stopScanner = useCallback(() => {
88
+ scannerRef.current?.stop().catch(() => { });
89
+ setScanning(false);
90
+ }, []);
91
+ const resetResult = useCallback(() => {
92
+ setResult(null);
93
+ }, []);
94
+ useEffect(() => {
95
+ return () => {
96
+ scannerRef.current?.stop().catch(() => { });
97
+ };
98
+ }, []);
99
+ return { scanning, processing, result, startScanner, stopScanner, resetResult };
100
+ }
101
+ //# sourceMappingURL=useScan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScan.js","sourceRoot":"","sources":["../../hooks/useScan.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -7,5 +7,17 @@ export { DAY_MULTIPLIER, DAY_MODULO, YEAR_MODULO, MAX_SEQUENCE, MAX_TICKET_VALUE
7
7
  export { TICKETING_PERMISSIONS, TICKETING_PERMISSION_DEFINITIONS, TICKETING_CATEGORY_DEFINITIONS } from './lib/permissions';
8
8
  export type { TicketingPermission } from './lib/permissions';
9
9
  export { ticketingMenuContribution } from './lib/menu';
10
- export type { CodeFormat, ValidityMode, TicketStatus, ScanResult, ScanMethod, DenyReason, ScanInput, ScanOutput, ScanGrantedResult, ScanDeniedResult, TicketInfo, ClientInfo, AccessInfo, CreateTicketInput, ScanHandlerConfig, TicketsHandlerConfig, } from './types/index';
10
+ export { default as ScannerView } from './components/ScannerView';
11
+ export { default as ScanResultCard, ScanEmptyState } from './components/ScanResultCard';
12
+ export { useScan } from './hooks/useScan';
13
+ export { playBeep, playGranted, playDenied } from './lib/audio';
14
+ export { loadModels, isLoaded, detectFace, detectAllFaces, extractDescriptor } from './lib/face-api';
15
+ export { compareFaces, findMatch, findAllMatches } from './lib/face-matcher';
16
+ export { descriptorToArray, arrayToDescriptor, isValidDescriptor, drawDetection } from './lib/face-utils';
17
+ export { useCamera } from './hooks/useCamera';
18
+ export { useFaceDetection } from './hooks/useFaceDetection';
19
+ export { default as FaceDetector } from './components/FaceDetector';
20
+ export type { FaceDetectorProps } from './components/FaceDetector';
21
+ export type { CodeFormat, ValidityMode, TicketStatus, ScanResult, ScanMethod, DenyReason, ScanInput, ScanOutput, ScanGrantedResult, ScanDeniedResult, TicketInfo, ClientInfo, AccessInfo, CreateTicketInput, ScanHandlerConfig, TicketsHandlerConfig, ScanResultData, ScanTicketInfo, ScanClientInfo, ScanAccessInfo, ScannerViewProps, ScanResultCardProps, UseScanOptions, UseScanReturn, MostaFaceConfig, FaceDetectionResult, FaceMatchResult, FaceDescriptor, FaceSettings, } from './types/index';
22
+ export { DEFAULT_FACE_SETTINGS } from './types/index';
11
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAG5E,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAGvF,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EACvE,YAAY,EAAE,kBAAkB,EAChC,4BAA4B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,yBAAyB,EACpG,iBAAiB,EAAE,mBAAmB,GACvC,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAAE,qBAAqB,EAAE,gCAAgC,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAA;AAC3H,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAG5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAGtD,YAAY,EACV,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAC9D,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EACtE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EACrD,iBAAiB,EAAE,oBAAoB,GACxC,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAG5E,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAGvF,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EACvE,YAAY,EAAE,kBAAkB,EAChC,4BAA4B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,yBAAyB,EACpG,iBAAiB,EAAE,mBAAmB,GACvC,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAAE,qBAAqB,EAAE,gCAAgC,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAA;AAC3H,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAG5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAGtD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACvF,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAG/D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACpG,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACzG,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACnE,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAGlE,YAAY,EACV,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAC9D,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EACtE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EACrD,iBAAiB,EAAE,oBAAoB,EAEvC,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAC9D,gBAAgB,EAAE,mBAAmB,EAAE,cAAc,EAAE,aAAa,EAEpE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,GACpF,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // @mostajs/ticketing — Client-safe barrel (NO ORM imports)
1
+ // @mostajs/ticketing — Client-safe barrel (merged: ticketing + scan + face)
2
2
  // Author: Dr Hamid MADANI drmdh@msn.com
3
3
  // For server-side code (repos), use '@mostajs/ticketing/server'
4
4
  // Schemas (pure data — no ORM)
@@ -13,4 +13,17 @@ export { DAY_MULTIPLIER, DAY_MODULO, YEAR_MODULO, MAX_SEQUENCE, MAX_TICKET_VALUE
13
13
  export { TICKETING_PERMISSIONS, TICKETING_PERMISSION_DEFINITIONS, TICKETING_CATEGORY_DEFINITIONS } from './lib/permissions';
14
14
  // Menu
15
15
  export { ticketingMenuContribution } from './lib/menu';
16
+ // ── Scan UI (from @mostajs/scan) ───────────────────────────────────
17
+ export { default as ScannerView } from './components/ScannerView';
18
+ export { default as ScanResultCard, ScanEmptyState } from './components/ScanResultCard';
19
+ export { useScan } from './hooks/useScan';
20
+ export { playBeep, playGranted, playDenied } from './lib/audio';
21
+ // ── Face (from @mostajs/face) ──────────────────────────────────────
22
+ export { loadModels, isLoaded, detectFace, detectAllFaces, extractDescriptor } from './lib/face-api';
23
+ export { compareFaces, findMatch, findAllMatches } from './lib/face-matcher';
24
+ export { descriptorToArray, arrayToDescriptor, isValidDescriptor, drawDetection } from './lib/face-utils';
25
+ export { useCamera } from './hooks/useCamera';
26
+ export { useFaceDetection } from './hooks/useFaceDetection';
27
+ export { default as FaceDetector } from './components/FaceDetector';
28
+ export { DEFAULT_FACE_SETTINGS } from './types/index';
16
29
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,wCAAwC;AACxC,gEAAgE;AAEhE,+BAA+B;AAC/B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE5E,uCAAuC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEvF,iCAAiC;AACjC,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EACvE,YAAY,EAAE,kBAAkB,EAChC,4BAA4B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,yBAAyB,EACpG,iBAAiB,EAAE,mBAAmB,GACvC,MAAM,qBAAqB,CAAA;AAE5B,cAAc;AACd,OAAO,EAAE,qBAAqB,EAAE,gCAAgC,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAA;AAG3H,OAAO;AACP,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,wCAAwC;AACxC,gEAAgE;AAEhE,+BAA+B;AAC/B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE5E,uCAAuC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEvF,iCAAiC;AACjC,OAAO,EACL,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EACvE,YAAY,EAAE,kBAAkB,EAChC,4BAA4B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,yBAAyB,EACpG,iBAAiB,EAAE,mBAAmB,GACvC,MAAM,qBAAqB,CAAA;AAE5B,cAAc;AACd,OAAO,EAAE,qBAAqB,EAAE,gCAAgC,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAA;AAG3H,OAAO;AACP,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAEtD,sEAAsE;AACtE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACvF,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE/D,sEAAsE;AACtE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACpG,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACzG,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAenE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Play a beep sound at the given frequency and duration.
3
+ * Silently fails if AudioContext is unavailable (e.g., SSR).
4
+ */
5
+ export declare function playBeep(frequency: number, duration: number): void;
6
+ /** Play a success beep (high pitch, short) */
7
+ export declare function playGranted(freq?: number): void;
8
+ /** Play a denied beep (low pitch, longer) */
9
+ export declare function playDenied(freq?: number): void;
10
+ //# sourceMappingURL=audio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../lib/audio.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAYlE;AAED,8CAA8C;AAC9C,wBAAgB,WAAW,CAAC,IAAI,SAAM,GAAG,IAAI,CAE5C;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,IAAI,SAAM,GAAG,IAAI,CAE3C"}
@@ -0,0 +1,29 @@
1
+ // @mostajs/ticketing — Audio feedback utilities (from @mostajs/scan)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ /**
4
+ * Play a beep sound at the given frequency and duration.
5
+ * Silently fails if AudioContext is unavailable (e.g., SSR).
6
+ */
7
+ export function playBeep(frequency, duration) {
8
+ try {
9
+ const ctx = new AudioContext();
10
+ const osc = ctx.createOscillator();
11
+ osc.type = 'sine';
12
+ osc.frequency.value = frequency;
13
+ osc.connect(ctx.destination);
14
+ osc.start();
15
+ setTimeout(() => { osc.stop(); ctx.close(); }, duration);
16
+ }
17
+ catch {
18
+ // AudioContext not available (SSR, permissions, etc.)
19
+ }
20
+ }
21
+ /** Play a success beep (high pitch, short) */
22
+ export function playGranted(freq = 800) {
23
+ playBeep(freq, 200);
24
+ }
25
+ /** Play a denied beep (low pitch, longer) */
26
+ export function playDenied(freq = 300) {
27
+ playBeep(freq, 400);
28
+ }
29
+ //# sourceMappingURL=audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.js","sourceRoot":"","sources":["../../lib/audio.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,wCAAwC;AAExC;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,SAAiB,EAAE,QAAgB;IAC1D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,YAAY,EAAE,CAAA;QAC9B,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,EAAE,CAAA;QAClC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAA;QACjB,GAAG,CAAC,SAAS,CAAC,KAAK,GAAG,SAAS,CAAA;QAC/B,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAC5B,GAAG,CAAC,KAAK,EAAE,CAAA;QACX,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;AACH,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,WAAW,CAAC,IAAI,GAAG,GAAG;IACpC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AACrB,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,UAAU,CAAC,IAAI,GAAG,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AACrB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { MostaFaceConfig } from '../types';
2
+ /**
3
+ * Load the face-api.js library and 3 models (TinyFaceDetector, landmarks68, recognition).
4
+ */
5
+ export declare function loadModels(config?: MostaFaceConfig): Promise<void>;
6
+ /** Check if models are loaded */
7
+ export declare function isLoaded(): boolean;
8
+ /**
9
+ * Detect a single face with landmarks.
10
+ */
11
+ export declare function detectFace(input: HTMLVideoElement | HTMLCanvasElement, config?: MostaFaceConfig): Promise<import("@vladmandic/face-api").WithFaceLandmarks<{
12
+ detection: import("@vladmandic/face-api").FaceDetection;
13
+ }, import("@vladmandic/face-api").FaceLandmarks68> | null>;
14
+ /**
15
+ * Detect all faces with landmarks.
16
+ */
17
+ export declare function detectAllFaces(input: HTMLVideoElement | HTMLCanvasElement, config?: MostaFaceConfig): Promise<import("@vladmandic/face-api").WithFaceLandmarks<{
18
+ detection: import("@vladmandic/face-api").FaceDetection;
19
+ }, import("@vladmandic/face-api").FaceLandmarks68>[]>;
20
+ /**
21
+ * Extract the 128-dimensional face descriptor from a single face.
22
+ */
23
+ export declare function extractDescriptor(input: HTMLVideoElement | HTMLCanvasElement, config?: MostaFaceConfig): Promise<Float32Array | null>;
24
+ //# sourceMappingURL=face-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-api.d.ts","sourceRoot":"","sources":["../../lib/face-api.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAK/C;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAaxE;AAED,iCAAiC;AACjC,wBAAgB,QAAQ,IAAI,OAAO,CAElC;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,EAC3C,MAAM,CAAC,EAAE,eAAe;;2DAezB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,EAC3C,MAAM,CAAC,EAAE,eAAe;;sDAazB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,EAC3C,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAe9B"}
@@ -0,0 +1,66 @@
1
+ // @mostajs/ticketing — Core face-api.js service (from @mostajs/face, CLIENT-SIDE only)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ let fapi = null;
4
+ let modelsLoaded = false;
5
+ /**
6
+ * Load the face-api.js library and 3 models (TinyFaceDetector, landmarks68, recognition).
7
+ */
8
+ export async function loadModels(config) {
9
+ if (modelsLoaded)
10
+ return;
11
+ fapi = await import('@vladmandic/face-api');
12
+ const modelUrl = config?.modelsPath ?? '/models/face-api';
13
+ await Promise.all([
14
+ fapi.nets.tinyFaceDetector.loadFromUri(modelUrl),
15
+ fapi.nets.faceLandmark68Net.loadFromUri(modelUrl),
16
+ fapi.nets.faceRecognitionNet.loadFromUri(modelUrl),
17
+ ]);
18
+ modelsLoaded = true;
19
+ }
20
+ /** Check if models are loaded */
21
+ export function isLoaded() {
22
+ return modelsLoaded;
23
+ }
24
+ /**
25
+ * Detect a single face with landmarks.
26
+ */
27
+ export async function detectFace(input, config) {
28
+ if (!fapi)
29
+ throw new Error('face-api not loaded — call loadModels() first');
30
+ const detection = await fapi
31
+ .detectSingleFace(input, new fapi.TinyFaceDetectorOptions({
32
+ inputSize: config?.inputSize ?? 320,
33
+ scoreThreshold: config?.scoreThreshold ?? 0.5,
34
+ }))
35
+ .withFaceLandmarks();
36
+ return detection || null;
37
+ }
38
+ /**
39
+ * Detect all faces with landmarks.
40
+ */
41
+ export async function detectAllFaces(input, config) {
42
+ if (!fapi)
43
+ throw new Error('face-api not loaded — call loadModels() first');
44
+ return fapi
45
+ .detectAllFaces(input, new fapi.TinyFaceDetectorOptions({
46
+ inputSize: config?.inputSize ?? 320,
47
+ scoreThreshold: config?.scoreThreshold ?? 0.5,
48
+ }))
49
+ .withFaceLandmarks();
50
+ }
51
+ /**
52
+ * Extract the 128-dimensional face descriptor from a single face.
53
+ */
54
+ export async function extractDescriptor(input, config) {
55
+ if (!fapi)
56
+ throw new Error('face-api not loaded — call loadModels() first');
57
+ const result = await fapi
58
+ .detectSingleFace(input, new fapi.TinyFaceDetectorOptions({
59
+ inputSize: config?.inputSize ?? 320,
60
+ scoreThreshold: config?.scoreThreshold ?? 0.5,
61
+ }))
62
+ .withFaceLandmarks()
63
+ .withFaceDescriptor();
64
+ return result?.descriptor || null;
65
+ }
66
+ //# sourceMappingURL=face-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-api.js","sourceRoot":"","sources":["../../lib/face-api.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,wCAAwC;AAIxC,IAAI,IAAI,GAAiD,IAAI,CAAA;AAC7D,IAAI,YAAY,GAAG,KAAK,CAAA;AAExB;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAwB;IACvD,IAAI,YAAY;QAAE,OAAM;IAExB,IAAI,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAA;IAC3C,MAAM,QAAQ,GAAG,MAAM,EAAE,UAAU,IAAI,kBAAkB,CAAA;IAEzD,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,QAAQ,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,QAAQ,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,QAAQ,CAAC;KACnD,CAAC,CAAA;IAEF,YAAY,GAAG,IAAI,CAAA;AACrB,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,QAAQ;IACtB,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAA2C,EAC3C,MAAwB;IAExB,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAE3E,MAAM,SAAS,GAAG,MAAM,IAAI;SACzB,gBAAgB,CACf,KAAK,EACL,IAAI,IAAI,CAAC,uBAAuB,CAAC;QAC/B,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,GAAG;QACnC,cAAc,EAAE,MAAM,EAAE,cAAc,IAAI,GAAG;KAC9C,CAAC,CACH;SACA,iBAAiB,EAAE,CAAA;IAEtB,OAAO,SAAS,IAAI,IAAI,CAAA;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAA2C,EAC3C,MAAwB;IAExB,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAE3E,OAAO,IAAI;SACR,cAAc,CACb,KAAK,EACL,IAAI,IAAI,CAAC,uBAAuB,CAAC;QAC/B,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,GAAG;QACnC,cAAc,EAAE,MAAM,EAAE,cAAc,IAAI,GAAG;KAC9C,CAAC,CACH;SACA,iBAAiB,EAAE,CAAA;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAA2C,EAC3C,MAAwB;IAExB,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAE3E,MAAM,MAAM,GAAG,MAAM,IAAI;SACtB,gBAAgB,CACf,KAAK,EACL,IAAI,IAAI,CAAC,uBAAuB,CAAC;QAC/B,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,GAAG;QACnC,cAAc,EAAE,MAAM,EAAE,cAAc,IAAI,GAAG;KAC9C,CAAC,CACH;SACA,iBAAiB,EAAE;SACnB,kBAAkB,EAAE,CAAA;IAEvB,OAAO,MAAM,EAAE,UAAU,IAAI,IAAI,CAAA;AACnC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { FaceDescriptor, FaceMatchResult } from '../types';
2
+ /**
3
+ * Compute Euclidean distance between two 128-dim face descriptors.
4
+ */
5
+ export declare function compareFaces(d1: FaceDescriptor, d2: FaceDescriptor): number;
6
+ /**
7
+ * Find the best match among candidates.
8
+ * Returns null if no match is below the threshold.
9
+ */
10
+ export declare function findMatch<T extends {
11
+ faceDescriptor: number[];
12
+ }>(descriptor: FaceDescriptor, candidates: T[], threshold?: number): FaceMatchResult<T> | null;
13
+ /**
14
+ * Find all matches below the threshold, sorted by distance.
15
+ */
16
+ export declare function findAllMatches<T extends {
17
+ faceDescriptor: number[];
18
+ }>(descriptor: FaceDescriptor, candidates: T[], threshold?: number): FaceMatchResult<T>[];
19
+ //# sourceMappingURL=face-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-matcher.d.ts","sourceRoot":"","sources":["../../lib/face-matcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/D;;GAEG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,cAAc,EAClB,EAAE,EAAE,cAAc,GACjB,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS;IAAE,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,EAC9D,UAAU,EAAE,cAAc,EAC1B,UAAU,EAAE,CAAC,EAAE,EACf,SAAS,SAAM,GACd,eAAe,CAAC,CAAC,CAAC,GAAG,IAAI,CAkB3B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,EACnE,UAAU,EAAE,cAAc,EAC1B,UAAU,EAAE,CAAC,EAAE,EACf,SAAS,SAAM,GACd,eAAe,CAAC,CAAC,CAAC,EAAE,CAYtB"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Compute Euclidean distance between two 128-dim face descriptors.
3
+ */
4
+ export function compareFaces(d1, d2) {
5
+ const a = d1 instanceof Float32Array ? d1 : new Float32Array(d1);
6
+ const b = d2 instanceof Float32Array ? d2 : new Float32Array(d2);
7
+ if (a.length !== 128 || b.length !== 128) {
8
+ throw new Error('Descriptors must be 128 elements');
9
+ }
10
+ let sum = 0;
11
+ for (let i = 0; i < 128; i++) {
12
+ const diff = a[i] - b[i];
13
+ sum += diff * diff;
14
+ }
15
+ return Math.sqrt(sum);
16
+ }
17
+ /**
18
+ * Find the best match among candidates.
19
+ * Returns null if no match is below the threshold.
20
+ */
21
+ export function findMatch(descriptor, candidates, threshold = 0.6) {
22
+ let bestMatch = null;
23
+ let bestDistance = Infinity;
24
+ for (const candidate of candidates) {
25
+ if (!candidate.faceDescriptor || candidate.faceDescriptor.length !== 128)
26
+ continue;
27
+ const distance = compareFaces(descriptor, candidate.faceDescriptor);
28
+ if (distance < bestDistance) {
29
+ bestDistance = distance;
30
+ bestMatch = candidate;
31
+ }
32
+ }
33
+ if (bestMatch && bestDistance < threshold) {
34
+ return { match: bestMatch, distance: bestDistance };
35
+ }
36
+ return null;
37
+ }
38
+ /**
39
+ * Find all matches below the threshold, sorted by distance.
40
+ */
41
+ export function findAllMatches(descriptor, candidates, threshold = 0.6) {
42
+ const results = [];
43
+ for (const candidate of candidates) {
44
+ if (!candidate.faceDescriptor || candidate.faceDescriptor.length !== 128)
45
+ continue;
46
+ const distance = compareFaces(descriptor, candidate.faceDescriptor);
47
+ if (distance < threshold) {
48
+ results.push({ match: candidate, distance });
49
+ }
50
+ }
51
+ return results.sort((a, b) => a.distance - b.distance);
52
+ }
53
+ //# sourceMappingURL=face-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-matcher.js","sourceRoot":"","sources":["../../lib/face-matcher.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,EAAkB,EAClB,EAAkB;IAElB,MAAM,CAAC,GAAG,EAAE,YAAY,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,CAAC,CAAA;IAChE,MAAM,CAAC,GAAG,EAAE,YAAY,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,CAAC,CAAA;IAEhE,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACrD,CAAC;IAED,IAAI,GAAG,GAAG,CAAC,CAAA;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACxB,GAAG,IAAI,IAAI,GAAG,IAAI,CAAA;IACpB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,UAA0B,EAC1B,UAAe,EACf,SAAS,GAAG,GAAG;IAEf,IAAI,SAAS,GAAa,IAAI,CAAA;IAC9B,IAAI,YAAY,GAAG,QAAQ,CAAA;IAE3B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,SAAS,CAAC,cAAc,IAAI,SAAS,CAAC,cAAc,CAAC,MAAM,KAAK,GAAG;YAAE,SAAQ;QAClF,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,cAAc,CAAC,CAAA;QACnE,IAAI,QAAQ,GAAG,YAAY,EAAE,CAAC;YAC5B,YAAY,GAAG,QAAQ,CAAA;YACvB,SAAS,GAAG,SAAS,CAAA;QACvB,CAAC;IACH,CAAC;IAED,IAAI,SAAS,IAAI,YAAY,GAAG,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAA;IACrD,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,UAA0B,EAC1B,UAAe,EACf,SAAS,GAAG,GAAG;IAEf,MAAM,OAAO,GAAyB,EAAE,CAAA;IAExC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,SAAS,CAAC,cAAc,IAAI,SAAS,CAAC,cAAc,CAAC,MAAM,KAAK,GAAG;YAAE,SAAQ;QAClF,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,cAAc,CAAC,CAAA;QACnE,IAAI,QAAQ,GAAG,SAAS,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC9C,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;AACxD,CAAC"}