@cside.dev/device-intelligence
v0.0.1
Published
Load the cside Device Intelligence script and collect client telemetry
Readme
@cside.dev/device-intelligence
Load the cside Device Intelligence script and collect client telemetry, with types.
Installation
# npm
npm i @cside.dev/device-intelligence
# yarn
yarn add @cside.dev/device-intelligence
# pnpm
pnpm i @cside.dev/device-intelligenceMinimum release age
We recommend configuring a minimum release age so your package manager only installs versions that have been published for at least 7 days. Compromised package versions are typically detected and unpublished within days of publication, so a cooldown protects you from npm supply chain attacks. This applies to all your dependencies, not just this package.
# npm (.npmrc, in days)
min-release-age=7# yarn (.yarnrc.yml, as a duration string)
npmMinimalAgeGate: "7d"# pnpm (pnpm-workspace.yaml, in minutes)
minimumReleaseAge: 10080Usage
import { initDeviceIntelligence } from "@cside.dev/device-intelligence";
const { sendClientTelemetry } = await initDeviceIntelligence({
teamID: "your-team-id",
});
const { token, errors } = await sendClientTelemetry({ userId: "u_123" });initDeviceIntelligence appends the Device Intelligence script to <head> and resolves once it has loaded and exposed sendClientTelemetry. Send the returned token to your backend to look up the device.
sendClientTelemetry never rejects: when telemetry could not be collected it resolves with { token: null, errors }.
One team per page. The script exposes a single, unscoped global, so while one team is loaded or loading, a call for a different team loads nothing. It resolves with a
sendClientTelemetrythat reports the conflict instead:{ token: null, errors: ["[cside] Unable to initialize ID b, ID a is already initialized."] }. Calling it again for the same team is safe: it joins the load that is already running, or starts a new one when the previous attempt failed. Because it joins rather than starting a second load,timeoutis the one the first call passed. A failed attempt claims nothing, so any team can be initialized after one.
React
Calling it from an effect is fine, including under StrictMode, where the effect runs twice: the second call joins the first one's load instead of appending another tag.
import {
initDeviceIntelligence,
type SendClientTelemetry,
} from "@cside.dev/device-intelligence";
import { useEffect, useState } from "react";
export function useDeviceIntelligence(teamID: string) {
const [send, setSend] = useState<SendClientTelemetry>();
useEffect(() => {
let active = true;
initDeviceIntelligence({ teamID }).then(({ sendClientTelemetry }) => {
// Wrapped in a callback so React stores the function itself.
if (active) setSend(() => sendClientTelemetry);
});
return () => {
active = false;
};
}, [teamID]);
return send;
}Options
| Option | Type | Default | Description |
| --------- | -------- | ------- | -------------------------------------------------------------------------------------------------- |
| teamID | string | required | Your cside team ID. The script is loaded from https://{teamID}.csidefd.com/client.js. |
| timeout | number | 10000 | How long to wait for the script to load, in milliseconds, before the returned promise rejects. |
API
initDeviceIntelligence(options)
Returns a Promise<{ sendClientTelemetry }>.
- Loads
https://{teamID}.csidefd.com/client.jswithasyncandreferrerpolicy="origin", unless a tag for that URL is already on the page (injected by@cside.dev/vite,@cside.dev/next, or the dashboard bootstrap), in which case it waits for that one. - Rejects when the script fails to load, when it does not load within
timeout, or when it loads without exposingsendClientTelemetry. A call for a different team does not reject: it resolves with asendClientTelemetrythat reports the conflict. - After a failed load, a later call starts over: the failed tag is never waited on again, one this package appended is removed from the page, and no team is held.
- Server-side (no
document) it resolves with asendClientTelemetrythat returns{ token: null, errors }instead of throwing, so shared code can call it unguarded. That call does not count as the one initialization.
sendClientTelemetry(externalIds?)
Collects device signals and exchanges them for a device token.
const { token, errors } = await sendClientTelemetry({ userId: "u_123" });externalIds is an optional map of your own identifiers to attach to the collected signals. Resolves with { token } on success and { token: null, errors } on failure.
buildDeviceIntelligenceScriptUrl(teamID)
Returns the script URL for a team, so you can allowlist it in a Content-Security-Policy without hardcoding the format:
buildDeviceIntelligenceScriptUrl("abc123"); // https://abc123.csidefd.com/client.jsContent-Security-Policy
The script is loaded from your team subdomain and posts telemetry to the cside edge:
script-src https://{teamID}.csidefd.com;
connect-src https://edge.csidefd.com;Documentation
See the cside docs for more.
