@siteplane/analytics
v0.1.23
Published
The public Siteplane Analytics SDK for explicitly instrumented React and Next.js websites, whether they are operated directly or managed for a client.
Readme
@siteplane/analytics
The public Siteplane Analytics SDK for explicitly instrumented React and Next.js websites, whether they are operated directly or managed for a client.
The stable import surfaces are:
@siteplane/analyticsfor the browser SDK.@siteplane/analytics/serverfor server-side events.@siteplane/analytics/reactfor React integrations.
The current SDK candidate starts disabled and reads site approval from
GET /api/analytics/config on the explicitly configured collector origin.
Site approval and the visitor's stored choice are separate gates. A server clock up to ten seconds ahead is accepted without extending the 60-second lease or the response's shorter lifetime. Expiry, revocation and visitor consent still close the measurement gate.
This package is under active pre-release development. Production use is gated by Siteplane Plan 19 and its published release evidence.
Supported environments
- Node.js 20 and 22 for
@siteplane/analytics/server. - Next.js 15 and 16.
- React 19 for
@siteplane/analytics/react. - Current Chromium, Firefox and WebKit for the browser SDK.
The root export is browser-safe and framework-independent. The server export does not read browser globals. Analytics starts disabled until fresh server approval and the visitor's decision permit tracking.
Canonical setup
Use the public Siteplane CLI from an existing initialized project:
npx -y siteplane@latest analytics init --agent-client codex
npx -y siteplane@latest analytics check
npx -y siteplane@latest analytics sync
npx -y siteplane@latest analytics test --page-url https://example.comProvider imports use normalized UTF-8 JSON. The default command is a dry-run;
apply additionally requires --apply --confirm-hash <hash> --yes and a live,
owner-approved analytics:import grant on the existing Project Connection.
Browser tracking contract
Configure the public key and collector endpoint:
const analytics = initSiteplaneAnalytics({
siteKey: process.env.NEXT_PUBLIC_SITEPLANE_ANALYTICS_SITE_KEY!,
endpoint: "https://siteplane.io/api/analytics/collect"
});The initializer has no siteMode option. The SDK keeps approval only in memory
for up to 60 seconds, refreshes every 30 seconds in visible tabs and checks again
on page return. A failed or expired response clears queued events and active
identifiers without changing the visitor's saved choice. Approval captures the
current page; actions taken while disabled are discarded.
endpoint is required. The browser SDK does not infer a collector from the
customer website origin. It sends normal scheduled and explicit flushes with
fetch, evaluates the HTTP status and uses sendBeacon only as a best-effort
pagehide delivery path.
The SDK adds installationGeneration and measurementMode to live browser
events. The collector requires those fields and verifies the current approval
again at the database write. They are permission metadata, not visitor identity.
The SDK exposes no setter for the installation generation.
Each request contains at most 50 events and its actual UTF-8 JSON body is
strictly smaller than 64 KiB. Event IDs are created once before the first send
and stay stable across chunking and retries. Network failures, 429 and 5xx
receive at most three attempts with exponential backoff and jitter;
Retry-After is honored. Events that remain transiently unsent return to the
in-memory queue unless approval expires, consent changes, opt-out or disposal clears it.
Permanent 4xx responses are not retried and can be observed through the
typed onDiagnostic callback.
session_only uses a session ID in sessionStorage and no persistent visitor
ID. full_analytics stays at consent_required until the visitor makes an
explicit compatible choice:
analytics.setConsent("full_analytics");
analytics.setConsent("session_only");
analytics.optOut();For a website with an existing CMP, pass consentAdapter to the initializer. Its
getConsent() returns unknown until that CMP has resolved the visitor's choice,
disabled for rejection, or session_only / full_analytics for an actual grant.
subscribe(onChange) must notify on every choice change and return an unsubscribe
function. The SDK reads the current choice after subscribing and on each callback;
read or subscription failures block measurement. Dispose the SDK when its owning
client component unmounts.
CMP grants are capped by the server-approved site mode and any more restrictive
Siteplane choice. They do not overwrite Siteplane consent records; an existing
Siteplane opt-out still requires a new explicit Siteplane choice. Do not copy a
CMP snapshot into setConsent. Unknown/denied CMP state clears queued events and
IDs without inventing a Siteplane denial tombstone. When access becomes allowed,
only the current pageview and new actions are captured. Server pause and preview
suppression remain independent gates.
Omit the adapter only after checking that the website has no existing CMP. An unrecognized CMP needs its actual callbacks connected before Analytics is ready; a constant grant or no-op adapter is not a verified integration. This adapter is part of the unreleased Batch 2 candidate; integrated product acceptance is pending.
Visitor and session IDs are generated internally as cryptographically random, fixed-format opaque values. The public SDK exposes no identity setter or identity injection option; the collector validates the format and persists only site-scoped HMAC pseudonyms, never the browser values themselves.
Positive choices and anonymous visitor IDs are versioned localStorage
records with a maximum lifetime of 180 days. Reject/optOut() first stores a
durable denial tombstone and then clears the in-memory queue, acquisition
context, session ID and visitor ID. enable() never overrides that tombstone;
the visitor must make a new explicit choice. If required browser storage is
missing or blocked, the SDK fails closed without sending a request or creating
an identifier.
The optional vanilla and React consent banners show equal Accept and Reject
actions and accept a configurable privacyPolicyUrl. They are integration
templates, not replacements for an existing CMP or legal review.
Public opt-out is local SDK behavior on the customer website origin. The Siteplane app cannot clear another origin's storage and exposes no anonymous retrospective visitor-delete endpoint. Owner/admin/compliance deletion is a separate protected workflow; anonymous opt-out does not remove preserved aggregates.
Browser URLs and referrers never retain query strings or fragments. Known route
templates are preferred; otherwise e-mail, UUID, token/JWT and high-entropy
path segments are replaced with :redacted. Only bounded non-sensitive UTM
values and event-specific property allowlists survive both client and server
sanitization. Raw form values, names, e-mail addresses, phone numbers, messages,
auth fields and full destination URLs are discarded.
installDataAttributeTracking sends section views only after a valid section
target is at least 50% visible for 500ms. Form starts are emitted once per form
and route view, while native validation errors are captured and deduplicated per
submit attempt. The SDK never prevents the website's own form handlers.
Every browser event receives the current normalized page path, known route
template, sanitized referrer and the acquisition UTMs captured when the SDK was
initialized. Next.js App Router sites should call the explicit adapter from a
client component whenever usePathname() changes:
const navigation = createNextAppRouterNavigationAdapter({
setPageContext: analytics.setPageContext,
trackPageView: () => analytics.track("page_view")
});
navigation.trackNavigation({
pathname,
routeTemplate: pathname,
search: window.location.search
});Pass analytics.getRouteViewId to installDataAttributeTracking, call
attributes.refresh() after an App Router transition, and dispose both the
attribute tracker and Analytics instance on unmount. Generic React apps may use
the History API fallback instead.
Siteplane editor URLs carry siteplaneSessionId,
siteplaneAdminOrigin and siteplanePreview=1. The SDK suppresses measurement
before generating IDs or sending events in that context and validates later
bridge messages against both the parent window and expected admin origin.
The separately authorized RuntimeBridge readiness probe remains a test path;
it does not enable regular preview tracking.
Server tracking contract
Import the server-only entry point and pass an absolute HTTP(S) collector endpoint explicitly:
import { trackServer } from "@siteplane/analytics/server";
const result = await trackServer(
{
event: "lead_created",
siteKey: process.env.SITEPLANE_ANALYTICS_SITE_KEY,
secretKey: process.env.SITEPLANE_ANALYTICS_SECRET_KEY,
targetId: "contact_form",
targetType: "conversion",
idempotencyKey: "lead:123",
properties: {
conversion_id: "contact_form"
}
},
{
endpoint: "https://siteplane.io/api/analytics/collect",
timeoutMs: 5000
}
);The default timeout is five seconds. Network failures, 429 and 5xx use the
same bounded three-attempt retry contract and honor Retry-After. The event ID
and serialized request remain stable across attempts. Use a stable
idempotencyKey for any retryable mutation; revenue events require it. Auth,
permanent collector, unavailable collector and network failures return typed
error codes instead of silently succeeding.
Owner activation checks
Version 0.1.22 handles Siteplane's short-lived owner test link during the normal SDK initialization. The temporary test tab sends one authorized runtime receipt and stays entirely outside visitor tracking, storage and consent writes. The editor iframe supplies its own separate suppression receipt. Ordinary website visits never enter this test path. Do not add custom probe code or copy test links into source or logs.
