@segment/analytics-browser-actions-sprig 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 ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@segment/analytics-browser-actions-sprig",
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
+ "@segment/actions-core": "^3.71.0",
15
+ "@segment/browser-destination-runtime": "^1.0.0"
16
+ },
17
+ "peerDependencies": {
18
+ "@segment/analytics-next": "*"
19
+ }
20
+ }
@@ -0,0 +1,18 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Sprig initialization can load Sprig:
4
+ <script
5
+ src="https://cdn.sprig.com/shim.js?id=testEnvId"
6
+ status="loaded"
7
+ type="text/javascript"
8
+ />,
9
+ <script>
10
+ // the emptiness
11
+ </script>,
12
+ 1`] = `
13
+ NodeList [
14
+ <script>
15
+ // the emptiness
16
+ </script>,
17
+ ]
18
+ `;
@@ -0,0 +1,48 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import sprigWebDestination, { destination } from '../index'
3
+ import { Subscription } from '@segment/browser-destination-runtime/types'
4
+
5
+ const subscriptions: 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
+ }
16
+ }
17
+ ]
18
+
19
+ describe('Sprig initialization', () => {
20
+ beforeAll(() => {
21
+ jest.mock('@segment/browser-destination-runtime/load-script', () => ({
22
+ loadScript: (_src: any, _attributes: any) => {}
23
+ }))
24
+ })
25
+ test('can load Sprig', async () => {
26
+ const [event] = await sprigWebDestination({
27
+ envId: 'testEnvId',
28
+ subscriptions
29
+ })
30
+
31
+ jest.spyOn(destination, 'initialize')
32
+
33
+ await event.load(Context.system(), {} as Analytics)
34
+ expect(destination.initialize).toHaveBeenCalled()
35
+
36
+ const scripts = window.document.querySelectorAll('script')
37
+ expect(scripts).toMatchSnapshot(`
38
+ <script
39
+ src="https://cdn.sprig.com/shim.js?id=testEnvId"
40
+ status="loaded"
41
+ type="text/javascript"
42
+ />,
43
+ <script>
44
+ // the emptiness
45
+ </script>,
46
+ `)
47
+ })
48
+ })
@@ -0,0 +1,12 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Settings {
4
+ /**
5
+ * Your environment ID (production or development).
6
+ */
7
+ envId: string
8
+ /**
9
+ * Enable debug mode for testing purposes.
10
+ */
11
+ debugMode?: boolean
12
+ }
@@ -0,0 +1,65 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import sprigWebDestination, { destination } from '../../index'
3
+ import { Subscription } from '@segment/browser-destination-runtime/types'
4
+
5
+ const subscriptions: Subscription[] = [
6
+ {
7
+ partnerAction: 'identifyUser',
8
+ name: 'Identify User',
9
+ enabled: true,
10
+ subscribe: 'type = "identify"',
11
+ mapping: {
12
+ anonymousId: {
13
+ '@path': '$.anonymousId'
14
+ },
15
+ userId: {
16
+ '@path': '$.userId'
17
+ },
18
+ traits: {
19
+ '@path': '$.traits'
20
+ }
21
+ }
22
+ }
23
+ ]
24
+
25
+ describe('identifyUser', () => {
26
+ beforeAll(() => {
27
+ jest.mock('@segment/browser-destination-runtime/load-script', () => ({
28
+ loadScript: (_src: any, _attributes: any) => {}
29
+ }))
30
+ })
31
+ test('it maps event parameters correctly to identify function ', async () => {
32
+ const [identifyEvent] = await sprigWebDestination({
33
+ envId: 'testEnvId',
34
+ subscriptions
35
+ })
36
+
37
+ destination.actions.identifyUser.perform = jest.fn()
38
+ jest.spyOn(destination.actions.identifyUser, 'perform')
39
+ await identifyEvent.load(Context.system(), {} as Analytics)
40
+
41
+ await identifyEvent.identify?.(
42
+ new Context({
43
+ type: 'identify',
44
+ anonymousId: 'anonymous-id-0',
45
+ userId: 'user-id-1',
46
+ traits: {
47
+ email: 'test-email-2@gmail.com'
48
+ }
49
+ })
50
+ )
51
+
52
+ expect(destination.actions.identifyUser.perform).toHaveBeenCalledWith(
53
+ expect.anything(),
54
+ expect.objectContaining({
55
+ payload: {
56
+ anonymousId: 'anonymous-id-0',
57
+ userId: 'user-id-1',
58
+ traits: {
59
+ email: 'test-email-2@gmail.com'
60
+ }
61
+ }
62
+ })
63
+ )
64
+ })
65
+ })
@@ -0,0 +1,18 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * Unique identifier for the user
6
+ */
7
+ userId?: string
8
+ /**
9
+ * Anonymous identifier for the user
10
+ */
11
+ anonymousId?: string
12
+ /**
13
+ * The Segment user traits to be forwarded to Sprig and set as attributes
14
+ */
15
+ traits?: {
16
+ [k: string]: unknown
17
+ }
18
+ }
@@ -0,0 +1,78 @@
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 { Sprig } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Sprig, Payload> = {
7
+ title: 'Identify User',
8
+ description: 'Set user ID and/or attributes.',
9
+ platform: 'web',
10
+ defaultSubscription: 'type = "identify"',
11
+ fields: {
12
+ userId: {
13
+ type: 'string',
14
+ required: false,
15
+ description: 'Unique identifier for the user',
16
+ label: 'User ID',
17
+ default: {
18
+ '@path': '$.userId'
19
+ }
20
+ },
21
+ anonymousId: {
22
+ type: 'string',
23
+ required: false,
24
+ description: 'Anonymous identifier for the user',
25
+ label: 'Anonymous ID',
26
+ default: {
27
+ '@path': '$.anonymousId'
28
+ }
29
+ },
30
+ traits: {
31
+ type: 'object',
32
+ required: false,
33
+ description: 'The Segment user traits to be forwarded to Sprig and set as attributes',
34
+ label: 'User Attributes',
35
+ default: {
36
+ '@path': '$.traits'
37
+ }
38
+ }
39
+ },
40
+ perform: (Sprig, event) => {
41
+ const payload = event.payload
42
+ if (!payload || typeof payload !== 'object' || !(payload.userId || payload.anonymousId || payload.traits)) {
43
+ console.warn(
44
+ '[Sprig] received invalid payload (expected userId, anonymousId, or traits to be present); skipping identifyUser',
45
+ payload
46
+ )
47
+ return
48
+ }
49
+
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ const sprigIdentifyAndSetAttributesPayload: {
52
+ userId?: string
53
+ anonymousId?: string
54
+ attributes?: { [key: string]: any }
55
+ } = {}
56
+
57
+ if (payload.userId) {
58
+ sprigIdentifyAndSetAttributesPayload.userId = payload.userId
59
+ }
60
+
61
+ if (payload.anonymousId) {
62
+ sprigIdentifyAndSetAttributesPayload.anonymousId = payload.anonymousId
63
+ }
64
+
65
+ if (payload.traits && Object.keys(payload.traits).length > 0) {
66
+ const traits = { ...payload.traits }
67
+ if (traits.email) {
68
+ traits['!email'] = traits.email
69
+ delete traits.email
70
+ }
71
+ sprigIdentifyAndSetAttributesPayload.attributes = traits
72
+ }
73
+
74
+ Sprig('identifyAndSetAttributes', sprigIdentifyAndSetAttributesPayload)
75
+ }
76
+ }
77
+
78
+ export default action
package/src/index.ts ADDED
@@ -0,0 +1,91 @@
1
+ import type { Settings } from './generated-types'
2
+ import type { BrowserDestinationDefinition } from '@segment/browser-destination-runtime/types'
3
+ import { browserDestination } from '@segment/browser-destination-runtime/shim'
4
+ import { Sprig } from './types'
5
+ import identifyUser from './identifyUser'
6
+ import signoutUser from './signoutUser'
7
+ import trackEvent from './trackEvent'
8
+ import updateUserId from './updateUserId'
9
+ import { defaultValues } from '@segment/actions-core'
10
+
11
+ declare global {
12
+ interface Window {
13
+ Sprig: Sprig
14
+ UserLeap: Sprig
15
+ }
16
+ }
17
+
18
+ export const destination: BrowserDestinationDefinition<Settings, Sprig> = {
19
+ name: 'Sprig (Actions)',
20
+ slug: 'sprig-web',
21
+ mode: 'device',
22
+
23
+ presets: [
24
+ {
25
+ name: 'Identify User',
26
+ subscribe: 'type = "identify"',
27
+ partnerAction: 'identifyUser',
28
+ mapping: defaultValues(identifyUser.fields)
29
+ },
30
+ {
31
+ name: 'Sign Out User',
32
+ subscribe: 'type = "track" and event = "Signed Out"',
33
+ partnerAction: 'signoutUser',
34
+ mapping: defaultValues(signoutUser.fields)
35
+ },
36
+ {
37
+ name: 'Track Event',
38
+ subscribe: 'type = "track" and event != "Signed Out"',
39
+ partnerAction: 'trackEvent',
40
+ mapping: defaultValues(trackEvent.fields)
41
+ },
42
+ {
43
+ name: 'Update User ID',
44
+ subscribe: 'type = "alias"',
45
+ partnerAction: 'updateUserId',
46
+ mapping: defaultValues(updateUserId.fields)
47
+ }
48
+ ],
49
+
50
+ settings: {
51
+ envId: {
52
+ description: 'Your environment ID (production or development).',
53
+ label: 'Environment ID',
54
+ type: 'string',
55
+ required: true
56
+ },
57
+ debugMode: {
58
+ description: 'Enable debug mode for testing purposes.',
59
+ label: 'Debug mode',
60
+ type: 'boolean',
61
+ required: false,
62
+ default: false
63
+ }
64
+ },
65
+
66
+ actions: {
67
+ identifyUser,
68
+ signoutUser,
69
+ trackEvent,
70
+ updateUserId
71
+ },
72
+
73
+ initialize: async ({ settings }, deps) => {
74
+ if (!window.Sprig || !window.Sprig.envId) {
75
+ window.Sprig = function (...args) {
76
+ S._queue && S._queue.push(args)
77
+ }
78
+ const S = window.Sprig
79
+ S.envId = settings.envId
80
+ S.debugMode = !!settings.debugMode
81
+ S._queue = []
82
+ S._segment = 1
83
+ window.UserLeap = S
84
+ await deps.loadScript(`https://cdn.sprig.com/shim.js?id=${S.envId}`)
85
+ }
86
+
87
+ return window.Sprig
88
+ }
89
+ }
90
+
91
+ export default browserDestination(destination)
@@ -0,0 +1,3 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {}
@@ -0,0 +1,17 @@
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 { Sprig } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Sprig, Payload> = {
7
+ title: 'Sign Out User',
8
+ description: 'Clear stored user ID so that future events and traits are not associated with this user.',
9
+ platform: 'web',
10
+ defaultSubscription: 'type = "track" and event = "Signed Out"',
11
+ fields: {},
12
+ perform: (Sprig, _event) => {
13
+ Sprig('logoutUser')
14
+ }
15
+ }
16
+
17
+ export default action
@@ -0,0 +1,60 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import sprigWebDestination, { destination } from '../../index'
3
+ import { Subscription } from '@segment/browser-destination-runtime/types'
4
+
5
+ const subscriptions: 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
+ anonymousId: {
16
+ '@path': '$.anonymousId'
17
+ },
18
+ userId: {
19
+ '@path': '$.userId'
20
+ },
21
+ properties: {
22
+ '@path': '$.properties'
23
+ }
24
+ }
25
+ }
26
+ ]
27
+
28
+ describe('trackEvent', () => {
29
+ beforeAll(() => {
30
+ jest.mock('@segment/browser-destination-runtime/load-script', () => ({
31
+ loadScript: (_src: any, _attributes: any) => {}
32
+ }))
33
+ })
34
+ test('it maps event parameters correctly to track function ', async () => {
35
+ const [trackEvent] = await sprigWebDestination({
36
+ envId: 'testEnvId',
37
+ subscriptions
38
+ })
39
+
40
+ destination.actions.trackEvent.perform = jest.fn()
41
+ jest.spyOn(destination.actions.trackEvent, 'perform')
42
+ await trackEvent.load(Context.system(), {} as Analytics)
43
+ const properties = { property1: 'value1', property2: false }
44
+ await trackEvent.track?.(
45
+ new Context({
46
+ type: 'track',
47
+ name: 'Button Clicked',
48
+ anonymousId: 'anonymous-id-0',
49
+ properties
50
+ })
51
+ )
52
+
53
+ expect(destination.actions.trackEvent.perform).toHaveBeenCalledWith(
54
+ expect.anything(),
55
+ expect.objectContaining({
56
+ payload: { name: 'Button Clicked', anonymousId: 'anonymous-id-0', properties }
57
+ })
58
+ )
59
+ })
60
+ })
@@ -0,0 +1,22 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * The event name that will be shown on Sprig's dashboard
6
+ */
7
+ name: string
8
+ /**
9
+ * Unique identifier for the user
10
+ */
11
+ userId?: string
12
+ /**
13
+ * Anonymous identifier for the user
14
+ */
15
+ anonymousId?: string
16
+ /**
17
+ * Object containing the properties of the event
18
+ */
19
+ properties?: {
20
+ [k: string]: unknown
21
+ }
22
+ }
@@ -0,0 +1,80 @@
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 { Sprig } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Sprig, Payload> = {
7
+ title: 'Track Event',
8
+ description: 'Track event to potentially filter user studies (microsurveys) later, or trigger a study now.',
9
+ platform: 'web',
10
+ defaultSubscription: 'type = "track" and event != "Signed Out"',
11
+ fields: {
12
+ name: {
13
+ description: "The event name that will be shown on Sprig's dashboard",
14
+ label: 'Event name',
15
+ required: true,
16
+ type: 'string',
17
+ default: {
18
+ '@path': '$.event'
19
+ }
20
+ },
21
+ userId: {
22
+ type: 'string',
23
+ required: false,
24
+ description: 'Unique identifier for the user',
25
+ label: 'User ID',
26
+ default: {
27
+ '@path': '$.userId'
28
+ }
29
+ },
30
+ anonymousId: {
31
+ type: 'string',
32
+ required: false,
33
+ description: 'Anonymous identifier for the user',
34
+ label: 'Anonymous ID',
35
+ default: {
36
+ '@path': '$.anonymousId'
37
+ }
38
+ },
39
+ properties: {
40
+ type: 'object',
41
+ required: false,
42
+ description: 'Object containing the properties of the event',
43
+ label: 'Event Properties',
44
+ default: {
45
+ '@path': '$.properties'
46
+ }
47
+ }
48
+ },
49
+ perform: (Sprig, event) => {
50
+ const payload = event.payload
51
+ if (!payload || typeof payload !== 'object' || !payload.name) {
52
+ console.warn('[Sprig] received invalid payload (expected name to be present); skipping trackEvent', payload)
53
+ return
54
+ }
55
+
56
+ const sprigIdentifyAndTrackPayload: {
57
+ eventName: string
58
+ userId?: string
59
+ anonymousId?: string
60
+ properties?: { [key: string]: unknown }
61
+ } = {
62
+ eventName: payload.name
63
+ }
64
+ if (payload.userId) {
65
+ sprigIdentifyAndTrackPayload.userId = payload.userId
66
+ }
67
+
68
+ if (payload.anonymousId) {
69
+ sprigIdentifyAndTrackPayload.anonymousId = payload.anonymousId
70
+ }
71
+
72
+ if (payload.properties) {
73
+ sprigIdentifyAndTrackPayload.properties = payload.properties
74
+ }
75
+
76
+ Sprig('identifyAndTrack', sprigIdentifyAndTrackPayload)
77
+ }
78
+ }
79
+
80
+ export default action
package/src/types.ts ADDED
@@ -0,0 +1,7 @@
1
+ export interface Sprig {
2
+ (...args: unknown[]): unknown
3
+ envId?: string
4
+ debugMode?: boolean
5
+ _queue?: unknown[]
6
+ _segment?: number
7
+ }
@@ -0,0 +1,81 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import sprigWebDestination, { destination } from '../../index'
3
+ import { Subscription } from '@segment/browser-destination-runtime/types'
4
+
5
+ const subscriptions: Subscription[] = [
6
+ {
7
+ partnerAction: 'updateUserId',
8
+ name: 'Update User ID',
9
+ enabled: true,
10
+ subscribe: 'type = "alias"',
11
+ mapping: {
12
+ anonymousId: {
13
+ '@path': '$.anonymousId'
14
+ },
15
+ userId: {
16
+ '@path': '$.userId'
17
+ }
18
+ }
19
+ }
20
+ ]
21
+
22
+ describe('updateUserId', () => {
23
+ beforeAll(() => {
24
+ jest.mock('@segment/browser-destination-runtime/load-script', () => ({
25
+ loadScript: (_src: any, _attributes: any) => {}
26
+ }))
27
+ })
28
+ test('it maps event parameters correctly to alias function with user id', async () => {
29
+ const [aliasEvent] = await sprigWebDestination({
30
+ envId: 'testEnvId',
31
+ subscriptions
32
+ })
33
+
34
+ destination.actions.updateUserId.perform = jest.fn()
35
+ jest.spyOn(destination.actions.updateUserId, 'perform')
36
+ await aliasEvent.load(Context.system(), {} as Analytics)
37
+
38
+ await aliasEvent.alias?.(
39
+ new Context({
40
+ type: 'alias',
41
+ userId: 'user-id-1'
42
+ })
43
+ )
44
+
45
+ expect(destination.actions.updateUserId.perform).toHaveBeenCalledWith(
46
+ expect.anything(),
47
+ expect.objectContaining({
48
+ payload: {
49
+ userId: 'user-id-1'
50
+ }
51
+ })
52
+ )
53
+ })
54
+
55
+ test('it maps event parameters correctly to alias function with anonymous id', async () => {
56
+ const [aliasEvent] = await sprigWebDestination({
57
+ envId: 'testEnvId',
58
+ subscriptions
59
+ })
60
+
61
+ destination.actions.updateUserId.perform = jest.fn()
62
+ jest.spyOn(destination.actions.updateUserId, 'perform')
63
+ await aliasEvent.load(Context.system(), {} as Analytics)
64
+
65
+ await aliasEvent.alias?.(
66
+ new Context({
67
+ type: 'alias',
68
+ anonymousId: 'anonymous-id-0'
69
+ })
70
+ )
71
+
72
+ expect(destination.actions.updateUserId.perform).toHaveBeenCalledWith(
73
+ expect.anything(),
74
+ expect.objectContaining({
75
+ payload: {
76
+ anonymousId: 'anonymous-id-0'
77
+ }
78
+ })
79
+ )
80
+ })
81
+ })
@@ -0,0 +1,12 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * New unique identifier for the user
6
+ */
7
+ userId?: string
8
+ /**
9
+ * New anonymous identifier for the user
10
+ */
11
+ anonymousId?: string
12
+ }
@@ -0,0 +1,55 @@
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 { Sprig } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Sprig, Payload> = {
7
+ title: 'Update User ID',
8
+ description: 'Set updated user ID.',
9
+ platform: 'web',
10
+ defaultSubscription: 'type = "alias"',
11
+ fields: {
12
+ userId: {
13
+ type: 'string',
14
+ required: false,
15
+ description: 'New unique identifier for the user',
16
+ label: 'User ID',
17
+ default: {
18
+ '@path': '$.userId'
19
+ }
20
+ },
21
+ anonymousId: {
22
+ type: 'string',
23
+ required: false,
24
+ description: 'New anonymous identifier for the user',
25
+ label: 'Anonymous ID',
26
+ default: {
27
+ '@path': '$.anonymousId'
28
+ }
29
+ }
30
+ },
31
+ perform: (Sprig, event) => {
32
+ const payload = event.payload
33
+ if (!payload || typeof payload !== 'object' || !(payload.userId || payload.anonymousId)) {
34
+ console.warn(
35
+ '[Sprig] received invalid payload (expected userId or anonymousId to be present); skipping updateUserId',
36
+ payload
37
+ )
38
+ return
39
+ }
40
+
41
+ const sprigIdentifyAndSetAttributesPayload: { userId?: string; anonymousId?: string } = {}
42
+
43
+ if (payload.userId) {
44
+ sprigIdentifyAndSetAttributesPayload.userId = payload.userId
45
+ }
46
+
47
+ if (payload.anonymousId) {
48
+ sprigIdentifyAndSetAttributesPayload.anonymousId = payload.anonymousId
49
+ }
50
+
51
+ Sprig('identifyAndSetAttributes', sprigIdentifyAndSetAttributesPayload)
52
+ }
53
+ }
54
+
55
+ export default action
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "baseUrl": "."
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["dist", "**/__tests__"]
9
+ }