Skip to content

Commit

Permalink
prepare 2.10.0 release (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
eli-darkly authored Apr 20, 2019
1 parent 73598dd commit a00682c
Show file tree
Hide file tree
Showing 40 changed files with 2,445 additions and 2,072 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
All notable changes to the LaunchDarkly client-side JavaScript SDKs will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org).

## [2.10.0] - 2019-04-19
### Added:
- Generated TypeDoc documentation for all types, properties, and methods is now available online at [https://launchdarkly.github.io/js-client/](https://launchdarkly.github.io/js-client/). Currently this will only be for the latest released version.
- The SDK now allows you to specify an anonymous user without a key (i.e. the `anonymous` property is `true`, and there is no `key` property). In that case, the SDK will generate a UUID and send that as the user key. It will also cache this generated key in local storage (if local storage is available) so that anonymous users in the same browser will always get the same key.

### Fixed:
- Setting user attributes to non-string values when a string was expected would prevent evaluations and analytics events from working. The SDK will now convert attribute values to strings as needed.

## [2.9.7] - 2019-04-16
### Fixed:
- If there are pending analytics events when the page is being closed, the SDK normally attempts to deliver them by making a synchronous HTTP request. Chrome, as of version 73, does not allow this and logs an error. An upcoming release will change how events are sent, but as a temporary measure to avoid these errors, the SDK will now simply discard any pending events when the page is being closed _if_ the browser is Chrome version 73 or higher. In other browsers, there is no change. Note that this means that in Chrome 73, some events may be lost; that was already the case. The purpose of this patch is simply to avoid triggering errors. ([#178](https://github.com/launchdarkly/js-client-private/pull/178))
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ var user = { key: 'user.example.com' };
var client = LDClient.initialize('YOUR_CLIENT_SIDE_ID', user);
```

The user object can contain any of the properties described [here](https://docs.launchdarkly.com/docs/targeting-users). The SDK always has a single current user; you can change it after initialization (see "Changing users").
The user object can contain any of the properties described [here](https://docs.launchdarkly.com/docs/targeting-users). The SDK always has a single current user; you can change it after initialization (see "Changing users"). If you want the SDK to generate a unique key for the user, omit the `key` property and set the `anonymous` property to `true`.

The client is initialized asynchronously, so if you want to determine when the client is ready to evaluate feature flags, use the `ready` event, or the Promise-based method `waitForInitialization()`:

Expand Down Expand Up @@ -305,7 +305,7 @@ client.identify(newUser, hash, function() {

For an additional overview with code samples, see the online [JavaScript SDK Reference](https://docs.launchdarkly.com/docs/js-sdk-reference).

The authoritative full description of all properties and methods is in the TypeScript declaration files for [`ldclient-js`](https://github.com/launchdarkly/js-client/blob/master/packages/ldclient-js/typings.d.ts) and [`ldclient-js-common`](https://github.com/launchdarkly/js-client/blob/master/packages/ldclient-js-common/typings.d.ts) (a common package used by LaunchDarkly's JavaScript, React, and Electron SDKs).
The authoritative full description of all properties, types, and methods is the [online TypeScript documentation](https://launchdarkly.github.io/js-client/). If you are not using TypeScript, then the types are only for your information and are not enforced, although the properties and methods are still the same as described in the documentation.

For examples of using the SDK in a simple JavaScript application, see [`hello-js`](https://github.com/launchdarkly/hello-js) and [`hello-bootstrap`](https://github.com/launchdarkly/hello-bootstrap).

Expand Down
22 changes: 22 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
jobs:
- job: build
pool:
vmImage: 'vs2017-win2016'
steps:
- task: PowerShell@2
displayName: 'install dependencies'
inputs:
targetType: inline
script: |
node --version
npm install
- task: PowerShell@2
displayName: 'build'
inputs:
targetType: inline
script: npm run build
- task: PowerShell@2
displayName: 'test'
inputs:
targetType: inline
script: npm test
8 changes: 7 additions & 1 deletion docs/typedoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
// the properties are equivalent to the command-line options described here:
// https://typedoc.org/api/

let version = process.env.VERSION;
if (!version) {
const package = require('../packages/ldclient-js/package.json');
version = package.version;
}

module.exports = {
out: './build/html',
name: 'LaunchDarkly JavaScript SDK',
name: 'LaunchDarkly JavaScript SDK (' + version + ')',
readme: 'none', // don't add a home page with a copy of README.md
mode: 'file', // don't treat "typings.d.ts" itself as a parent module
includeDeclarations: true, // allows it to process a .d.ts file instead of actual TS code
Expand Down
9 changes: 4 additions & 5 deletions packages/ldclient-js-common/package-lock.json

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

3 changes: 2 additions & 1 deletion packages/ldclient-js-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
},
"dependencies": {
"base64-js": "1.3.0",
"fast-deep-equal": "2.0.1"
"fast-deep-equal": "2.0.1",
"uuid": "3.3.2"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/ldclient-js-common/src/EventProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default function EventProcessor(platform, options, environmentId, logger,
}
};

processor.flush = function(sync) {
processor.flush = function() {
if (disabled) {
return Promise.resolve();
}
Expand All @@ -99,7 +99,7 @@ export default function EventProcessor(platform, options, environmentId, logger,
}
queue = [];
logger.debug(messages.debugPostingEvents(eventsToSend.length));
return eventSender.sendEvents(eventsToSend, sync).then(responseInfo => {
return eventSender.sendEvents(eventsToSend).then(responseInfo => {
if (responseInfo) {
if (responseInfo.serverTime) {
lastKnownPastTime = responseInfo.serverTime;
Expand Down
93 changes: 43 additions & 50 deletions packages/ldclient-js-common/src/EventSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@ export default function EventSender(platform, eventsUrl, environmentId, imageCre
const imageUrl = eventsUrl + '/a/' + environmentId + '.gif';
const sender = {};

function loadUrlUsingImage(src, onDone) {
function loadUrlUsingImage(src) {
const img = new window.Image();
if (onDone) {
img.addEventListener('load', onDone);
}
img.src = src;
}

function getResponseInfo(xhr) {
const ret = { status: xhr.status };
const dateStr = xhr.getResponseHeader('Date');
function getResponseInfo(result) {
const ret = { status: result.status };
const dateStr = result.header('date');
if (dateStr) {
const time = Date.parse(dateStr);
if (time) {
Expand All @@ -28,59 +25,55 @@ export default function EventSender(platform, eventsUrl, environmentId, imageCre
return ret;
}

function sendChunk(events, usePost, sync) {
function sendChunk(events, usePost) {
const createImage = imageCreator || loadUrlUsingImage;
const jsonBody = JSON.stringify(events);
const send = onDone => {
function createRequest(canRetry) {
const xhr = platform.newHttpRequest();
xhr.open('POST', postUrl, !sync);
utils.addLDHeaders(xhr, platform);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-LaunchDarkly-Event-Schema', '3');
if (!sync) {
xhr.addEventListener('load', () => {
if (xhr.status >= 400 && errors.isHttpErrorRecoverable(xhr.status) && canRetry) {
createRequest(false).send(jsonBody);
} else {
onDone(getResponseInfo(xhr));
}
});

function doPostRequest(canRetry) {
const headers = utils.extend(
{
'Content-Type': 'application/json',
'X-LaunchDarkly-Event-Schema': '3',
},
utils.getLDHeaders(platform)
);
return platform
.httpRequest('POST', postUrl, headers, jsonBody)
.promise.then(result => {
if (!result) {
// This was a response from a fire-and-forget request, so we won't have a status.
return;
}
if (result.status >= 400 && errors.isHttpErrorRecoverable(result.status) && canRetry) {
return doPostRequest(false);
} else {
return getResponseInfo(result);
}
})
.catch(() => {
if (canRetry) {
xhr.addEventListener('error', () => {
createRequest(false).send(jsonBody);
});
return doPostRequest(false);
}
}
return xhr;
}
if (usePost) {
createRequest(true).send(jsonBody);
} else {
const src = imageUrl + '?d=' + utils.base64URLEncode(jsonBody);
createImage(src, sync ? null : onDone);
}
};
return Promise.reject();
});
}

if (sync) {
send();
if (usePost) {
return doPostRequest(true).catch(() => {});
} else {
return new Promise(resolve => {
send(resolve);
});
const src = imageUrl + '?d=' + utils.base64URLEncode(jsonBody);
createImage(src);
return Promise.resolve();
// We do not specify an onload handler for the image because we don't want the client to wait around
// for the image to load - it won't provide a server response, there's nothing to be done.
}
}

sender.sendEvents = function(events, sync) {
if (!platform.newHttpRequest) {
return Promise.resolve();
}
// Workaround for non-support of sync XHR in some browsers - https://github.com/launchdarkly/js-client/issues/147
if (sync && !(platform.httpAllowsSync && platform.httpAllowsSync())) {
sender.sendEvents = function(events) {
if (!platform.httpRequest) {
return Promise.resolve();
}
const canPost = platform.httpAllowsPost();
const finalSync = sync === undefined ? false : sync;
let chunks;
if (canPost) {
// no need to break up events into chunks if we can send a POST
Expand All @@ -90,9 +83,9 @@ export default function EventSender(platform, eventsUrl, environmentId, imageCre
}
const results = [];
for (let i = 0; i < chunks.length; i++) {
results.push(sendChunk(chunks[i], canPost, finalSync));
results.push(sendChunk(chunks[i], canPost));
}
return sync ? Promise.resolve() : Promise.all(results);
return Promise.all(results);
};

return sender;
Expand Down
14 changes: 4 additions & 10 deletions packages/ldclient-js-common/src/Identity.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import * as utils from './utils';

function sanitizeUser(u) {
const sane = utils.clone(u);
if (sane.key) {
sane.key = sane.key.toString();
}
return sane;
}

export default function Identity(initialUser, onChange) {
const ident = {};
let user;

ident.setUser = function(u) {
user = sanitizeUser(u);
onChange && onChange(utils.clone(user));
user = utils.sanitizeUser(u);
if (user && onChange) {
onChange(utils.clone(user));
}
};

ident.getUser = function() {
Expand Down
Loading

0 comments on commit a00682c

Please sign in to comment.