0.1.0 • Published 17h ago
@mostajs/browser-kit
Licence
AGPL-3.0-or-later
Version
0.1.0
Deps
0
Size
40 kB
Vulns
0
Weekly
0
@mostajs/browser-kit
Auteur : Dr Hamid MADANI drmdh@msn.com
Façade unifiée des Web APIs du navigateur : une API cohérente, sûre et testable par-dessus une vingtaine de capacités natives. Membre de mosta-browser-stack.
Principes (chaque capacité respecte le même contrat) :
- Feature-detection —
isXSupported()oucapabilities()avant tout appel. - Promesses — pas de callbacks bruts ; les abonnements renvoient une fonction de désabonnement.
- Dégradation gracieuse — sur navigateur non compatible (ou sous Node : SSR/tests), rien ne jette :
false/null/ no-op. - Tree-shaking — import global ou par sous-chemin :
@mostajs/browser-kit/wake-lock. - Repli quand pertinent —
clipboard(execCommand),file-system-access(<input type=file>/<a download>).
npm i @mostajs/browser-kitDémarrage rapide
import { capabilities, createWakeLock, share, vibrate } from '@mostajs/browser-kit';
// 1) Que sait faire ce navigateur ?
console.log(capabilities());
// { wakeLock:true, fullscreen:true, share:true, clipboard:true, geolocation:true, … }
// 2) Empêcher la veille pendant une diffusion / lecture vidéo
const wl = createWakeLock({ onChange: (on) => console.log('écran maintenu allumé:', on) });
await wl.acquire();
wl.bindAutoReacquire(); // ré-acquiert tout seul au retour au premier plan
// … à la fin : await wl.dispose();
// 3) Partager + vibrer (mobile)
if (await share({ title: 'VoxStudio', url: location.href })) vibrate(50);Sous Node,
capabilities()renvoie tout àfalseet chaque appel dégrade sans erreur — le module est SSR-safe et testable.
How to use — recettes par cas d'usage
Garder l'écran allumé pendant un live (cas VoxStudio)
import { createWakeLock } from '@mostajs/browser-kit/wake-lock';
const wl = createWakeLock();
startPublishBtn.onclick = async () => { /* … getUserMedia + WHIP … */ await wl.acquire(); wl.bindAutoReacquire(); };
stopBtn.onclick = async () => { await wl.dispose(); };Bouton plein écran (avec icône qui suit l'état)
import { toggleFullscreen, onFullscreenChange } from '@mostajs/browser-kit/fullscreen';
btn.onclick = () => toggleFullscreen(document.getElementById('player'));
onFullscreenChange((on) => btn.textContent = on ? '⤡ Quitter' : '⤢ Plein écran');Mettre l'app en pause quand l'onglet est caché (économie CPU/batterie)
import { onHidden, onVisible } from '@mostajs/browser-kit/visibility';
onHidden(() => player.pause());
onVisible(() => player.play());Bandeau « hors-ligne » + adaptation à la qualité réseau
import { onOffline, onOnline, connectionInfo } from '@mostajs/browser-kit/network';
onOffline(() => banner.show('Hors-ligne — reconnexion…'));
onOnline(() => banner.hide());
const net = connectionInfo(); // { effectiveType:'4g', downlink:10, saveData:false }
if (net?.saveData || net?.effectiveType === '2g') useLowQuality();Copier un lien d'invitation (repli automatique en HTTP)
import { writeText } from '@mostajs/browser-kit/clipboard';
copyBtn.onclick = async () => { if (await writeText(inviteUrl)) toast('Lien copié ✓'); };Notifier + pastille d'icône (PWA) à l'arrivée d'une question Q&A
import { notify } from '@mostajs/browser-kit/notifications';
import { setBadge } from '@mostajs/browser-kit/badging';
await notify('Nouvelle question', { body: q.text, icon: '/logo.png' });
await setBadge(unread);Position de l'utilisateur (carte d'un événement)
import { getCurrentPosition } from '@mostajs/browser-kit/geolocation';
const pos = await getCurrentPosition(); // { lat, lng, accuracy } ou null si refusé
if (pos) centerMap(pos.lat, pos.lng);Choisir caméra/micro + partage d'écran (plateau)
import { enumerateDevices, getUserMedia, getDisplayMedia, stopStream } from '@mostajs/browser-kit/media-devices';
const { cameras, microphones } = await enumerateDevices();
const cam = await getUserMedia({ video: { deviceId: cameras[0]?.deviceId }, audio: true });
const screen = await getDisplayMedia(); // partage d'écran
// … stopStream(cam) à l'arrêtBip de notification synthétisé (sans fichier audio)
import { beep, playSequence } from '@mostajs/browser-kit/audio';
beep({ freq: 880, duration: 120 }); // après un geste utilisateur
playSequence([{ freq: 660, duration: 100 }, { freq: 990, duration: 140 }]);Référence des capacités
capabilities() → carte booléenne de tout ce qui suit (21 clés).
Lot 1
| Module / sous-chemin | Exports |
|---|---|
wake-lock |
isWakeLockSupported() · createWakeLock({onChange}) → { isSupported, acquire(), release(), isActive(), bindAutoReacquire()→off, dispose() } |
fullscreen |
isFullscreenSupported() · isFullscreen() · requestFullscreen(el?) · exitFullscreen() · toggleFullscreen(el?) · onFullscreenChange(cb)→off |
visibility |
isVisibilitySupported() · isVisible() · onVisibilityChange(cb)→off · onVisible(cb) · onHidden(cb) |
network |
isOnline() · isNetworkInfoSupported() · connectionInfo() → {effectiveType,downlink,rtt,saveData,type} · onNetworkChange(cb) · onOnline(cb) · onOffline(cb) · onConnectionChange(cb) |
web-share |
isShareSupported() · canShareFiles() · canShare(data) · share({title,text,url,files}) → bool |
clipboard |
isClipboardSupported() · writeText(s) → bool (repli execCommand) · readText() → string|null |
vibration |
isVibrationSupported() · vibrate(pattern) · stopVibration() |
screen-orientation |
isOrientationSupported() · currentOrientation() → {type,angle} · lockOrientation(type) · unlockOrientation() · onOrientationChange(cb)→off |
Lot 2
| Module / sous-chemin | Exports |
|---|---|
geolocation |
isGeolocationSupported() · getCurrentPosition(opts) → {lat,lng,accuracy,altitude,heading,speed,timestamp}|null · watchPosition(cb,opts)→off |
notifications |
isNotificationSupported() · notificationPermission() · requestNotificationPermission() → état · notify(title,opts) → Notification|null |
permissions |
isPermissionsSupported() · queryPermission(name) → 'granted'|'denied'|'prompt'|null · onPermissionChange(name,cb)→off |
battery |
isBatterySupported() · getBattery() → {level,charging,chargingTime,dischargingTime}|null · onBatteryChange(cb)→off |
idle-detection |
isIdleDetectionSupported() · requestIdlePermission() · createIdleDetector({threshold,onChange}) → {start(),stop()} |
media-devices |
isMediaDevicesSupported() · isDisplayMediaSupported() · enumerateDevices() → {cameras,microphones,speakers} · getUserMedia(c) · getDisplayMedia(c) · stopStream(s) · onDeviceChange(cb)→off |
sensors |
isOrientationSensorSupported() · isMotionSensorSupported() · requestSensorPermission() (iOS 13+) · onDeviceOrientation(cb)→off · onDeviceMotion(cb)→off |
audio |
isAudioSupported() · beep({freq,duration,type,volume}) → bool · playSequence([{freq,duration,gap}]) (Web Audio, sans fichier) |
badging |
isBadgingSupported() · setBadge(count?) · clearBadge() (pastille icône PWA) |
file-system-access |
isFileSystemAccessSupported() · openFile(opts) → File|null (repli <input file>) · saveFile(blob,opts) → bool (repli <a download>) |
contact-picker |
isContactPickerSupported() · pickContacts(props,opts) → contacts|null · availableContactProperties() |
Notes & limites
- Contexte sécurisé (HTTPS) requis par la plupart des API (Wake Lock, Clipboard async, getUserMedia, Geolocation…).
- Geste utilisateur requis pour : Fullscreen, Web Share, déblocage du contexte Audio, permission capteurs iOS.
- Permissions : Geolocation, Notifications, Camera/Micro, Idle Detection demandent l'accord de l'utilisateur — vérifier l'état via
permissions. - Disponibilité variable :
idle-detection,badging,contact-picker,file-system-access, Network Information sont surtout sur Chromium. Toujours tester viacapabilities()et prévoir le repli.
Tests
npm test — 6 tests (@mostajs/mjs-unit) : dégradé sous Node + navigateur simulé (globalThis.navigator/Notification).
Licence : AGPL-3.0-or-later.