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

@action-x/ad-sdk

v0.1.13

Published

Zero-dependency ad SDK for ActionX

Readme

@action-x/ad-sdk Usage Guide

Zero-dependency ad SDK for AI conversations, supporting Vanilla JS, Vue, React, Next.js, and all frameworks.


Quick Start

Installation

npm install @action-x/ad-sdk

Minimal Example

import { AdManager } from '@action-x/ad-sdk';
import '@action-x/ad-sdk/style.css';

const manager = new AdManager({
  apiBaseUrl: 'https://network.actionx.top/api/v1',
  apiKey: 'ak_your_api_key',
});

// Request ads after AI conversation completes
await manager.requestAds({
  query: 'Recommend a Bluetooth headset',
  response: 'AI response content...',
});

// Render ad
manager.render(document.getElementById('ad-container'));

Core Concepts

1. AdManager Configuration

const manager = new AdManager({
  apiBaseUrl: string;        // Required: API endpoint
  apiKey: string;            // Required: API key
  cardOption?: {             // Optional: Card configuration
    variant?: 'horizontal' | 'vertical' | 'compact';  // Layout style
    count?: number;          // Number of ads to request, default 1
  };
  enabled?: boolean;         // Enable ads, default true
  debug?: boolean;           // Debug mode, default false
});

2. Request Ads

// Simplified call (recommended)
await manager.requestAds({
  query: string;             // Required: User query
  response: string;          // Required: AI response content
});

// Full call
await manager.requestAds({
  conversationContext: {
    query: string;
    response: string;
  },
  userContext?: {
    sessionId?: string;
    userId?: string;
  }
});

3. Render Ads

// Simplified call (renders action_card slot by default)
manager.render(
  container: HTMLElement,
  options?: {
    adIndex?: number;            // Ad index, default 0
    variant?: string;            // Layout variant
    onClick?: (ad) => void;      // Click callback
    onImpression?: (ad) => void; // Impression callback
  }
);

// Specify slot
manager.render(
  slotId: string,
  container: HTMLElement,
  options?: { ... }
);

4. Event Listeners

manager.on('adsUpdated', (slots) => {
  console.log('Ad data updated');
});

manager.on('adsLoading', () => {
  console.log('Requesting ads');
});

manager.on('adsError', (error) => {
  console.warn('Ad request failed', error);
});

manager.on('adClicked', (adId, slotId) => {
  console.log('Ad clicked', adId);
});

manager.on('adImpression', (adId, slotId) => {
  console.log('Ad impression', adId);
});

Framework Integration Examples

Vanilla JavaScript

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/@action-x/ad-sdk@latest/dist/style.css" />
</head>
<body>
  <div id="ad-container"></div>

  <script src="https://unpkg.com/@action-x/ad-sdk@latest/dist/index.umd.js"></script>
  <script>
    const { AdManager } = ActionXAdSDK;

    const manager = new AdManager({
      apiBaseUrl: 'https://network.actionx.top/api/v1',
      apiKey: 'ak_your_api_key',
      debug: false,
    });

    manager.on('adsUpdated', () => {
      manager.render(document.getElementById('ad-container'));
    });

    // Call after conversation completes
    function onChatComplete(query, response) {
      manager.requestAds({ query, response });
    }
  </script>
</body>
</html>

Vue 3

Install and import styles:

// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import '@action-x/ad-sdk/style.css';

createApp(App).mount('#app');

Create Composable:

// composables/useAdManager.ts
import { ref, shallowRef, onUnmounted } from 'vue';
import { AdManager } from '@action-x/ad-sdk';

export function useAdManager() {
  const manager = shallowRef(
    new AdManager({
      apiBaseUrl: 'https://network.actionx.top/api/v1',
      apiKey: 'ak_your_api_key',
      debug: false,
    })
  );

  const isLoading = ref(false);
  const error = ref<Error | null>(null);

  manager.value.on('adsLoading', () => {
    isLoading.value = true;
    error.value = null;
  });

  manager.value.on('adsUpdated', () => {
    isLoading.value = false;
  });

  manager.value.on('adsError', (e) => {
    isLoading.value = false;
    error.value = e;
  });

  onUnmounted(() => manager.value.destroy());

  async function requestAds(query: string, response: string) {
    await manager.value.requestAds({ query, response });
  }

  return { manager, isLoading, error, requestAds };
}

Use in component:

<template>
  <div class="chat-response">
    <div class="response-text">{{ responseText }}</div>
    <div ref="adRef" class="ad-mount"></div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, nextTick } from 'vue';
import { useAdManager } from '@/composables/useAdManager';

const props = defineProps<{
  query: string;
  responseText: string;
  isStreaming: boolean;
}>();

const adRef = ref<HTMLElement>();
const { manager, requestAds } = useAdManager();

watch(
  () => props.isStreaming,
  async (streaming) => {
    if (!streaming && props.responseText) {
      await requestAds(props.query, props.responseText);
      await nextTick();

      if (adRef.value) {
        manager.value.render(adRef.value);
      }
    }
  }
);
</script>

React / Next.js

Import styles:

// Next.js App Router: app/layout.tsx
import '@action-x/ad-sdk/style.css';

export default function RootLayout({ children }) {
  return <html><body>{children}</body></html>;
}

// Vite + React: src/main.tsx
import '@action-x/ad-sdk/style.css';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(<App />);

Create Hook:

// hooks/useAdManager.ts
import { useEffect, useRef, useState, useCallback } from 'react';
import { AdManager } from '@action-x/ad-sdk';

export function useAdManager() {
  const managerRef = useRef<AdManager | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    const manager = new AdManager({
      apiBaseUrl: 'https://network.actionx.top/api/v1',
      apiKey: 'ak_your_api_key',
      debug: false,
    });

    manager.on('adsLoading', () => setIsLoading(true));
    manager.on('adsUpdated', () => {
      setIsLoading(false);
      setIsReady(true);
    });
    manager.on('adsError', (e) => {
      setIsLoading(false);
      setError(e);
    });

    managerRef.current = manager;
    return () => manager.destroy();
  }, []);

  const requestAds = useCallback(async (query: string, response: string) => {
    if (!managerRef.current) return;
    await managerRef.current.requestAds({ query, response });
  }, []);

  return { manager: managerRef.current, isLoading, isReady, error, requestAds };
}

Create Ad Component:

// components/AdCard.tsx
'use client';

import { useEffect, useRef } from 'react';
import type { AdManager } from '@action-x/ad-sdk';

interface AdCardProps {
  manager: AdManager | null;
  isReady: boolean;
  className?: string;
}

export function AdCard({ manager, isReady, className }: AdCardProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!isReady || !manager || !containerRef.current) return;
    manager.render(containerRef.current);
  }, [isReady, manager]);

  return <div ref={containerRef} className={className} />;
}

Use in page:

// app/chat/page.tsx
'use client';

import { useState } from 'react';
import { useAdManager } from '@/hooks/useAdManager';
import { AdCard } from '@/components/AdCard';

export default function ChatPage() {
  const [query, setQuery] = useState('');
  const [response, setResponse] = useState('');
  const [isStreaming, setIsStreaming] = useState(false);

  const { manager, isReady, requestAds } = useAdManager();

  const handleSend = async () => {
    setIsStreaming(true);
    const aiResponse = await fetchAIResponse(query);
    setResponse(aiResponse);
    setIsStreaming(false);

    await requestAds(query, aiResponse);
  };

  return (
    <div>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <button onClick={handleSend}>Send</button>

      {response && (
        <div>
          <p>{response}</p>
          <AdCard manager={manager} isReady={isReady} className="mt-4" />
        </div>
      )}
    </div>
  );
}

Custom Styles

The SDK uses the ax-ad-* namespace. Override styles via CSS:

/* Custom CTA button */
.ax-ad-cta {
  background-color: #7c3aed !important;
  border-radius: 20px;
}

.ax-ad-cta:hover {
  background-color: #6d28d9 !important;
}

/* Custom card style */
.ax-ad-card {
  border: none;
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
  .ax-ad-card {
    background: #1f2937;
    border-color: #374151;
    color: #f9fafb;
  }
  .ax-ad-title { color: #f9fafb; }
  .ax-ad-body { color: #9ca3af; }
}

/* Hide on mobile */
@media (max-width: 640px) {
  .ax-ad-card { display: none; }
}

Debugging Tips

Enable Debug Logging

const manager = new AdManager({
  apiBaseUrl: '...',
  apiKey: '...',
  debug: true,  // Outputs detailed logs to console
});

Check Ad Response

manager.on('adsUpdated', (slots) => {
  console.log('Ad slots:', Object.keys(slots));

  Object.entries(slots).forEach(([slotId, slot]) => {
    console.log(`[${slotId}] Status:`, slot.status);
    console.log(`[${slotId}] Ad count:`, slot.ads.length);

    if (slot.status === 'no_fill') {
      console.log('  → No fill (insufficient inventory or targeting mismatch)');
    }
    if (slot.status === 'error') {
      console.error('  → Slot error:', slot.error);
    }
  });
});

Common Issues

| Issue | Possible Cause | Solution | |-------|---------------|----------| | Empty ad container | render() called before requestAds() completes | Call render() inside adsUpdated event | | Styles not applied | Forgot to import CSS | Ensure import '@action-x/ad-sdk/style.css' | | Console 403 error | Invalid or missing apiKey | Check apiKey configuration | | TypeScript type errors | Type definitions not generated | Run npm run build | | Ad status 'no_fill' | No matching ads | Expected behavior, check manager.hasAds() before rendering | | Clicks unresponsive | Container obscured | Check CSS z-index and pointer-events |


API Reference

AdManager

Constructor

new AdManager(config: AdManagerConfig)

Methods

// Request ads
requestAds(context: { query: string; response: string }): Promise<AdResponseBatch>
requestAds(context: { conversationContext, userContext? }): Promise<AdResponseBatch>

// Render ads
render(container: HTMLElement, options?: RenderOptions): HTMLElement | null
render(slotId: string, container: HTMLElement, options?: RenderOptions): HTMLElement | null

// Check if ads available
hasAds(slotId: string): boolean

// Get ad data
getAds(slotId: string): any[]
getSlots(slotId?: string): Record<string, AdSlotResponse> | AdSlotResponse | undefined

// Destroy instance
destroy(): void

// Event listeners
on(event: string, handler: Function): void
off(event: string, handler: Function): void

Events

  • adsLoading - Started requesting ads
  • adsUpdated - Ad data updated
  • adsError - Request failed
  • adClicked - Ad clicked
  • adImpression - Ad impression

Version Info

  • Current version: 0.1.12
  • License: MIT
  • Repository: GitHub

Support

For questions, contact ActionX support or submit an Issue.