Skip to content

Commit 8163706

Browse files
committed
QS
1 parent b6df071 commit 8163706

File tree

3 files changed

+98
-19
lines changed

3 files changed

+98
-19
lines changed

README.md

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,52 @@
11
# Telemetry Deck JavaScript SDK
22

3-
This package allows you to send signals to [TelemetryDeck](https://telemetrydeck.com) from your JavaScript code.
3+
This package allows you to send signals to [TelemetryDeck](https://telemetrydeck.com) from JavaScript code.
4+
5+
> [!NOTE]
6+
> If you want to use TelemetryDeck for your blog or static website, we recommend the [TelemetryDeck Web SDK](https://github.com/TelemetryDeck/WebSDK) instead of this JavaScript SDK.
47
58
It has no dependencies and supports **modern evergreen browsers** and modern versions of Node.js with support for [cryptography](https://caniuse.com/cryptography).
69

7-
Signals sent with this SDK do not send any default values, besides signal `type`, `appID`, `user` and `sessionID`.
10+
## Usage
811

9-
If you want to use this package in your web application, see recommended parameters below.
12+
### Usage in Browser Based Applications that use a bundler (React, Vue, Angular, Svelte, Ember, …)
1013

11-
## Usage
14+
#### 1. Install the package
1215

13-
### 📦 Advanced usage for applications that use a bundler (like Webpack, Rollup, …)
16+
Please install the package using npm or the package manager of your choice
17+
18+
#### 2. Initialize TelemetryDeck
19+
20+
Initialize the TelemetryDeck SDK with your app ID and your user's user identifer.
21+
22+
```javascript
23+
import TelemetryDeck from '@telemetrydeck/sdk';
24+
25+
const td = new TelemetryDeck({
26+
appID: '<YOUR_APP_ID>'
27+
user: '<YOUR_USER_IDENTIFIER>',
28+
});
29+
```
30+
31+
Please replace `<YOUR_APP_ID>` with the app ID in TelemetryDeck ([Dashboard](https://dashboard.telemetrydeck.com) -> App -> Set Up App).
32+
33+
You also need to identify your logged in user. Instead of `<YOUR_USER_IDENTIFIER>`, pass in any string that uniquely identifies your user, such as an email address. It will be cryptographically anonymized with a hash function.
34+
35+
```javascript
36+
// Basic signal
37+
td.signal('<SIGNAL_TYPE>');
38+
39+
// Advanced: Signal with custom payload
40+
td.signal('<SIGNAL_TYPE>', {
41+
volume: '11',
42+
});
43+
```
44+
45+
46+
47+
If you want to pass optional parameters to the signal being sent, add them to the optional payload object.
48+
49+
### Usage in Node.js Applications
1450

1551
After installing the package via NPM, use it like this:
1652

@@ -35,10 +71,29 @@ Please replace `YOUR_APP_ID` with the app ID you received from TelemetryDeck. If
3571

3672
If you want to pass optional parameters to the signal being sent, add them to the optional payload object.
3773

38-
## Usage with React Native
74+
75+
#### Usage with React Native
3976

4077
React Native does not support the `crypto` module, which is required for the SDK to work. We found [react-native-quick-crypto](https://github.com/margelo/react-native-quick-crypto) to be a suitable polyfill. Please note that this is not an officially supported solution.
4178

79+
80+
## Advanced Initalization Options
81+
82+
See [Sauce](./src/telemetrydeck.js#L6-L15) for a full list of options.
83+
84+
85+
86+
87+
88+
89+
90+
91+
92+
### 📦 Advanced usage for applications that use a bundler (like Webpack, Rollup, …)
93+
94+
95+
96+
4297
## Queueing Signals
4398

4499
The `TelemetryDeck` class comes with a built-in queuing mechanism for storing signals until they are flushed in a single request. Queued signals are sent with `receivedAt` prefilled with the time they were queued.

src/telemetrydeck.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import { version } from './utils/version.js';
99
* @property {string} appID the app ID to send telemetry data to
1010
* @property {string} clientUser the clientUser ID to send telemetry data to
1111
* @property {string} [target] the target URL to send telemetry data to
12-
* @property {string} [sessionID]
13-
* @property {string} [salt]
14-
* @property {boolean} [testMode]
15-
* @property {Store} [store]
12+
* @property {string} [sessionID] An optional session ID to include in each signal
13+
* @property {string} [salt] A salt to use when hashing the clientUser ID
14+
* @property {boolean} [testMode] If "true", signals will be marked as test signals and only show up in Test Mode in the Dashbaord
15+
* @property {Store} [store] A store to use for queueing signals
16+
* @property {Function} [cryptoDigest] A function to use for calculating the SHA-256 hash of the clientUser ID. Null to use the browser's built-in crypto.subtle.digest function.
1617
*/
1718

1819
export default class TelemetryDeck {
@@ -27,7 +28,7 @@ export default class TelemetryDeck {
2728
* @param {TelemetryDeckOptions} options
2829
*/
2930
constructor(options = {}) {
30-
const { target, appID, clientUser, sessionID, salt, testMode, store } = options;
31+
const { target, appID, clientUser, sessionID, salt, testMode, store, cryptoDigest } = options;
3132

3233
if (!appID) {
3334
throw new Error('appID is required');
@@ -40,6 +41,7 @@ export default class TelemetryDeck {
4041
this.sessionID = sessionID ?? randomString();
4142
this.salt = salt;
4243
this.testMode = testMode ?? this.testMode;
44+
this.cryptoDigest = cryptoDigest;
4345
}
4446

4547
/**
@@ -81,6 +83,23 @@ export default class TelemetryDeck {
8183
return flushPromise;
8284
}
8385

86+
_clientUser = ''
87+
_clientUserHashed = ''
88+
89+
/**
90+
* Return a hashed version of the clientUser ID, with caching to avoid unnecessary hashing.
91+
*
92+
* @param {string} clientUser The clientUser ID to hash
93+
*/
94+
async _hashedClientUser(clientUser) {
95+
if (clientUser !== this._clientUser) {
96+
this._clientUserHashed = await sha256([this.clientUser, this.salt].join(''), this.cryptoDigest);
97+
this._clientUser = this.clientUser;
98+
}
99+
100+
return this._clientUserHashed;
101+
}
102+
84103
async _build(type, payload, options, receivedAt) {
85104
const { appID, salt, testMode } = this;
86105
let { clientUser, sessionID } = this;
@@ -101,7 +120,7 @@ export default class TelemetryDeck {
101120
throw new Error(`TelemetryDeck: "clientUser" is not set`);
102121
}
103122

104-
clientUser = await sha256([clientUser, salt].join(''));
123+
clientUser = await this._hashedClientUser(clientUser);
105124

106125
const body = {
107126
clientUser,

src/utils/sha256.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { getCrypto } from './crypto.js';
2-
3-
// https://stackoverflow.com/a/48161723/54547
4-
export async function sha256(message) {
5-
const crypto = await getCrypto();
6-
1+
/**
2+
* Calculate the SHA-256 hash of a string using a provided crypto digest function.
3+
* Defaults to globalThis.crypto.subtle.digest if available.
4+
*
5+
* // https://stackoverflow.com/a/48161723/54547
6+
*
7+
* @param {Function} cryptoDigest
8+
* @param {string} message
9+
* @returns {Promise<string>}
10+
*/
11+
export async function sha256(message, cryptoDigest = globalThis?.crypto?.subtle?.digest) {
712
// encode as UTF-8
813
const messageBuffer = new TextEncoder().encode(message);
914

1015
// hash the message
11-
const hashBuffer = await crypto.subtle.digest('SHA-256', messageBuffer);
16+
const hashBuffer = await cryptoDigest('SHA-256', messageBuffer);
1217

1318
// convert ArrayBuffer to Array
1419
const hashArray = [...new Uint8Array(hashBuffer)];

0 commit comments

Comments
 (0)