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

@kundai/angular

v1.1.1

Published

Module Angular KundAi — tracking sessions, UTM, conversions et identify en 2 lignes.

Readme

@kundai/angular

Module Angular officiel pour intégrer le tracking KundAi dans une application Angular.


Sommaire


Installation

npm install @kundai/angular

Versions Angular supportées : Angular 16, 17, 18, 19+


Configuration

Les deux modes de déploiement

Le baseUrl détermine vers qui le frontend envoie les données de tracking. Deux modes sont possibles :

Mode direct — le frontend appelle KundAi directement

Frontend Angular  ──────────────────────────────►  KundAi tracking :3002

Simple, aucun backend intermédiaire. Utiliser quand le backend Ndewa360 n'a pas besoin d'être dans la boucle pour les événements navigateur.

...KundaiModule.forRoot({
  baseUrl: environment.kundaiTrackingUrl,  // 'https://api.kundai.io/v1/tracking'
  apiKey: environment.kundaiApiKey,         // clé API KundAi
  appId: 'ndewa360-web',
})

Mode proxy — le frontend passe par le backend Ndewa360

Frontend Angular  ──►  Backend Ndewa360 (/tracking/*)  ──►  KundAi tracking :3002

Utile si tu veux ajouter de la logique métier avant le tracking (vérification d'authentification, enrichissement des événements, validation). Le backend Ndewa360 doit avoir @kundai/nestjs installé et configuré.

...KundaiModule.forRoot({
  baseUrl: environment.ndewa360ApiUrl + '/tracking',  // 'https://api.ndewa360.com/tracking'
  apiKey: environment.ndewa360ApiKey,                  // ton token d'auth Ndewa360 (pas la clé KundAi)
  appId: 'ndewa360-web',
})

Dans ce mode, c'est le backend Ndewa360 qui détient la clé API KundAi. Le frontend ne la connaît pas.

Quel mode choisir ?

| | Mode direct | Mode proxy | |---|---|---| | Simplicité | ✅ aucun backend intermédiaire | ❌ backend requis | | Clé API KundAi exposée | dans le frontend (visible) | uniquement côté serveur | | Logique métier avant tracking | ❌ impossible | ✅ possible | | Latence | plus faible | légèrement plus élevée |

Pour Ndewa360, le mode direct est recommandé pour les événements navigateur (sessions, pages vues, clics). Le mode proxy n'est utile que si tu as des exigences de sécurité spécifiques (ne pas exposer la clé API KundAi dans le bundle JS).


App standalone (Angular 17+)

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { KundaiModule } from '@kundai/angular';
import { routes } from './app.routes';
import { environment } from '../environments/environment';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    ...KundaiModule.forRoot({
      baseUrl: environment.kundaiTrackingUrl,
      apiKey: environment.kundaiApiKey,
      appId: 'ndewa360-web',
    }),
  ],
};

AppModule (Angular classique)

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { KundaiModule } from '@kundai/angular';
import { environment } from '../environments/environment';

@NgModule({
  imports: [BrowserModule],
  providers: [
    ...KundaiModule.forRoot({
      baseUrl: environment.kundaiTrackingUrl,
      apiKey: environment.kundaiApiKey,
      appId: 'ndewa360-web',
    }),
  ],
})
export class AppModule {}

Options de configuration

| Option | Type | Requis | Description | |---|---|---|---| | baseUrl | string | ✅ | URL du endpoint de tracking (KundAi direct ou backend proxy) | | apiKey | string | ✅ | Clé API KundAi (mode direct) ou token d'auth backend (mode proxy) | | appId | string | — | Identifiant de ton app (défaut : "web") | | timeoutMs | number | — | Timeout des requêtes HTTP en ms (défaut : 5000) | | autoPageView | boolean | — | Page-view auto à chaque route (défaut : true) |

Variables d'environnement

Mode direct — le frontend appelle KundAi directement :

// environments/environment.ts
export const environment = {
  production: false,
  kundaiTrackingUrl: 'http://localhost:3000/v1/tracking',  // api-gateway KundAi en local
  kundaiApiKey: 'kundai_xxxxxxxxxxxx',                      // clé API KundAi
};

// environments/environment.prod.ts
export const environment = {
  production: true,
  kundaiTrackingUrl: 'https://api.kundai.io/v1/tracking',
  kundaiApiKey: 'kundai_xxxxxxxxxxxx',
};

Mode proxy — le frontend passe par le backend Ndewa360 :

// environments/environment.ts
export const environment = {
  production: false,
  // Le backend Ndewa360 expose POST /tracking/* via @kundai/nestjs
  kundaiTrackingUrl: 'http://localhost:4000/tracking',
  // Vide : le backend gère lui-même l'authentification vers KundAi
  // Ou un token JWT Ndewa360 si tes routes /tracking/* sont protégées
  kundaiApiKey: '',
};

// environments/environment.prod.ts
export const environment = {
  production: true,
  kundaiTrackingUrl: 'https://api.ndewa360.com/tracking',
  kundaiApiKey: '',
};

En mode proxy, la clé API KundAi reste uniquement dans les variables d'environnement du backend Ndewa360 (KUNDAI_API_KEY). Elle n'est jamais exposée dans le bundle JavaScript du frontend.


Démarrage automatique

Dès que KundaiModule.forRoot() est configuré, le module :

  1. Génère un visitorId persistant (localStorage)
  2. Capture les paramètres UTM et ad click IDs depuis l'URL
  3. Enregistre un clic si des UTM sont présents (touchpoint d'attribution)
  4. Démarre une session
  5. Enregistre la première page-view
  6. Écoute les changements de route Angular et enregistre une page-view à chaque NavigationEnd
  7. Clôture la session à la fermeture de l'onglet (pagehide)

Aucun code supplémentaire n'est nécessaire pour ces comportements.


Identifier un utilisateur

À appeler après la connexion de l'utilisateur pour lier son identité à la session.

import { inject, Injectable } from '@angular/core';
import { KundaiService } from '@kundai/angular';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private readonly kundai = inject(KundaiService);

  async afterLogin(user: { id: string; email: string }) {
    // Lie l'utilisateur à la session navigateur courante
    await this.kundai.identify(user.id, user.email);
  }

  afterLogout() {
    // Efface l'identité locale (ne clôture pas la session)
    this.kundai.clearUser();
  }
}

L'email n'est jamais stocké en clair — il est haché SHA-256 côté serveur avant persistance en base de données.


Événements

Événements comportementaux libres

track() accepte n'importe quel nom d'événement et des propriétés libres.

import { Component, inject } from '@angular/core';
import { KundaiService } from '@kundai/angular';

@Component({ ... })
export class MyComponent {
  private readonly kundai = inject(KundaiService);

  onButtonClick() {
    this.kundai.track('button_click', { button: 'cta_hero', plan: 'pro' });
  }

  onFormSubmit() {
    this.kundai.track('form_submit', { form: 'contact' });
  }

  onVideoPlay(videoId: string) {
    this.kundai.track('video_play', { videoId });
  }
}

Événements recommandés par catégorie

Ces noms sont des conventions — KundAi les reconnaît et les affiche dans le dashboard.

Acquisition / Onboarding

| Événement | Propriétés suggérées | Description | |---|---|---| | page_view | url, title | Vue de page (automatique) | | signup_started | plan, source | Début d'inscription | | signup_completed | plan, method | Inscription terminée | | account_created | plan, country | Compte créé | | login | method | Connexion réussie | | logout | — | Déconnexion |

Engagement

| Événement | Propriétés suggérées | Description | |---|---|---| | button_click | button, page | Clic sur un bouton | | form_submit | form | Soumission de formulaire | | search | query, results_count | Recherche effectuée | | video_play | videoId, duration | Lecture vidéo | | download | file, type | Téléchargement | | share | platform, content | Partage | | feature_used | feature | Fonctionnalité utilisée |

Funnel commercial

| Événement | Propriétés suggérées | Description | |---|---|---| | plan_viewed | plan | Page de tarification vue | | plan_selected | plan, price, currency | Plan sélectionné | | checkout_started | plan, amount, currency | Paiement initié | | checkout_abandoned | plan, step | Paiement abandonné |

Conversions (déclenchent l'attribution dans KundAi)

| Événement | Propriétés requises | Propriétés optionnelles | |---|---|---| | purchase | amount, currency | planId, txRef | | payment | amount, currency | planId, provider | | conversion | amount, currency | planId | | checkout | amount, currency | planId |

Les événements purchase, payment, conversion et checkout sont traités spécialement par KundAi : ils déclenchent le moteur d'attribution et marquent la session comme convertie.


Conversions

Conversion côté frontend (paiement popup)

Pour les paiements confirmés directement dans le navigateur (ex. Flutterwave popup) :

onPaymentSuccess(result: { amount: number; currency: string; planId: string }) {
  this.kundai.trackConversion({
    amount: result.amount,
    currency: result.currency,
    planId: result.planId,
    event: 'purchase',
  });
}

Conversion côté serveur (webhook)

Pour les paiements confirmés par webhook (Flutterwave, Stripe, etc.), le frontend doit passer son sessionId au backend. Voir la section suivante.


Passer le sessionId au backend

Le sessionId est le lien entre la session navigateur et les conversions confirmées côté serveur. Sans lui, l'attribution est aveugle.

// checkout.component.ts
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { KundaiService } from '@kundai/angular';

@Component({ ... })
export class CheckoutComponent {
  private readonly kundai = inject(KundaiService);
  private readonly http = inject(HttpClient);

  async pay(planId: string, amount: number) {
    this.kundai.track('checkout_started', { planId, amount, currency: 'XOF' });

    await this.http.post('/api/payments/checkout', {
      planId,
      amount,
      currency: 'XOF',
      sessionId: this.kundai.getSessionId(),  // ← transmettre au backend
    }).toPromise();
  }
}

Le backend stocke ce sessionId et le renvoie à KundAi lors de la confirmation du paiement via @kundai/nestjs. Voir la documentation de @kundai/nestjs.


Référence API

// Injecter le service
private readonly kundai = inject(KundaiService);

// Identifier l'utilisateur connecté
await kundai.identify(userId: string, email?: string): Promise<void>

// Effacer l'identité locale (après logout)
kundai.clearUser(): void

// Envoyer un événement comportemental
kundai.track(event: string, properties?: Record<string, unknown>): void

// Notifier une conversion avec revenu (retry automatique x3)
kundai.trackConversion(opts: {
  amount: number
  currency: string        // ISO 4217 : 'XOF', 'EUR', 'USD', 'GHS', 'NGN'...
  planId?: string
  event?: 'conversion' | 'purchase' | 'payment' | 'checkout'
  metadata?: Record<string, unknown>
}): void

// Enregistrer manuellement une page-view
await kundai.recordPageView(): Promise<void>

// Récupérer le sessionId courant (à passer au backend)
kundai.getSessionId(): string | null

// Récupérer le visitorId courant
kundai.getVisitorId(): string

Ce qui est collecté automatiquement

| Donnée | Stockage | Note | |---|---|---| | visitorId | localStorage | UUID généré à la première visite | | UTM (utm_source, utm_medium, utm_campaign, utm_content, utm_term) | localStorage | Capturés depuis l'URL, persistés pour les conversions futures | | Ad click IDs (fbclid, gclid, ttclid, liclid) | localStorage | Capturés depuis l'URL | | IP address | Serveur | Anonymisée (dernier octet masqué IPv4, 80 bits IPv6) | | User-Agent | Serveur | Utilisé pour détecter device/browser/OS | | Email | Serveur | Haché SHA-256, jamais stocké en clair |