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

@singcl/ad-execute-manager

v1.16.1

Published

A powerful and flexible ad execution management library for handling reward-based ads, interstitial ads, and other advertising formats in JavaScript applications.

Downloads

5,817

Readme

AD Execute Manager

A powerful and flexible ad execution management library for handling reward-based ads, interstitial ads, and other advertising formats in JavaScript applications.

Table of Contents

Features

  • Unified Ad Execution Interface: Single interface for managing different types of ads
  • Task Queue Management: Handles multiple ad execution tasks in a queue
  • Flexible Control Flow: Manual control over ad execution flow with next function
  • Comprehensive Error Handling: Complete error handling and logging
  • State Persistence: Built-in storage for ad state management
  • Analytics Integration: Built-in analytics support
  • Middleware Pattern: Uses middleware pattern for ad execution flow
  • Cancellation Support: Ability to clear and cancel pending tasks

Installation

npm install @singcl/ad-execute-manager

Quick Start

Basic Usage

import { AdExecuteManager, RewardAdFather } from '@singcl/ad-execute-manager';

// Get the singleton instance
const adManager = AdExecuteManager.getInstance();

// Create an ad instance (extend RewardAdFather)
class MyRewardAd extends RewardAdFather {
  async ad(ctx, next) {
    // Your ad logic here
    console.log('Executing reward ad');
    
    // Call next when ready to proceed
    await next();
    
    return { success: true, message: 'Ad executed successfully' };
  }
}

// Create ad instance
const myAd = new MyRewardAd();

// Add task to execution queue
const result = await adManager.addTask(myAd, {
  options: { /* ad options */ },
  collection: { /* callback collection */ }
});

Advanced Usage

import { AdExecuteManager, RewardAdSceneTriggerManager } from '@singcl/ad-execute-manager';

// Initialize with logging enabled
const adManager = AdExecuteManager.getInstance({ log: true });

// Check if manager is running
if (adManager.isRunning()) {
  console.log('Ad manager is currently executing tasks');
}

// Get current task ID
const currentTaskId = adManager.getCurrentTaskId();

// Get total number of pending tasks
const taskCount = adManager.getTaskCount();

// Wait for all tasks to complete
await adManager.whenAllTasksComplete();

// Clear all pending tasks
adManager.clearTasks();

Core Concepts

Ad Execution Flow

AD Execute Manager uses a middleware pattern to handle ad execution flow. Each ad task goes through the following steps:

  1. Initialization: Create ad instance and configure parameters
  2. Queue Management: Ad task enters the execution queue
  3. Ad Execution: Call the ad method of the ad instance
  4. Completion Callback: Call callback functions after ad execution completes
  5. Task Cleanup: Clean up task resources and execute the next task

Ad Types

  • Reward Ads: Inherit from RewardAdFather, used for scenarios where users need to complete viewing to receive rewards
  • Interstitial Ads: Inherit from InterstitialAdFather, used for scenarios where ads are inserted into the application flow

Scene Management

Manage ad trigger scenes through RewardAdSceneTriggerManager, which allows executing different ad logic based on different scenes.

Frequency Control

Use CountRecorder to implement ad display frequency control, which can set daily display limits.

Example Code

1. Launch Reward Ad

import { CountRecorder, PubSub } from '@singcl/ad-execute-manager';
import CommonSettings from './CommonSettings';
import { SCENT_TEXT_OBJ } from './const';
import RewardAdNovelExb from './RewardAdNovelExb';

class RewardAdLaunch extends RewardAdNovelExb {
  _scene = SCENT_TEXT_OBJ.launch_ad; // Ad execution scene
  
  constructor(args) {
    super(args);
    this.launchSettings = RewardAdLaunchSettings.new();
  }

  adCloseLister(args) {
    this._clearAdTimeout();
    this._adCloseGlobalRecorder(args);
    this._adCloseSuccessAnalytics({ scene: this._scene, ad_is_completed: args.isEnded ? 1 : 0, ad_count: args.count });

    this.launchSettings.updateToday(); // Update today
    this.launchSettings.updateFrequency();
    const baseArgs = { ...args, scene: this._scene };
    const _end = (ctx) => {
      this.adDestroy();
      this._resolve?.(Object.assign({}, args, { scene: this._scene }, ctx));
      this._resolve = null;
      this._next?.(); // Execute the next task's callback function to continue the flow
      this._next = null;
    };

    const _circle = () => {
      if (this.launchSettings.remainFrequency() > 0) {
        this._adInner({ options: { scene: this._scene }, collection: { resolve: this._resolve } }, this._next);
      } else {
        _end({ frequency: this.launchSettings.getFrequency() });
      }
    };
    if (args.isEnded) {
      this.launchSettings.updateFinished();
      if (this.launchSettings.remainFrequency() == 0) {
        this.launchSettings.updateLastFinished(true);
      }
      this._outerFinishedCallback(baseArgs);
      this._onFinish?.(baseArgs);
    } else {
      this._outerHalfwayCallback(baseArgs);
      this._onHalfway?.(baseArgs);
    }
    this._outerCloseCallback();
    _circle();
  }

  static build(args) {
    if (!RewardAdLaunch.instance) {
      RewardAdLaunch.instance = new RewardAdLaunch(args);
    }
    return RewardAdLaunch.instance;
  }

  static getInstance() {
    if (!RewardAdLaunch.instance) {
      throw new Error('RewardAdLaunch instance is not init');
    }
    return RewardAdLaunch.instance;
  }

  static new(args) {
    return new RewardAdLaunch(args);
  }

  static ad = new Promise((resolve) => {
    RewardAdLaunch.adResolve = resolve;
  });

  static satisfy(options, callback) {
    const con = RewardAdLaunchSettings.new().condition();
    if (typeof callback !== 'function') {
      return Promise.resolve();
    }
    return callback(con ? { options } : null);
  }

  static eventEmitter = new PubSub();
}

export default RewardAdLaunch;

class RewardAdLaunchSettings {
  _fixedFrequency = null; // Fixed display frequency, if null, use configuration
  frequency = {
    total: 0,
    current: 0,
    finished: 0,
    lastFinished: false, // Whether the last one is completed
  };
  constructor() {
    this.commonSettings = CommonSettings.new();
    this.countRecorder = CountRecorder.new({
      local_sign: 'launch_count',
      total: this._adTimes(),
      userId: this.commonSettings.getUserId(),
    });
    this.frequency.total = this.timesPerFrequency();
  }

  _adTimes() {
    // Get launch ad configuration from system configuration
    const { inter_site_pop_ups, inter_site_pop_ups_num } = this.commonSettings.getSysConfig();
    // Return configuration value if it exists, otherwise return 0
    return Number(inter_site_pop_ups) > 0 && inter_site_pop_ups_num >= 1 ? Number(inter_site_pop_ups_num) : 0;
  }

  updateToday() {
    this.countRecorder.updateToday();
  }

  updateFrequency() {
    this.frequency.current += 1;
  }

  updateFinished() {
    this.frequency.finished += 1;
  }

  updateLastFinished(v = true) {
    this.frequency.lastFinished = v;
  }

  remainFrequency() {
    return this.frequency.total - this.frequency.current;
  }

  getFrequency() {
    return Object.assign({}, this.frequency);
  }

  timesPerFrequency() {
    const { inter_site_pop_ups } = this.commonSettings.getSysConfig();
    const ups = this._fixedFrequency ?? Number(inter_site_pop_ups);
    const count = Math.min(Math.max(0, Number(ups)), this._remain());
    return Math.max(0, count);
  }

  _remain() {
    return this.countRecorder.remain();
  }

  condition() {
    const remain = this._remain();
    return Number(remain) > 0 && this.timesPerFrequency() > 0;
  }

  static new(args) {
    return new RewardAdLaunchSettings(args);
  }
}

2. Interstitial Ad

import AdExecuteManager, { CountRecorder, Logger } from '@singcl/ad-execute-manager';
import { matchErrorWithKeywords, getCurrentPageInterScene } from './_utils';
import { SCENT_TEXT_OBJ } from './const';
import InterstitialAdNovelExb from './InterstitialAdNovelExb';
import RewardAdGlobalRecorder from './RewardAdGlobalRecorder';
import CommonSettings from './CommonSettings';

class InterstitialAdNormal extends InterstitialAdNovelExb {
  _scene = SCENT_TEXT_OBJ.other;
  _launchGap = 30 * 1000;
  _adBetweenGap = 60 * 1000;
  _timer = null;
  _adClose = 20000;
  _backgroundRetryTime = 3000; // Retry interval when app is in background
  _foregroundRetryTime = 5000; // Retry interval when app is in foreground
  
  constructor(args) {
    super(args);
    this.logger = new Logger({ prefix: InterstitialAdNormal.name });
    this.commonSettings = CommonSettings.new();
    this.countRecorder = CountRecorder.new({
      local_sign: 'interstitial_show_count',
      total: this.commonSettings.getCpAdDetails().dayAd,
      userId: this.commonSettings.getUserId(),
    });
    this._launchGap = this.commonSettings.getCpAdDetails().firstAdGap * 1000;
    this._adBetweenGap = this.commonSettings.getCpAdDetails().secondAdGap * 1000;
    this._adClose = this.commonSettings.getCpAdDetails().adClose ?? 20000;
  }

  _adCloseSuccessAnalytics(_args) {
    // ExbAnalyticsJS.getInstance().track('incentive_ad_close', {
    //   scene: _args.scene,
    //   ad_is_completed: _args.ad_is_completed,
    // });
  }

  _onInnerAdShowSuccess() {
    this.countRecorder.updateToday(); // Update today's display count
    if (this._adClose) {
      setTimeout(() => {
        this.adCloseLister();
      }, this._adClose);
    }
  }

  async launch() {
    const recordIns = RewardAdGlobalRecorder.getInstance();
    const launchAdLastShowTime = recordIns.launchAdLastShowTime;
    const startLaunchTime = Math.max(this._launchGap - (new Date().getTime() - launchAdLastShowTime), 0);
    
    const circle = () => {
      return new Promise((resolve) => {
        const _fn = async () => {
          await AdExecuteManager.getInstance().whenAllTasksComplete();
          const nowTime = new Date().getTime();
          const rewardAdLastShowTime = recordIns.rewardAdLastShowTime;
          const interstitialAdLastShowTime = recordIns.interstitialAdLastShowTime;
          
          const remain = Math.max(
            this._adBetweenGap - (nowTime - Math.max(rewardAdLastShowTime, interstitialAdLastShowTime)),
            0
          );
          if (remain > 0) {
            setTimeout(_fn, remain);
          } else {
            resolve();
          }
        };
        _fn();
      });
    };
    
    const fn2 = async (t) => {
      this._timer = setTimeout(async () => {
        if (this.countRecorder.remain() <= 0) {
          clearTimeout(this._timer);
          this._timer = null;
          return;
        }
        await circle();
        let res = null;
        if (this.getAppDisplayStatus() === 'hide') {
          const msg = `app in background: pause ad show, interstitial ad, time retry: ${this._backgroundRetryTime}ms`;
          this.logger.log(msg);
          res = {
            apiError: {
              errMsg: msg,
              errorCode: 110000,
            },
          };
          this.record(res);
        } else {
          this.logger.log(`Ad entering queue, will play soon, GAP: ${this._adBetweenGap}ms`);
          res = await this.addExecuteManager({
            options: { retry: 0, scene: getCurrentPageInterScene() || this._scene },
          });
        }
        if (res && !res.apiError) {
          clearTimeout(this._timer);
          this._timer = null;
          fn2(this._adBetweenGap);
        } else {
          const e = res?.apiError;
          if (matchErrorWithKeywords(this._ttErrorMsgs, e?.errMsg) || this._ttErrorCodes.includes(e?.errorCode)) {
            clearTimeout(this._timer);
            this._timer = null;
            return;
          }
          setTimeout(
            () => {
              clearTimeout(this._timer);
              this._timer = null;
              fn2(0);
            },
            this.getAppDisplayStatus() === 'hide' ? this._backgroundRetryTime : this._foregroundRetryTime
          );
        }
      }, t);
    };

    fn2(startLaunchTime);
  }

  getAppDisplayStatus() {
    return RewardAdGlobalRecorder.getInstance().appDisplayStatus;
  }

  static build(args) {
    if (!InterstitialAdNormal.instance) {
      InterstitialAdNormal.instance = new InterstitialAdNormal(args);
    }
    return InterstitialAdNormal.instance;
  }

  static getInstance() {
    if (!InterstitialAdNormal.instance) {
      throw new Error('InterstitialAdNormal instance is not init');
    }
    return InterstitialAdNormal.instance;
  }

  static new(args) {
    return new InterstitialAdNormal(args);
  }
}

export default InterstitialAdNormal;

3. Ad Scene Trigger Manager

import { Logger } from '@singcl/ad-execute-manager';
import { SCENT_TYPE_OBJ } from './const';

class RewardAdSceneTriggerManager {
  static instance = null;
  _initSign = ''; // Initialization sign
  _currScene = null; // Current scene
  
  constructor(args) {
    if (RewardAdSceneTriggerManager.instance) {
      return RewardAdSceneTriggerManager.instance;
    }
    this._initSign = args?.sign ?? ''; // Initialization sign
    this.logger = new Logger({ prefix: '' });
    RewardAdSceneTriggerManager.instance = this;
  }

  initialize(args) {
    // Initialization logic
  }

  addScene(value) {
    this.logger.log('-------------AD trigger scene:--------------', value);
    this._currScene = value;
    return this;
  }

  addSceneType(args) {
    this.addScene(SCENT_TYPE_OBJ[args.scene]);
    return this;
  }

  getCurrentScene() {
    return this._currScene;
  }

  placeholder() {
    return null;
  }

  static build(args) {
    if (!RewardAdSceneTriggerManager.instance) {
      RewardAdSceneTriggerManager.instance = new RewardAdSceneTriggerManager(args);
    }
    return RewardAdSceneTriggerManager.instance;
  }

  static getInstance() {
    if (!RewardAdSceneTriggerManager.instance) {
      throw new Error('RewardAdSceneTriggerManager instance is not init');
    }
    return RewardAdSceneTriggerManager.instance;
  }
}

export default RewardAdSceneTriggerManager;

API Reference

AdExecuteManager

The main class for managing ad execution flow.

Methods

  • getInstance(args): Get the singleton instance of AdExecuteManager
  • getSafeInstance(): Get the instance, returns null if not initialized
  • addTask(adInstance, ctx): Add an ad task to the execution queue
  • clearTasks(): Cancel all pending tasks
  • getTaskCount(): Get the number of pending tasks
  • isRunning(): Check if tasks are currently running
  • getCurrentTaskId(): Get the ID of the current executing task
  • whenAllTasksComplete(): Returns a Promise that resolves when all tasks are complete

RewardAdFather

Base class for reward ad implementations.

InterstitialAdFather

Base class for interstitial ad implementations.

Other Exports

  • SerializableError: Error class that can be serialized
  • Logger: Logging utility
  • Storage: Storage management
  • CountRecorder: Ad execution counter
  • RewardAdGlobalRecorder: Global ad recorder
  • RewardAdSceneTriggerManager: Scene-based ad trigger manager
  • AdAnalyticsJS: Analytics integration
  • RewardAdNovel: Novel-specific reward ad implementation
  • InterstitialAdNovel: Novel-specific interstitial ad implementation
  • PubSub: Publish-subscribe pattern implementation

Development Guide

Development Scripts

  • npm run build: Build the library for production
  • npm run dev: Turn on watch mode
  • npm run lint: Lint your code
  • npm run format: Format your code
  • npm run test: Run tests

Project Structure

src/
  ad/             # Core ad-related code
  helper/         # Helper utilities
  typings/        # Type definitions
  utils/          # Common utilities
  index.js        # Entry file
example/         # Example code
  AdUnlock/       # Ad unlock related examples
  mini_program/   # Mini program examples
  typings/        # Example type definitions
  *.js            # Various ad implementation examples

License

This project is licensed under the MIT License - see the LICENSE file for details.