Aller au contenu principal

Adaptateurs de framework

SDK TypeScript

Un seul paquet, six points d'entrée. Importez celui qui correspond à votre environnement, et la construction avec élagage laisse le reste hors de votre bundle.

Où il est publié

@prodantix/sdk on npm.

Installation

Bash
bun add @prodantix/sdk

Points d'entrée

Chaque sous-chemin fournit sa propre build ESM et CommonJS avec ses propres types. La racine est indépendante de l'environnement, les autres ajoutent ce dont un environnement précis a besoin.

SubpathWhat it exports
@prodantix/sdkProdantixClient, createClient, evaluateFlag, FlagsClient, MessagesClient
@prodantix/sdk/webcreateWebClient, installAutocapture, installLinkCarry, installSessionReplay, WebTransport
@prodantix/sdk/nodecreateNodeClient, readSharedIdentity
@prodantix/sdk/cspprodantixConnectSrc, prodantixImgSrc, prodantixScriptSrc
@prodantix/sdk/chatmountChat, ChatClient, ChatPanel, resolveAppearance
@prodantix/sdk/messengerfetchMessengerConfig, cachedMessengerConfig, MESSENGER_CACHE_KEY

@prodantix/sdk

Le client, l'évaluateur de drapeaux et les interfaces de stockage et de transport. Rien ici ne touche au DOM : vous pouvez fournir vos propres stockage, transport, horloge ou fabrique d'identifiants et l'exécuter n'importe où.

TypeScript
import { createClient, MemoryStorage } from '@prodantix/sdk';

const prodantix = createClient({
  apiKey: PRODANTIX_PUBLIC_KEY,
  host: 'https://eu.api.prodantix.com',
  storage: new MemoryStorage(),
});

prodantix.capture('order.completed', { properties: { amount: 4200, currency: 'XAF' } });
await prodantix.flush();

@prodantix/sdk/web

Le câblage navigateur au-dessus du même client : persistance dans le stockage local, envoi par balise quand la page se masque, et capture automatique des pages vues et des clics. La relecture de session reste désactivée tant que vous ne fournissez pas d'hôte de relecture.

TypeScript
import { createWebClient } from '@prodantix/sdk/web';

const prodantix = createWebClient({
  apiKey: PRODANTIX_PUBLIC_KEY,
  host: 'https://eu.api.prodantix.com',
  flagsHost: 'https://api.prodantix.com',
  cookieDomain: 'example.com',
  autocaptureOptions: { clicks: true, pageviews: true },
});

prodantix.identify('user-42', { set: { plan: 'pro' } });

if (await prodantix.isFeatureEnabled('new-checkout')) {
  // ...
}

// Every per-flag read records one $feature_flag_called exposure.
const variant = await prodantix.getVariant('new-checkout');
if (variant?.key === 'treatment') {
  // ...
}

// A flag targeting a cohort reads this user's memberships from the store;
// pass them yourself to skip the lookup.
if (await prodantix.isFeatureEnabledLocal('members', { cohorts: ['<cohort id>'] })) {
  // ...
}

Sur le web, l'identifiant anonyme du visiteur et sa session vivent dans un seul cookie first-party sur votre domaine enregistrable, si bien que xyz.com, app.xyz.com et console.xyz.com voient une seule personne : la visite marketing, l'inscription et la première session dans l'application tombent sur la même chronologie dès que l'application l'identifie. L'identifiant identifié reste par origine dans le stockage local et n'entre jamais dans le cookie, parce que chaque hôte sous le domaine peut le lire. Le domaine est trouvé en interrogeant le navigateur, ou fixé avec cookieDomain ; un site dont les sous-domaines appartiennent à des locataires différents doit le fixer à son propre hôte. Passez sharedStorage pour n'écrire aucun cookie, par exemple tant que le consentement n'est pas donné, et le stockage local reste le repli quand les cookies sont refusés. Un identifiant de session est créé à chaque visite, renouvelé après trente minutes d'inactivité ou vingt-quatre heures, apposé sur chaque événement et partagé avec la relecture de session.

Un cookie ne peut pas franchir un domaine enregistrable : un visiteur qui suit un lien d'un site vers un autre serait donc deux visiteurs. Passez linkedDomains, vide par défaut, et le client décore les liens sortants vers ces domaines, sur click et auxclick, avec un paramètre _pdx portant trois choses : l'identifiant anonyme, l'identifiant de session et l'instant où la session a commencé, si bien que la fenêtre de trente minutes et le plafond de vingt-quatre heures se poursuivent après le saut. Seul l'identifiant anonyme voyage, jamais un identifiant identifié, car la destination identifie la personne elle-même. Un lien vers le même domaine enregistrable, un lien portant déjà _pdx, ainsi que les liens mailto, tel, javascript et les ancres, sont tous laissés intacts. À l'arrivée, createWebClient lit _pdx avant de construire l'identité, écrit l'identifiant et la session transportés dans le stockage partagé, puis retire le paramètre avec history.replaceState : il n'apparaît donc ni dans $current_url ni plus loin ; un paramètre dont les trois parties ne se lisent pas est ignoré et l'adresse laissée telle quelle. Si cet hôte détenait déjà un autre identifiant anonyme, le client envoie $create_alias, de sorte que ce qu'il avait enregistré se replie sur l'identifiant transporté ; et un hôte qui a déjà identifié une personne la conserve : l'identifiant transporté devient son identifiant anonyme, jamais son identifiant distinct.

TypeScript
import { createWebClient } from '@prodantix/sdk/web';

const prodantix = createWebClient({
  apiKey: PRODANTIX_PUBLIC_KEY,
  host: 'https://eu.api.prodantix.com',
  linkedDomains: ['example.com', 'example-app.com'],
});

// A link to example-app.com now leaves as
// https://example-app.com/pricing?_pdx=<anonymousId>~<sessionId>~<startedAt>
// and the arriving page adopts it before it builds an identity of its own.

// The visitor's answer, from your own consent banner.
prodantix.setConsent({ identityLink: false });
prodantix.consent(); // { identityLink: false }

La réponse du visiteur lui-même sur ce rapprochement circule dans le même stockage partagé, sous pdx..consent, si bien que chaque sous-domaine et un serveur lisant le cookie voient une seule réponse. Enregistrez-la avec setConsent({ identityLink: false }), relisez-la avec consent(), et semez-la avec l'option consent quand votre application détient déjà la réponse : la bannière de consentement est à vous de construire, et prodantix respecte ce qu'elle recueille. Tant que identityLink vaut false, identify n'envoie aucun identifiant anonyme à rapprocher de la personne, et un lien vers un domaine listé reste non décoré, puisque transporter l'identifiant revient au même rapprochement par un autre chemin. Non défini ou true autorise les deux, sous réserve du réglage propre au projet dans la console, appliqué par le worker d'état et non par quoi que ce soit qu'envoie le navigateur. Changer la réponse prend effet au prochain identify et au prochain clic ; rien de déjà rapproché n'est défait.

@prodantix/sdk/node

Le même client avec des valeurs par défaut côté serveur. Les événements sont mis en tampon et envoyés en arrière-plan : appelez shutdown avant la fin du processus, sinon le dernier lot ne part jamais.

TypeScript
import { createNodeClient } from '@prodantix/sdk/node';

// A node client streams flag changes over Socket.IO by default and refetches
// the snapshot on push; streamFlags: false leaves only the 30s poll.
const prodantix = createNodeClient({
  apiKey: process.env.PRODANTIX_PUBLIC_KEY,
  host: 'https://eu.api.prodantix.com',
  streamFlags: true,
});

prodantix.capture('job.completed', { properties: { queue: 'emails' } });
await prodantix.shutdown();

Une connexion qui s'achève par une redirection serveur n'affiche aucune page : l'identify du navigateur ne s'y exécute donc jamais. readSharedIdentity(cookieHeader, apiKey) lit le cookie écrit par le client web et répond l'identifiant anonyme, la session dans laquelle il se trouve et le consentement du visiteur, ou null quand le cookie est absent ou n'appartient pas à ce SDK. link(distinctId, anonymousId, traits) envoie ensuite l'unique $identify que le navigateur aurait envoyé, pour les identifiants donnés, sans toucher à l'identité propre de ce client. Le login de Bomdisoft appelle les deux à la fin de la connexion, dans l'unique fonction que ses huit points de connexion appellent déjà : c'est là que le subject de la personne devient connu côté serveur alors que le cookie est encore sur la requête. Six de ces points sont des actions serveur sans objet de requête, et c'est pourquoi la fonction prend une chaîne d'en-tête de cookies plutôt qu'une requête. Sautez link quand la réponse relue vaut identityLink false.

TypeScript
import { createNodeClient, readSharedIdentity } from '@prodantix/sdk/node';

const prodantix = createNodeClient({
  apiKey: PRODANTIX_PUBLIC_KEY,
  host: 'https://eu.api.prodantix.com',
});

// At sign-in completion, where the person is known and the browser's
// cookie is still on the request.
export const completeSignIn = (sub: string, cookieHeader: string): void => {
  const shared = readSharedIdentity(cookieHeader, PRODANTIX_PUBLIC_KEY);
  if (shared !== null && shared.consent?.identityLink !== false) {
    prodantix.link(sub, shared.anonymousId);
  }
};

@prodantix/sdk/csp

Les sources de politique de sécurité du contenu dont une application a besoin pour joindre Prodantix. Ce module n'importe rien de React ni du navigateur : un fichier de configuration de build peut donc le lire directement.

TypeScript
import { prodantixConnectSrc, prodantixImgSrc, prodantixScriptSrc } from '@prodantix/sdk/csp';

const hosts = {
  ingest: 'https://eu.api.prodantix.com',
  edge: 'https://eu.edge.prodantix.com',
  replay: 'https://eu.replay.prodantix.com',
};

const policy = [
  `connect-src ${prodantixConnectSrc(hosts)}`,
  `script-src ${prodantixScriptSrc(hosts)}`,
  `img-src ${prodantixImgSrc(hosts, ["'self'", 'data:'])}`,
].join('; ');

Le widget de support affiche les photos de votre équipe depuis l'edge et dessine les fichiers envoyés par les visiteurs depuis des adresses blob: : le img-src de la politique doit donc autoriser les deux. prodantixImgSrc les ajoute aux sources d'images que vous autorisez déjà. Sans elles, le navigateur bloque ces images et ne le signale que dans sa propre console.

@prodantix/sdk/chat

Le widget de support. Montez-le et vous obtenez une poignée qui ouvre, ferme, détruit et identifie. Il se rend dans une racine fantôme fermée : les styles de la page hôte ne l'atteignent jamais.

TypeScript
import { mountChat } from '@prodantix/sdk/chat';

const messenger = mountChat({
  apiKey: PRODANTIX_PUBLIC_KEY,
  host: 'https://eu.edge.prodantix.com',
  distinctId: prodantix.distinctId,
});

document.querySelector('#help')?.addEventListener('click', () => messenger.open());

Chaque message du visiteur porte l'endroit où il a été écrit. Le panneau lit location et document.title au moment de l'envoi et transmet host, path et title, path étant le seul chemin : jamais la requête ni le fragment, parce qu'un lien de réinitialisation, un lien magique ou un retour de paiement y porte son jeton, et la boîte de réception d'un agent n'est pas sa place. Le panneau n'en dessine rien, si bien que le fil du visiteur reste ses propres mots, et la réponse de la périphérie vers le SDK l'omet également, de sorte qu'une page qui lit son propre fil ne voit pas ce que voit la console. Là où il apparaît, c'est dans la boîte de réception, en une ligne discrète sous un message entrant.

@prodantix/sdk/messenger

Le lecteur de configuration derrière le widget : il récupère la configuration de la messagerie d'un projet, la met en cache et indique si le projet a activé la messagerie. Le fragment auto-exécutable est construit séparément et reste inaccessible par ce sous-chemin.

TypeScript
import { fetchMessengerConfig } from '@prodantix/sdk/messenger';

const config = await fetchMessengerConfig({
  apiKey: PRODANTIX_PUBLIC_KEY,
  host: 'https://eu.edge.prodantix.com',
});

if (config?.enabled) {
  // the project has the messenger switched on
}