TypeScript SDK
一つのパッケージに六つのエントリポイント。実行環境に合うものをインポートすれば、ツリーシェイキングされたビルドが残りをバンドルから外します。
公開先
@prodantix/sdk on npm.
インストール
bun add @prodantix/sdkエントリポイント
サブパスごとに ESM と CommonJS のビルドと型定義が付属します。ルートは実行環境に依存せず、ほかは特定の実行環境に必要なものを足します。
| Subpath | What it exports |
|---|---|
| @prodantix/sdk | ProdantixClient, createClient, evaluateFlag, FlagsClient, MessagesClient |
| @prodantix/sdk/web | createWebClient, installAutocapture, installLinkCarry, installSessionReplay, WebTransport |
| @prodantix/sdk/node | createNodeClient, readSharedIdentity |
| @prodantix/sdk/csp | prodantixConnectSrc, prodantixImgSrc, prodantixScriptSrc |
| @prodantix/sdk/chat | mountChat, ChatClient, ChatPanel, resolveAppearance |
| @prodantix/sdk/messenger | fetchMessengerConfig, cachedMessengerConfig, MESSENGER_CACHE_KEY |
@prodantix/sdk
クライアント、フラグ評価器、ストレージとトランスポートのインターフェース。ここには DOM に触れるものがないため、独自のストレージ、トランスポート、時計、id ファクトリを渡してどこでも動かせます。
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
同じクライアントの上に載るブラウザ配線です。ローカルストレージへの保存、ページが隠れたときのビーコン送信、ページビューとクリックの自動取得。セッションリプレイはリプレイホストを渡すまで無効のままです。
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>'] })) {
// ...
}ウェブでは、訪問者の匿名 id とセッションは登録可能ドメイン上のひとつのファーストパーティ cookie に置かれるため、xyz.com、app.xyz.com、console.xyz.com は同じ一人を見ます。マーケティングサイトの訪問、サインアップ、アプリでの最初のセッションは、アプリがその人を識別した時点で同じタイムラインに並びます。識別済みの id はオリジンごとにローカルストレージに残り、ドメイン配下のどのホストからも読めてしまうため cookie には決して入りません。ドメインはブラウザに問い合わせて求めるか、cookieDomain で指定します。サブドメインが別々のテナントに属するサイトは、自分のホストを指定しなければなりません。cookie を書かないようにするには sharedStorage を渡します。たとえば同意が得られるまでの間などで、cookie が拒否された場合はローカルストレージが引き続き代替になります。セッション id は訪問ごとに発行され、三十分の無操作または二十四時間で更新され、すべてのイベントに付与され、セッションリプレイと共有されます。
Cookie は登録可能ドメインを越えられないため、あるサイトから別のサイトへのリンクをたどった訪問者は二人の訪問者になってしまいます。既定では空の linkedDomains を渡すと、クライアントは click と auxclick の時点で、それらのドメインへ向かう外部リンクに _pdx パラメータを付けます。パラメータが運ぶのは三つ、匿名 id、セッション id、そしてセッションが始まった時刻で、これにより三十分の窓と二十四時間の上限がこの跳躍をまたいで続きます。運ばれるのは匿名 id だけで、識別済みの id が運ばれることはありません。人物を識別するのは移動先自身だからです。同じ登録可能ドメインへのリンク、すでに _pdx を持つリンク、mailto、tel、javascript、およびハッシュのリンクはいずれも手つかずのままです。到着すると createWebClient は識別情報を組み立てる前に _pdx を読み、運ばれてきた id とセッションを共有ストアに書き込み、history.replaceState でパラメータを取り除きます。したがってそれは $current_url にも現れず、その先へも運ばれません。三つの部分が読み取れないパラメータは無視され、アドレスはそのまま残ります。このホストがすでに別の匿名 id を持っていた場合、クライアントは $create_alias を送るので、以前に記録した分は運ばれてきた id に畳み込まれます。すでに人物を識別済みのホストはその人物を保ちます。運ばれてきた id はその人の匿名 id になり、distinct id になることはありません。
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 }訪問者自身のその結合に対する答えも同じ共有ストアに、pdx..consent の下に置かれるため、すべてのサブドメインと Cookie を読むサーバーは一つの答えを見ます。setConsent({ identityLink: false }) で記録し、consent() で読み戻し、アプリがすでに答えを持っているなら consent オプションで植え付けてください。同意バナーを作るのはあなたであり、prodantix はそれが集めたものを尊重します。identityLink が false のあいだ、identify はその人物へ結合するための匿名 id を送らず、一覧にあるドメインへのリンクも装飾されません。id を持ち越すことは別経路の同じ結合だからです。未設定または true は両方を許しますが、コンソールにあるプロジェクト自身の設定に従います。その設定を執行するのはステートワーカーであって、ブラウザが送る何かではありません。答えの変更は次の identify と次のクリックから効きます。すでに結合されたものがほどけることはありません。
@prodantix/sdk/node
サーバー向け既定値を備えた同じクライアントです。イベントはバッファに溜められ、バックグラウンドで送信されるため、プロセス終了前に shutdown を呼ばないと最後のバッチは出ていきません。
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();サーバーのリダイレクトで完了するサインインはページを描かないため、ブラウザ自身の identify はそこで走りません。readSharedIdentity(cookieHeader, apiKey) はウェブクライアントが書いた Cookie を読み、匿名 id、その id が属するセッション、訪問者の同意を返します。Cookie が無い場合やこの SDK のものでない場合は null を返します。続いて link(distinctId, anonymousId, traits) が、ブラウザなら送っていたはずの一件の $identify を、渡された id で送り、このクライアント自身の識別情報には手を触れません。Bomdisoft のログインはサインイン完了時に両方を呼びます。八つのサインイン地点がすでに共通で呼んでいる、その一つの関数の中です。そこは人物の subject がサーバー側で初めて分かり、なお Cookie がリクエストに載っている場所です。そのうち六つはリクエストオブジェクトを持たないサーバーアクションであり、だからこの関数はリクエストではなく Cookie ヘッダーの文字列を受け取ります。読み戻した答えが identityLink の false なら link は飛ばしてください。
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
アプリが Prodantix に到達するために必要なコンテンツセキュリティポリシーのソースです。React からもブラウザからも何も読み込まないため、ビルド設定ファイルが直接読み取れます。
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('; ');サポートウィジェットは、チームの写真を edge から、訪問者が送ったファイルを blob: アドレスから表示するため、ポリシーの img-src には両方が必要です。prodantixImgSrc は、すでに許可している画像ソースにこれらを追加します。これがないとブラウザはそれらの画像をブロックし、そのことを自身のコンソールでしか知らせません。
@prodantix/sdk/chat
サポートウィジェットです。マウントすると、開く、閉じる、破棄する、利用者を名乗らせる操作を持つハンドルが返ります。閉じたシャドウルート内に描画されるため、ホストページのスタイルが届くことはありません。
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());訪問者のメッセージはどこで書かれたかを一緒に運びます。パネルは送信の瞬間に location と document.title を読み、host、path、title を送ります。path はパス名だけで、クエリもフラグメントも決して含みません。パスワード再設定リンク、マジックリンク、決済からの復帰はそこにトークンを載せており、担当者の受信箱はその置き場ではないからです。パネルはそのどれも描かないので、訪問者のスレッドは本人の言葉のままです。エッジから SDK への応答もこれを省くため、自分のスレッドを読むページはコンソールが見るものを見られません。現れるのは受信箱で、受信メッセージの下の控えめな一行としてです。
@prodantix/sdk/messenger
ウィジェットの背後にある設定リーダーです。プロジェクトのメッセンジャー設定を取得してキャッシュし、そのプロジェクトでメッセンジャーが有効かどうかを伝えます。自己実行型のスニペットは別途ビルドされ、このサブパスからは到達できません。
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
}