Skip to content

Commit 52abaf0

Browse files
feat: support web worker env (#467)
1 parent af2ec5d commit 52abaf0

24 files changed

+464
-39
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ clean:
3535

3636
test: build
3737
@$(KARMA) start karma.conf.js
38+
@$(KARMA) start karma-web-worker.conf.js
3839

3940
test-sauce: build
4041
@$(KARMA) start karma.conf.js --browsers sauce_chrome_windows

karma-web-worker.conf.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module.exports = config => {
2+
config.set({
3+
frameworks: ['mocha-webworker'],
4+
files: [
5+
{
6+
pattern: 'test/web-worker.js',
7+
included: false,
8+
},
9+
{
10+
pattern: 'amplitude.js',
11+
included: false,
12+
},
13+
{
14+
pattern: 'node_modules/sinon/pkg/sinon.js',
15+
included: false,
16+
},
17+
],
18+
browsers: ['ChromeHeadless'],
19+
autoWatch: false,
20+
singleRun: true,
21+
reporters: ['mocha'],
22+
client: {
23+
mochaWebWorker: {
24+
pattern: [
25+
'test/web-worker.js',
26+
'amplitude.js',
27+
'node_modules/sinon/pkg/sinon.js'
28+
],
29+
worker: 'Worker',
30+
mocha: {
31+
ui: 'bdd'
32+
}
33+
}
34+
}
35+
});
36+
};

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"karma-firefox-launcher": "^1.0.1",
4949
"karma-mocha": "^1.3.0",
5050
"karma-mocha-reporter": "^2.2.5",
51+
"karma-mocha-webworker": "^1.3.0",
5152
"karma-sauce-launcher": "^2.0.2",
5253
"karma-sinon": "^1.0.5",
5354
"karma-sourcemap-loader": "^0.3.7",

src/amplitude-client.js

+13-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import getHost from './get-host';
1919
import baseCookie from './base-cookie';
2020
import { AmplitudeServerZone, getEventLogApi } from './server-zone';
2121
import ConfigManager from './config-manager';
22+
import GlobalScope from './global-scope';
2223

2324
/**
2425
* AmplitudeClient SDK API - instance constructor.
@@ -28,7 +29,7 @@ import ConfigManager from './config-manager';
2829
* @example var amplitudeClient = new AmplitudeClient();
2930
*/
3031
var AmplitudeClient = function AmplitudeClient(instanceName) {
31-
if (!isBrowserEnv()) {
32+
if (!isBrowserEnv() && !utils.isWebWorkerEnvironment()) {
3233
utils.log.warn(
3334
'amplitude-js will not work in a non-browser environment. If you are planning to add Amplitude to a node environment, please use @amplitude/node',
3435
);
@@ -80,7 +81,11 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
8081

8182
try {
8283
_parseConfig(this.options, opt_config);
83-
if (isBrowserEnv() && window.Prototype !== undefined && Array.prototype.toJSON) {
84+
if (
85+
(isBrowserEnv() || utils.isWebWorkerEnvironment()) &&
86+
GlobalScope.Prototype !== undefined &&
87+
Array.prototype.toJSON
88+
) {
8489
prototypeJsFix();
8590
utils.log.warn(
8691
'Prototype.js injected Array.prototype.toJSON. Deleting Array.prototype.toJSON to prevent double-stringify',
@@ -243,7 +248,7 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
243248
// Monitoring just page exits because that is the most requested feature for now
244249
// "If you're specifically trying to detect page unload events, the pagehide event is the best option."
245250
// https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
246-
window.addEventListener(
251+
GlobalScope.addEventListener(
247252
'pagehide',
248253
() => {
249254
handleVisibilityChange();
@@ -254,7 +259,7 @@ AmplitudeClient.prototype.init = function init(apiKey, opt_userId, opt_config, o
254259
}
255260
} catch (err) {
256261
utils.log.error(err);
257-
if (type(opt_config.onError) === 'function') {
262+
if (opt_config && type(opt_config.onError) === 'function') {
258263
opt_config.onError(err);
259264
}
260265
}
@@ -272,7 +277,7 @@ AmplitudeClient.prototype.deleteLowerLevelDomainCookies = function () {
272277
const cookieHost =
273278
this.options.domain && this.options.domain[0] === '.' ? this.options.domain.slice(1) : this.options.domain;
274279

275-
if (!cookieHost) {
280+
if (!cookieHost || !utils.isWebWorkerEnvironment()) {
276281
return;
277282
}
278283

@@ -751,14 +756,14 @@ var _sendParamsReferrerUserProperties = function _sendParamsReferrerUserProperti
751756
* @private
752757
*/
753758
AmplitudeClient.prototype._getReferrer = function _getReferrer() {
754-
return document.referrer;
759+
return typeof document !== 'undefined' ? document.referrer : '';
755760
};
756761

757762
/**
758763
* @private
759764
*/
760765
AmplitudeClient.prototype._getUrlParams = function _getUrlParams() {
761-
return location.search;
766+
return GlobalScope.location.search;
762767
};
763768

764769
/**
@@ -1780,7 +1785,7 @@ AmplitudeClient.prototype.sendEvents = function sendEvents() {
17801785
}
17811786
this._sending = true;
17821787
}
1783-
var protocol = this.options.forceHttps ? 'https' : 'https:' === window.location.protocol ? 'https' : 'http';
1788+
var protocol = this.options.forceHttps ? 'https' : 'https:' === GlobalScope.location.protocol ? 'https' : 'http';
17841789
var url = protocol + '://' + this.options.apiEndpoint;
17851790

17861791
// fetch events to send

src/base-cookie.js

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ const sortByEventTime = (cookies) => {
9595
// test that cookies are enabled - navigator.cookiesEnabled yields false positives in IE, need to test directly
9696
const areCookiesEnabled = (opts = {}) => {
9797
const cookieName = Constants.COOKIE_TEST_PREFIX + base64Id();
98+
if (typeof document === 'undefined') {
99+
return false;
100+
}
98101
let _areCookiesEnabled = false;
99102
try {
100103
const uid = String(new Date());

src/base64.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import UTF8 from './utf8';
2+
import GlobalScope from './global-scope';
23

34
/*
45
* Base64 encoder/decoder
@@ -9,8 +10,8 @@ var Base64 = {
910

1011
encode: function (input) {
1112
try {
12-
if (window.btoa && window.atob) {
13-
return window.btoa(unescape(encodeURIComponent(input)));
13+
if (GlobalScope.btoa && GlobalScope.atob) {
14+
return GlobalScope.btoa(unescape(encodeURIComponent(input)));
1415
}
1516
} catch (e) {
1617
//log(e);
@@ -53,8 +54,8 @@ var Base64 = {
5354

5455
decode: function (input) {
5556
try {
56-
if (window.btoa && window.atob) {
57-
return decodeURIComponent(escape(window.atob(input)));
57+
if (GlobalScope.btoa && GlobalScope.atob) {
58+
return decodeURIComponent(escape(GlobalScope.atob(input)));
5859
}
5960
} catch (e) {
6061
//log(e);

src/config-manager.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Constants from './constants';
22
import { getDynamicConfigApi } from './server-zone';
3+
import GlobalScope from './global-scope';
34
/**
45
* Dynamic Configuration
56
* Find the best server url automatically based on app users' geo location.
@@ -15,14 +16,14 @@ class ConfigManager {
1516

1617
refresh(serverZone, forceHttps, callback) {
1718
let protocol = 'https';
18-
if (!forceHttps && 'https:' !== window.location.protocol) {
19+
if (!forceHttps && 'https:' !== GlobalScope.location.protocol) {
1920
protocol = 'http';
2021
}
2122
const dynamicConfigUrl = protocol + '://' + getDynamicConfigApi(serverZone);
2223
const self = this;
23-
const isIE = window.XDomainRequest ? true : false;
24+
const isIE = GlobalScope.XDomainRequest ? true : false;
2425
if (isIE) {
25-
const xdr = new window.XDomainRequest();
26+
const xdr = new GlobalScope.XDomainRequest();
2627
xdr.open('GET', dynamicConfigUrl, true);
2728
xdr.onload = function () {
2829
const response = JSON.parse(xdr.responseText);

src/cookiestorage.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import Cookie from './cookie';
77
import localStorage from './localstorage';
88
import baseCookie from './base-cookie';
9+
import GlobalScope from './global-scope';
910

1011
var cookieStorage = function () {
1112
this.storage = null;
@@ -43,7 +44,7 @@ cookieStorage.prototype.getStorage = function () {
4344
this._options.expirationDays = opts.expirationDays || this._options.expirationDays;
4445
// localStorage is specific to subdomains
4546
this._options.domain =
46-
opts.domain || this._options.domain || (window && window.location && window.location.hostname);
47+
opts.domain || this._options.domain || (GlobalScope && GlobalScope.location && GlobalScope.location.hostname);
4748
return (this._options.secure = opts.secure || false);
4849
},
4950
get: function (name) {

src/get-host.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1+
import GlobalScope from './global-scope';
2+
13
const getHost = (url) => {
2-
const a = document.createElement('a');
3-
a.href = url;
4-
return a.hostname || location.hostname;
4+
if (url) {
5+
if (typeof document !== 'undefined') {
6+
const a = document.createElement('a');
7+
a.href = url;
8+
return a.hostname || GlobalScope.location.hostname;
9+
}
10+
if (typeof URL === 'function') {
11+
const u = new URL(url);
12+
return u.hostname || GlobalScope.location.hostname;
13+
}
14+
}
15+
return GlobalScope.location.hostname;
516
};
617

718
export default getHost;

src/get-location.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import GlobalScope from './global-scope';
2+
13
const getLocation = () => {
2-
return window.location;
4+
return GlobalScope.location;
35
};
46

57
export default getLocation;

src/global-scope.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const GlobalScope = typeof window !== 'undefined' ? window : self;
2+
export default GlobalScope;

src/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Entry point
22
import Amplitude from './amplitude';
3+
import GlobalScope from './global-scope';
34

4-
const old = (typeof window !== 'undefined' && window.amplitude) || {};
5+
const old = (typeof GlobalScope !== 'undefined' && GlobalScope.amplitude) || {};
56
const newInstance = new Amplitude();
67
newInstance._q = old._q || [];
78

src/localstorage.js

+14-7
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
* Implement localStorage to support Firefox 2-3 and IE 5-7
33
*/
44

5+
import GlobalScope from './global-scope';
6+
import WorkerStorage from './worker-storage';
7+
import utils from './utils';
8+
59
var localStorage;
610

711
if (!BUILD_COMPAT_LOCAL_STORAGE) {
8-
localStorage = window.localStorage;
12+
localStorage = GlobalScope.localStorage;
913
}
1014

1115
if (BUILD_COMPAT_LOCAL_STORAGE) {
@@ -14,9 +18,9 @@ if (BUILD_COMPAT_LOCAL_STORAGE) {
1418
var uid = new Date();
1519
var result;
1620
try {
17-
window.localStorage.setItem(uid, uid);
18-
result = window.localStorage.getItem(uid) === String(uid);
19-
window.localStorage.removeItem(uid);
21+
GlobalScope.localStorage.setItem(uid, uid);
22+
result = GlobalScope.localStorage.getItem(uid) === String(uid);
23+
GlobalScope.localStorage.removeItem(uid);
2024
return result;
2125
} catch (e) {
2226
// localStorage not available
@@ -25,12 +29,12 @@ if (BUILD_COMPAT_LOCAL_STORAGE) {
2529
};
2630

2731
if (windowLocalStorageAvailable()) {
28-
localStorage = window.localStorage;
29-
} else if (typeof window !== 'undefined' && window.globalStorage) {
32+
localStorage = GlobalScope.localStorage;
33+
} else if (typeof GlobalScope !== 'undefined' && GlobalScope.globalStorage) {
3034
// Firefox 2-3 use globalStorage
3135
// See https://developer.mozilla.org/en/dom/storage#globalStorage
3236
try {
33-
localStorage = window.globalStorage[window.location.hostname];
37+
localStorage = GlobalScope.globalStorage[GlobalScope.location.hostname];
3438
} catch (e) {
3539
// Something bad happened...
3640
}
@@ -85,6 +89,9 @@ if (BUILD_COMPAT_LOCAL_STORAGE) {
8589
} else {
8690
/* Nothing we can do ... */
8791
}
92+
} else if (utils.isWebWorkerEnvironment()) {
93+
// Web worker
94+
localStorage = new WorkerStorage();
8895
}
8996
if (!localStorage) {
9097
/* eslint-disable no-unused-vars */

src/metadata-storage.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import getLocation from './get-location';
1010
import ampLocalStorage from './localstorage';
1111
import topDomain from './top-domain';
1212
import utils from './utils';
13+
import GlobalScope from './global-scope';
1314

1415
const storageOptionExists = {
1516
[Constants.STORAGE_COOKIES]: true,
@@ -88,8 +89,8 @@ class MetadataStorage {
8889

8990
switch (this.storage) {
9091
case Constants.STORAGE_SESSION:
91-
if (window.sessionStorage) {
92-
window.sessionStorage.setItem(this.storageKey, value);
92+
if (GlobalScope.sessionStorage) {
93+
GlobalScope.sessionStorage.setItem(this.storageKey, value);
9394
}
9495
break;
9596
case Constants.STORAGE_LOCAL:
@@ -131,7 +132,7 @@ class MetadataStorage {
131132
}
132133
if (!str) {
133134
try {
134-
str = window.sessionStorage && window.sessionStorage.getItem(this.storageKey);
135+
str = GlobalScope.sessionStorage && GlobalScope.sessionStorage.getItem(this.storageKey);
135136
} catch (e) {
136137
utils.log.info(`window.sessionStorage unavailable. Reason: "${e}"`);
137138
}
@@ -187,8 +188,8 @@ class MetadataStorage {
187188
}
188189
if (!str) {
189190
try {
190-
str = window.sessionStorage && window.sessionStorage.getItem(this.storageKey);
191-
window.sessionStorage.clear();
191+
str = GlobalScope.sessionStorage && GlobalScope.sessionStorage.getItem(this.storageKey);
192+
GlobalScope.sessionStorage.clear();
192193
} catch (e) {
193194
utils.log.info(`window.sessionStorage unavailable. Reason: "${e}"`);
194195
}

src/top-domain.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import baseCookie from './base-cookie';
22
import base64Id from './base64Id';
33
import getHost from './get-host';
4+
import utils from './utils';
45

56
// Utility that finds top level domain to write to
67
const topDomain = (url) => {
@@ -9,6 +10,8 @@ const topDomain = (url) => {
910
const levels = [];
1011
const cname = '_tldtest_' + base64Id();
1112

13+
if (utils.isWebWorkerEnvironment()) return '';
14+
1215
for (let i = parts.length - 2; i >= 0; --i) {
1316
levels.push(parts.slice(i).join('.'));
1417
}

0 commit comments

Comments
 (0)