npm.io
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-detectionisXSupported() ou capabilities() 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-kit

Dé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();          //-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 à false et 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êt
Bip 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 via capabilities() 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.

Keywords