Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 19.6.0
Changed: the key `Gleap.initialize` takes is now called **Project ID** (formerly "SDK key" or "API key"). The value is the same and every existing key keeps working; nothing needs to change in your app. Typings, JSDoc and the README name the parameter `projectId`.
Added: `Gleap.initialize({ projectId, sessionDuration })` takes the Project ID inside the options object, and `Gleap.initialize(undefined, { projectId })` works too. The positional form `Gleap.initialize(projectId, options)` and the legacy boolean second argument are unchanged. The options are typed as `Gleap.InitializeOptions`.

## 19.5.2
Fixed: `Gleap.setUserJwtProvider` alone never signed the session again after a page reload. When the grant cookie was missing (an IP-address host, or the cookie deleted) and the app set only the provider, without calling `setUserJwt`, the user stayed unsigned although the server reported Signed mode. The SDK now asks the provider once per page load when the server reports Signed mode for an identified session without a valid grant and no token is held, and identifies with the returned token like `setUserJwt` (a token for another user goes through the usual `identity_mismatch` recovery). A provider that returns `null` sends nothing. Also applies when the provider is set after the session loaded.
Fixed: cookies were never stored on IP-address hosts (for example `http://127.0.0.1:8080` or `http://[::1]:8080`). The SDK wrote an invalid `domain` attribute there, so the browser dropped the session and grant cookies. On an IP address they are now host-only cookies (no `domain`), and erasing them works the same way. Domain hosts and `localhost` are unchanged.
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ import Gleap from 'gleap';

### Initialize the SDK

Add the following code to initialize the Gleap JavaScript SDK. Replace "SDK-TOKEN" with your actual SDK token from the [Gleap dashboard](https://app.gleap.io).
Add the following code to initialize the Gleap JavaScript SDK. Replace "YOUR_PROJECT_ID" with your Project ID from the [Gleap dashboard](https://app.gleap.io) (Settings > Project). The Project ID was called "SDK key" or "API key" before; the value is the same and existing keys keep working.

```
Gleap.initialize("SDK-TOKEN");
Gleap.initialize("YOUR_PROJECT_ID");
```

### Data region (optional)
Expand All @@ -44,7 +44,7 @@ Projects hosted in the US data region select it before initializing. The default

```
Gleap.setRegion("us");
Gleap.initialize("SDK-TOKEN");
Gleap.initialize("YOUR_PROJECT_ID");
```

### Identify your users
Expand Down Expand Up @@ -79,10 +79,10 @@ Per-user tokens for your API tools (used as `{{session.authTokens.<name>}}`) are
Gleap.setAuthTokens({ backend: userAccessToken });
```

A valid token signs the session: the server answers with a grant that the SDK sends as the `Gleap-Grant` header on every request and hands to the messenger. It is kept in memory and in the `gleap-grant-<SDK-TOKEN>` cookie on your top-level domain (unless `Gleap.setUseCookies(false)`; on an IP address such as `127.0.0.1` it is a host-only cookie), so a page reload and a help center on a subdomain stay signed until it expires. The project's session max age (Settings > Security, 7 days by default) sets its lifetime; shorten it per app with `sessionDuration` in milliseconds:
A valid token signs the session: the server answers with a grant that the SDK sends as the `Gleap-Grant` header on every request and hands to the messenger. It is kept in memory and in the `gleap-grant-<YOUR_PROJECT_ID>` cookie on your top-level domain (unless `Gleap.setUseCookies(false)`; on an IP address such as `127.0.0.1` it is a host-only cookie), so a page reload and a help center on a subdomain stay signed until it expires. The project's session max age (Settings > Security, 7 days by default) sets its lifetime; shorten it per app with `sessionDuration` in milliseconds:

```
Gleap.initialize("SDK-TOKEN", { sessionDuration: 24 * 60 * 60 * 1000 });
Gleap.initialize("YOUR_PROJECT_ID", { sessionDuration: 24 * 60 * 60 * 1000 });
```

or later with `Gleap.setSessionDuration(24 * 60 * 60 * 1000)` (the same call as in the mobile SDKs).
Expand Down
30 changes: 27 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,39 @@
export type GleapRegion = "eu" | "us";

export namespace Gleap {
/** Options for `initialize`. */
interface InitializeOptions {
/**
* Your Project ID (Gleap dashboard: Settings > Project, or the install page).
* Only needed when `initialize` is called with the options object alone.
*/
projectId?: string;
/**
* How long (ms) a signed session stays signed before a new identify is needed.
* The server never goes beyond the project's session max age.
*/
sessionDuration?: number;
/** @deprecated Has no effect: the ping it controlled no longer exists. */
disablePing?: boolean;
}
/**
* Initializes Gleap with your Project ID (formerly called "SDK key" or "API key";
* the value is the same, existing keys keep working).
*
* `options.sessionDuration`: how long (ms) a signed session stays signed before a new
* identify is needed. The server never goes beyond the project's session max age.
* A boolean second argument is the legacy `disablePing` flag and is still accepted
* (the ping it controlled no longer exists, so it has no effect).
*
* @example Gleap.initialize("YOUR_PROJECT_ID");
* @example Gleap.initialize({ projectId: "YOUR_PROJECT_ID", sessionDuration: 86400000 });
*/
function initialize(
sdkKey: string,
options?: boolean | { disablePing?: boolean; sessionDuration?: number }
projectId: string,
options?: boolean | InitializeOptions
): void;
function initialize(
options: InitializeOptions & { projectId: string }
): void;
function sendSilentCrashReport(
description: string,
Expand Down Expand Up @@ -200,7 +224,7 @@ export namespace Gleap {
function setUserJwtProvider(provider: (() => string | null | Promise<string | null>) | null): void;
/**
* How long (ms) a signed session stays signed; the server never goes beyond the
* project's maximum. Same as `initialize(sdkKey, { sessionDuration })`. `null` drops it.
* project's maximum. Same as `initialize(projectId, { sessionDuration })`. `null` drops it.
*/
function setSessionDuration(sessionDuration: number | null): void;
/**
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"description": "JavaScript and TypeScript SDK for Gleap, an Intercom alternative with AI customer support, live chat, in-app bug reporting and customer feedback.",
"name": "gleap",
"version": "19.5.2",
"version": "19.6.0",
"main": "build/cjs/index.js",
"module": "build/esm/index.mjs",
"exports": {
Expand Down
22 changes: 16 additions & 6 deletions src/Gleap.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,20 +235,30 @@ class Gleap {
}

/**
* Initializes the SDK
* @param {*} sdkKey
* @param {boolean | { disablePing?: boolean, sessionDuration?: number }} [options]
* Initializes the SDK with your Project ID (formerly called "SDK key"; the value is
* the same, existing keys keep working).
* @param {string | { projectId: string, sessionDuration?: number }} projectId
* The Project ID, or an options object that carries it as `projectId`.
* @param {boolean | { projectId?: string, disablePing?: boolean, sessionDuration?: number }} [options]
* sessionDuration: how long (ms) a signed session stays signed; the server never goes
* beyond the project's maximum. A boolean (the legacy `disablePing` argument) and
* `disablePing` are accepted and have no effect: the ping no longer exists.
*/
static initialize(sdkKey, options) {
static initialize(projectId, options) {
const instance = this.getInstance();
if (instance.initialized) {
console.warn('Gleap already initialized.');
return;
}

// Gleap.initialize({ projectId, ...options })
if (projectId && typeof projectId === 'object') {
options = projectId;
projectId = options.projectId;
} else if (!projectId && options && typeof options === 'object' && options.projectId) {
projectId = options.projectId;
}

if (options && typeof options === 'object' && 'sessionDuration' in options) {
GleapSession.getInstance().setSessionDuration(options.sessionDuration);
}
Expand All @@ -273,7 +283,7 @@ class Gleap {

// Start session
const sessionInstance = GleapSession.getInstance();
sessionInstance.sdkKey = sdkKey;
sessionInstance.sdkKey = projectId;
sessionInstance.setOnSessionReady(() => {
// Run auto configuration.
setTimeout(() => {
Expand Down Expand Up @@ -498,7 +508,7 @@ class Gleap {
/**
* How long (ms) a signed session stays signed; the server never goes beyond
* the project's maximum. Sent with every identify. `null` drops it.
* Same as `initialize(sdkKey, { sessionDuration })`.
* Same as `initialize(projectId, { sessionDuration })`.
* @param {number | null} sessionDuration
*/
static setSessionDuration(sessionDuration) {
Expand Down
52 changes: 52 additions & 0 deletions src/GleapInitialize.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* @jest-environment jsdom
*/
import Gleap, { GleapSession } from './Gleap';

// initialize() names the key "Project ID"; the positional form keeps working.
describe('Gleap.initialize(projectId)', () => {
let startSpy;
let warnSpy;

beforeEach(() => {
Gleap.getInstance().initialized = false;
GleapSession.instance = undefined;
// startSession is an instance arrow property: stub it on the fresh singleton.
startSpy = jest.spyOn(GleapSession.getInstance(), 'startSession').mockImplementation(() => {});
warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
});

afterEach(() => {
startSpy.mockRestore();
warnSpy.mockRestore();
Gleap.getInstance().initialized = false;
});

test('positional Project ID (the former SDK key) is used as before', () => {
Gleap.initialize('PROJECT_ID_1');
expect(GleapSession.getInstance().sdkKey).toBe('PROJECT_ID_1');
expect(startSpy).toHaveBeenCalledTimes(1);
});

test('positional Project ID with options keeps sessionDuration', () => {
Gleap.initialize('PROJECT_ID_2', { sessionDuration: 60000 });
expect(GleapSession.getInstance().sdkKey).toBe('PROJECT_ID_2');
expect(GleapSession.getInstance().sessionDuration).toBe(60000);
});

test('legacy boolean second argument is still accepted', () => {
Gleap.initialize('PROJECT_ID_3', true);
expect(GleapSession.getInstance().sdkKey).toBe('PROJECT_ID_3');
});

test('options object with projectId', () => {
Gleap.initialize({ projectId: 'PROJECT_ID_4', sessionDuration: 120000 });
expect(GleapSession.getInstance().sdkKey).toBe('PROJECT_ID_4');
expect(GleapSession.getInstance().sessionDuration).toBe(120000);
});

test('projectId in the second argument when the first is empty', () => {
Gleap.initialize(undefined, { projectId: 'PROJECT_ID_5' });
expect(GleapSession.getInstance().sdkKey).toBe('PROJECT_ID_5');
});
});
4 changes: 2 additions & 2 deletions src/GleapSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default class GleapSession {
// How the last identify failed ('refused' when a refusal was logged, else
// 'network error' / 'HTTP <status>'), for setUserJwt's log line.
lastIdentifyFailure = null;
// Gleap.initialize(sdkKey, { sessionDuration }): asks the server for a
// Gleap.initialize(projectId, { sessionDuration }): asks the server for a
// shorter grant (ms). Sent with every identify.
sessionDuration = null;
// Bumped only by clearSession (logout, destroy, rejected session). An
Expand Down Expand Up @@ -1457,7 +1457,7 @@ export default class GleapSession {
return retry;
};

/** Gleap.setSessionDuration / initialize(sdkKey, { sessionDuration }): a positive number of milliseconds, or null to drop it. */
/** Gleap.setSessionDuration / initialize(projectId, { sessionDuration }): a positive number of milliseconds, or null to drop it. */
setSessionDuration = (sessionDuration) => {
if (sessionDuration === undefined || sessionDuration === null) {
this.sessionDuration = null;
Expand Down
Loading