npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@glassly/engine

v0.3.6

Published

Glassly Engine — miniapp registry, WebView bridge, and miniapp store

Readme

@glassly/engine

Glassly Engine — the on-device miniapp library.

Installation

npm install @glassly/engine

Peer packages: the engine's @glassly/* peer dependencies (crust, cloud-client, cloud-protocol, miniapp) must be available to your package manager. Until every peer is published to npm, consume the engine from this monorepo's workspace (as the example OEM app does).

Module format: the published entry points target React Native / Metro consumers (the react-native exports condition). Loading @glassly/engine from plain Node (require/ESM) is not supported at 0.1.x — the default condition's build output is ESM with extensionless imports and will not resolve under Node's loader.

Entry points

The package exposes its Engine entry points and a supported Bluetooth SDK facade (declared in package.json exports, with the react-native condition pointing at src/ so Metro, tsc and jest resolve live TypeScript source):

  • @glassly/engine (main, src/index.ts) — the OEM-facing surface: the engine namespace (configure/start/stop + typed domain facades), contract/read-model types, and pure helpers host UI renders with (decideReconnect, deriveDisplayState, the useApps-style hooks, OTA policy constants, hardware capability tables, BgTimer). Judgment rule: read models, commands, pure functions and types are main; anything that mutates runtime state or exposes a store/service is not.
  • @glassly/engine/ota (src/react/index.ts) — the complete Mentra Live OTA controller plus its stock full-screen renderer. useMentraLiveOta owns the check, hotspot or Wi-Fi install, APK/MTK/BES progress, reboot, retry, and final verification behavior. MentraLiveOtaFlow renders that same public hook.
  • @glassly/engine/bluetooth-sdk (src/bluetooth-sdk/index.ts) — the complete public Bluetooth SDK surface, re-exported without a wrapper so it has the same singleton identity as @glassly/bluetooth-sdk. The supported SDK react, types, photo-receiver, ota-transport, and debug subpaths are mirrored below this path. The SDK's internal entrypoint is deliberately not exposed.

See cloud/docs/issues/020-glasses-status-boundary/integration-review.md §D for the burn-down plan.

This module owns the pieces of miniapp logic and handling that aren't tied to the rest of the manager app: the WebView message bus, the in-memory running registry, and the JS globals that we inject into every miniapp WebView.

The goal is for all miniapp logic to live here over time. Today the move is incremental — only the self-contained services have moved. Cross-cutting services (LocalMiniappRuntime, MantleManager, Composer install pipeline) still live under mobile/src/ because they reach back into the manager's stores and sockets.

Public surface

import {engine, decideDevLaunchRoute} from "@glassly/engine"
import BluetoothSdk from "@glassly/engine/bluetooth-sdk"
  • decideDevLaunchRoute — pre-flight a dev URL's miniapp.json to decide whether to mount live or take the user to the offline screen.

Mentra Live OTA flow

Bluetooth-only hosts can render the OTA flow without configuring or starting the authenticated cloud connection or miniapp runtime:

import {MentraLiveOtaFlow} from "@glassly/engine/ota"
;<MentraLiveOtaFlow onFinished={() => setShowOta(false)} onOpenWifiSetup={() => setShowWifiSetup(true)} />

The component starts only the glasses-status and OTA projections. A host that already called engine.start() should pass initializeRuntime={false}. Show the flow after a Mentra Live connects. Hosts must provide onOpenWifiSetup; the flow invokes it only for older glasses that cannot provide the OTA hotspot transport.

Apps that need different pages can render the same controller directly:

import {useMentraLiveOta} from "@glassly/engine/ota"

const ota = useMentraLiveOta({onFinished, onOpenWifiSetup})
// Render ota.state.screen and invoke ota.install(), retryInstall(), finish(), etc.

On up_to_date, use ota.state.completedUpdate to distinguish a completed update session from a standalone check. releaseTransition is optional release metadata and is not a completion marker.

Customize presentation only. Engine must remain responsible for OTA ordering, hotspot staging, restart recovery, retries, and verification. The canonical stock and custom integration guide is Update Mentra Live.

Released Engine packages contain a literal immutable OTA manifest URL and SHA-256 generated by coordinated release CI. Modern-glasses manifest selection is developer override, host-app release pin, then Engine release pin. It does not derive a URL from the Bluetooth SDK version or fall through to a glasses-reported or mutable production URL. An unpinned source build has OTA disabled unless an Expo/React Native host explicitly sets EXPO_PUBLIC_ASG_OTA_VERSION_URL; native source hosts use the Bluetooth SDK's debug setOtaVersionUrl surface. Pre-39 glasses retain their separate legacy path because those clients ignore the URL sent by the phone.

Phone notifications

engine.phoneNotifications owns the notification policy for every host. Capture (Android's listener, or glasses that read the phone's stream) is never gated by it. Presentation has one owner per event, from presentationOwner(hostActive):

  • viewer — the engine's notifications page, on connected Glassly CFW glasses
  • firmware — glasses whose firmware presents notifications (hasNativeNotifications); on Android the host hands the event over with uploadToGlasses(event), on iOS the glasses already have it
  • host — the host's own card and speech, while its surface (Notify) runs
  • none — notifications are off, or nothing is there to show them

Miniapp forwarding is not presentation and stays independent.

Four settings drive firmware and viewer alike: notifications_enabled, notifications_popups, notifications_popup_seconds (0 = the presenter's default) and notifications_hold_during_apps, plus the shared notifications_blocklist and the phone-activity gate notifications_ignore_while_active. PhoneNotificationsSync folds them into one GlassesNotificationConfig and pushes it through the Bluetooth SDK on every change and every connection; the pure mapping lives in services/notifications/notificationPolicy.ts. captureAuthorization() tells the settings UI whether the glasses may read the phone's notifications (not_needed where the phone captures); popupSecondsOptions() lists the durations the connected firmware accepts.

Imports

Inside mobile/modules/engine/src/, use relative paths (./services/..., ../utils/...). The mobile app's @/* alias is not configured here — there is no build-time path rewriter for this module.

Testing

Run the suite with bun run test (from this directory). It executes scripts/test.sh, which runs each test file in its own bun process — do not replace it with a single bun test src.

Why: bun's mock.module patches one process-wide module registry with live ESM bindings, last write wins. Several suites mock the same specifiers ("@glassly/bluetooth-sdk/internal" alone is mocked by audioTestMocks.ts, PhonePhotoCoordinator.test.ts, and others), so in a shared process one file's mock clobbers another's and suites that pass alone fail in the combined run. Per-file processes give every suite an isolated registry.

The same rule applies when writing tests: it is fine to mock.module any specifier your suite needs, but never rely on a mock installed by a different test file — each file must set up everything it imports.