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

@ainativekit/devtools

v1.2.0

Published

Development tools for building and testing ChatGPT Apps using ChatGPT Apps SDK

Downloads

40

Readme

@ainativekit/devtools

Development tools for building and testing ChatGPT Apps using ChatGPT Apps SDK

npm version License: MIT

A powerful, zero-configuration development environment for building ChatGPT apps. Simulate the ChatGPT production environment locally with interactive controls for testing different states, themes, and device types.

✨ Features

  • 🎭 Mock ChatGPT Environment - Simulates window.openai API exactly like production
  • 🎨 Theme Switching - Test light and dark themes instantly
  • 📱 Device Simulation - Desktop (768px), Tablet (576px), Mobile (380px) viewports aligned with OpenAI Apps SDK
  • 🔄 State Testing - Loading, data, and error states with customizable delays
  • 🐛 Debug Overlays - Visual boundary indicators for development
  • 🚀 Zero Configuration - Works out of the box with sensible defaults
  • 📦 Production Ready - Clean separation between dev tools and widget code
  • 🔌 ChatGPT Apps SDK Compatible - Works with any ChatGPT Apps SDK implementation

📋 Requirements

  • React 18.0.0 or higher
  • Node.js 18.0.0 or higher
  • Modern browser with ES2020+ support
    • Chrome 80+, Firefox 75+, Safari 13.1+, Edge 80+

Optional Dependencies

  • @ainativekit/ui 1.0.0 or higher (optional, but recommended for enhanced theming)
    • DevTools includes basic theme support out of the box
    • For full design system integration, install @ainativekit/ui and use AppsSDKUIProvider
    • See usage examples below for both standalone and integrated approaches

📦 Installation

npm install --save-dev @ainativekit/devtools

or with yarn:

yarn add -D @ainativekit/devtools

🚀 Quick Start

Standalone Usage (No Dependencies)

DevTools works out of the box with built-in theme support:

import { DevContainer } from '@ainativekit/devtools';
import App from './App';

// Only use DevContainer in development
const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  import.meta.env.DEV ? (
    <DevContainer>
      <App />
    </DevContainer>
  ) : (
    <App />
  )
);

Enhanced Usage (with @ainativekit/ui)

For full design system integration and advanced theming:

import { DevContainer } from '@ainativekit/devtools';
import { ThemeProvider } from '@ainativekit/ui';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <ThemeProvider>
    {import.meta.env.DEV ? (
      <DevContainer>
        <App />
      </DevContainer>
    ) : (
      <App />
    )}
  </ThemeProvider>
);

With Custom Data Loader

<DevContainer
  dataLoader={async () => {
    // Return your widget-specific mock data
    const response = await fetch('/api/mock-data');
    return response.json();
  }}
  loadingDelay={3000} // Test loading states
  theme="dark" // Start with dark theme
>
  <YourWidget />
</DevContainer>

Multiple Widgets with Widget-Specific Data Loaders (v1.0.0+)

DevContainer automatically detects when you have multiple widgets and shows a selector. Each widget can have its own data loader:

import { DevContainer } from '@ainativekit/devtools';
import { AppsSDKUIProvider } from '@ainativekit/ui';
import CarouselWidget from './widgets/CarouselWidget';
import MapWidget from './widgets/MapWidget';
import ListWidget from './widgets/ListWidget';
import AlbumWidget from './widgets/AlbumWidget';

function App() {
  return (
    <AppsSDKUIProvider linkComponent="a">
      <DevContainer
        widgets={[
          {
            id: 'carousel',
            name: 'Pizza Carousel',
            component: CarouselWidget,
            dataLoader: () => carouselData,
            emptyDataLoader: () => emptyCarouselData
          },
          {
            id: 'map',
            name: 'Pizza Map',
            component: MapWidget,
            dataLoader: () => mapData
          },
          {
            id: 'list',
            name: 'Pizza List',
            component: ListWidget,
            dataLoader: () => listData,
            emptyDataLoader: () => emptyListData
          },
          {
            id: 'album',
            name: 'Photo Albums',
            component: AlbumWidget,
            dataLoader: () => albumData
          }
        ]}
        loadingDelay={1500}
        theme="light"
        autoLoad={true}
        defaultWidget="carousel"
      />
    </AppsSDKUIProvider>
  );
}

Features:

  • Single dev server for all widgets
  • Widget-specific data loaders (v1.0.0+)
  • Automatic widget selector (only shows when multiple widgets)
  • URL support (?widget=map) for deep linking
  • Persistent widget selection
  • Auto-reload data when switching widgets

📖 API Reference

DevContainer Props

Single Widget Mode

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | React.ReactNode | - | Single widget component | | dataLoader | () => Promise<any> \| any | - | Data loader function | | emptyDataLoader | () => Promise<any> \| any | - | Empty state data loader |

Multi-Widget Mode

| Prop | Type | Default | Description | |------|------|---------|-------------| | widgets | Widget[] | - | Array of widget configurations | | dataLoaders | Record<string, Function> | {} | Map of global data loader functions | | emptyDataLoaders | Record<string, Function> | {} | Map of global empty data loader functions | | defaultDataLoader | string | - | Key for default data loader | | defaultWidget | string | - | ID of default widget to show |

Widget Type (v1.0.0+)

| Property | Type | Required | Description | |----------|------|----------|-------------| | id | string | Yes | Unique identifier for the widget | | name | string | Yes | Display name for the widget selector | | component | React.ComponentType | Yes | The widget component | | dataLoader | () => Promise<any> \| any | No | Widget-specific data loader (single, hides dropdown) | | emptyDataLoader | () => Promise<any> \| any | No | Widget-specific empty state data loader | | dataLoaders | Record<string, Function> | No | Multiple named data loaders (v1.2.0+, shows dropdown) | | emptyDataLoaders | Record<string, Function> | No | Matching empty state loaders for dataLoaders | | defaultDataLoader | string | No | Default data loader key for the widget |

Common Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | loadingDelay | number | 2000 | Delay (ms) before loading data | | theme | 'light' \| 'dark' | 'light' | Initial theme | | autoLoad | boolean | true | Auto-load data on mount |

createMockData

createMockData<T>(fullData: T, config?: MockDataConfig<T>): MockData<T>

Creates type-safe mock data with automatic empty state generation.

Config Options:

  • emptyData: Explicit empty state data
  • emptyTransform: Function to derive empty state from full data
  • If neither provided, generates empty object automatically

Mock OpenAI API

The DevContainer automatically mocks the window.openai API with these methods:

window.openai = {
  callTool: async (name, args) => { /* mocked */ },
  sendFollowUpMessage: async ({ prompt }) => { /* mocked */ },
  openExternal: ({ href }) => { /* mocked */ },
  setWidgetState: (state) => { /* mocked */ },
  // Plus all OpenAiGlobals properties
  theme: 'light' | 'dark',
  toolOutput: any,
  toolResponseMetadata: any, // Server _meta field (v1.2.0+)
  locale: string,
  maxHeight: number,
  userAgent: { device: { type }, capabilities: { hover, touch } },
  // DevTools-specific (v1.1.0+)
  widgetState: 'loading' | 'data' | 'empty' | 'error' // Current dev tool state
}

🎮 Interactive Controls

Toolbar Features

  • State Controls: Switch between Loading, Instant Data, Delayed Data, Empty, and Error states
  • Theme Toggle: Switch between light and dark themes
  • Device Simulation: Test desktop, tablet, and mobile viewports
  • Debug Border: Toggle visual boundary indicators
  • Collapsible UI: Hide/show dev tools with a single click

💡 Common Patterns

Testing Loading States

<DevContainer
  loadingDelay={5000} // 5 second delay
  dataLoader={async () => {
    // Simulate slow API
    await new Promise(resolve => setTimeout(resolve, 2000));
    return { data: 'loaded' };
  }}
>
  <App />
</DevContainer>

Testing Empty States

<DevContainer
  emptyDataLoader={() => {
    // Return widget-specific empty state
    return {
      type: 'search-results',
      properties: [],
      searchInfo: { totalResults: 0, location: 'Sydney, NSW' }
    };
  }}
  dataLoader={async () => {
    // Regular data when not in empty state
    return await fetchMockData();
  }}
>
  <SearchWidget />
</DevContainer>

Testing Error States

<DevContainer
  dataLoader={() => {
    // Return error data
    return { error: 'Something went wrong' };
  }}
>
  <App />
</DevContainer>

Custom Mock Data

// mockData.ts
export const mockSearchResults = {
  type: 'search-results',
  items: [
    { id: 1, title: 'Result 1' },
    { id: 2, title: 'Result 2' }
  ]
};

// App.tsx
import { mockSearchResults } from './mockData';

<DevContainer
  dataLoader={() => mockSearchResults}
>
  <SearchWidget />
</DevContainer>

🏗 Architecture

The DevContainer follows these principles:

  1. Zero Widget Contamination - Your widget code contains no dev-specific logic
  2. Production Parity - Uses the same APIs as production ChatGPT Apps
  3. External Debugging - All debug overlays are applied from outside the widget
  4. Type Safety - Full TypeScript support with AINativeKit types
  5. Clean Separation - Dev tools are never included in production builds

🔧 Development

Building the Package

npm run build

Development Mode

npm run dev

Type Checking

npm run type-check

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT © Jake Lin

🔗 Links

🙏 Acknowledgments

Built with ❤️ for the ChatGPT app developer community. This tool helps developers build and test ChatGPT Apps using the ChatGPT Apps SDK, making development faster and more enjoyable.