@segment/analytics-browser-actions-fullstory 1.0.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/package.json +21 -0
- package/src/__tests__/fullstory.test.ts +205 -0
- package/src/generated-types.ts +16 -0
- package/src/identifyUser/generated-types.ts +26 -0
- package/src/identifyUser/index.ts +123 -0
- package/src/index.ts +71 -0
- package/src/trackEvent/generated-types.ts +14 -0
- package/src/trackEvent/index.ts +37 -0
- package/src/types.ts +10 -0
- package/src/viewedPage/generated-types.ts +14 -0
- package/src/viewedPage/index.ts +45 -0
- package/tsconfig.json +9 -0
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@segment/analytics-browser-actions-fullstory",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "./dist/cjs",
|
|
6
|
+
"module": "./dist/esm",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "yarn build:esm && yarn build:cjs",
|
|
9
|
+
"build:cjs": "tsc --module commonjs --outDir ./dist/cjs",
|
|
10
|
+
"build:esm": "tsc --outDir ./dist/esm"
|
|
11
|
+
},
|
|
12
|
+
"typings": "./dist/esm",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@fullstory/browser": "^1.4.9",
|
|
15
|
+
"@segment/actions-core": "^3.71.0",
|
|
16
|
+
"@segment/browser-destination-runtime": "^1.0.0"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@segment/analytics-next": "*"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { Analytics, Context } from '@segment/analytics-next'
|
|
2
|
+
import fullstory, { destination } from '..'
|
|
3
|
+
import { Subscription } from '@segment/browser-destination-runtime/types'
|
|
4
|
+
|
|
5
|
+
const example: Subscription[] = [
|
|
6
|
+
{
|
|
7
|
+
partnerAction: 'trackEvent',
|
|
8
|
+
name: 'Track Event',
|
|
9
|
+
enabled: true,
|
|
10
|
+
subscribe: 'type = "track"',
|
|
11
|
+
mapping: {
|
|
12
|
+
name: {
|
|
13
|
+
'@path': '$.name'
|
|
14
|
+
},
|
|
15
|
+
properties: {
|
|
16
|
+
'@path': '$.properties'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
partnerAction: 'identifyUser',
|
|
22
|
+
name: 'Identify User',
|
|
23
|
+
enabled: true,
|
|
24
|
+
subscribe: 'type = "identify"',
|
|
25
|
+
mapping: {
|
|
26
|
+
anonymousId: {
|
|
27
|
+
'@path': '$.anonymousId'
|
|
28
|
+
},
|
|
29
|
+
userId: {
|
|
30
|
+
'@path': '$.userId'
|
|
31
|
+
},
|
|
32
|
+
email: {
|
|
33
|
+
'@path': '$.traits.email'
|
|
34
|
+
},
|
|
35
|
+
traits: {
|
|
36
|
+
'@path': '$.traits'
|
|
37
|
+
},
|
|
38
|
+
displayName: {
|
|
39
|
+
'@path': '$.traits.name'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
test('can load fullstory', async () => {
|
|
46
|
+
const [event] = await fullstory({
|
|
47
|
+
orgId: 'thefullstory.com',
|
|
48
|
+
subscriptions: example
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
jest.spyOn(destination.actions.trackEvent, 'perform')
|
|
52
|
+
jest.spyOn(destination, 'initialize')
|
|
53
|
+
|
|
54
|
+
await event.load(Context.system(), {} as Analytics)
|
|
55
|
+
expect(destination.initialize).toHaveBeenCalled()
|
|
56
|
+
|
|
57
|
+
const ctx = await event.track?.(
|
|
58
|
+
new Context({
|
|
59
|
+
type: 'track',
|
|
60
|
+
properties: {
|
|
61
|
+
banana: '📞'
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
expect(destination.actions.trackEvent.perform).toHaveBeenCalled()
|
|
67
|
+
expect(ctx).not.toBeUndefined()
|
|
68
|
+
|
|
69
|
+
const scripts = window.document.querySelectorAll('script')
|
|
70
|
+
expect(scripts).toMatchInlineSnapshot(`
|
|
71
|
+
NodeList [
|
|
72
|
+
<script
|
|
73
|
+
crossorigin="anonymous"
|
|
74
|
+
src="https://edge.fullstory.com/s/fs.js"
|
|
75
|
+
/>,
|
|
76
|
+
<script>
|
|
77
|
+
// the emptiness
|
|
78
|
+
</script>,
|
|
79
|
+
]
|
|
80
|
+
`)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('#track', () => {
|
|
84
|
+
it('sends record events to fullstory on "event"', async () => {
|
|
85
|
+
const [event] = await fullstory({
|
|
86
|
+
orgId: 'thefullstory.com',
|
|
87
|
+
subscriptions: example
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
await event.load(Context.system(), {} as Analytics)
|
|
91
|
+
const fs = jest.spyOn(window.FS, 'event')
|
|
92
|
+
|
|
93
|
+
await event.track?.(
|
|
94
|
+
new Context({
|
|
95
|
+
type: 'track',
|
|
96
|
+
name: 'hello!',
|
|
97
|
+
properties: {
|
|
98
|
+
banana: '📞'
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
expect(fs).toHaveBeenCalledWith(
|
|
104
|
+
'hello!',
|
|
105
|
+
{
|
|
106
|
+
banana: '📞'
|
|
107
|
+
},
|
|
108
|
+
'segment-browser-actions'
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe('#identify', () => {
|
|
114
|
+
it('should default to anonymousId', async () => {
|
|
115
|
+
const [_, identifyUser] = await fullstory({
|
|
116
|
+
orgId: 'thefullstory.com',
|
|
117
|
+
subscriptions: example
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
await identifyUser.load(Context.system(), {} as Analytics)
|
|
121
|
+
const fs = jest.spyOn(window.FS, 'setUserVars')
|
|
122
|
+
const fsId = jest.spyOn(window.FS, 'identify')
|
|
123
|
+
|
|
124
|
+
await identifyUser.identify?.(
|
|
125
|
+
new Context({
|
|
126
|
+
type: 'identify',
|
|
127
|
+
anonymousId: 'anon',
|
|
128
|
+
traits: {
|
|
129
|
+
testProp: false
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
expect(fs).toHaveBeenCalled()
|
|
135
|
+
expect(fsId).not.toHaveBeenCalled()
|
|
136
|
+
expect(fs).toHaveBeenCalledWith({ segmentAnonymousId_str: 'anon', testProp: false }, 'segment-browser-actions')
|
|
137
|
+
}),
|
|
138
|
+
it('should send an id', async () => {
|
|
139
|
+
const [_, identifyUser] = await fullstory({
|
|
140
|
+
orgId: 'thefullstory.com',
|
|
141
|
+
subscriptions: example
|
|
142
|
+
})
|
|
143
|
+
await identifyUser.load(Context.system(), {} as Analytics)
|
|
144
|
+
const fsId = jest.spyOn(window.FS, 'identify')
|
|
145
|
+
|
|
146
|
+
await identifyUser.identify?.(new Context({ type: 'identify', userId: 'id' }))
|
|
147
|
+
expect(fsId).toHaveBeenCalledWith('id', {}, 'segment-browser-actions')
|
|
148
|
+
}),
|
|
149
|
+
it('should camelCase custom traits', async () => {
|
|
150
|
+
const [_, identifyUser] = await fullstory({
|
|
151
|
+
orgId: 'thefullstory.com',
|
|
152
|
+
subscriptions: example
|
|
153
|
+
})
|
|
154
|
+
await identifyUser.load(Context.system(), {} as Analytics)
|
|
155
|
+
const fsId = jest.spyOn(window.FS, 'identify')
|
|
156
|
+
|
|
157
|
+
await identifyUser.identify?.(
|
|
158
|
+
new Context({
|
|
159
|
+
type: 'identify',
|
|
160
|
+
userId: 'id',
|
|
161
|
+
traits: {
|
|
162
|
+
'not-cameled': false,
|
|
163
|
+
'first name': 'John',
|
|
164
|
+
lastName: 'Doe'
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
)
|
|
168
|
+
expect(fsId).toHaveBeenCalledWith(
|
|
169
|
+
'id',
|
|
170
|
+
{ notCameled: false, firstName: 'John', lastName: 'Doe' },
|
|
171
|
+
'segment-browser-actions'
|
|
172
|
+
)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('can set user vars', async () => {
|
|
176
|
+
const [_, identifyUser] = await fullstory({
|
|
177
|
+
orgId: 'thefullstory.com',
|
|
178
|
+
subscriptions: example
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
await identifyUser.load(Context.system(), {} as Analytics)
|
|
182
|
+
const fs = jest.spyOn(window.FS, 'setUserVars')
|
|
183
|
+
|
|
184
|
+
await identifyUser.identify?.(
|
|
185
|
+
new Context({
|
|
186
|
+
type: 'identify',
|
|
187
|
+
traits: {
|
|
188
|
+
name: 'Hasbulla',
|
|
189
|
+
email: 'thegoat@world',
|
|
190
|
+
height: '50cm'
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
expect(fs).toHaveBeenCalledWith(
|
|
196
|
+
{
|
|
197
|
+
displayName: 'Hasbulla',
|
|
198
|
+
email: 'thegoat@world',
|
|
199
|
+
height: '50cm',
|
|
200
|
+
name: 'Hasbulla'
|
|
201
|
+
},
|
|
202
|
+
'segment-browser-actions'
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Settings {
|
|
4
|
+
/**
|
|
5
|
+
* The organization ID for FullStory.
|
|
6
|
+
*/
|
|
7
|
+
orgId: string
|
|
8
|
+
/**
|
|
9
|
+
* Enables FullStory debug mode.
|
|
10
|
+
*/
|
|
11
|
+
debug?: boolean
|
|
12
|
+
/**
|
|
13
|
+
* Enables FullStory inside an iframe.
|
|
14
|
+
*/
|
|
15
|
+
recordOnlyThisIFrame?: boolean
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The user's id
|
|
6
|
+
*/
|
|
7
|
+
userId?: string
|
|
8
|
+
/**
|
|
9
|
+
* The user's anonymous id
|
|
10
|
+
*/
|
|
11
|
+
anonymousId?: string
|
|
12
|
+
/**
|
|
13
|
+
* The user's display name
|
|
14
|
+
*/
|
|
15
|
+
displayName?: string
|
|
16
|
+
/**
|
|
17
|
+
* The user's email
|
|
18
|
+
*/
|
|
19
|
+
email?: string
|
|
20
|
+
/**
|
|
21
|
+
* The Segment traits to be forwarded to FullStory
|
|
22
|
+
*/
|
|
23
|
+
traits?: {
|
|
24
|
+
[k: string]: unknown
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
import type { Settings } from '../generated-types'
|
|
3
|
+
import type { Payload } from './generated-types'
|
|
4
|
+
import type { FS } from '../types'
|
|
5
|
+
import camelCase from 'lodash/camelCase'
|
|
6
|
+
import { segmentEventSource } from '..'
|
|
7
|
+
|
|
8
|
+
// Change from unknown to the partner SDK types
|
|
9
|
+
const action: BrowserActionDefinition<Settings, FS, Payload> = {
|
|
10
|
+
title: 'Identify User',
|
|
11
|
+
description: 'Sets user identity variables',
|
|
12
|
+
platform: 'web',
|
|
13
|
+
defaultSubscription: 'type = "identify"',
|
|
14
|
+
fields: {
|
|
15
|
+
userId: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
required: false,
|
|
18
|
+
description: "The user's id",
|
|
19
|
+
label: 'User ID',
|
|
20
|
+
default: {
|
|
21
|
+
'@path': '$.userId'
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
anonymousId: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
required: false,
|
|
27
|
+
description: "The user's anonymous id",
|
|
28
|
+
label: 'Anonymous ID',
|
|
29
|
+
default: {
|
|
30
|
+
'@path': '$.anonymousId'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
displayName: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
required: false,
|
|
36
|
+
description: "The user's display name",
|
|
37
|
+
label: 'Display Name',
|
|
38
|
+
default: {
|
|
39
|
+
'@path': '$.traits.name'
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
email: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
required: false,
|
|
45
|
+
description: "The user's email",
|
|
46
|
+
label: 'Email',
|
|
47
|
+
default: {
|
|
48
|
+
'@path': '$.traits.email'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
traits: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
required: false,
|
|
54
|
+
description: 'The Segment traits to be forwarded to FullStory',
|
|
55
|
+
label: 'Traits',
|
|
56
|
+
default: {
|
|
57
|
+
'@path': '$.traits'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
perform: (FS, event) => {
|
|
62
|
+
let newTraits: Record<string, unknown> = {}
|
|
63
|
+
|
|
64
|
+
if (event.payload.traits) {
|
|
65
|
+
newTraits = Object.entries(event.payload.traits).reduce(
|
|
66
|
+
(acc, [key, value]) => ({
|
|
67
|
+
...acc,
|
|
68
|
+
[camelCaseField(key)]: value
|
|
69
|
+
}),
|
|
70
|
+
{}
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (event.payload.anonymousId) {
|
|
75
|
+
newTraits.segmentAnonymousId_str = event.payload.anonymousId
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (event.payload.userId) {
|
|
79
|
+
FS.identify(event.payload.userId, newTraits, segmentEventSource)
|
|
80
|
+
} else {
|
|
81
|
+
FS.setUserVars({
|
|
82
|
+
...newTraits,
|
|
83
|
+
...(event.payload.email !== undefined && { email: event.payload.email }),
|
|
84
|
+
...(event.payload.displayName !== undefined && { displayName: event.payload.displayName })
|
|
85
|
+
}, segmentEventSource)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Camel cases `.`, `-`, `_`, and white space within fieldNames. Leaves type suffix alone.
|
|
92
|
+
*
|
|
93
|
+
* NOTE: Does not fix otherwise malformed fieldNames.
|
|
94
|
+
* FullStory will scrub characters from keys that do not conform to /^[a-zA-Z][a-zA-Z0-9_]*$/.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} fieldName
|
|
97
|
+
*/
|
|
98
|
+
function camelCaseField(fieldName: string) {
|
|
99
|
+
// Do not camel case across type suffixes.
|
|
100
|
+
const parts = fieldName.split('_')
|
|
101
|
+
if (parts.length > 1) {
|
|
102
|
+
const typeSuffix = parts.pop()
|
|
103
|
+
switch (typeSuffix) {
|
|
104
|
+
case 'str':
|
|
105
|
+
case 'int':
|
|
106
|
+
case 'date':
|
|
107
|
+
case 'real':
|
|
108
|
+
case 'bool':
|
|
109
|
+
case 'strs':
|
|
110
|
+
case 'ints':
|
|
111
|
+
case 'dates':
|
|
112
|
+
case 'reals':
|
|
113
|
+
case 'bools':
|
|
114
|
+
return camelCase(parts.join('_')) + '_' + typeSuffix
|
|
115
|
+
default: // passthrough
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// No type suffix found. Camel case the whole field name.
|
|
120
|
+
return camelCase(fieldName)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default action
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { FS } from './types'
|
|
2
|
+
import type { BrowserDestinationDefinition } from '@segment/browser-destination-runtime/types'
|
|
3
|
+
import { FSPackage } from './types'
|
|
4
|
+
import { browserDestination } from '@segment/browser-destination-runtime/shim'
|
|
5
|
+
import type { Settings } from './generated-types'
|
|
6
|
+
import trackEvent from './trackEvent'
|
|
7
|
+
import identifyUser from './identifyUser'
|
|
8
|
+
import viewedPage from './viewedPage'
|
|
9
|
+
import { defaultValues } from '@segment/actions-core'
|
|
10
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
interface Window {
|
|
13
|
+
FS: FS
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const segmentEventSource = 'segment-browser-actions'
|
|
18
|
+
|
|
19
|
+
export const destination: BrowserDestinationDefinition<Settings, FS> = {
|
|
20
|
+
name: 'Fullstory (Actions)',
|
|
21
|
+
slug: 'actions-fullstory',
|
|
22
|
+
mode: 'device',
|
|
23
|
+
presets: [
|
|
24
|
+
{
|
|
25
|
+
name: 'Track Event',
|
|
26
|
+
subscribe: 'type = "track"',
|
|
27
|
+
partnerAction: 'trackEvent',
|
|
28
|
+
mapping: defaultValues(trackEvent.fields)
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Identify User',
|
|
32
|
+
subscribe: 'type = "identify"',
|
|
33
|
+
partnerAction: 'identifyUser',
|
|
34
|
+
mapping: defaultValues(identifyUser.fields)
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
settings: {
|
|
38
|
+
orgId: {
|
|
39
|
+
description: 'The organization ID for FullStory.',
|
|
40
|
+
label: 'FS Org',
|
|
41
|
+
type: 'string',
|
|
42
|
+
required: true
|
|
43
|
+
},
|
|
44
|
+
debug: {
|
|
45
|
+
description: 'Enables FullStory debug mode.',
|
|
46
|
+
label: 'Debug mode',
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
required: false,
|
|
49
|
+
default: false
|
|
50
|
+
},
|
|
51
|
+
recordOnlyThisIFrame: {
|
|
52
|
+
description: 'Enables FullStory inside an iframe.',
|
|
53
|
+
label: 'Capture only this iFrame',
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
required: false,
|
|
56
|
+
default: false
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
actions: {
|
|
60
|
+
trackEvent,
|
|
61
|
+
identifyUser,
|
|
62
|
+
viewedPage
|
|
63
|
+
},
|
|
64
|
+
initialize: async ({ settings }, dependencies) => {
|
|
65
|
+
FSPackage.init(settings)
|
|
66
|
+
await dependencies.resolveWhen(() => Object.prototype.hasOwnProperty.call(window, 'FS'), 100)
|
|
67
|
+
return window.FS
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default browserDestination(destination)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The name of the event.
|
|
6
|
+
*/
|
|
7
|
+
name: string
|
|
8
|
+
/**
|
|
9
|
+
* A JSON object containing additional information about the event that will be indexed by FullStory.
|
|
10
|
+
*/
|
|
11
|
+
properties?: {
|
|
12
|
+
[k: string]: unknown
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
import type { Settings } from '../generated-types'
|
|
3
|
+
import type { Payload } from './generated-types'
|
|
4
|
+
import type { FS } from '../types'
|
|
5
|
+
import { segmentEventSource } from '..'
|
|
6
|
+
|
|
7
|
+
const action: BrowserActionDefinition<Settings, FS, Payload> = {
|
|
8
|
+
title: 'Track Event',
|
|
9
|
+
description: 'Track events',
|
|
10
|
+
platform: 'web',
|
|
11
|
+
defaultSubscription: 'type = "track"',
|
|
12
|
+
fields: {
|
|
13
|
+
name: {
|
|
14
|
+
description: 'The name of the event.',
|
|
15
|
+
label: 'Name',
|
|
16
|
+
required: true,
|
|
17
|
+
type: 'string',
|
|
18
|
+
default: {
|
|
19
|
+
'@path': '$.event'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
properties: {
|
|
23
|
+
description: 'A JSON object containing additional information about the event that will be indexed by FullStory.',
|
|
24
|
+
label: 'Properties',
|
|
25
|
+
required: false,
|
|
26
|
+
type: 'object',
|
|
27
|
+
default: {
|
|
28
|
+
'@path': '$.properties'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
perform: (FS, event) => {
|
|
33
|
+
FS.event(event.payload.name, event.payload.properties ?? {}, segmentEventSource)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default action
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as FullStory from '@fullstory/browser'
|
|
2
|
+
|
|
3
|
+
export const FSPackage = FullStory
|
|
4
|
+
export type FS = typeof FullStory & {
|
|
5
|
+
// setVars is not available on the FS client yet.
|
|
6
|
+
setVars: (eventName: string, eventProperties: object, source: string) => {}
|
|
7
|
+
setUserVars: (eventProperties: object, source: string) => void
|
|
8
|
+
event: (eventName: string, eventProperties: { [key: string]: unknown }, source: string) => void
|
|
9
|
+
identify: (uid: string, customVars: FullStory.UserVars, source: string) => void
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated file. DO NOT MODIFY IT BY HAND.
|
|
2
|
+
|
|
3
|
+
export interface Payload {
|
|
4
|
+
/**
|
|
5
|
+
* The name of the page that was viewed.
|
|
6
|
+
*/
|
|
7
|
+
pageName?: string
|
|
8
|
+
/**
|
|
9
|
+
* The properties of the page that was viewed.
|
|
10
|
+
*/
|
|
11
|
+
properties?: {
|
|
12
|
+
[k: string]: unknown
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { BrowserActionDefinition } from '@segment/browser-destination-runtime/types'
|
|
2
|
+
import type { Settings } from '../generated-types'
|
|
3
|
+
import type { Payload } from './generated-types'
|
|
4
|
+
import type { FS } from '../types'
|
|
5
|
+
import { segmentEventSource } from '..'
|
|
6
|
+
|
|
7
|
+
const action: BrowserActionDefinition<Settings, FS, Payload> = {
|
|
8
|
+
title: 'Viewed Page',
|
|
9
|
+
description: 'Sets page properties events',
|
|
10
|
+
defaultSubscription: 'type = "page"',
|
|
11
|
+
platform: 'web',
|
|
12
|
+
fields: {
|
|
13
|
+
pageName: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
required: false,
|
|
16
|
+
description: 'The name of the page that was viewed.',
|
|
17
|
+
label: 'Page Name',
|
|
18
|
+
default: {
|
|
19
|
+
'@if': {
|
|
20
|
+
exists: { '@path': '$.category' },
|
|
21
|
+
then: { '@path': '$.category' },
|
|
22
|
+
else: { '@path': '$.name' }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
properties: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
required: false,
|
|
29
|
+
description: 'The properties of the page that was viewed.',
|
|
30
|
+
label: 'Properties',
|
|
31
|
+
default: {
|
|
32
|
+
'@path': '$.properties'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
perform: (FS, event) => {
|
|
37
|
+
if (event.payload.pageName) {
|
|
38
|
+
FS.setVars('page', { pageName: event.payload.pageName, ...event.payload.properties }, segmentEventSource)
|
|
39
|
+
} else if (event.payload.properties) {
|
|
40
|
+
FS.setVars('page', event.payload.properties, segmentEventSource)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default action
|