JSPM

@mostajs/pwa-scan

1.1.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 10
  • Score
    100M100P100Q69116F
  • License AGPL-3.0-or-later

PWA QR Scanner — installable scanner for @mostajs apps (QR code + face recognition)

Package Exports

  • @mostajs/pwa-scan
  • @mostajs/pwa-scan/components/PwaScanner
  • @mostajs/pwa-scan/hooks/usePwaScan
  • @mostajs/pwa-scan/manifest

Readme

@mostajs/pwa-scan

PWA QR Scanner universel — fonctionne avec @mostajs/net, endpoints custom, tourniquets, ou n'importe quel backend REST.

npm

Installation

npm install @mostajs/pwa-scan

Quick Start

import { PwaScanner } from '@mostajs/pwa-scan'

// Auto-configurable — affiche le formulaire si pas de serverUrl
export default function ScanPage() {
  return <PwaScanner />
}

3 Modes d'utilisation

Mode NET (@mostajs/net)

Recherche directe dans une collection via l'API REST de @mostajs/net :

<PwaScanner
  serverUrl="http://localhost:4488"
  collection="reservations"
  codeField="qrCode"
  token="jwt-token"
/>

Mode Custom Endpoint

POST vers un endpoint custom. Le serveur gère toute la logique de validation :

<PwaScanner
  serverUrl=""
  scanEndpoint="/api/scan"
  appName="Mon Scanner"
/>
// POST { code: "...", action: "lookup" } → { status, message, data }

Mode Turnstile / IoT

Avec bodyMapper et responseMapper pour adapter le format à n'importe quel backend :

<PwaScanner
  serverUrl=""
  scanEndpoint="/api/turnstiles/validate"
  gateId="GATE-01"
  deviceId="scanner-mobile-01"
  apiKey="my-secret-key"
  userAgent="MostaGare-Turnstile"
  bodyMapper={(code, config) => ({
    ticketNumber: code,
    gateId: config.gateId,
    deviceId: config.deviceId,
    timestamp: new Date().toISOString(),
  })}
  responseMapper={(json, code) => ({
    status: json.valid ? 'granted' : 'denied',
    message: json.message,
    data: json.ticketInfo,
    code,
  })}
/>

Features

  • QR scanning via caméra (html5-qrcode)
  • Saisie manuelle en fallback
  • 3 modes : NET, endpoint custom, turnstile/IoT
  • Auto-config via URL params : ?url=http://server&token=jwt&apiKey=xxx
  • Historique offline en localStorage
  • PWA installable — manifest.json inclus
  • Audio feedback — bip accordé / bip refusé
  • bodyMapper — transforme le code scanné en body request
  • responseMapper — transforme la réponse serveur en ScanResult
  • Auto-détection du format réponse ({ status } ou { valid })

ScannerConfig

interface ScannerConfig {
  serverUrl: string              // URL du serveur (vide = même origine)

  // Endpoint
  scanEndpoint?: string          // POST custom (remplace /api/v1/)
  collection?: string            // Mode NET : collection (default: 'reservations')
  codeField?: string             // Mode NET : champ QR (default: 'qrCode')

  // Authentification
  token?: string                 // → Authorization: Bearer xxx
  apiKey?: string                // → x-api-key: xxx

  // Identité device
  deviceId?: string              // Identifiant device
  gateId?: string                // Identifiant gate/checkpoint
  userAgent?: string             // Custom User-Agent

  // Extensibilité
  headers?: Record<string, string>                          // Headers custom
  bodyMapper?: (code: string, config: ScannerConfig) => object   // Body custom
  responseMapper?: (json: any, code: string) => ScanResult       // Réponse custom

  // UI
  appName?: string               // Nom affiché (default: 'MostaJS Scanner')
}

Headers automatiques

Prop Header généré
token Authorization: Bearer {token}
apiKey x-api-key: {apiKey}
userAgent User-Agent: {userAgent}
headers Merge additionnel (priorité max)

Auto-détection de réponse

Si pas de responseMapper, le module détecte automatiquement le format :

Format serveur Mapping
{ status: 'granted'|'denied'|'error', message, data } Pass-through (ScanResult natif)
{ valid: true|false, message, ticketInfo } validstatus, ticketInfodata

PwaScannerProps

interface PwaScannerProps extends Partial<PwaScanConfig> {
  serverUrl?: string
  renderResult?: (result: ScanResult) => React.ReactNode  // Rendu custom du résultat
  onResult?: (result: ScanResult) => void                  // Callback à chaque scan
}

Hook usePwaScan

Pour construire votre propre UI :

import { usePwaScan } from '@mostajs/pwa-scan'

const { scanning, result, processing, startScanner, stopScanner, resetResult, scanManual } = usePwaScan({
  serverUrl: '',
  scanEndpoint: '/api/turnstiles/validate',
  gateId: 'GATE-01',
  bodyMapper: (code, cfg) => ({ ticketNumber: code, gateId: cfg.gateId }),
  responseMapper: (json, code) => ({
    status: json.valid ? 'granted' : 'denied',
    message: json.message,
    data: json.ticketInfo,
    code,
  }),
})

API directe

import { validateScan, testConnection } from '@mostajs/pwa-scan'

// Valider un code
const result = await validateScan('QR-CODE-123', config)
// → { status: 'granted', message: '...', data: {...}, code: 'QR-CODE-123' }

// Tester la connexion
const { ok, error } = await testConnection(config)

Config par priorité

  1. Props<PwaScanner serverUrl="..." bodyMapper={...} />
  2. URL params?url=http://server&token=jwt&apiKey=xxx
  3. localStorage — persisté après premier enregistrement
  4. Config form — affiché si aucune config trouvée

Exemples d'intégration

MostaGare — Scanner agent (validation ticket bus)

<PwaScanner
  serverUrl=""
  scanEndpoint="/api/turnstiles/validate"
  gateId="WEB-AGENT"
  appName="MostaGare Agent Scanner"
  bodyMapper={(code, cfg) => ({
    ticketNumber: code,
    gateId: cfg.gateId,
    timestamp: new Date().toISOString(),
  })}
  responseMapper={(json, code) => ({
    status: json.valid ? 'granted' : 'denied',
    message: json.message,
    data: json.ticketInfo,
    code,
  })}
  renderResult={(result) => (
    <div>{result.data?.passengerName} — Siège {result.data?.seatNumber}</div>
  )}
/>

SecuAccessPro — Contrôle d'accès avec face recognition

<PwaScanner
  serverUrl=""
  scanEndpoint="/api/scan"
  appName="SecuAccess Scanner"
  faceEnabled={true}
  onResult={(result) => {
    if (result.status === 'granted' && result.data?.faceDescriptor) {
      // Lancer vérification faciale
    }
  }}
/>

Booking-Baloon — Réservation via @mostajs/net

<PwaScanner
  serverUrl="http://localhost:4488"
  collection="reservations"
  codeField="qrCode"
  token="jwt-token"
/>

IoT / Tourniquet physique

<PwaScanner
  serverUrl="https://api.gare.dz"
  scanEndpoint="/api/turnstiles/validate"
  apiKey="turnstile-secret-2024"
  userAgent="MostaGare-Turnstile"
  deviceId="turnstile-gate-A-001"
  gateId="GATE-A"
  headers={{ 'X-Firmware': 'v2.1' }}
  bodyMapper={(code, cfg) => ({
    ticketNumber: code,
    gateId: cfg.gateId,
    deviceId: cfg.deviceId,
    timestamp: new Date().toISOString(),
  })}
/>

Exports

// Components
export { PwaScanner } from '@mostajs/pwa-scan'

// Hooks
export { usePwaScan } from '@mostajs/pwa-scan'

// API
export { validateScan, testConnection } from '@mostajs/pwa-scan'

// Audio
export { playBeep, playGranted, playDenied } from '@mostajs/pwa-scan'

// Types
export type { ScannerConfig, ScanResult, PwaScanConfig, PwaScannerProps } from '@mostajs/pwa-scan'

License

MIT — © 2026 Dr Hamid MADANI drmdh@msn.com