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

xcpack

v0.1.0

Published

Native project wiring for Catalyst and AI-powered Expo apps

Readme

xcpack

Plugin

Add xcpack to your Expo config:

export default {
  expo: {
    plugins: [
      [
        "xcpack",
        {
          catalyst: true,
          ai: {
            image: true,
          },
        },
      ],
    ],
  },
};

When you run xcpack prebuild or xcpack run:catalyst, xcpack also injects this script into the app's package.json:

{
  "scripts": {
    "catalyst": "xcpack run:catalyst"
  }
}

xcpack run:catalyst reuses the existing Catalyst app build when native/prebuild-relevant inputs have not changed. It rebuilds automatically when files under ios/ or Expo config files like app.json, app.config.*, or package.json change. Pass --clean or -c to always force a fresh rebuild.

Options

Current plugin shape:

xcpack({
  catalyst: true | {
    toolbarStyle?: "automatic" | "unified" | "unifiedCompact" | "preference" | "expanded"
    titleVisibility?: "visible" | "hidden"
    fs?: true | {}
  },
  ai?: {
    image?: true | {
      appGroupIdentifier?: string
    }
  }
})

Examples:

xcpack({
  catalyst: true,
})
xcpack({
  catalyst: {
    toolbarStyle: "unified",
    titleVisibility: "hidden",
  },
})
xcpack({
  catalyst: {
    fs: true,
  },
})
xcpack({
  ai: {
    image: true,
  },
})

API

Current public AI entrypoints:

import * as ai from "xcpack/ai";
import * as image from "xcpack/ai/image";
import * as runtime from "xcpack/ai/runtime";

Mac Catalyst toolbar entrypoints:

import { Toolbar, Window, navigation, useNavigation, useToolbar } from "xcpack";

Toolbar items can be configured through the direct API, hooks, or the declarative component API.

Toolbar.Menu uses an icon-only toolbar control. Provide an explicit icon; the label is still required for semantics and menu presentation.

Direct API example:

await navigation.setToolbarItems([
  {
    id: "filters",
    kind: "menu",
    icon: "line.3.horizontal.decrease.circle",
    label: "All Types",
    items: [
      { id: "all", label: "All Types" },
      { id: "feature", label: "Feature" },
      { id: "bug", label: "Bug" },
    ],
  },
  {
    id: "status",
    kind: "group",
    selectionMode: "selectOne",
    selectedIndex: 0,
    items: [
      { id: "open", label: "Open" },
      { id: "closed", label: "Closed" },
    ],
  },
]);

Component API example:

<Window title="Issues" toolbarStyle="unified" titleVisibility="visible" />

<Toolbar>
  <Toolbar.Menu
    id="type-filter"
    icon="line.3.horizontal.decrease.circle"
    label="All Types"
    items={[
      { id: "all", label: "All Types", onPress: () => setType("all") },
      { id: "feature", label: "Feature", onPress: () => setType("feature") },
      { id: "bug", label: "Bug", onPress: () => setType("bug") },
    ]}
  />
  <Toolbar.Group
    id="status-filter"
    selectionMode="selectOne"
    selectedIndex={0}
    items={[
      { id: "open", label: "Open" },
      { id: "closed", label: "Closed" },
    ]}
    onChange={(event) => setStatus(event.itemId ?? "open")}
  />
</Toolbar>

Supported Catalyst toolbar item kinds:

  • button
  • space
  • flexibleSpace
  • menu
  • group

Image generation uses the ai.image surface:

import { load, adapter, assets, registry, flux, zImage } from "xcpack/ai/image";

Load image runtimes through model-specific helpers:

const zImageRuntime = await load(zImage.load.turbo());

const fluxRuntime = await load(
  flux.load.klein9B(
    flux.models.klein9B.default,
    flux.textEncoders.klein9B.default,
  ),
);

await zImageRuntime.generate(
  zImage.generate.turbo(zImage.models.turbo.default, {
    prompt: "An editorial beach portrait at sunset",
    enhancePrompt: true,
  }),
);

await fluxRuntime.generate(
  flux.generate.klein9B(flux.models.klein9B.default, {
    prompt: "Restyle this portrait as a cinematic studio photo",
    sourceImage: "file:///tmp/source.png",
    strength: 0.65,
  }),
);

await fluxRuntime.generate(
  flux.generate.klein9B(flux.models.klein9B.default, {
    prompt: "Design language inspired by these references",
    referenceImages: ["/tmp/ref-a.png", "/tmp/ref-b.png"],
  }),
);

for await (const event of zImageRuntime.stream(
  zImage.stream.turbo(zImage.models.turbo.default, {
    prompt: "An editorial beach portrait at sunset",
    guidanceScale: 0,
    enhancePrompt: true,
  }),
)) {
  // handle progress / previews / result
}

On devices with limited GPU memory (~4 GB), pass memoryProfile: "lowVRAM4GB" to reduce peak VRAM usage:

await zImageRuntime.generate(
  zImage.generate.turbo(zImage.models.turbo.default, {
    prompt: "An editorial beach portrait at sunset",
    memoryProfile: "lowVRAM4GB",
  }),
);

When memoryProfile: "lowVRAM4GB" is set:

  • The model is automatically switched to zimage-turbo-8bit unless an explicit model was provided.
  • Resolution defaults to 768×768 instead of 1024×1024.
  • One-shot residency is forced — the transformer and text encoder are evicted from memory immediately after use.
  • Prompt enhancement token budget is capped at 128 (down from 512) if enhancePrompt is enabled.
  • Generation preset uses 9 steps, guidance 0.0, and a max sequence length of 256.

Notes:

  • Flux models require both a model id and a text encoder id.
  • Z-Image models do not use external text encoders.
  • flux.models, flux.textEncoders, and zImage.models expose slug-safe constants.
  • Model and text-encoder families expose .default plus .all literal tuples.
  • Flux img2img accepts sourceImage?: string, referenceImages?: [string] | [string, string] | [string, string, string], and strength?: number.
  • Flux keeps sourceImage separate from referenceImages; the source image is not counted against the 3-reference limit.
  • Flux strength must be between 0 and 1.
  • Flux image inputs can be plain local file paths or file:// URIs.
  • Z-Image-Turbo behaves best close to its intended no-CFG regime. In practice, prefer guidanceScale: 0 and enhancePrompt: true before increasing CFG.
  • Do not assume Flux and Z-Image should share the same guidance defaults. Flux often benefits from positive guidance values; Z-Image-Turbo can become noticeably softer when forced into Flux-style CFG settings.

The ai.image adapter still accepts options.model, and Flux adapter calls must also provide options.textEncoderId:

await adapter.generate({
  prompt: "A silver robot in fog",
  options: {
    model: flux.models.klein9B.default,
    textEncoderId: flux.textEncoders.klein9B.default,
  },
});

Asset downloads are handle-based and cancellable:

const download = await assets.startDownload(zImage.load.turbo());

const fluxDownload = await assets.startDownload(
  flux.load.klein4B(
    flux.models.klein4B.default,
    flux.textEncoders.klein4B.default,
  ),
);

await assets.cancelDownload(download.handle, {
  modelId: zImage.models.turbo.default,
  deleteSharedAssets: false,
});

Notes:

  • Only one active download is allowed per modelId.
  • Calling assets.startDownload(modelId) again while that model is already downloading returns the existing handle.
  • Flux callers can opt into a specific encoder by passing the existing helper output, for example assets.startDownload(flux.load.klein4B(...)); if omitted, xcpack keeps its own default encoder choice.
  • assets.status() includes activeDownloads, so the current process can recover active handles if UI state is lost.
  • Download handles are process-local and should not be treated as persistent across app restarts.
  • deleteSharedAssets only affects shared assets created by the cancelled download. Pre-existing assets are never deleted.
  • cancelDownload(...) is model-aware: if the provided handle is stale but the model still has one active download, xcpack cancels that active download.
  • Cancelling a stale or already-finished download is a no-op and returns the current asset status.

Multimodal generation uses the ai.multimodal surface:

import { multimodal } from "xcpack/ai/multimodal";

const runtime = await multimodal.load.qwen35();

const result = await runtime.generate(
  multimodal.generate.qwen35("qwen35-0.8b-abliterated-mxfp8", {
    prompt: "Describe this image in detail",
    images: ["/tmp/photo.png"],
    maxOutputTokens: 256,
  }),
);

for await (const event of runtime.stream(
  multimodal.stream.qwen35("qwen35-0.8b-abliterated-mxfp8", {
    prompt: "Compare these images",
    images: ["/tmp/a.png", "/tmp/b.png"],
  }),
)) {
  // handle token chunks / completion info
}

Notes:

  • multimodal.load.qwen35() currently defaults to the qwen35-0.8b-abliterated-mxfp8 variant slug.
  • multimodal.load.qwen3VL() currently defaults to the qwen3vl-2b-instruct-abliterated-4bit variant slug.
  • multimodal.models.qwen35 and multimodal.models.qwen3VL map variant slugs to repo ids.
  • multimodal.assets and multimodal.registry expose the same download/inspection surface shape as ai.image, backed by a simpler shared core-model asset layer.
  • Plain text generation is just the no-image case of the same API.
  • Adapter usage still takes options.model:
import { generate } from "xcpack/ai";
import { adapter, multimodal } from "xcpack/ai/multimodal";

await generate({
  adapter,
  input: {
    ...multimodal.generate.qwen35("qwen35-0.8b-abliterated-mxfp8", {
      prompt: "Summarize this screenshot",
      images: ["/tmp/screenshot.png"],
    }),
    options: { model: multimodal.models.qwen35.default },
  },
});

Text generation uses the ai.text surface:

import { text } from "xcpack/ai/text";

const runtime = await text.load.qwen3();
const gemma = await text.load.gemma3();

const result = await runtime.generate(
  text.generate.qwen3("qwen3-0.6b-abliterated-8bit", {
    system: "Be concise.",
    prompt: "Write a haiku about fog.",
    maxOutputTokens: 128,
  }),
);

for await (const event of runtime.stream(
  text.stream.qwen3("qwen3-0.6b-abliterated-8bit", {
    prompt: "Write a tagline for a camera app.",
    maxOutputTokens: 32,
  }),
)) {
  // handle token chunks / completion info
}

const gemmaResult = await gemma.generate(
  text.generate.gemma3("gemma3-270m-it-abliterated-8bit", {
    prompt: "Write a three-word product tagline.",
    maxOutputTokens: 24,
  }),
);

Notes:

  • text.load.qwen3() currently defaults to the qwen3-0.6b-abliterated-8bit variant slug.
  • text.load.gemma3() currently defaults to the gemma3-270m-it-abliterated-8bit variant slug.
  • text.models.qwen3 and text.models.gemma3 map variant slugs to the underlying repo ids.
  • text.assets and text.registry expose the same download/inspection surface shape as ai.image.
  • Adapter usage takes options.model in the same way as ai.multimodal.

The generic model runtime uses the neutral runtime surface:

import { runtime, Model, ModelContainer, PreparedInput, Tokenizer } from "xcpack";