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

@bty/feed_app-runtime-sdk

v0.1.5

Published

Runtime SDK for feed-app template: auth / AI capabilities, multi-environment bridge (native App / iframe / web).

Readme

@bty/feed_app-runtime-sdk

Runtime SDK for Feed-App pages.

It provides a small set of browser-safe APIs for reading host user context, calling the Feed-App AI gateway, and reaching native device capabilities. The package has no root entry; import from the capability sub-entries directly (/user, /ai, /react, /device).

Install

pnpm add @bty/feed_app-runtime-sdk

Failure conventions

Two failure styles, split by sub-entry — know which you're calling:

| Sub-entry | On failure | Why | |---|---|---| | /ai | throws a typed AiError subclass | callers must distinguish 401 / 402 / 429 / 4xx / 5xx to react correctly | | /user | returns an empty value (never throws) | no host user context is a normal runtime state | | /device | returns DeviceResult<T> (never throws) | denied / cancelled / unsupported native capability is normal control flow — branch on result.ok |

Empty value = "" for the one string API (getAuthTokenAsync), null for getUserInfoAsync. Device APIs use one envelope instead: { ok: true, source, value } or { ok: false, source, reason, errorCode?, errorMessage? }.

Rule of thumb: wrap /ai calls in try/catch; branch on the return value for /user; branch on result.ok for /device.

User

import {
  getAuthTokenAsync,
  getUserInfoAsync,
} from '@bty/feed_app-runtime-sdk/user'

const token = await getAuthTokenAsync()
const user = await getUserInfoAsync()

getAuthTokenAsync() resolves to an auth token when the current host environment can provide one. getUserInfoAsync() resolves to user information or null.

Both APIs are safe to import during prerender or SSR. In a non-browser environment they resolve to empty values instead of touching window.

AI

import { configureRuntime, openai } from '@bty/feed_app-runtime-sdk/ai'

configureRuntime({
  apiBaseUrl: 'https://example.com',
})

const stream = await openai.chat.completions.create({
  model: 'gpt-5.4',
  messages: [{ role: 'user', content: 'Hello' }],
  stream: true,
})

for await (const chunk of stream) {
  const text = chunk.choices[0]?.delta?.content
  if (text) {
    console.log(text)
  }
}

The AI entry supports:

  • OpenAI-compatible chat completions
  • Anthropic-compatible messages
  • Image generation
  • Text-to-speech
  • Video generation
  • AbortSignal cancellation
  • Structured AI errors

When an AI HTTP request receives a non-2xx response, the SDK emits a sanitized host notification before throwing the typed AiError. In the product page:

window.addEventListener('feed-app-runtime-sdk:ai-error', (event) => {
  // event.detail: { status, code, message, path, method, ... }
})

When the product runs inside a host iframe, the same payload is also sent to window.parent.postMessage({ type: 'feed-app-runtime-sdk:ai-error', detail }, '*'). The payload intentionally omits raw provider bodies and headers; branch on detail.status / detail.code for host-level UI.

Media payloads are pass-through. images.generate / audio.speech.create / video.generations.create forward the body verbatim to the upstream provider — there is no client-side reshaping, and each model has its own shape. The param types only require model; build the rest of the body from the model's parameters.shape in the runtime model catalog, not from generic OpenAI SDK fields (quality: 'standard', style: 'vivid', … may be ignored or rejected by the target model).

import {
  AuthRequiredError,
  RateLimitError,
  openai,
} from '@bty/feed_app-runtime-sdk/ai'

try {
  await openai.chat.completions.create({
    model: 'gpt-5.4',
    messages: [{ role: 'user', content: 'Hello' }],
  })
} catch (error) {
  if (error instanceof AuthRequiredError) {
    // Ask the user to sign in.
  } else if (error instanceof RateLimitError) {
    // Show a rate limit message.
  }
}

React

import { useAuthToken, useUserInfo } from '@bty/feed_app-runtime-sdk/react'

export function App() {
  const { token, loading: tokenLoading } = useAuthToken()
  const { user } = useUserInfo()

  return (
    <main>
      <p>{tokenLoading ? 'Loading...' : (user?.username ?? user?.nickname)}</p>
    </main>
  )
}

For chat / image / TTS / video, call openai / anthropic from /ai directly and wire your own loading / cancel / error state. Product-specific chat UX varies enough that a generic hook tends to leak abstractions.

React is an optional peer dependency. Projects that do not import the /react entry do not need to install React.

Device

Six host-bridged capabilities, each with a layered fallback: native App bridge → iframe parent relay → browser Web API → SDK failure. Import the device namespace, or the individual capabilities for tree-shaking.

import {
  haptics,
  geolocation,
  sensors,
  camera,
  files,
  microphone,
} from '@bty/feed_app-runtime-sdk/device'

await haptics.impact('medium')
const pos = await geolocation.getCurrentPosition() // DeviceResult<PositionSample>
const motion = await sensors.watchMotion((s) => console.log(s.accelerationWithGravity))
const photo = await camera.capturePhoto({ camera: 'back' }) // DeviceResult<CapturedPhoto>
const picked = await files.pickFiles({ accept: ['image/*'], multiple: true })
const saved = await files.saveFile(blob, 'export.json') // DeviceResult<{ path?: string }>
const clip = await microphone.recordAudio({ maxDurationMs: 5000 }) // DeviceResult<AudioRecording>

if (pos.ok) console.log(pos.value.latitude, pos.value.longitude)
if (motion.ok) motion.value() // dispose the motion subscription
if (!saved.ok) console.warn(saved.reason, saved.errorCode, saved.errorMessage)

Every device call returns DeviceResult<T> and never throws for normal runtime states. On success, read result.value; on failure, branch on result.reason ("cancelled", "permission_denied", "not_supported", "timeout", "invalid_response", or "failed"). source is "native", "iframe", "web", or "sdk" and is useful for diagnostics.

pickFiles succeeds with { files: { file, path? }[] } (path present only on the native bridge). watchPosition, watchMotion, and watchOrientation succeed with a cleanup function in value. haptics.* and camera.stopStream succeed with value: undefined.

For native/App saves, saveFile sends the Blob bytes through the bridge as base64 with { filename, mime, size, base64 }. The host App must return an ok: true envelope with result.saved === true; older metadata-only success responses are ignored. USER_CANCELLED maps to reason: "cancelled"; native failure responses keep errorCode / errorMessage on the returned FileSaveResult. Top-level HostApp WebViews do not trust browser download fallbacks after a native save failure, because WebView downloads can report success without writing a file. Diagnostics are part of the normal saveFile Result envelope.

Capability detection (synchronous, cheap — gate UI ahead of a call):

| Capability | Probe | |---|---| | Haptics | haptics.isSupported() | | Geolocation | geolocation.isSupported() | | Camera | camera.isSupported() | | Sensors | sensors.isMotionSupported() / sensors.isOrientationSupported() | | Files | files.isPickerSupported() (the modern picker; plain pick always works) | | Microphone | microphone.isSupported() |

requestMotionPermission(), capturePhoto(), saveFile(), and pickFiles() must be called from inside a user-gesture handler (tap / click) — browsers silently deny these outside a gesture.

When a feed-app runs inside the host SPA iframe, one-shot device calls reuse the same native command frame through window.parent.postMessage. The parent shell forwards supported commands to the native App and returns the App envelope to the child frame. If the parent cannot relay, SDK calls fall through to their web fallback. camera.openStream() and microphone.startRecording() remain web-only streaming APIs.

Published Files

The npm package publishes only built output and this README:

  • dist/**/*.js
  • dist/**/*.d.ts
  • README.md
  • package.json

Source files and sourcemaps are not included in the npm tarball.