@fullstory/browser 2.0.7 → 2.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +12 -21
- package/dist/index.esm.js +70 -46
- package/dist/index.js +70 -48
- package/package.json +13 -13
- package/src/index.test.ts +99 -38
- package/src/index.ts +50 -47
package/dist/index.d.ts
CHANGED
|
@@ -11,17 +11,22 @@ import { FSApi } from '@fullstory/snippet';
|
|
|
11
11
|
* - recordCrossDomainIFrames: FullStory can record cross-domain iFrames. Defaults to `false`. Certain limitations apply and can be found [here](https://help.fullstory.com/hc/en-us/articles/360020622514-Can-FullStory-capture-content-that-is-presented-in-iframes-#h_01F1G333PYKPGZ4B42WDBV3YKV).
|
|
12
12
|
* - recordOnlyThisIFrame: FullStory can record the iFrame as its own unique session. Defaults to `false`. Additional conditions apply and can be found [here](https://help.fullstory.com/hc/en-us/articles/360020622514-Can-FullStory-capture-content-that-is-presented-in-iframes-#h_01F1G33B40Q2TPQA8MA7SF8Y5P).
|
|
13
13
|
* - devMode: In dev mode FullStory won't record sessions. Any calls to SDK methods will `console.warn` that FullStory is in `devMode`. Defaults to `false`.
|
|
14
|
+
* - sessionUid (beta): Sets the session UID passed to the `FS('init', ...)` v2 operation.
|
|
14
15
|
*/
|
|
15
16
|
export interface SnippetOptions {
|
|
16
17
|
orgId: string;
|
|
17
|
-
|
|
18
|
+
appHost?: string;
|
|
19
|
+
assetMapId?: string;
|
|
20
|
+
cookieDomain?: string;
|
|
18
21
|
debug?: boolean;
|
|
22
|
+
devMode?: boolean;
|
|
19
23
|
host?: string;
|
|
20
|
-
|
|
21
|
-
cookieDomain?: string;
|
|
24
|
+
namespace?: string;
|
|
22
25
|
recordCrossDomainIFrames?: boolean;
|
|
23
26
|
recordOnlyThisIFrame?: boolean;
|
|
24
|
-
|
|
27
|
+
sessionUid?: string;
|
|
28
|
+
script?: string;
|
|
29
|
+
startCaptureManually?: boolean;
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* A callback that will be invoked when FullStory has begun a session.
|
|
@@ -33,21 +38,7 @@ type ReadyCallback = (data: {
|
|
|
33
38
|
sessionUrl: string;
|
|
34
39
|
settings: Readonly<object>;
|
|
35
40
|
}) => void;
|
|
36
|
-
declare
|
|
37
|
-
|
|
38
|
-
_fs_cookie_domain?: string;
|
|
39
|
-
_fs_debug?: boolean;
|
|
40
|
-
_fs_dev_mode?: boolean;
|
|
41
|
-
_fs_host?: string;
|
|
42
|
-
_fs_initialized?: boolean;
|
|
43
|
-
_fs_is_outer_script?: boolean;
|
|
44
|
-
_fs_namespace?: string;
|
|
45
|
-
_fs_org?: string;
|
|
46
|
-
_fs_run_in_iframe?: boolean;
|
|
47
|
-
_fs_script?: string;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
export declare const init: (inputOptions: SnippetOptions, readyCallback?: ReadyCallback) => void;
|
|
51
|
-
export declare const isInitialized: () => boolean;
|
|
41
|
+
declare const init: (inputOptions: SnippetOptions, readyCallback?: ReadyCallback) => void;
|
|
42
|
+
declare const isInitialized: () => boolean;
|
|
52
43
|
declare const FullStory: FSApi;
|
|
53
|
-
export
|
|
44
|
+
export { FullStory, init, isInitialized };
|
package/dist/index.esm.js
CHANGED
|
@@ -14,6 +14,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
14
14
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
15
|
PERFORMANCE OF THIS SOFTWARE.
|
|
16
16
|
***************************************************************************** */
|
|
17
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
18
|
+
|
|
17
19
|
|
|
18
20
|
var __assign = function() {
|
|
19
21
|
__assign = Object.assign || function __assign(t) {
|
|
@@ -24,17 +26,21 @@ var __assign = function() {
|
|
|
24
26
|
return t;
|
|
25
27
|
};
|
|
26
28
|
return __assign.apply(this, arguments);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
32
|
+
var e = new Error(message);
|
|
33
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
27
34
|
};
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
};
|
|
36
|
+
// Module-level state set during `_init` (replaces former window `_fs_*` globals).
|
|
37
|
+
var _namespace;
|
|
38
|
+
var _initialized = false;
|
|
39
|
+
var _isDevMode = false;
|
|
40
|
+
var getFullStory = function (ns) { return (window[ns]); };
|
|
35
41
|
var ensureSnippetLoaded = function () {
|
|
36
|
-
var fs = getFullStory();
|
|
37
|
-
if (!fs) {
|
|
42
|
+
var fs = _namespace ? getFullStory(_namespace) : undefined;
|
|
43
|
+
if (!_namespace || !fs) {
|
|
38
44
|
throw Error('FullStory is not loaded, please ensure the init function is invoked before calling FullStory API functions');
|
|
39
45
|
}
|
|
40
46
|
return fs;
|
|
@@ -42,21 +48,36 @@ var ensureSnippetLoaded = function () {
|
|
|
42
48
|
var _init = function (inputOptions, readyCallback) {
|
|
43
49
|
// Make a copy so we can modify `options` if desired.
|
|
44
50
|
var options = __assign({}, inputOptions);
|
|
45
|
-
|
|
51
|
+
var ns = options.namespace || 'FS';
|
|
52
|
+
_namespace = ns;
|
|
53
|
+
if (getFullStory(ns)) {
|
|
46
54
|
console.warn('The FullStory snippet has already been defined elsewhere (likely in the <head> element)');
|
|
47
55
|
return;
|
|
48
56
|
}
|
|
57
|
+
var fsInitEnv = {};
|
|
49
58
|
// see README for details on the recordCrossDomainIFrames option
|
|
50
59
|
if (options.recordCrossDomainIFrames) {
|
|
51
|
-
|
|
60
|
+
fsInitEnv.runInIframe = true;
|
|
61
|
+
}
|
|
62
|
+
if (options.appHost) {
|
|
63
|
+
fsInitEnv.appHost = options.appHost;
|
|
64
|
+
}
|
|
65
|
+
if (options.assetMapId) {
|
|
66
|
+
fsInitEnv.assetMapId = options.assetMapId;
|
|
67
|
+
}
|
|
68
|
+
if (options.startCaptureManually) {
|
|
69
|
+
fsInitEnv.captureOnStartup = false;
|
|
52
70
|
}
|
|
53
71
|
// record the contents of this iFrame when embedded in a parent site
|
|
54
72
|
if (options.recordOnlyThisIFrame) {
|
|
55
|
-
|
|
73
|
+
fsInitEnv.isOuterScript = true;
|
|
56
74
|
}
|
|
57
75
|
// Set cookie domain if it was specified.
|
|
58
76
|
if (options.cookieDomain) {
|
|
59
|
-
|
|
77
|
+
fsInitEnv.cookieDomain = options.cookieDomain;
|
|
78
|
+
}
|
|
79
|
+
if (options.sessionUid) {
|
|
80
|
+
fsInitEnv.sessionUid = options.sessionUid;
|
|
60
81
|
}
|
|
61
82
|
if (options.debug === true) {
|
|
62
83
|
if (!options.script) {
|
|
@@ -67,11 +88,12 @@ var _init = function (inputOptions, readyCallback) {
|
|
|
67
88
|
}
|
|
68
89
|
}
|
|
69
90
|
initFS(options);
|
|
70
|
-
var fs = getFullStory();
|
|
91
|
+
var fs = getFullStory(_namespace);
|
|
71
92
|
if (!fs) {
|
|
72
|
-
console.warn('
|
|
93
|
+
console.warn('Failed to initialize FS snippet');
|
|
73
94
|
return;
|
|
74
95
|
}
|
|
96
|
+
fs('init', { env: fsInitEnv });
|
|
75
97
|
if (readyCallback) {
|
|
76
98
|
fs('observe', { type: 'start', callback: readyCallback });
|
|
77
99
|
}
|
|
@@ -80,26 +102,25 @@ var _init = function (inputOptions, readyCallback) {
|
|
|
80
102
|
fs('trackEvent', {
|
|
81
103
|
name: 'FullStory Dev Mode',
|
|
82
104
|
properties: {
|
|
83
|
-
|
|
105
|
+
message: message,
|
|
84
106
|
}
|
|
85
107
|
});
|
|
86
108
|
fs('shutdown');
|
|
87
|
-
|
|
109
|
+
_isDevMode = true;
|
|
88
110
|
console.warn(message);
|
|
89
111
|
}
|
|
90
112
|
};
|
|
91
113
|
var initOnce = function (message) { return function (inputOptions, readyCallback) {
|
|
92
|
-
if (
|
|
114
|
+
if (_initialized) {
|
|
93
115
|
if (message)
|
|
94
116
|
console.warn(message);
|
|
95
117
|
return;
|
|
96
118
|
}
|
|
97
119
|
_init(inputOptions, readyCallback);
|
|
98
|
-
|
|
120
|
+
_initialized = true;
|
|
99
121
|
}; };
|
|
100
122
|
var init = initOnce('FullStory init has already been called once, additional invocations are ignored');
|
|
101
|
-
|
|
102
|
-
var isInitialized = function () { return !!window._fs_initialized; };
|
|
123
|
+
var isInitialized = function () { return _initialized; };
|
|
103
124
|
var hasFullStoryWithFunction = function () {
|
|
104
125
|
var testNames = [];
|
|
105
126
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
@@ -113,39 +134,42 @@ var guard = function (name) { return function () {
|
|
|
113
134
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
114
135
|
args[_i] = arguments[_i];
|
|
115
136
|
}
|
|
116
|
-
if (
|
|
117
|
-
var message = "FullStory is in dev mode and is not
|
|
137
|
+
if (_isDevMode) {
|
|
138
|
+
var message = "FullStory is in dev mode and is not capturing: ".concat(name, " method not executed");
|
|
118
139
|
console.warn(message);
|
|
119
140
|
return message;
|
|
120
141
|
}
|
|
121
|
-
var fs = getFullStory();
|
|
142
|
+
var fs = _namespace ? getFullStory(_namespace) : undefined;
|
|
122
143
|
if (hasFullStoryWithFunction(name) && fs) {
|
|
123
144
|
return fs[name].apply(fs, args);
|
|
124
145
|
}
|
|
125
146
|
console.warn("FS.".concat(name, " not ready"));
|
|
126
147
|
return null;
|
|
127
148
|
}; };
|
|
128
|
-
var
|
|
129
|
-
var
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
var buildFullStoryShim = function () {
|
|
150
|
+
var FS = function (operation, options, source) {
|
|
151
|
+
var fs = ensureSnippetLoaded();
|
|
152
|
+
if (_isDevMode) {
|
|
153
|
+
var message = "FullStory is in dev mode and is not capturing: ".concat(operation, " not executed");
|
|
154
|
+
console.warn(message);
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return fs(operation, options, source);
|
|
158
|
+
};
|
|
159
|
+
FS.anonymize = guard('anonymize');
|
|
160
|
+
FS.consent = guard('consent');
|
|
161
|
+
FS.disableConsole = guard('disableConsole');
|
|
162
|
+
FS.enableConsole = guard('enableConsole');
|
|
163
|
+
FS.event = guard('event');
|
|
164
|
+
FS.getCurrentSessionURL = guard('getCurrentSessionURL');
|
|
165
|
+
FS.identify = guard('identify');
|
|
166
|
+
FS.log = guard('log');
|
|
167
|
+
FS.restart = guard('restart');
|
|
168
|
+
FS.setUserVars = guard('setUserVars');
|
|
169
|
+
FS.setVars = guard('setVars');
|
|
170
|
+
FS.shutdown = guard('shutdown');
|
|
171
|
+
return FS;
|
|
172
|
+
};
|
|
173
|
+
var FullStory = buildFullStoryShim();
|
|
150
174
|
|
|
151
|
-
export { FullStory
|
|
175
|
+
export { FullStory, init, isInitialized };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var snippet = require('@fullstory/snippet');
|
|
6
4
|
|
|
7
5
|
/******************************************************************************
|
|
@@ -18,6 +16,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
18
16
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
19
17
|
PERFORMANCE OF THIS SOFTWARE.
|
|
20
18
|
***************************************************************************** */
|
|
19
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
20
|
+
|
|
21
21
|
|
|
22
22
|
var __assign = function() {
|
|
23
23
|
__assign = Object.assign || function __assign(t) {
|
|
@@ -28,17 +28,21 @@ var __assign = function() {
|
|
|
28
28
|
return t;
|
|
29
29
|
};
|
|
30
30
|
return __assign.apply(this, arguments);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
34
|
+
var e = new Error(message);
|
|
35
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
31
36
|
};
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
};
|
|
38
|
+
// Module-level state set during `_init` (replaces former window `_fs_*` globals).
|
|
39
|
+
var _namespace;
|
|
40
|
+
var _initialized = false;
|
|
41
|
+
var _isDevMode = false;
|
|
42
|
+
var getFullStory = function (ns) { return (window[ns]); };
|
|
39
43
|
var ensureSnippetLoaded = function () {
|
|
40
|
-
var fs = getFullStory();
|
|
41
|
-
if (!fs) {
|
|
44
|
+
var fs = _namespace ? getFullStory(_namespace) : undefined;
|
|
45
|
+
if (!_namespace || !fs) {
|
|
42
46
|
throw Error('FullStory is not loaded, please ensure the init function is invoked before calling FullStory API functions');
|
|
43
47
|
}
|
|
44
48
|
return fs;
|
|
@@ -46,21 +50,36 @@ var ensureSnippetLoaded = function () {
|
|
|
46
50
|
var _init = function (inputOptions, readyCallback) {
|
|
47
51
|
// Make a copy so we can modify `options` if desired.
|
|
48
52
|
var options = __assign({}, inputOptions);
|
|
49
|
-
|
|
53
|
+
var ns = options.namespace || 'FS';
|
|
54
|
+
_namespace = ns;
|
|
55
|
+
if (getFullStory(ns)) {
|
|
50
56
|
console.warn('The FullStory snippet has already been defined elsewhere (likely in the <head> element)');
|
|
51
57
|
return;
|
|
52
58
|
}
|
|
59
|
+
var fsInitEnv = {};
|
|
53
60
|
// see README for details on the recordCrossDomainIFrames option
|
|
54
61
|
if (options.recordCrossDomainIFrames) {
|
|
55
|
-
|
|
62
|
+
fsInitEnv.runInIframe = true;
|
|
63
|
+
}
|
|
64
|
+
if (options.appHost) {
|
|
65
|
+
fsInitEnv.appHost = options.appHost;
|
|
66
|
+
}
|
|
67
|
+
if (options.assetMapId) {
|
|
68
|
+
fsInitEnv.assetMapId = options.assetMapId;
|
|
69
|
+
}
|
|
70
|
+
if (options.startCaptureManually) {
|
|
71
|
+
fsInitEnv.captureOnStartup = false;
|
|
56
72
|
}
|
|
57
73
|
// record the contents of this iFrame when embedded in a parent site
|
|
58
74
|
if (options.recordOnlyThisIFrame) {
|
|
59
|
-
|
|
75
|
+
fsInitEnv.isOuterScript = true;
|
|
60
76
|
}
|
|
61
77
|
// Set cookie domain if it was specified.
|
|
62
78
|
if (options.cookieDomain) {
|
|
63
|
-
|
|
79
|
+
fsInitEnv.cookieDomain = options.cookieDomain;
|
|
80
|
+
}
|
|
81
|
+
if (options.sessionUid) {
|
|
82
|
+
fsInitEnv.sessionUid = options.sessionUid;
|
|
64
83
|
}
|
|
65
84
|
if (options.debug === true) {
|
|
66
85
|
if (!options.script) {
|
|
@@ -71,11 +90,12 @@ var _init = function (inputOptions, readyCallback) {
|
|
|
71
90
|
}
|
|
72
91
|
}
|
|
73
92
|
snippet.initFS(options);
|
|
74
|
-
var fs = getFullStory();
|
|
93
|
+
var fs = getFullStory(_namespace);
|
|
75
94
|
if (!fs) {
|
|
76
|
-
console.warn('
|
|
95
|
+
console.warn('Failed to initialize FS snippet');
|
|
77
96
|
return;
|
|
78
97
|
}
|
|
98
|
+
fs('init', { env: fsInitEnv });
|
|
79
99
|
if (readyCallback) {
|
|
80
100
|
fs('observe', { type: 'start', callback: readyCallback });
|
|
81
101
|
}
|
|
@@ -84,26 +104,25 @@ var _init = function (inputOptions, readyCallback) {
|
|
|
84
104
|
fs('trackEvent', {
|
|
85
105
|
name: 'FullStory Dev Mode',
|
|
86
106
|
properties: {
|
|
87
|
-
|
|
107
|
+
message: message,
|
|
88
108
|
}
|
|
89
109
|
});
|
|
90
110
|
fs('shutdown');
|
|
91
|
-
|
|
111
|
+
_isDevMode = true;
|
|
92
112
|
console.warn(message);
|
|
93
113
|
}
|
|
94
114
|
};
|
|
95
115
|
var initOnce = function (message) { return function (inputOptions, readyCallback) {
|
|
96
|
-
if (
|
|
116
|
+
if (_initialized) {
|
|
97
117
|
if (message)
|
|
98
118
|
console.warn(message);
|
|
99
119
|
return;
|
|
100
120
|
}
|
|
101
121
|
_init(inputOptions, readyCallback);
|
|
102
|
-
|
|
122
|
+
_initialized = true;
|
|
103
123
|
}; };
|
|
104
124
|
var init = initOnce('FullStory init has already been called once, additional invocations are ignored');
|
|
105
|
-
|
|
106
|
-
var isInitialized = function () { return !!window._fs_initialized; };
|
|
125
|
+
var isInitialized = function () { return _initialized; };
|
|
107
126
|
var hasFullStoryWithFunction = function () {
|
|
108
127
|
var testNames = [];
|
|
109
128
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
@@ -117,41 +136,44 @@ var guard = function (name) { return function () {
|
|
|
117
136
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
118
137
|
args[_i] = arguments[_i];
|
|
119
138
|
}
|
|
120
|
-
if (
|
|
121
|
-
var message = "FullStory is in dev mode and is not
|
|
139
|
+
if (_isDevMode) {
|
|
140
|
+
var message = "FullStory is in dev mode and is not capturing: ".concat(name, " method not executed");
|
|
122
141
|
console.warn(message);
|
|
123
142
|
return message;
|
|
124
143
|
}
|
|
125
|
-
var fs = getFullStory();
|
|
144
|
+
var fs = _namespace ? getFullStory(_namespace) : undefined;
|
|
126
145
|
if (hasFullStoryWithFunction(name) && fs) {
|
|
127
146
|
return fs[name].apply(fs, args);
|
|
128
147
|
}
|
|
129
148
|
console.warn("FS.".concat(name, " not ready"));
|
|
130
149
|
return null;
|
|
131
150
|
}; };
|
|
132
|
-
var
|
|
133
|
-
var
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
var buildFullStoryShim = function () {
|
|
152
|
+
var FS = function (operation, options, source) {
|
|
153
|
+
var fs = ensureSnippetLoaded();
|
|
154
|
+
if (_isDevMode) {
|
|
155
|
+
var message = "FullStory is in dev mode and is not capturing: ".concat(operation, " not executed");
|
|
156
|
+
console.warn(message);
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
return fs(operation, options, source);
|
|
160
|
+
};
|
|
161
|
+
FS.anonymize = guard('anonymize');
|
|
162
|
+
FS.consent = guard('consent');
|
|
163
|
+
FS.disableConsole = guard('disableConsole');
|
|
164
|
+
FS.enableConsole = guard('enableConsole');
|
|
165
|
+
FS.event = guard('event');
|
|
166
|
+
FS.getCurrentSessionURL = guard('getCurrentSessionURL');
|
|
167
|
+
FS.identify = guard('identify');
|
|
168
|
+
FS.log = guard('log');
|
|
169
|
+
FS.restart = guard('restart');
|
|
170
|
+
FS.setUserVars = guard('setUserVars');
|
|
171
|
+
FS.setVars = guard('setVars');
|
|
172
|
+
FS.shutdown = guard('shutdown');
|
|
173
|
+
return FS;
|
|
174
|
+
};
|
|
175
|
+
var FullStory = buildFullStoryShim();
|
|
154
176
|
|
|
155
|
-
exports.
|
|
177
|
+
exports.FullStory = FullStory;
|
|
156
178
|
exports.init = init;
|
|
157
179
|
exports.isInitialized = isInitialized;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fullstory/browser",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.1.0-beta.0",
|
|
4
4
|
"description": "The official Fullstory browser SDK",
|
|
5
5
|
"repository": "git://github.com/fullstorydev/fullstory-browser-sdk.git",
|
|
6
6
|
"homepage": "https://github.com/fullstorydev/fullstory-browser-sdk",
|
|
@@ -25,30 +25,30 @@
|
|
|
25
25
|
"sdk"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@fullstory/snippet": "2.0.
|
|
28
|
+
"@fullstory/snippet": "2.1.0-beta.0"
|
|
29
|
+
},
|
|
30
|
+
"overrides": {
|
|
31
|
+
"brace-expansion": "^5.0.8"
|
|
29
32
|
},
|
|
30
33
|
"devDependencies": {
|
|
31
34
|
"@babel/core": "^7.8.7",
|
|
32
|
-
"@babel/eslint-parser": "^7.16.5",
|
|
33
35
|
"@babel/preset-env": "^7.10.4",
|
|
34
36
|
"@babel/register": "^7.8.6",
|
|
37
|
+
"@eslint/js": "^10.0.1",
|
|
35
38
|
"@rollup/plugin-typescript": "^11.1.0",
|
|
36
39
|
"@types/jest": "^29.5.0",
|
|
37
40
|
"@types/node": "^18.15.11",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"eslint-plugin-import": "^2.20.1",
|
|
43
|
-
"eslint-plugin-jsx-a11y": "^6.5.1",
|
|
44
|
-
"eslint-plugin-react": "^7.28.0",
|
|
45
|
-
"jest": "^29.7.0",
|
|
41
|
+
"eslint": "^10.8.0",
|
|
42
|
+
"eslint-plugin-import-x": "^4.17.1",
|
|
43
|
+
"globals": "^17.8.0",
|
|
44
|
+
"jest": "^30.4.2",
|
|
46
45
|
"jest-environment-jsdom": "^30.3.0",
|
|
47
|
-
"rimraf": "^
|
|
46
|
+
"rimraf": "^6.1.3",
|
|
48
47
|
"rollup": "^3.21.0",
|
|
49
|
-
"ts-jest": "^29.
|
|
48
|
+
"ts-jest": "^29.4.12",
|
|
50
49
|
"tslib": "^2.5.0",
|
|
51
50
|
"typescript": "^4.2.4",
|
|
51
|
+
"typescript-eslint": "^8.65.0",
|
|
52
52
|
"webpack": "^5.76.1"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FullStory as FS,
|
|
3
|
-
init,
|
|
4
|
-
isInitialized,
|
|
5
|
-
SnippetOptions,
|
|
6
|
-
} from '.';
|
|
1
|
+
import type { SnippetOptions } from '.';
|
|
7
2
|
|
|
8
3
|
const testOrg = '123';
|
|
9
4
|
|
|
5
|
+
type FsQueue = unknown[][];
|
|
6
|
+
type FsWithQueue = { q?: FsQueue };
|
|
7
|
+
|
|
8
|
+
const getFsQueue = (ns = 'FS'): FsQueue | undefined => (
|
|
9
|
+
(window[ns] as unknown as FsWithQueue | undefined)?.q
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
/** Browser-sdk init call (after snippet's own init); last matching entry wins. */
|
|
13
|
+
const findBrowserInitEnv = (ns = 'FS'): Record<string, unknown> | undefined => {
|
|
14
|
+
const initCalls = getFsQueue(ns)?.filter((call) => call[0] === 'init') ?? [];
|
|
15
|
+
// Snippet 2.1 pushes an init with orgId/host/script; browser SDK pushes a follow-up.
|
|
16
|
+
const browserInit = initCalls.length > 1 ? initCalls[initCalls.length - 1] : initCalls[0];
|
|
17
|
+
const initOptions = browserInit?.[1] as { env?: Record<string, unknown> } | undefined;
|
|
18
|
+
return initOptions?.env;
|
|
19
|
+
};
|
|
20
|
+
|
|
10
21
|
beforeEach(() => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
// Module-level init state lives in the SDK; re-import for a clean slate per test.
|
|
23
|
+
jest.resetModules();
|
|
24
|
+
Object.keys(window).forEach((key) => {
|
|
25
|
+
const value = window[key as keyof Window];
|
|
26
|
+
if (value && typeof value === 'function' && (value as FsWithQueue).q) {
|
|
27
|
+
delete window[key as keyof Window];
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
delete (window as { FS?: unknown }).FS;
|
|
17
31
|
});
|
|
18
32
|
|
|
33
|
+
const loadSdk = async () => {
|
|
34
|
+
const mod = await import('.');
|
|
35
|
+
return {
|
|
36
|
+
FS: mod.FullStory,
|
|
37
|
+
init: mod.init,
|
|
38
|
+
isInitialized: mod.isInitialized,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
19
42
|
describe('init', () => {
|
|
20
|
-
it('should throw error if not initialized with an orgId', () => {
|
|
43
|
+
it('should throw error if not initialized with an orgId', async () => {
|
|
44
|
+
const { init } = await loadSdk();
|
|
21
45
|
expect(() => { init({} as SnippetOptions); }).toThrow();
|
|
22
46
|
});
|
|
23
47
|
|
|
24
|
-
it('should throw error if API called before init', () => {
|
|
48
|
+
it('should throw error if API called before init', async () => {
|
|
49
|
+
const { FS, init } = await loadSdk();
|
|
25
50
|
try {
|
|
26
51
|
FS('log', { msg: 'my log' });
|
|
27
52
|
throw new Error('this should have thrown');
|
|
@@ -32,58 +57,65 @@ describe('init', () => {
|
|
|
32
57
|
expect(() => { FS('log', { msg: 'my log' }); }).not.toThrow();
|
|
33
58
|
});
|
|
34
59
|
|
|
35
|
-
it('should
|
|
60
|
+
it('should load FS onto the window under the default namespace', async () => {
|
|
61
|
+
const { init } = await loadSdk();
|
|
36
62
|
init({ orgId: testOrg });
|
|
37
|
-
expect(window.
|
|
63
|
+
expect((window as unknown as { FS?: unknown }).FS).toBeDefined();
|
|
38
64
|
});
|
|
39
65
|
|
|
40
|
-
it('should
|
|
66
|
+
it('should pass runInIframe via FS init env', async () => {
|
|
67
|
+
const { init } = await loadSdk();
|
|
41
68
|
init({
|
|
42
69
|
orgId: testOrg,
|
|
43
70
|
recordCrossDomainIFrames: true,
|
|
44
71
|
});
|
|
45
|
-
expect(
|
|
72
|
+
expect(findBrowserInitEnv()?.runInIframe).toBe(true);
|
|
46
73
|
});
|
|
47
74
|
|
|
48
|
-
it('should
|
|
75
|
+
it('should pass isOuterScript via FS init env', async () => {
|
|
76
|
+
const { init } = await loadSdk();
|
|
49
77
|
init({
|
|
50
78
|
orgId: testOrg,
|
|
51
79
|
recordOnlyThisIFrame: true,
|
|
52
80
|
});
|
|
53
|
-
expect(
|
|
81
|
+
expect(findBrowserInitEnv()?.isOuterScript).toBe(true);
|
|
54
82
|
});
|
|
55
83
|
|
|
56
|
-
it('should
|
|
84
|
+
it('should stop recording when initialized with devMode', async () => {
|
|
85
|
+
const { init } = await loadSdk();
|
|
57
86
|
init({
|
|
58
87
|
orgId: testOrg,
|
|
59
88
|
devMode: true,
|
|
60
89
|
});
|
|
61
90
|
|
|
62
|
-
|
|
91
|
+
const queue = getFsQueue();
|
|
92
|
+
expect(queue?.some((call) => call[0] === 'shutdown')).toBe(true);
|
|
63
93
|
});
|
|
64
94
|
|
|
65
|
-
it('should return whether initialized', () => {
|
|
66
|
-
|
|
67
|
-
expect(
|
|
95
|
+
it('should return whether initialized', async () => {
|
|
96
|
+
const { init, isInitialized } = await loadSdk();
|
|
97
|
+
expect(isInitialized()).toBe(false);
|
|
68
98
|
|
|
69
99
|
init({
|
|
70
100
|
orgId: testOrg,
|
|
71
101
|
});
|
|
72
102
|
|
|
73
|
-
|
|
74
|
-
expect(isInit).toBe(true);
|
|
103
|
+
expect(isInitialized()).toBe(true);
|
|
75
104
|
});
|
|
76
105
|
|
|
77
|
-
it('should load fs-debug.js when debug is set', () => {
|
|
106
|
+
it('should load fs-debug.js when debug is set', async () => {
|
|
107
|
+
const { init } = await loadSdk();
|
|
78
108
|
init({
|
|
79
109
|
orgId: testOrg,
|
|
80
110
|
debug: true,
|
|
81
111
|
});
|
|
82
112
|
|
|
83
|
-
|
|
113
|
+
const script = document.querySelector('script[data-fs-namespace]') as HTMLScriptElement | null;
|
|
114
|
+
expect(script?.src).toMatch(/fs-debug\.js/);
|
|
84
115
|
});
|
|
85
116
|
|
|
86
|
-
it('should pass sessionUid to FS init', () => {
|
|
117
|
+
it('should pass sessionUid to FS init', async () => {
|
|
118
|
+
const { init } = await loadSdk();
|
|
87
119
|
const sessionUid = 'session-123';
|
|
88
120
|
|
|
89
121
|
init({
|
|
@@ -91,11 +123,35 @@ describe('init', () => {
|
|
|
91
123
|
sessionUid,
|
|
92
124
|
});
|
|
93
125
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
126
|
+
expect(findBrowserInitEnv()?.sessionUid).toBe(sessionUid);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should pass cookieDomain, appHost, and assetMapId via FS init env', async () => {
|
|
130
|
+
const { init } = await loadSdk();
|
|
131
|
+
init({
|
|
132
|
+
orgId: testOrg,
|
|
133
|
+
cookieDomain: 'example.com',
|
|
134
|
+
appHost: 'app.fullstory.com',
|
|
135
|
+
assetMapId: 'map-1',
|
|
136
|
+
startCaptureManually: true,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const env = findBrowserInitEnv();
|
|
140
|
+
expect(env?.cookieDomain).toBe('example.com');
|
|
141
|
+
expect(env?.appHost).toBe('app.fullstory.com');
|
|
142
|
+
expect(env?.assetMapId).toBe('map-1');
|
|
143
|
+
expect(env?.captureOnStartup).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should use a custom namespace on window', async () => {
|
|
147
|
+
const { init, FS } = await loadSdk();
|
|
148
|
+
init({
|
|
149
|
+
orgId: testOrg,
|
|
150
|
+
namespace: 'MyFS',
|
|
151
|
+
});
|
|
97
152
|
|
|
98
|
-
expect(
|
|
153
|
+
expect((window as unknown as { MyFS?: unknown }).MyFS).toBeDefined();
|
|
154
|
+
expect(() => { FS('log', { msg: 'ok' }); }).not.toThrow();
|
|
99
155
|
});
|
|
100
156
|
});
|
|
101
157
|
|
|
@@ -111,7 +167,8 @@ describe('devMode', () => {
|
|
|
111
167
|
consoleWarnedMessage = undefined;
|
|
112
168
|
});
|
|
113
169
|
|
|
114
|
-
it('should return a message for functions invoked when in devMode', () => {
|
|
170
|
+
it('should return a message for functions invoked when in devMode', async () => {
|
|
171
|
+
const { init, FS } = await loadSdk();
|
|
115
172
|
expect(consoleWarnedMessage).toBeUndefined();
|
|
116
173
|
init({
|
|
117
174
|
orgId: testOrg,
|
|
@@ -127,7 +184,8 @@ describe('devMode', () => {
|
|
|
127
184
|
});
|
|
128
185
|
|
|
129
186
|
describe('getCurrentSessionURL', () => {
|
|
130
|
-
it('should return null before fs.js is fully bootstrapped', () => {
|
|
187
|
+
it('should return null before fs.js is fully bootstrapped', async () => {
|
|
188
|
+
const { init, FS } = await loadSdk();
|
|
131
189
|
init({ orgId: testOrg });
|
|
132
190
|
// in theory, this is a race condition - assuming that fs.js
|
|
133
191
|
// can't load by the time the following statement is executed
|
|
@@ -137,7 +195,8 @@ describe('getCurrentSessionURL', () => {
|
|
|
137
195
|
});
|
|
138
196
|
|
|
139
197
|
describe('typescript safety', () => {
|
|
140
|
-
it('provides type assistance matching the api', () => {
|
|
198
|
+
it('provides type assistance matching the api', async () => {
|
|
199
|
+
const { init, FS } = await loadSdk();
|
|
141
200
|
// Just a quick non-exhaustive check that types are working as expected.
|
|
142
201
|
// The "@ts-expect-error" declaration will fail if the types do NOT throw
|
|
143
202
|
// an error.
|
|
@@ -190,7 +249,8 @@ describe('typescript safety', () => {
|
|
|
190
249
|
expect(true).toBe(true);
|
|
191
250
|
});
|
|
192
251
|
|
|
193
|
-
it('allows the optional "source" param', () => {
|
|
252
|
+
it('allows the optional "source" param', async () => {
|
|
253
|
+
const { init, FS } = await loadSdk();
|
|
194
254
|
init({ orgId: testOrg });
|
|
195
255
|
|
|
196
256
|
FS('setProperties', {
|
|
@@ -223,6 +283,7 @@ describe('typescript safety', () => {
|
|
|
223
283
|
|
|
224
284
|
// NOTE: don't run this test, it will hang since fs.js isn't really running. It's only for typescript safety checks.
|
|
225
285
|
it.skip('provides type assistance for the async api', async () => {
|
|
286
|
+
const { init, FS } = await loadSdk();
|
|
226
287
|
init({ orgId: testOrg });
|
|
227
288
|
|
|
228
289
|
const disconnector = await FS('observeAsync', { type: 'start', callback: () => console.log('STARTED') });
|
package/src/index.ts
CHANGED
|
@@ -38,34 +38,29 @@ export interface SnippetOptions {
|
|
|
38
38
|
*/
|
|
39
39
|
type ReadyCallback = (data: { sessionUrl: string, settings: Readonly<object> }) => void;
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
_fs_initialized?: boolean;
|
|
51
|
-
_fs_is_outer_script?: boolean;
|
|
52
|
-
_fs_namespace?: string;
|
|
53
|
-
_fs_org?: string;
|
|
54
|
-
_fs_run_in_iframe?: boolean;
|
|
55
|
-
_fs_script?: string;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const getFullStory = (): FSApi | undefined => {
|
|
60
|
-
if (window._fs_namespace) {
|
|
61
|
-
return window[window._fs_namespace];
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
41
|
+
/** Env overrides passed to `FS('init', { env })` after the snippet loads. */
|
|
42
|
+
type FsInitEnv = {
|
|
43
|
+
appHost?: string;
|
|
44
|
+
assetMapId?: string;
|
|
45
|
+
captureOnStartup?: boolean;
|
|
46
|
+
cookieDomain?: string;
|
|
47
|
+
isOuterScript?: boolean;
|
|
48
|
+
runInIframe?: boolean;
|
|
49
|
+
sessionUid?: string;
|
|
64
50
|
};
|
|
65
51
|
|
|
52
|
+
// Module-level state set during `_init` (replaces former window `_fs_*` globals).
|
|
53
|
+
let _namespace: string | undefined;
|
|
54
|
+
let _initialized = false;
|
|
55
|
+
let _isDevMode = false;
|
|
56
|
+
|
|
57
|
+
const getFullStory = (ns: string): FSApi | undefined => (
|
|
58
|
+
(window as unknown as Record<string, FSApi | undefined>)[ns]
|
|
59
|
+
);
|
|
60
|
+
|
|
66
61
|
const ensureSnippetLoaded = (): FSApi => {
|
|
67
|
-
const fs = getFullStory();
|
|
68
|
-
if (!fs) {
|
|
62
|
+
const fs = _namespace ? getFullStory(_namespace) : undefined;
|
|
63
|
+
if (!_namespace || !fs) {
|
|
69
64
|
throw Error(
|
|
70
65
|
'FullStory is not loaded, please ensure the init function is invoked before calling FullStory API functions'
|
|
71
66
|
);
|
|
@@ -77,36 +72,45 @@ const ensureSnippetLoaded = (): FSApi => {
|
|
|
77
72
|
const _init = (inputOptions: SnippetOptions, readyCallback?: ReadyCallback) => {
|
|
78
73
|
// Make a copy so we can modify `options` if desired.
|
|
79
74
|
const options = { ...inputOptions };
|
|
80
|
-
|
|
75
|
+
const ns = options.namespace || 'FS';
|
|
76
|
+
_namespace = ns;
|
|
77
|
+
|
|
78
|
+
if (getFullStory(ns)) {
|
|
81
79
|
console.warn('The FullStory snippet has already been defined elsewhere (likely in the <head> element)');
|
|
82
80
|
return;
|
|
83
81
|
}
|
|
84
82
|
|
|
83
|
+
const fsInitEnv: FsInitEnv = {};
|
|
84
|
+
|
|
85
85
|
// see README for details on the recordCrossDomainIFrames option
|
|
86
86
|
if (options.recordCrossDomainIFrames) {
|
|
87
|
-
|
|
87
|
+
fsInitEnv.runInIframe = true;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
if (options.appHost) {
|
|
91
|
-
|
|
91
|
+
fsInitEnv.appHost = options.appHost;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
if (options.assetMapId) {
|
|
95
|
-
|
|
95
|
+
fsInitEnv.assetMapId = options.assetMapId;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
if (options.startCaptureManually) {
|
|
99
|
-
|
|
99
|
+
fsInitEnv.captureOnStartup = false;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// record the contents of this iFrame when embedded in a parent site
|
|
103
103
|
if (options.recordOnlyThisIFrame) {
|
|
104
|
-
|
|
104
|
+
fsInitEnv.isOuterScript = true;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// Set cookie domain if it was specified.
|
|
108
108
|
if (options.cookieDomain) {
|
|
109
|
-
|
|
109
|
+
fsInitEnv.cookieDomain = options.cookieDomain;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (options.sessionUid) {
|
|
113
|
+
fsInitEnv.sessionUid = options.sessionUid;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
if (options.debug === true) {
|
|
@@ -119,16 +123,14 @@ const _init = (inputOptions: SnippetOptions, readyCallback?: ReadyCallback) => {
|
|
|
119
123
|
|
|
120
124
|
initFS(options);
|
|
121
125
|
|
|
122
|
-
const fs = getFullStory();
|
|
126
|
+
const fs = getFullStory(_namespace);
|
|
123
127
|
|
|
124
128
|
if (!fs) {
|
|
125
129
|
console.warn('Failed to initialize FS snippet');
|
|
126
130
|
return;
|
|
127
131
|
}
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
fs('init', { env: { sessionUid: options.sessionUid } });
|
|
131
|
-
}
|
|
133
|
+
fs('init', { env: fsInitEnv });
|
|
132
134
|
|
|
133
135
|
if (readyCallback) {
|
|
134
136
|
fs('observe', { type: 'start', callback: readyCallback });
|
|
@@ -143,38 +145,37 @@ const _init = (inputOptions: SnippetOptions, readyCallback?: ReadyCallback) => {
|
|
|
143
145
|
}
|
|
144
146
|
});
|
|
145
147
|
fs('shutdown');
|
|
146
|
-
|
|
148
|
+
_isDevMode = true;
|
|
147
149
|
console.warn(message);
|
|
148
150
|
}
|
|
149
151
|
};
|
|
150
152
|
|
|
151
153
|
const initOnce = (message) => (inputOptions: SnippetOptions, readyCallback?: ReadyCallback) => {
|
|
152
|
-
if (
|
|
154
|
+
if (_initialized) {
|
|
153
155
|
if (message) console.warn(message);
|
|
154
156
|
return;
|
|
155
157
|
}
|
|
156
158
|
_init(inputOptions, readyCallback);
|
|
157
|
-
|
|
159
|
+
_initialized = true;
|
|
158
160
|
};
|
|
159
161
|
|
|
160
|
-
|
|
162
|
+
const init = initOnce('FullStory init has already been called once, additional invocations are ignored');
|
|
161
163
|
|
|
162
|
-
|
|
163
|
-
export const isInitialized = () => !!window._fs_initialized;
|
|
164
|
+
const isInitialized = () => _initialized;
|
|
164
165
|
|
|
165
|
-
const hasFullStoryWithFunction = (...testNames) => {
|
|
166
|
+
const hasFullStoryWithFunction = (...testNames:string[]) => {
|
|
166
167
|
const fs = ensureSnippetLoaded();
|
|
167
168
|
return testNames.every((current) => fs[current]);
|
|
168
169
|
};
|
|
169
170
|
|
|
170
171
|
const guard = (name) => (...args) => {
|
|
171
|
-
if (
|
|
172
|
+
if (_isDevMode) {
|
|
172
173
|
const message = `FullStory is in dev mode and is not capturing: ${name} method not executed`;
|
|
173
174
|
console.warn(message);
|
|
174
175
|
return message;
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
const fs = getFullStory();
|
|
178
|
+
const fs = _namespace ? getFullStory(_namespace) : undefined;
|
|
178
179
|
if (hasFullStoryWithFunction(name) && fs) {
|
|
179
180
|
return fs[name](...args);
|
|
180
181
|
}
|
|
@@ -186,7 +187,7 @@ const buildFullStoryShim = (): FSApi => {
|
|
|
186
187
|
const FS = (operation, options, source) => {
|
|
187
188
|
const fs = ensureSnippetLoaded();
|
|
188
189
|
|
|
189
|
-
if (
|
|
190
|
+
if (_isDevMode) {
|
|
190
191
|
const message = `FullStory is in dev mode and is not capturing: ${operation} not executed`;
|
|
191
192
|
console.warn(message);
|
|
192
193
|
return undefined;
|
|
@@ -210,4 +211,6 @@ const buildFullStoryShim = (): FSApi => {
|
|
|
210
211
|
return FS as FSApi;
|
|
211
212
|
};
|
|
212
213
|
|
|
213
|
-
|
|
214
|
+
const FullStory: FSApi = buildFullStoryShim();
|
|
215
|
+
|
|
216
|
+
export { FullStory, init, isInitialized };
|