@ht-sdks/events-sdk-js-browser 1.1.0 → 1.3.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/README.md +72 -4
- package/dist/cjs/browser/index.js +76 -42
- package/dist/cjs/browser/index.js.map +1 -1
- package/dist/cjs/core/analytics/index.js +1 -1
- package/dist/cjs/core/analytics/index.js.map +1 -1
- package/dist/cjs/core/http-cookies/index.js +174 -0
- package/dist/cjs/core/http-cookies/index.js.map +1 -0
- package/dist/cjs/core/user/index.js +32 -6
- package/dist/cjs/core/user/index.js.map +1 -1
- package/dist/cjs/core/user/tld.js +7 -3
- package/dist/cjs/core/user/tld.js.map +1 -1
- package/dist/cjs/generated/version.js +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/node/index.js +1 -1
- package/dist/cjs/node/index.js.map +1 -1
- package/dist/cjs/plugins/analytics-node/index.js +2 -2
- package/dist/cjs/plugins/analytics-node/index.js.map +1 -1
- package/dist/cjs/plugins/destinations/destination.js +81 -0
- package/dist/cjs/plugins/destinations/destination.js.map +1 -0
- package/dist/cjs/plugins/destinations/google-tag-manager.js +47 -0
- package/dist/cjs/plugins/destinations/google-tag-manager.js.map +1 -0
- package/dist/cjs/plugins/destinations/index.js +22 -0
- package/dist/cjs/plugins/destinations/index.js.map +1 -0
- package/dist/cjs/plugins/destinations/types.js +3 -0
- package/dist/cjs/plugins/destinations/types.js.map +1 -0
- package/dist/pkg/browser/index.js +76 -42
- package/dist/pkg/browser/index.js.map +1 -1
- package/dist/pkg/core/analytics/index.js +1 -1
- package/dist/pkg/core/analytics/index.js.map +1 -1
- package/dist/pkg/core/http-cookies/index.js +171 -0
- package/dist/pkg/core/http-cookies/index.js.map +1 -0
- package/dist/pkg/core/user/index.js +32 -6
- package/dist/pkg/core/user/index.js.map +1 -1
- package/dist/pkg/core/user/tld.js +7 -3
- package/dist/pkg/core/user/tld.js.map +1 -1
- package/dist/pkg/generated/version.js +1 -1
- package/dist/pkg/index.js +1 -0
- package/dist/pkg/index.js.map +1 -1
- package/dist/pkg/node/index.js +1 -1
- package/dist/pkg/node/index.js.map +1 -1
- package/dist/pkg/plugins/analytics-node/index.js +2 -2
- package/dist/pkg/plugins/analytics-node/index.js.map +1 -1
- package/dist/pkg/plugins/destinations/destination.js +78 -0
- package/dist/pkg/plugins/destinations/destination.js.map +1 -0
- package/dist/pkg/plugins/destinations/google-tag-manager.js +45 -0
- package/dist/pkg/plugins/destinations/google-tag-manager.js.map +1 -0
- package/dist/pkg/plugins/destinations/index.js +18 -0
- package/dist/pkg/plugins/destinations/index.js.map +1 -0
- package/dist/pkg/plugins/destinations/types.js +2 -0
- package/dist/pkg/plugins/destinations/types.js.map +1 -0
- package/dist/types/browser/index.d.ts.map +1 -1
- package/dist/types/core/analytics/index.d.ts +14 -0
- package/dist/types/core/analytics/index.d.ts.map +1 -1
- package/dist/types/core/buffer/index.d.ts +1 -1
- package/dist/types/core/http-cookies/index.d.ts +57 -0
- package/dist/types/core/http-cookies/index.d.ts.map +1 -0
- package/dist/types/core/user/index.d.ts +5 -0
- package/dist/types/core/user/index.d.ts.map +1 -1
- package/dist/types/core/user/tld.d.ts.map +1 -1
- package/dist/types/generated/version.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/plugins/destinations/destination.d.ts +32 -0
- package/dist/types/plugins/destinations/destination.d.ts.map +1 -0
- package/dist/types/plugins/destinations/google-tag-manager.d.ts +30 -0
- package/dist/types/plugins/destinations/google-tag-manager.d.ts.map +1 -0
- package/dist/types/plugins/destinations/index.d.ts +6 -0
- package/dist/types/plugins/destinations/index.d.ts.map +1 -0
- package/dist/types/plugins/destinations/types.d.ts +5 -0
- package/dist/types/plugins/destinations/types.d.ts.map +1 -0
- package/dist/umd/events.min.js +1 -1
- package/dist/umd/events.min.js.map +1 -1
- package/dist/umd/google-tag-manager.bundle.c27daab560c3298c04ca.js +2 -0
- package/dist/umd/google-tag-manager.bundle.c27daab560c3298c04ca.js.map +1 -0
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +14 -19
- package/src/browser/index.ts +20 -0
- package/src/core/analytics/index.ts +20 -0
- package/src/core/http-cookies/README.md +97 -0
- package/src/core/http-cookies/index.ts +165 -0
- package/src/core/http-cookies/server-examples/node-aws-lambda.md +167 -0
- package/src/core/http-cookies/server-examples/node-express-js.md +103 -0
- package/src/core/http-cookies/server-examples/node-next-js.md +75 -0
- package/src/core/user/index.ts +42 -4
- package/src/core/user/tld.ts +8 -4
- package/src/generated/version.ts +1 -1
- package/src/index.ts +1 -0
- package/src/node/index.ts +1 -1
- package/src/plugins/analytics-node/index.ts +2 -2
- package/src/plugins/destinations/destination.ts +85 -0
- package/src/plugins/destinations/google-tag-manager.ts +96 -0
- package/src/plugins/destinations/index.ts +19 -0
- package/src/plugins/destinations/types.ts +8 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
**These server examples should not be used as is. They should be adapted to your setup and "productionized".**
|
|
2
|
+
|
|
3
|
+
An example HTTPCookieService written as a Next.js API Route.
|
|
4
|
+
|
|
5
|
+
```Javascript
|
|
6
|
+
import type { NextApiRequest, NextApiResponse } from "next";
|
|
7
|
+
|
|
8
|
+
const USER_COOKIE = "htjs_user_id";
|
|
9
|
+
const ANON_COOKIE = "htjs_anonymous_id";
|
|
10
|
+
|
|
11
|
+
function getDomain(request: NextApiRequest) {
|
|
12
|
+
const domain = request.headers.host?.toString() ?? "";
|
|
13
|
+
if (domain.startsWith("localhost")) return "localhost";
|
|
14
|
+
return domain;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function renewCookies(request: NextApiRequest, response: NextApiResponse, browserName: string, serverName: string) {
|
|
18
|
+
const cookie = request.cookies[browserName] ?? request.cookies[serverName];
|
|
19
|
+
if (!cookie) return "";
|
|
20
|
+
const maxAge = 31_536_000; // 1 year in seconds
|
|
21
|
+
const domain = getDomain(request);
|
|
22
|
+
response.setHeader("Set-Cookie", [
|
|
23
|
+
...((response.getHeader("Set-Cookie") as string[]) ?? []),
|
|
24
|
+
`${browserName}=${cookie}; Max-Age=${maxAge}; Domain=${domain}; Path=/; SameSite=Lax;`,
|
|
25
|
+
`${serverName}=${cookie}; Max-Age=${maxAge}; Domain=${domain}; Path=/; SameSite=Lax; httpOnly=true;`,
|
|
26
|
+
]);
|
|
27
|
+
return cookie;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function clearServerCookie(request: NextApiRequest, response: NextApiResponse, serverName: string) {
|
|
31
|
+
const cookie = "";
|
|
32
|
+
const maxAge = 0;
|
|
33
|
+
const domain = getDomain(request);
|
|
34
|
+
response.setHeader("Set-Cookie", [
|
|
35
|
+
...((response.getHeader("Set-Cookie") as string[]) ?? []),
|
|
36
|
+
`${serverName}=${cookie}; Max-Age=${maxAge}; Domain=${domain}; Path=/; SameSite=Lax; httpOnly;`,
|
|
37
|
+
]);
|
|
38
|
+
return cookie;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default function handler(request: NextApiRequest, response: NextApiResponse) {
|
|
42
|
+
if (request.method?.toUpperCase() !== "POST") {
|
|
43
|
+
response.status(404);
|
|
44
|
+
response.end();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const slug = (request.query.slug as string).toLowerCase();
|
|
49
|
+
|
|
50
|
+
if (slug === "renew") {
|
|
51
|
+
response.status(200);
|
|
52
|
+
response.json({
|
|
53
|
+
userId: renewCookies(request, response, USER_COOKIE, `${USER_COOKIE}_srvr`),
|
|
54
|
+
anonymousId: renewCookies(request, response, ANON_COOKIE, `${ANON_COOKIE}_srvr`),
|
|
55
|
+
});
|
|
56
|
+
} else if (slug === "clear") {
|
|
57
|
+
response.status(200);
|
|
58
|
+
response.json({
|
|
59
|
+
userId: clearServerCookie(request, response, `${USER_COOKIE}_srvr`),
|
|
60
|
+
anonymousId: clearServerCookie(request, response, `${ANON_COOKIE}_srvr`),
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
response.status(404);
|
|
64
|
+
response.end();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The HTTPCookieService must live on the same domain and IP address as your website's HTML document.
|
|
70
|
+
|
|
71
|
+
As one way to accomplish this, you could create one Next.js project to serve both your HTML site and your API.
|
|
72
|
+
|
|
73
|
+
The above example code might live at `src/pages/api/ht/[slug].ts`, while your other non-API pages might live somewhere like `src/pages/blog/index.tsx`.
|
|
74
|
+
|
|
75
|
+
See [Next.js](https://nextjs.org/docs) for more information.
|
package/src/core/user/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
hasSessionExpired,
|
|
22
22
|
updateSessionExpiration,
|
|
23
23
|
} from '../session'
|
|
24
|
+
import type { HTTPCookieService } from '../http-cookies'
|
|
24
25
|
|
|
25
26
|
export type ID = string | null | undefined
|
|
26
27
|
|
|
@@ -32,6 +33,11 @@ export interface UserOptions {
|
|
|
32
33
|
localStorageFallbackDisabled?: boolean
|
|
33
34
|
persist?: boolean
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Replicates "BrowserCookie" actions against a matching "ServerCookie".
|
|
38
|
+
*/
|
|
39
|
+
httpCookieService?: HTTPCookieService
|
|
40
|
+
|
|
35
41
|
cookie?: {
|
|
36
42
|
key?: string
|
|
37
43
|
oldKey?: string
|
|
@@ -134,6 +140,14 @@ export class User {
|
|
|
134
140
|
legacyUser.id && this.id(legacyUser.id)
|
|
135
141
|
legacyUser.traits && this.traits(legacyUser.traits)
|
|
136
142
|
}
|
|
143
|
+
|
|
144
|
+
// HTTPCookies require that localStorage values be synced to cookies
|
|
145
|
+
if (this.options.httpCookieService) {
|
|
146
|
+
this.identityStore.getAndSync(this.anonKey)
|
|
147
|
+
this.identityStore.getAndSync(this.idKey)
|
|
148
|
+
this.options.httpCookieService?.dispatchCreate()
|
|
149
|
+
}
|
|
150
|
+
|
|
137
151
|
autoBind(this)
|
|
138
152
|
}
|
|
139
153
|
|
|
@@ -145,11 +159,22 @@ export class User {
|
|
|
145
159
|
const prevId = this.identityStore.getAndSync(this.idKey)
|
|
146
160
|
|
|
147
161
|
if (id !== undefined) {
|
|
162
|
+
const clearingIdentity = id === null
|
|
163
|
+
const changingIdentity = id !== prevId && prevId !== null && id !== null
|
|
164
|
+
const creatingIdentity = id !== prevId && prevId === null && id !== null
|
|
165
|
+
|
|
148
166
|
this.identityStore.set(this.idKey, id)
|
|
149
167
|
|
|
150
|
-
|
|
168
|
+
if (clearingIdentity) {
|
|
169
|
+
this.options?.httpCookieService?.dispatchClear()
|
|
170
|
+
}
|
|
171
|
+
|
|
151
172
|
if (changingIdentity) {
|
|
152
|
-
this.anonymousId(null)
|
|
173
|
+
this.anonymousId(null) // this also runs dispatchClear()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (changingIdentity || creatingIdentity) {
|
|
177
|
+
this.options?.httpCookieService?.dispatchCreate()
|
|
153
178
|
}
|
|
154
179
|
}
|
|
155
180
|
|
|
@@ -176,28 +201,36 @@ export class User {
|
|
|
176
201
|
|
|
177
202
|
if (id === undefined) {
|
|
178
203
|
let val = this.identityStore.getAndSync(this.anonKey)
|
|
204
|
+
let migrated = false
|
|
179
205
|
|
|
180
206
|
// support anonymousId migration from other analytics providers
|
|
181
207
|
if (!val) {
|
|
182
208
|
val = decryptRudderHtValue(
|
|
183
209
|
this.identityStore.getAndSync(rudderHtAnonymousIdKey) ?? ''
|
|
184
210
|
)
|
|
211
|
+
migrated = Boolean(val)
|
|
185
212
|
if (val) this.identityStore.set(this.anonKey, val)
|
|
186
213
|
}
|
|
187
214
|
if (!val) {
|
|
188
215
|
val = this.identityStore.getAndSync(segmentAnonymousIdKey)
|
|
216
|
+
migrated = Boolean(val)
|
|
189
217
|
if (val) this.identityStore.set(this.anonKey, val)
|
|
190
218
|
}
|
|
191
219
|
if (!val) {
|
|
192
220
|
val = decryptRudderValue(
|
|
193
221
|
this.identityStore.getAndSync(rudderAnonymousIdKey) ?? ''
|
|
194
222
|
)
|
|
223
|
+
migrated = Boolean(val)
|
|
195
224
|
if (val) this.identityStore.set(this.anonKey, val)
|
|
196
225
|
}
|
|
197
226
|
if (!val) {
|
|
198
227
|
val = this.legacySIO()?.[0] ?? null
|
|
199
228
|
}
|
|
200
229
|
|
|
230
|
+
if (migrated) {
|
|
231
|
+
this.options?.httpCookieService?.dispatchCreate()
|
|
232
|
+
}
|
|
233
|
+
|
|
201
234
|
if (val) {
|
|
202
235
|
return val
|
|
203
236
|
}
|
|
@@ -205,11 +238,16 @@ export class User {
|
|
|
205
238
|
|
|
206
239
|
if (id === null) {
|
|
207
240
|
this.identityStore.set(this.anonKey, null)
|
|
208
|
-
|
|
241
|
+
const clearedVal = this.identityStore.getAndSync(this.anonKey)
|
|
242
|
+
this.options?.httpCookieService?.dispatchClear()
|
|
243
|
+
return clearedVal
|
|
209
244
|
}
|
|
210
245
|
|
|
211
246
|
this.identityStore.set(this.anonKey, id ?? uuid())
|
|
212
|
-
|
|
247
|
+
const syncedVal = this.identityStore.getAndSync(this.anonKey)
|
|
248
|
+
|
|
249
|
+
this.options?.httpCookieService?.dispatchCreate()
|
|
250
|
+
return syncedVal
|
|
213
251
|
}
|
|
214
252
|
|
|
215
253
|
traits = (traits?: Traits | null): Traits | undefined => {
|
package/src/core/user/tld.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import cookie from 'js-cookie'
|
|
1
|
+
import cookie, { CookieAttributes } from 'js-cookie'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Levels returns all levels of the given url.
|
|
@@ -45,11 +45,15 @@ export function tld(url: string): string | undefined {
|
|
|
45
45
|
|
|
46
46
|
const lvls = levels(parsedUrl)
|
|
47
47
|
|
|
48
|
-
//
|
|
48
|
+
// Test for the top most domain that the browser allows
|
|
49
49
|
for (let i = 0; i < lvls.length; ++i) {
|
|
50
|
-
const cname =
|
|
50
|
+
const cname = Math.round(Math.random() * 10_000).toString()
|
|
51
51
|
const domain = lvls[i]
|
|
52
|
-
const opts = {
|
|
52
|
+
const opts = {
|
|
53
|
+
domain: '.' + domain,
|
|
54
|
+
path: '/',
|
|
55
|
+
sameSite: 'Lax',
|
|
56
|
+
} as CookieAttributes
|
|
53
57
|
|
|
54
58
|
try {
|
|
55
59
|
// cookie access throw an error if the library is ran inside a sandboxed environment (e.g. sandboxed iframe)
|
package/src/generated/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// This file is generated.
|
|
2
|
-
export const version = '1.
|
|
2
|
+
export const version = '1.3.0'
|
package/src/index.ts
CHANGED
|
@@ -9,5 +9,6 @@ export * from './core/user'
|
|
|
9
9
|
|
|
10
10
|
export type { HtEventsSnippet } from './browser/standalone-interface'
|
|
11
11
|
export type { MiddlewareFunction } from './plugins/middleware'
|
|
12
|
+
export { Destination } from './plugins/destinations'
|
|
12
13
|
export { getGlobalAnalytics } from './lib/global-analytics-helper'
|
|
13
14
|
export { UniversalStorage, Store, StorageObject } from './core/storage'
|
package/src/node/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ export async function post(
|
|
|
23
23
|
method: 'POST',
|
|
24
24
|
headers: {
|
|
25
25
|
'Content-Type': 'application/json',
|
|
26
|
-
'User-Agent': '
|
|
26
|
+
'User-Agent': 'events-sdk-js-node/latest',
|
|
27
27
|
Authorization: `Basic ${btoa(writeKey)}`,
|
|
28
28
|
},
|
|
29
29
|
body: JSON.stringify(event),
|
|
@@ -39,7 +39,7 @@ export async function post(
|
|
|
39
39
|
|
|
40
40
|
export function analyticsNode(settings: AnalyticsNodeSettings): Plugin {
|
|
41
41
|
const send = async (ctx: Context): Promise<Context> => {
|
|
42
|
-
ctx.updateEvent('context.library.name', '
|
|
42
|
+
ctx.updateEvent('context.library.name', 'events-sdk-js-node')
|
|
43
43
|
ctx.updateEvent('context.library.version', version)
|
|
44
44
|
ctx.updateEvent('_metadata.nodeVersion', process.versions.node)
|
|
45
45
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { DestinationPlugin, Plugin } from '../../core/plugin'
|
|
2
|
+
import { Context, ContextCancelation } from '../../core/context'
|
|
3
|
+
import {
|
|
4
|
+
applyDestinationMiddleware,
|
|
5
|
+
DestinationMiddlewareFunction,
|
|
6
|
+
} from '../middleware'
|
|
7
|
+
|
|
8
|
+
// removes the return type from a function
|
|
9
|
+
type NoReturn<T> = T extends (...args: any[]) => any
|
|
10
|
+
? (...args: Parameters<T>) => void | Promise<void>
|
|
11
|
+
: T
|
|
12
|
+
|
|
13
|
+
type PluginActions = Pick<
|
|
14
|
+
Plugin,
|
|
15
|
+
'alias' | 'group' | 'identify' | 'page' | 'screen' | 'track'
|
|
16
|
+
>
|
|
17
|
+
|
|
18
|
+
type DestinationActions = {
|
|
19
|
+
[K in keyof PluginActions]: NoReturn<PluginActions[K]>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Convenience class for writing 3rd party destination plugins
|
|
24
|
+
*/
|
|
25
|
+
export class Destination implements DestinationPlugin {
|
|
26
|
+
readonly type = 'destination'
|
|
27
|
+
readonly middleware: DestinationMiddlewareFunction[] = []
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
readonly name: string,
|
|
31
|
+
readonly version: string,
|
|
32
|
+
readonly actions: DestinationActions
|
|
33
|
+
) {}
|
|
34
|
+
|
|
35
|
+
isLoaded() {
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
load() {
|
|
40
|
+
console.debug(`loaded destination plugin: ${this.name} v${this.version}`)
|
|
41
|
+
return Promise.resolve()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
addMiddleware(...fn: DestinationMiddlewareFunction[]) {
|
|
45
|
+
this.middleware.push(...fn)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
alias = this._createMethod('alias')
|
|
49
|
+
group = this._createMethod('group')
|
|
50
|
+
identify = this._createMethod('identify')
|
|
51
|
+
page = this._createMethod('page')
|
|
52
|
+
screen = this._createMethod('screen')
|
|
53
|
+
track = this._createMethod('track')
|
|
54
|
+
|
|
55
|
+
private async transform(ctx: Context): Promise<Context> {
|
|
56
|
+
const modifiedEvent = await applyDestinationMiddleware(
|
|
57
|
+
this.name,
|
|
58
|
+
ctx.event,
|
|
59
|
+
this.middleware
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if (modifiedEvent == null) {
|
|
63
|
+
ctx.cancel(
|
|
64
|
+
new ContextCancelation({
|
|
65
|
+
retry: false,
|
|
66
|
+
reason: 'dropped by destination middleware',
|
|
67
|
+
})
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return new Context(modifiedEvent)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private _createMethod(action: keyof DestinationActions) {
|
|
75
|
+
return async (ctx: Context): Promise<Context> => {
|
|
76
|
+
if (!this.actions[action]) return ctx
|
|
77
|
+
|
|
78
|
+
const transformedCtx = await this.transform(ctx)
|
|
79
|
+
|
|
80
|
+
await this.actions[action]!(transformedCtx)
|
|
81
|
+
|
|
82
|
+
return ctx
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Context } from '../../core/context'
|
|
2
|
+
import type { DestinationFactory } from './types'
|
|
3
|
+
import { Destination } from './destination'
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
gtag: Function
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type GoogleTagManagerSettings = {
|
|
12
|
+
/**
|
|
13
|
+
* The Google measurement ID(s) to send events to (GA4, Ads)
|
|
14
|
+
*/
|
|
15
|
+
measurementId?: string | string[]
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* If a `Viewed Page` event should be sent for all `htevents.page` calls
|
|
19
|
+
*/
|
|
20
|
+
trackAllPages?: boolean
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* If a `Viewed <name> Page` event should be sent for `htevents.page('Name')` calls
|
|
24
|
+
*/
|
|
25
|
+
trackNamedPages?: boolean
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* If a `Viewed <category> <name> Page` event should be sent for `htevents.page('Category', 'Name')` calls
|
|
29
|
+
*/
|
|
30
|
+
trackCategorizedPages?: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* https://github.com/segmentio/analytics.js-integrations/blob/master/integrations/google-tag-manager/lib/index.js
|
|
35
|
+
*/
|
|
36
|
+
const googleTagManager: DestinationFactory<GoogleTagManagerSettings> = ({
|
|
37
|
+
measurementId = [],
|
|
38
|
+
trackAllPages = false,
|
|
39
|
+
trackNamedPages = true,
|
|
40
|
+
trackCategorizedPages = true,
|
|
41
|
+
}) => {
|
|
42
|
+
const measurementIds = (
|
|
43
|
+
Array.isArray(measurementId) ? measurementId : [measurementId]
|
|
44
|
+
).filter(Boolean)
|
|
45
|
+
|
|
46
|
+
const baseEvent = ({ event }: Context) => {
|
|
47
|
+
return {
|
|
48
|
+
...(event.userId && {
|
|
49
|
+
user_id: event.userId,
|
|
50
|
+
}),
|
|
51
|
+
...(event.anonymousId && {
|
|
52
|
+
hightouch_anonymous_id: event.anonymousId,
|
|
53
|
+
}),
|
|
54
|
+
...(measurementIds.length > 0 && {
|
|
55
|
+
send_to: measurementIds,
|
|
56
|
+
}),
|
|
57
|
+
...event.properties,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return new Destination('Google Tag Manager', '0.0.1', {
|
|
62
|
+
identify: (ctx) => {
|
|
63
|
+
if (ctx.event.userId) {
|
|
64
|
+
measurementIds.forEach((measurementId) => {
|
|
65
|
+
window.gtag('config', measurementId, {
|
|
66
|
+
user_id: ctx.event.userId,
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
page: (ctx) => {
|
|
73
|
+
if (
|
|
74
|
+
trackAllPages ||
|
|
75
|
+
(trackNamedPages && ctx.event.name) ||
|
|
76
|
+
(trackCategorizedPages && ctx.event.category)
|
|
77
|
+
) {
|
|
78
|
+
const eventName = ['Viewed', ctx.event.category, ctx.event.name, 'Page']
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join(' ')
|
|
81
|
+
|
|
82
|
+
window.gtag('event', eventName, {
|
|
83
|
+
...baseEvent(ctx),
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
track: (ctx) => {
|
|
89
|
+
window.gtag('event', ctx.event.event, {
|
|
90
|
+
...baseEvent(ctx),
|
|
91
|
+
})
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export default googleTagManager
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DestinationSettings } from './types'
|
|
2
|
+
import { Destination } from './destination'
|
|
3
|
+
|
|
4
|
+
export { Destination }
|
|
5
|
+
export type { DestinationSettings }
|
|
6
|
+
|
|
7
|
+
export async function createDestination(
|
|
8
|
+
name: string,
|
|
9
|
+
settings: DestinationSettings
|
|
10
|
+
): Promise<Destination | undefined> {
|
|
11
|
+
switch (name) {
|
|
12
|
+
case 'Google Tag Manager':
|
|
13
|
+
return import(
|
|
14
|
+
/* webpackChunkName: "google-tag-manager" */ './google-tag-manager'
|
|
15
|
+
).then((mod) => mod.default(settings as any))
|
|
16
|
+
default:
|
|
17
|
+
return undefined
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JSONObject } from '@ht-sdks/events-sdk-js-core'
|
|
2
|
+
import type { Destination } from './destination'
|
|
3
|
+
|
|
4
|
+
export type DestinationSettings = JSONObject
|
|
5
|
+
|
|
6
|
+
export type DestinationFactory<TSettings extends DestinationSettings> = (
|
|
7
|
+
settings: TSettings
|
|
8
|
+
) => Destination
|