Saltar al contenido principal

Adaptadores de framework

SDK de TypeScript

Un paquete con seis puntos de entrada. Importa el que corresponda a tu entorno de ejecución, y la compilación con tree shaking deja el resto fuera de tu bundle.

Dónde se publica

@prodantix/sdk on npm.

Instalación

Bash
bun add @prodantix/sdk

Puntos de entrada

Cada subruta trae su propia compilación ESM y CommonJS con sus propios tipos. La raíz es independiente del entorno, y las demás añaden lo que necesita un entorno concreto.

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

El cliente, el evaluador de banderas y las interfaces de almacenamiento y transporte. Nada de esto toca el DOM, así que puedes aportar tu propio almacenamiento, transporte, reloj o fábrica de ids y ejecutarlo donde quieras.

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

El cableado de navegador sobre el mismo cliente: persistencia en el almacenamiento local, envío por baliza cuando la página se oculta, y autocaptura de vistas de página y clics. La repetición de sesión sigue apagada hasta que le des un host de repetición.

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>'] })) {
  // ...
}

En la web, el id anónimo y la sesión del visitante viven en una sola cookie de primera parte en tu dominio registrable, de modo que xyz.com, app.xyz.com y console.xyz.com ven a una sola persona: la visita de marketing, el registro y la primera sesión en la app caen en la misma línea de tiempo en cuanto la app la identifica. El id identificado se queda por origen en el almacenamiento local y nunca entra en la cookie, porque cualquier host bajo el dominio puede leerla. El dominio se averigua preguntando al navegador, o se fija con cookieDomain; un sitio cuyos subdominios pertenecen a distintos inquilinos debe fijarlo en su propio host. Pasa sharedStorage para no escribir ninguna cookie, por ejemplo hasta que se dé el consentimiento, y el almacenamiento local sigue siendo el respaldo cuando las cookies se rechazan. Se crea un id de sesión por visita, se renueva tras treinta minutos de inactividad o veinticuatro horas, se estampa en cada evento y se comparte con la repetición de sesión.

Una cookie no puede cruzar un dominio registrable, así que un visitante que sigue un enlace de un sitio a otro sería dos visitantes. Pase linkedDomains, vacío por defecto, y el cliente decora los enlaces salientes hacia esos dominios, en click y auxclick, con un parámetro _pdx que lleva tres cosas: el id anónimo, el id de sesión y el instante en que la sesión empezó, de modo que la ventana de treinta minutos y el tope de veinticuatro horas continúan tras el salto. Solo viaja el id anónimo, nunca uno identificado, porque el destino identifica a la persona por sí mismo. Un enlace al mismo dominio registrable, uno que ya lleva _pdx, y los enlaces mailto, tel, javascript y de ancla se dejan todos intactos. Al llegar, createWebClient lee _pdx antes de construir la identidad, escribe el id y la sesión traídos en el almacén compartido y quita el parámetro con history.replaceState, de forma que no aparece en $current_url ni viaja más allá; un parámetro cuyas tres partes no se leen se ignora y la dirección queda como estaba. Si este host ya tenía otro id anónimo, el cliente envía $create_alias, y lo que registró antes se pliega en el id traído; y un host que ya identificó a una persona la conserva: el id traído pasa a ser su id anónimo, nunca su distinct id.

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 respuesta del propio visitante sobre esa unión viaja en el mismo almacén compartido, bajo pdx..consent, de modo que cada subdominio y un servidor que lea la cookie ven una sola respuesta. Regístrela con setConsent({ identityLink: false }), léala de vuelta con consent() y siémbrela con la opción consent cuando su aplicación ya tenga la respuesta: el aviso de consentimiento es suyo para construirlo, y prodantix respeta lo que recoge. Mientras identityLink sea false, identify no envía ningún id anónimo para unir a la persona, y un enlace a un dominio listado queda sin decorar, porque llevar el id al otro lado es la misma unión por otra vía. Sin definir o true permite ambas cosas, sujeto al ajuste propio del proyecto en la consola, que aplica el worker de estado y no nada que envíe el navegador. Cambiar la respuesta surte efecto en el siguiente identify y en el siguiente clic; nada ya unido se deshace.

@prodantix/sdk/node

El mismo cliente con valores por defecto de servidor. Los eventos se almacenan en un búfer y se envían en segundo plano, así que llama a shutdown antes de que el proceso termine o el último lote no saldrá nunca.

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();

Un inicio de sesión que termina en una redirección del servidor no dibuja ninguna página, así que el identify del propio navegador nunca se ejecuta allí. readSharedIdentity(cookieHeader, apiKey) lee la cookie que escribe el cliente web y responde el id anónimo, la sesión en la que está y el consentimiento del visitante, o null cuando la cookie falta o no es de este SDK. Después link(distinctId, anonymousId, traits) envía el único $identify que el navegador habría enviado, para los ids dados, y deja intacta la identidad del propio cliente. El login de Bomdisoft llama a ambos al completar el inicio de sesión, dentro de la única función que sus ocho puntos de entrada ya llaman: ahí es donde el subject de la persona se conoce por primera vez en el servidor mientras la cookie sigue en la petición. Seis de esos puntos son acciones de servidor sin objeto de petición, y por eso la función recibe una cadena de cabecera de cookies en lugar de una petición. Omita link cuando la respuesta releída sea 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

Las fuentes de la política de seguridad de contenido que una aplicación necesita para llegar a Prodantix. No importa nada de React ni del navegador, así que un archivo de configuración de compilación puede leerlo directamente.

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('; ');

El widget de soporte muestra las fotos de tu equipo desde el edge y dibuja los archivos que envían los visitantes desde direcciones blob:, así que el img-src de la política necesita ambos. prodantixImgSrc los añade a las fuentes de imágenes que ya permites. Sin ellos, el navegador bloquea esas imágenes y solo lo avisa en su propia consola.

@prodantix/sdk/chat

El widget de soporte. Móntalo y obtienes un manejador que abre, cierra, destruye e identifica al usuario. Se renderiza dentro de una shadow root cerrada, así que los estilos de la página anfitriona nunca lo alcanzan.

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());

Cada mensaje del visitante lleva dónde fue escrito. El panel lee location y document.title en el momento del envío y manda host, path y title, siendo path solo la ruta: nunca la consulta ni el fragmento, porque un enlace de restablecimiento, un enlace mágico o un regreso del pago llevan ahí su token, y la bandeja de un agente no es sitio para eso. El panel no dibuja nada de ello, así que el hilo del visitante sigue siendo sus propias palabras, y la respuesta del borde de vuelta al SDK también lo omite, de modo que una página que lee su propio hilo no ve lo que ve la consola. Donde sí aparece es en la bandeja, como una línea tenue bajo un mensaje entrante.

@prodantix/sdk/messenger

El lector de ajustes que hay detrás del widget: obtiene la configuración del mensajero de un proyecto, la guarda en caché e informa de si el proyecto tiene el mensajero activado. El fragmento autoejecutable se compila aparte y no es accesible por esta subruta.

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
}