@segment/analytics-browser-appcues-web-actions 1.1.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.
Files changed (93) hide show
  1. package/README.md +31 -0
  2. package/dist/cjs/constants.d.ts +4 -0
  3. package/dist/cjs/constants.js +8 -0
  4. package/dist/cjs/constants.js.map +1 -0
  5. package/dist/cjs/generated-types.d.ts +5 -0
  6. package/dist/cjs/generated-types.js +3 -0
  7. package/dist/cjs/generated-types.js.map +1 -0
  8. package/dist/cjs/group/generated-types.d.ts +6 -0
  9. package/dist/cjs/group/generated-types.js +3 -0
  10. package/dist/cjs/group/generated-types.js.map +1 -0
  11. package/dist/cjs/group/index.d.ts +6 -0
  12. package/dist/cjs/group/index.js +30 -0
  13. package/dist/cjs/group/index.js.map +1 -0
  14. package/dist/cjs/identify/generated-types.d.ts +6 -0
  15. package/dist/cjs/identify/generated-types.js +3 -0
  16. package/dist/cjs/identify/generated-types.js.map +1 -0
  17. package/dist/cjs/identify/index.d.ts +6 -0
  18. package/dist/cjs/identify/index.js +30 -0
  19. package/dist/cjs/identify/index.js.map +1 -0
  20. package/dist/cjs/index.d.ts +6 -0
  21. package/dist/cjs/index.js +67 -0
  22. package/dist/cjs/index.js.map +1 -0
  23. package/dist/cjs/page/generated-types.d.ts +2 -0
  24. package/dist/cjs/page/generated-types.js +3 -0
  25. package/dist/cjs/page/generated-types.js.map +1 -0
  26. package/dist/cjs/page/index.d.ts +6 -0
  27. package/dist/cjs/page/index.js +14 -0
  28. package/dist/cjs/page/index.js.map +1 -0
  29. package/dist/cjs/track/generated-types.d.ts +6 -0
  30. package/dist/cjs/track/generated-types.js +3 -0
  31. package/dist/cjs/track/generated-types.js.map +1 -0
  32. package/dist/cjs/track/index.d.ts +6 -0
  33. package/dist/cjs/track/index.js +30 -0
  34. package/dist/cjs/track/index.js.map +1 -0
  35. package/dist/cjs/types.d.ts +18 -0
  36. package/dist/cjs/types.js +3 -0
  37. package/dist/cjs/types.js.map +1 -0
  38. package/dist/esm/constants.d.ts +4 -0
  39. package/dist/esm/constants.js +5 -0
  40. package/dist/esm/constants.js.map +1 -0
  41. package/dist/esm/generated-types.d.ts +5 -0
  42. package/dist/esm/generated-types.js +2 -0
  43. package/dist/esm/generated-types.js.map +1 -0
  44. package/dist/esm/group/generated-types.d.ts +6 -0
  45. package/dist/esm/group/generated-types.js +2 -0
  46. package/dist/esm/group/generated-types.js.map +1 -0
  47. package/dist/esm/group/index.d.ts +6 -0
  48. package/dist/esm/group/index.js +28 -0
  49. package/dist/esm/group/index.js.map +1 -0
  50. package/dist/esm/identify/generated-types.d.ts +6 -0
  51. package/dist/esm/identify/generated-types.js +2 -0
  52. package/dist/esm/identify/generated-types.js.map +1 -0
  53. package/dist/esm/identify/index.d.ts +6 -0
  54. package/dist/esm/identify/index.js +28 -0
  55. package/dist/esm/identify/index.js.map +1 -0
  56. package/dist/esm/index.d.ts +6 -0
  57. package/dist/esm/index.js +63 -0
  58. package/dist/esm/index.js.map +1 -0
  59. package/dist/esm/page/generated-types.d.ts +2 -0
  60. package/dist/esm/page/generated-types.js +2 -0
  61. package/dist/esm/page/generated-types.js.map +1 -0
  62. package/dist/esm/page/index.d.ts +6 -0
  63. package/dist/esm/page/index.js +12 -0
  64. package/dist/esm/page/index.js.map +1 -0
  65. package/dist/esm/track/generated-types.d.ts +6 -0
  66. package/dist/esm/track/generated-types.js +2 -0
  67. package/dist/esm/track/generated-types.js.map +1 -0
  68. package/dist/esm/track/index.d.ts +6 -0
  69. package/dist/esm/track/index.js +28 -0
  70. package/dist/esm/track/index.js.map +1 -0
  71. package/dist/esm/types.d.ts +18 -0
  72. package/dist/esm/types.js +2 -0
  73. package/dist/esm/types.js.map +1 -0
  74. package/dist/tsconfig.tsbuildinfo +1 -0
  75. package/package.json +24 -0
  76. package/src/__tests__/initialization.test.ts +83 -0
  77. package/src/constants.ts +4 -0
  78. package/src/generated-types.ts +16 -0
  79. package/src/group/__tests__/index.test.ts +119 -0
  80. package/src/group/generated-types.ts +14 -0
  81. package/src/group/index.ts +33 -0
  82. package/src/identify/__tests__/index.test.ts +119 -0
  83. package/src/identify/generated-types.ts +14 -0
  84. package/src/identify/index.ts +33 -0
  85. package/src/index.ts +71 -0
  86. package/src/page/__tests__/index.test.ts +63 -0
  87. package/src/page/generated-types.ts +3 -0
  88. package/src/page/index.ts +17 -0
  89. package/src/track/__tests__/index.test.ts +125 -0
  90. package/src/track/generated-types.ts +14 -0
  91. package/src/track/index.ts +33 -0
  92. package/src/types.ts +21 -0
  93. package/tsconfig.json +9 -0
@@ -0,0 +1,119 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import { Subscription } from '@segment/browser-destination-runtime'
3
+ import AppcuesDestination, { destination } from '../../index'
4
+ import { Appcues } from '../../types'
5
+
6
+ describe('Appcues.identify', () => {
7
+ const settings = {
8
+ accountID: 'test-account-id',
9
+ region: 'US' as const,
10
+ enableURLDetection: true
11
+ }
12
+
13
+ let mockAppcues: Appcues
14
+ let event: any
15
+
16
+ beforeEach(async () => {
17
+ jest.restoreAllMocks()
18
+ jest.spyOn(destination, 'initialize').mockImplementation(() => {
19
+ mockAppcues = {
20
+ track: jest.fn(),
21
+ identify: jest.fn(),
22
+ group: jest.fn(),
23
+ page: jest.fn()
24
+ }
25
+ return Promise.resolve(mockAppcues)
26
+ })
27
+ })
28
+
29
+ test('identify() handled correctly', async () => {
30
+ const subscriptions: Subscription[] = [
31
+ {
32
+ partnerAction: 'identify',
33
+ name: 'Identify',
34
+ enabled: true,
35
+ subscribe: 'type = "identify"',
36
+ mapping: {
37
+ userId: { '@path': '$.userId' },
38
+ traits: { '@path': '$.traits' }
39
+ }
40
+ }
41
+ ]
42
+
43
+ const context = new Context({
44
+ messageId: 'ajs-test-message-id',
45
+ type: 'identify',
46
+ anonymousId: 'anonymous-id-123',
47
+ userId: 'user-123',
48
+ traits: {
49
+ name: 'John Doe',
50
+ email: 'john@example.com',
51
+ plan: 'premium',
52
+ age: 30
53
+ }
54
+ })
55
+
56
+ const [identifyEvent] = await AppcuesDestination({
57
+ ...settings,
58
+ subscriptions
59
+ })
60
+ event = identifyEvent
61
+
62
+ await event.load(Context.system(), {} as Analytics)
63
+ await event.identify?.(context)
64
+
65
+ expect(mockAppcues.identify).toHaveBeenCalledWith('user-123', {
66
+ name: 'John Doe',
67
+ email: 'john@example.com',
68
+ plan: 'premium',
69
+ age: 30
70
+ })
71
+ })
72
+
73
+ test('identify() handles nested traits and arrays', async () => {
74
+ const subscriptions: Subscription[] = [
75
+ {
76
+ partnerAction: 'identify',
77
+ name: 'Identify',
78
+ enabled: true,
79
+ subscribe: 'type = "identify"',
80
+ mapping: {
81
+ userId: { '@path': '$.userId' },
82
+ traits: { '@path': '$.traits' }
83
+ }
84
+ }
85
+ ]
86
+
87
+ const context = new Context({
88
+ messageId: 'ajs-test-message-id',
89
+ type: 'identify',
90
+ userId: 'user-123',
91
+ traits: {
92
+ name: 'John Doe',
93
+ address: {
94
+ city: 'San Francisco',
95
+ state: 'CA'
96
+ },
97
+ tags: ['vip', 'beta']
98
+ }
99
+ })
100
+
101
+ const [identifyEvent] = await AppcuesDestination({
102
+ ...settings,
103
+ subscriptions
104
+ })
105
+ event = identifyEvent
106
+
107
+ await event.load(Context.system(), {} as Analytics)
108
+ await event.identify?.(context)
109
+
110
+ expect(mockAppcues.identify).toHaveBeenCalledWith('user-123', {
111
+ name: 'John Doe',
112
+ address: {
113
+ city: 'San Francisco',
114
+ state: 'CA'
115
+ },
116
+ tags: ['vip', 'beta']
117
+ })
118
+ })
119
+ })
@@ -0,0 +1,14 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * The ID of the user to identify in Appcues.
6
+ */
7
+ userId: string
8
+ /**
9
+ * Properties to associate with the user.
10
+ */
11
+ traits?: {
12
+ [k: string]: unknown
13
+ }
14
+ }
@@ -0,0 +1,33 @@
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 { Appcues } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Appcues, Payload> = {
7
+ title: 'Identify',
8
+ description: 'Send Segment identify events to Appcues.',
9
+ platform: 'web',
10
+ fields: {
11
+ userId: {
12
+ label: 'User ID',
13
+ description: 'The ID of the user to identify in Appcues.',
14
+ required: true,
15
+ type: 'string',
16
+ default: { '@path': '$.userId' }
17
+ },
18
+ traits: {
19
+ label: 'User traits',
20
+ description: 'Properties to associate with the user.',
21
+ required: false,
22
+ type: 'object',
23
+ default: { '@path': '$.traits' }
24
+ }
25
+ },
26
+ defaultSubscription: 'type = "identify"',
27
+ perform: (appcues, { payload }) => {
28
+ const { userId, traits } = payload
29
+ appcues.identify(userId, traits)
30
+ }
31
+ }
32
+
33
+ export default action
package/src/index.ts ADDED
@@ -0,0 +1,71 @@
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 type { Appcues } from './types'
5
+ import track from './track'
6
+ import page from './page'
7
+ import identify from './identify'
8
+ import group from './group'
9
+ import { URL } from './constants'
10
+ import { defaultValues } from '@segment/actions-core'
11
+
12
+ export const destination: BrowserDestinationDefinition<Settings, Appcues> = {
13
+ name: 'Appcues Web',
14
+ slug: 'appcues-web-actions',
15
+ mode: 'device',
16
+ settings: {
17
+ accountID: {
18
+ label: 'Appcues Account ID',
19
+ description: 'Your Appcues Account ID.',
20
+ type: 'password',
21
+ required: true
22
+ },
23
+ region: {
24
+ label: 'Region',
25
+ description: 'Select the Appcues region for your account.',
26
+ type: 'string',
27
+ required: true,
28
+ choices: [
29
+ { label: 'US', value: 'US' },
30
+ { label: 'EU', value: 'EU' }
31
+ ],
32
+ default: 'US'
33
+ },
34
+ enableURLDetection: {
35
+ label: 'Enable URL Detection',
36
+ description: 'Enable or disable URL detection in Appcues. If enabled, page events should not be triggered manually using the page action.',
37
+ type: 'boolean',
38
+ required: true,
39
+ default: false
40
+ }
41
+ },
42
+ initialize: async ({ settings }, deps) => {
43
+ const {
44
+ region,
45
+ accountID,
46
+ enableURLDetection
47
+ } = settings
48
+ const url = region === 'EU' ? URL.EU : URL.US
49
+ window.AppcuesSettings = { enableURLDetection }
50
+ await deps.loadScript(`//${url}/${accountID}.js`)
51
+ await deps.resolveWhen(() => Object.prototype.hasOwnProperty.call(window, 'Appcues'), 100)
52
+ return window.Appcues
53
+ },
54
+ presets: [
55
+ {
56
+ name: 'Page',
57
+ subscribe: 'type = "page"',
58
+ partnerAction: 'page',
59
+ mapping: defaultValues(page.fields),
60
+ type: 'automatic'
61
+ }
62
+ ],
63
+ actions: {
64
+ track,
65
+ page,
66
+ identify,
67
+ group
68
+ }
69
+ }
70
+
71
+ export default browserDestination(destination)
@@ -0,0 +1,63 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import { Subscription } from '@segment/browser-destination-runtime'
3
+ import AppcuesDestination, { destination } from '../../index'
4
+ import { Appcues } from '../../types'
5
+
6
+ describe('Appcues.page', () => {
7
+ const settings = {
8
+ accountID: 'test-account-id',
9
+ region: 'US' as const,
10
+ enableURLDetection: true
11
+ }
12
+
13
+ let mockAppcues: Appcues
14
+ let event: any
15
+
16
+ beforeEach(async () => {
17
+ jest.restoreAllMocks()
18
+ jest.spyOn(destination, 'initialize').mockImplementation(() => {
19
+ mockAppcues = {
20
+ track: jest.fn(),
21
+ identify: jest.fn(),
22
+ group: jest.fn(),
23
+ page: jest.fn()
24
+ }
25
+ return Promise.resolve(mockAppcues)
26
+ })
27
+ })
28
+
29
+ test('page() handled correctly', async () => {
30
+ const subscriptions: Subscription[] = [
31
+ {
32
+ partnerAction: 'page',
33
+ name: 'Page',
34
+ enabled: true,
35
+ subscribe: 'type = "page"',
36
+ mapping: {}
37
+ }
38
+ ]
39
+
40
+ const context = new Context({
41
+ messageId: 'ajs-test-message-id',
42
+ type: 'page',
43
+ anonymousId: 'anonymous-id-123',
44
+ userId: 'user-123',
45
+ properties: {
46
+ url: 'https://example.com/home',
47
+ path: '/home',
48
+ title: 'Home Page'
49
+ }
50
+ })
51
+
52
+ const [pageEvent] = await AppcuesDestination({
53
+ ...settings,
54
+ subscriptions
55
+ })
56
+ event = pageEvent
57
+
58
+ await event.load(Context.system(), {} as Analytics)
59
+ await event.page?.(context)
60
+
61
+ expect(mockAppcues.page).toHaveBeenCalled()
62
+ })
63
+ })
@@ -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 { Appcues } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Appcues, Payload> = {
7
+ title: 'Page',
8
+ description: 'Send Segment page events to Appcues.',
9
+ platform: 'web',
10
+ fields: {},
11
+ defaultSubscription: 'type = "page"',
12
+ perform: (appcues) => {
13
+ appcues.page()
14
+ }
15
+ }
16
+
17
+ export default action
@@ -0,0 +1,125 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import { Subscription } from '@segment/browser-destination-runtime'
3
+ import AppcuesDestination, { destination } from '../../index'
4
+ import { Appcues } from '../../types'
5
+
6
+ describe('Appcues.track', () => {
7
+ const settings = {
8
+ accountID: 'test-account-id',
9
+ region: 'US' as const,
10
+ enableURLDetection: true
11
+ }
12
+
13
+ let mockAppcues: Appcues
14
+ let event: any
15
+
16
+ beforeEach(async () => {
17
+ jest.restoreAllMocks()
18
+ jest.spyOn(destination, 'initialize').mockImplementation(() => {
19
+ mockAppcues = {
20
+ track: jest.fn(),
21
+ identify: jest.fn(),
22
+ group: jest.fn(),
23
+ page: jest.fn()
24
+ }
25
+ return Promise.resolve(mockAppcues)
26
+ })
27
+ })
28
+
29
+ test('track() handled correctly', async () => {
30
+ const subscriptions: Subscription[] = [
31
+ {
32
+ partnerAction: 'track',
33
+ name: 'Track',
34
+ enabled: true,
35
+ subscribe: 'type = "track"',
36
+ mapping: {
37
+ event: { '@path': '$.event' },
38
+ properties: { '@path': '$.properties' }
39
+ }
40
+ }
41
+ ]
42
+
43
+ const context = new Context({
44
+ messageId: 'ajs-test-message-id',
45
+ type: 'track',
46
+ event: 'Button Clicked',
47
+ anonymousId: 'anonymous-id-123',
48
+ userId: 'user-123',
49
+ properties: {
50
+ buttonName: 'Sign Up',
51
+ color: 'blue',
52
+ position: 'header'
53
+ }
54
+ })
55
+
56
+ const [trackEvent] = await AppcuesDestination({
57
+ ...settings,
58
+ subscriptions
59
+ })
60
+ event = trackEvent
61
+
62
+ await event.load(Context.system(), {} as Analytics)
63
+ await event.track?.(context)
64
+
65
+ expect(mockAppcues.track).toHaveBeenCalledWith('Button Clicked', {
66
+ buttonName: 'Sign Up',
67
+ color: 'blue',
68
+ position: 'header'
69
+ })
70
+ })
71
+
72
+ test('track() handles nested properties and arrays', async () => {
73
+ const subscriptions: Subscription[] = [
74
+ {
75
+ partnerAction: 'track',
76
+ name: 'Track',
77
+ enabled: true,
78
+ subscribe: 'type = "track"',
79
+ mapping: {
80
+ event: { '@path': '$.event' },
81
+ properties: { '@path': '$.properties' }
82
+ }
83
+ }
84
+ ]
85
+
86
+ const context = new Context({
87
+ messageId: 'ajs-test-message-id',
88
+ type: 'track',
89
+ event: 'Purchase Completed',
90
+ userId: 'user-123',
91
+ properties: {
92
+ total: 99.99,
93
+ items: [
94
+ { name: 'Product A', price: 49.99 },
95
+ { name: 'Product B', price: 50.00 }
96
+ ],
97
+ shipping: {
98
+ method: 'express',
99
+ cost: 10
100
+ }
101
+ }
102
+ })
103
+
104
+ const [trackEvent] = await AppcuesDestination({
105
+ ...settings,
106
+ subscriptions
107
+ })
108
+ event = trackEvent
109
+
110
+ await event.load(Context.system(), {} as Analytics)
111
+ await event.track?.(context)
112
+
113
+ expect(mockAppcues.track).toHaveBeenCalledWith('Purchase Completed', {
114
+ total: 99.99,
115
+ items: [
116
+ { name: 'Product A', price: 49.99 },
117
+ { name: 'Product B', price: 50.00 }
118
+ ],
119
+ shipping: {
120
+ method: 'express',
121
+ cost: 10
122
+ }
123
+ })
124
+ })
125
+ })
@@ -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 to track in Appcues.
6
+ */
7
+ event: string
8
+ /**
9
+ * Properties to associate with the event.
10
+ */
11
+ properties?: {
12
+ [k: string]: unknown
13
+ }
14
+ }
@@ -0,0 +1,33 @@
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 { Appcues } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Appcues, Payload> = {
7
+ title: 'Track',
8
+ description: 'Send Segment track events to Appcues.',
9
+ platform: 'web',
10
+ fields: {
11
+ event: {
12
+ label: 'Event Name',
13
+ description: 'The name of the event to track in Appcues.',
14
+ required: true,
15
+ type: 'string',
16
+ default: { '@path': '$.event' }
17
+ },
18
+ properties: {
19
+ label: 'Event Properties',
20
+ description: 'Properties to associate with the event.',
21
+ required: false,
22
+ type: 'object',
23
+ default: { '@path': '$.properties' }
24
+ }
25
+ },
26
+ defaultSubscription: 'type = "track"',
27
+ perform: (appcues, { payload }) => {
28
+ const { event, properties } = payload
29
+ appcues.track(event, properties)
30
+ }
31
+ }
32
+
33
+ export default action
package/src/types.ts ADDED
@@ -0,0 +1,21 @@
1
+ declare global {
2
+ interface Window {
3
+ Appcues: Appcues
4
+ AppcuesSettings: AppcuesSettings
5
+ }
6
+ }
7
+
8
+ export interface Appcues {
9
+ track(event_name: string, properties?: Properties): void
10
+ identify(userId: string, traits?: Properties): void
11
+ group(groupId: string, traits?: Properties): void
12
+ page(): void
13
+ }
14
+
15
+ export interface AppcuesSettings {
16
+ enableURLDetection: boolean
17
+ }
18
+
19
+ export type Properties = {
20
+ [k: string]: unknown
21
+ }
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
+ }