@segment/analytics-browser-actions-wiseops 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-wiseops",
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,48 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import wisepopsDestination, { destination } from '../index'
3
+ import { Subscription } from '@segment/browser-destination-runtime/types'
4
+ import nock from 'nock'
5
+
6
+ const subscriptions: Subscription[] = [
7
+ {
8
+ partnerAction: 'trackPage',
9
+ name: 'Track Page',
10
+ enabled: true,
11
+ subscribe: 'type = "page"',
12
+ mapping: {}
13
+ }
14
+ ]
15
+
16
+ describe('Wisepops', () => {
17
+ test('initialize Wisepops with a website hash', async () => {
18
+ const startTime = Date.now();
19
+ jest.spyOn(destination, 'initialize')
20
+ nock('https://loader.wisepops.com').get('/get-loader.js?plugin=segment&v=1&site=1234567890').reply(200, {})
21
+
22
+ const [event] = await wisepopsDestination({
23
+ websiteId: '1234567890',
24
+ subscriptions
25
+ })
26
+
27
+ await event.load(Context.system(), {} as Analytics)
28
+ expect(destination.initialize).toHaveBeenCalled()
29
+
30
+ expect(window.wisepops.q).toEqual([['options', {autoPageview: false}]]);
31
+ expect(window.wisepops.l).toBeGreaterThanOrEqual(startTime);
32
+ expect(window.wisepops.l).toBeLessThanOrEqual(Date.now());
33
+
34
+ const scripts = window.document.querySelectorAll('script')
35
+ expect(scripts).toMatchInlineSnapshot(`
36
+ NodeList [
37
+ <script
38
+ src="https://loader.wisepops.com/get-loader.js?plugin=segment&v=1&site=1234567890"
39
+ status="loading"
40
+ type="text/javascript"
41
+ />,
42
+ <script>
43
+ // the emptiness
44
+ </script>,
45
+ ]
46
+ `)
47
+ })
48
+ })
@@ -0,0 +1,8 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Settings {
4
+ /**
5
+ * The identifier of your Wisepops' website. You can find it in [your setup code on Wisepops](https://app.wisepops.com/f/settings/websites).
6
+ */
7
+ websiteId: string
8
+ }
package/src/index.ts ADDED
@@ -0,0 +1,95 @@
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 { Wisepops } from './types'
5
+
6
+ import { defaultValues } from '@segment/actions-core'
7
+
8
+ import setCustomProperties from './setCustomProperties'
9
+ import trackEvent from './trackEvent'
10
+ import trackGoal from './trackGoal'
11
+ import trackPage from './trackPage'
12
+
13
+ declare global {
14
+ interface Window {
15
+ wisepops: Wisepops
16
+ WisePopsObject: string
17
+ }
18
+ }
19
+
20
+ export const destination: BrowserDestinationDefinition<Settings, Wisepops> = {
21
+ name: 'Wisepops',
22
+ slug: 'actions-wisepops',
23
+ mode: 'device',
24
+
25
+ presets: [
26
+ {
27
+ name: 'Set User Traits as Custom Properties',
28
+ subscribe: setCustomProperties.defaultSubscription!,
29
+ partnerAction: 'setCustomProperties',
30
+ mapping: defaultValues(setCustomProperties.fields)
31
+ },
32
+ {
33
+ name: 'Set Group Traits as Custom Properties',
34
+ subscribe: 'type = "group"',
35
+ partnerAction: 'setCustomProperties',
36
+ mapping: {
37
+ traits: { '@path': '$.traits' },
38
+ id: { '@path': '$.groupId' },
39
+ idProperty: 'groupId',
40
+ prefix: 'group',
41
+ }
42
+ },
43
+ {
44
+ name: trackEvent.title,
45
+ subscribe: trackEvent.defaultSubscription!,
46
+ partnerAction: 'trackEvent',
47
+ mapping: defaultValues(trackEvent.fields)
48
+ },
49
+ {
50
+ name: trackGoal.title,
51
+ subscribe: trackGoal.defaultSubscription!,
52
+ partnerAction: 'trackGoal',
53
+ mapping: defaultValues(trackGoal.fields)
54
+ },
55
+ {
56
+ name: trackPage.title,
57
+ subscribe: trackPage.defaultSubscription!,
58
+ partnerAction: 'trackPage',
59
+ mapping: defaultValues(trackPage.fields)
60
+ }
61
+ ],
62
+
63
+ settings: {
64
+ websiteId: {
65
+ description:
66
+ "The identifier of your Wisepops' website. You can find it in [your setup code on Wisepops](https://app.wisepops.com/f/settings/websites).",
67
+ label: 'Website Identifier',
68
+ type: 'string',
69
+ required: true
70
+ }
71
+ },
72
+
73
+ initialize: async ({ settings }, deps) => {
74
+ window.WisePopsObject = 'wisepops'
75
+ window.wisepops =
76
+ window.wisepops ||
77
+ function (...arg) {
78
+ ;(window.wisepops.q = window.wisepops.q || []).push(arg)
79
+ }
80
+ window.wisepops.l = Date.now()
81
+ window.wisepops('options', { autoPageview: false })
82
+ // Can load asynchronously, no need to wait
83
+ void deps.loadScript(`https://loader.wisepops.com/get-loader.js?plugin=segment&v=1&site=${settings.websiteId}`)
84
+ return window.wisepops
85
+ },
86
+
87
+ actions: {
88
+ setCustomProperties,
89
+ trackEvent,
90
+ trackGoal,
91
+ trackPage,
92
+ }
93
+ }
94
+
95
+ export default browserDestination(destination)
@@ -0,0 +1,163 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import setCustomPropertiesObject from '../index'
3
+ import wisepopsDestination from '../../index'
4
+ import { Subscription } from '@segment/browser-destination-runtime/types'
5
+
6
+ import { loadScript } from '@segment/browser-destination-runtime/load-script'
7
+ jest.mock('@segment/browser-destination-runtime/load-script')
8
+ beforeEach(async () => {
9
+ // Prevent Wisepops SDK from being loaded.
10
+ ;(loadScript as jest.Mock).mockResolvedValue(true)
11
+ })
12
+
13
+ describe('Wisepops.setCustomProperties', () => {
14
+ test('custom properties', async () => {
15
+ const subscriptions: Subscription[] = [
16
+ {
17
+ partnerAction: 'setCustomProperties',
18
+ name: setCustomPropertiesObject.title,
19
+ enabled: true,
20
+ subscribe: setCustomPropertiesObject.defaultSubscription!,
21
+ mapping: {
22
+ traits: {
23
+ '@path': '$.traits'
24
+ },
25
+ id: {
26
+ '@path': '$.userId'
27
+ },
28
+ idProperty: 'userId',
29
+ }
30
+ }
31
+ ]
32
+
33
+ const [setCustomProperties] = await wisepopsDestination({
34
+ websiteId: '1234567890',
35
+ subscriptions
36
+ })
37
+
38
+ expect(setCustomProperties).toBeDefined()
39
+
40
+ await setCustomProperties.load(Context.system(), {} as Analytics)
41
+ jest.spyOn(window.wisepops.q as any, 'push')
42
+
43
+ {
44
+ const context = new Context({
45
+ type: 'identify',
46
+ traits: {
47
+ firstName: 'John',
48
+ }
49
+ });
50
+
51
+ setCustomProperties.identify?.(context)
52
+
53
+ expect(window.wisepops.q.push).toHaveBeenCalledWith(['properties', {
54
+ firstName: 'John',
55
+ }])
56
+ }
57
+
58
+ {
59
+ const context = new Context({
60
+ type: 'identify',
61
+ userId: '42',
62
+ traits: {
63
+ email: 'test@example.com',
64
+ firstName: 'John',
65
+ address: {
66
+ city: 'Paris',
67
+ country: 'France'
68
+ }
69
+ }
70
+ });
71
+
72
+ setCustomProperties.identify?.(context)
73
+
74
+ expect(window.wisepops.q.push).toHaveBeenCalledWith(['properties', {
75
+ userId: '42',
76
+ email: 'test@example.com',
77
+ firstName: 'John',
78
+ address: {
79
+ city: 'Paris',
80
+ country: 'France'
81
+ }
82
+ }])
83
+ }
84
+ })
85
+
86
+ test('nested custom properties', async () => {
87
+ const subscriptions: Subscription[] = [
88
+ {
89
+ partnerAction: 'setCustomProperties',
90
+ name: setCustomPropertiesObject.title,
91
+ enabled: true,
92
+ subscribe: setCustomPropertiesObject.defaultSubscription!,
93
+ mapping: {
94
+ traits: {
95
+ '@path': '$.traits'
96
+ },
97
+ prefix: 'user',
98
+ id: {
99
+ '@path': '$.userId'
100
+ },
101
+ idProperty: 'userId',
102
+ }
103
+ }
104
+ ]
105
+
106
+ const [setCustomProperties] = await wisepopsDestination({
107
+ websiteId: '1234567890',
108
+ subscriptions
109
+ })
110
+
111
+ expect(setCustomProperties).toBeDefined()
112
+
113
+ await setCustomProperties.load(Context.system(), {} as Analytics)
114
+ jest.spyOn(window.wisepops.q as any, 'push')
115
+
116
+ {
117
+ const context = new Context({
118
+ type: 'identify',
119
+ traits: {
120
+ firstName: 'John',
121
+ }
122
+ });
123
+
124
+ setCustomProperties.identify?.(context)
125
+
126
+ expect(window.wisepops.q.push).toHaveBeenCalledWith(['properties', {
127
+ user: {
128
+ firstName: 'John',
129
+ }
130
+ }])
131
+ }
132
+
133
+ {
134
+ const context = new Context({
135
+ type: 'identify',
136
+ userId: '42',
137
+ traits: {
138
+ email: 'test@example.com',
139
+ firstName: 'John',
140
+ address: {
141
+ city: 'Paris',
142
+ country: 'France'
143
+ }
144
+ }
145
+ });
146
+
147
+ setCustomProperties.identify?.(context)
148
+
149
+ expect(window.wisepops.q.push).toHaveBeenCalledWith(['properties', {
150
+ user: {
151
+ userId: '42',
152
+ email: 'test@example.com',
153
+ firstName: 'John',
154
+ address: {
155
+ city: 'Paris',
156
+ country: 'France'
157
+ }
158
+ }
159
+ }])
160
+ }
161
+
162
+ })
163
+ })
@@ -0,0 +1,22 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * The custom properties to send to Wisepops.
6
+ */
7
+ traits: {
8
+ [k: string]: unknown
9
+ }
10
+ /**
11
+ * A unique identifier. Typically, a user ID or group ID.
12
+ */
13
+ id?: string
14
+ /**
15
+ * How to name the entity ID among the other custom properties?
16
+ */
17
+ idProperty?: string
18
+ /**
19
+ * This lets you define the properties as a nested object. If you set the property `"name"` with the prefix `"group"`, you'll access it in Wisepops as `"group.name"`.
20
+ */
21
+ prefix?: string
22
+ }
@@ -0,0 +1,58 @@
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 { Wisepops } from '../types'
5
+
6
+ const action: BrowserActionDefinition<Settings, Wisepops, Payload> = {
7
+ title: 'Set Custom Properties',
8
+ description: 'Define [custom properties](https://support.wisepops.com/article/yrdyv1tfih-set-up-custom-properties) to let Wisepops target them in your scenarios.',
9
+ defaultSubscription: 'type = "identify"',
10
+ platform: 'web',
11
+ fields: {
12
+ traits: {
13
+ description: "The custom properties to send to Wisepops.",
14
+ label: 'Custom Properties',
15
+ type: 'object',
16
+ required: true,
17
+ default: {
18
+ '@path': '$.traits'
19
+ }
20
+ },
21
+ id: {
22
+ description: 'A unique identifier. Typically, a user ID or group ID.',
23
+ label: 'Entity ID',
24
+ type: 'string',
25
+ required: false,
26
+ default: {
27
+ '@path': '$.userId'
28
+ }
29
+ },
30
+ idProperty: {
31
+ description: 'How to name the entity ID among the other custom properties?',
32
+ label: 'Property name for the entity ID',
33
+ type: 'string',
34
+ required: false,
35
+ default: 'userId'
36
+ },
37
+ prefix: {
38
+ description: 'This lets you define the properties as a nested object. If you set the property `"name"` with the prefix `"group"`, you\'ll access it in Wisepops as `"group.name"`.',
39
+ label: 'Prefix',
40
+ type: 'string',
41
+ required: false,
42
+ },
43
+ },
44
+ perform: (wisepops, event) => {
45
+ let properties = event.payload.traits;
46
+ if (event.payload.idProperty && event.payload.id) {
47
+ properties[event.payload.idProperty] = event.payload.id;
48
+ }
49
+ if (event.payload.prefix) {
50
+ properties = {
51
+ [event.payload.prefix]: properties
52
+ };
53
+ }
54
+ wisepops('properties', properties);
55
+ }
56
+ }
57
+
58
+ export default action
@@ -0,0 +1,50 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import trackEventObject from '../index'
3
+ import wisepopsDestination from '../../index'
4
+ import { Subscription } from '@segment/browser-destination-runtime/types'
5
+
6
+ import { loadScript } from '@segment/browser-destination-runtime/load-script'
7
+ jest.mock('@segment/browser-destination-runtime/load-script')
8
+ beforeEach(async () => {
9
+ // Prevent Wisepops SDK from being loaded.
10
+ ;(loadScript as jest.Mock).mockResolvedValue(true)
11
+ })
12
+
13
+ describe('Wisepops.trackEvent', () => {
14
+ const subscriptions: Subscription[] = [
15
+ {
16
+ partnerAction: 'trackEvent',
17
+ name: trackEventObject.title,
18
+ enabled: true,
19
+ subscribe: 'type = "track"',
20
+ mapping: {
21
+ eventName: {
22
+ '@path': '$.event'
23
+ }
24
+ }
25
+ }
26
+ ]
27
+
28
+ test('custom event', async () => {
29
+ const [trackEvent] = await wisepopsDestination({
30
+ websiteId: '1234567890',
31
+ subscriptions
32
+ })
33
+
34
+ expect(trackEvent).toBeDefined()
35
+
36
+ await trackEvent.load(Context.system(), {} as Analytics)
37
+ jest.spyOn(window.wisepops.q as any, 'push')
38
+
39
+ const context = new Context({
40
+ type: 'track',
41
+ event: 'Something happens',
42
+ properties: {
43
+ eventParam: 'An event parameter'
44
+ }
45
+ })
46
+ trackEvent.track?.(context)
47
+
48
+ expect(window.wisepops.q.push).toHaveBeenCalledWith(['event', 'Something happens'])
49
+ })
50
+ })
@@ -0,0 +1,8 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * The name of the event to send to Wisepops.
6
+ */
7
+ eventName: string
8
+ }
@@ -0,0 +1,27 @@
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 { Wisepops } from '../types';
5
+
6
+ const action: BrowserActionDefinition<Settings, Wisepops, Payload> = {
7
+ title: 'Track Event',
8
+ description: "Send a [custom event](https://support.wisepops.com/article/zbpq1z0exk-set-up-custom-events-to-trigger-popups) to Wisepops. Keep in mind that events are counted as page views in your Wisepops' monthly quota.",
9
+ defaultSubscription: 'type = "track"',
10
+ platform: 'web',
11
+ fields: {
12
+ eventName: {
13
+ description: 'The name of the event to send to Wisepops.',
14
+ label: 'Event Name',
15
+ type: 'string',
16
+ required: true,
17
+ default: {
18
+ '@path': '$.event'
19
+ }
20
+ },
21
+ },
22
+ perform: (wisepops, event) => {
23
+ wisepops('event', event.payload.eventName);
24
+ }
25
+ }
26
+
27
+ export default action
@@ -0,0 +1,52 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import trackGoalObject from '../index'
3
+ import wisepopsDestination from '../../index'
4
+ import { Subscription } from '@segment/browser-destination-runtime/types'
5
+
6
+ import { loadScript } from '@segment/browser-destination-runtime/load-script'
7
+ jest.mock('@segment/browser-destination-runtime/load-script')
8
+ beforeEach(async () => {
9
+ // Prevent Wisepops SDK from being loaded.
10
+ ;(loadScript as jest.Mock).mockResolvedValue(true)
11
+ })
12
+
13
+ describe('Wisepops.trackGoal', () => {
14
+ const subscriptions: Subscription[] = [
15
+ {
16
+ partnerAction: 'trackGoal',
17
+ name: trackGoalObject.title,
18
+ enabled: true,
19
+ subscribe: trackGoalObject.defaultSubscription!,
20
+ mapping: {
21
+ goalName: {
22
+ '@path': '$.event'
23
+ },
24
+ goalRevenue: {
25
+ '@path': '$.properties.revenue'
26
+ },
27
+ }
28
+ }
29
+ ]
30
+
31
+ test('named goal with revenue', async () => {
32
+ const [trackGoal] = await wisepopsDestination({
33
+ websiteId: '1234567890',
34
+ subscriptions
35
+ })
36
+ expect(trackGoal).toBeDefined()
37
+
38
+ await trackGoal.load(Context.system(), {} as Analytics)
39
+ jest.spyOn(window.wisepops.q as any, 'push')
40
+
41
+ const context = new Context({
42
+ type: 'track',
43
+ event: 'Order Completed',
44
+ properties: {
45
+ revenue: 15
46
+ }
47
+ })
48
+ trackGoal.track?.(context)
49
+
50
+ expect(window.wisepops.q.push).toHaveBeenCalledWith(['goal', 'Order Completed', 15])
51
+ })
52
+ })
@@ -0,0 +1,12 @@
1
+ // Generated file. DO NOT MODIFY IT BY HAND.
2
+
3
+ export interface Payload {
4
+ /**
5
+ * The name of the goal to send to Wisepops.
6
+ */
7
+ goalName?: string
8
+ /**
9
+ * The revenue associated with the goal.
10
+ */
11
+ goalRevenue?: string
12
+ }
@@ -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 { Wisepops } from '../types'
5
+
6
+ // Change from unknown to the partner SDK types
7
+ const action: BrowserActionDefinition<Settings, Wisepops, Payload> = {
8
+ title: 'Track Goal',
9
+ description: '[Track goals and revenue](https://support.wisepops.com/article/mx3z8na6yb-set-up-goal-tracking) to know which campaigns are generating the most value.',
10
+ defaultSubscription: 'type = "track" and event = "Order Completed"',
11
+ platform: 'web',
12
+ fields: {
13
+ goalName: {
14
+ description: 'The name of the goal to send to Wisepops.',
15
+ label: 'Goal Name',
16
+ type: 'string',
17
+ required: false,
18
+ default: {
19
+ '@path': '$.event'
20
+ }
21
+ },
22
+ goalRevenue: {
23
+ description: 'The revenue associated with the goal.',
24
+ label: 'Goal Revenue',
25
+ type: 'string',
26
+ required: false,
27
+ default: {
28
+ '@path': '$.properties.revenue'
29
+ }
30
+ },
31
+ },
32
+ perform: (wisepops, event) => {
33
+ wisepops('goal', event.payload.goalName, event.payload.goalRevenue);
34
+ }
35
+ }
36
+
37
+ export default action
@@ -0,0 +1,40 @@
1
+ import { Analytics, Context } from '@segment/analytics-next'
2
+ import trackPageObject from '../index'
3
+ import wisepopsDestination from '../../index'
4
+ import { Subscription } from '@segment/browser-destination-runtime/types'
5
+
6
+ import { loadScript } from '@segment/browser-destination-runtime/load-script'
7
+ jest.mock('@segment/browser-destination-runtime/load-script')
8
+ beforeEach(async () => {
9
+ // Prevent Wisepops SDK from being loaded.
10
+ ;(loadScript as jest.Mock).mockResolvedValue(true)
11
+ })
12
+
13
+ describe('Wisepops.trackPage', () => {
14
+ const subscriptions: Subscription[] = [
15
+ {
16
+ partnerAction: 'trackPage',
17
+ name: trackPageObject.title,
18
+ enabled: true,
19
+ subscribe: trackPageObject.defaultSubscription!,
20
+ mapping: {}
21
+ }
22
+ ]
23
+
24
+ test('pageview', async () => {
25
+ const [trackPage] = await wisepopsDestination({
26
+ websiteId: '1234567890',
27
+ subscriptions
28
+ })
29
+
30
+ expect(trackPage).toBeDefined()
31
+
32
+ await trackPage.load(Context.system(), {} as Analytics)
33
+ jest.spyOn(window.wisepops.q as any, 'push')
34
+
35
+ const context = new Context({type: 'page'})
36
+ trackPage.page?.(context)
37
+
38
+ expect(window.wisepops.q.push).toHaveBeenCalledWith(['pageview'])
39
+ })
40
+ })
@@ -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 { Wisepops } from '../types';
5
+
6
+ const action: BrowserActionDefinition<Settings, Wisepops, Payload> = {
7
+ title: 'Track Page',
8
+ description: 'Let Wisepops know when the visitor goes to a new page. This allows Wisepops to display campaigns at page change.',
9
+ defaultSubscription: 'type = "page"',
10
+ platform: 'web',
11
+ fields: {},
12
+ perform: (wisepops) => {
13
+ wisepops('pageview');
14
+ }
15
+ }
16
+
17
+ export default action
package/src/types.ts ADDED
@@ -0,0 +1,10 @@
1
+ type method = 'event' | 'goal' | 'options' | 'pageview' | 'properties'
2
+
3
+ type WisepopsFunction = (method: method, parameters?: unknown, options?: unknown) => void;
4
+
5
+ type WisepopsInternal = {
6
+ q: unknown[],
7
+ l: number
8
+ }
9
+
10
+ export type Wisepops = WisepopsFunction & WisepopsInternal;
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
+ }