@customerio/cdp-analytics-core 0.0.1
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/LICENSE.MD +22 -0
- package/README.md +3 -0
- package/dist/cjs/analytics/dispatch.js +54 -0
- package/dist/cjs/analytics/dispatch.js.map +1 -0
- package/dist/cjs/analytics/index.js +3 -0
- package/dist/cjs/analytics/index.js.map +1 -0
- package/dist/cjs/callback/index.js +46 -0
- package/dist/cjs/callback/index.js.map +1 -0
- package/dist/cjs/connection/index.js +16 -0
- package/dist/cjs/connection/index.js.map +1 -0
- package/dist/cjs/context/index.js +87 -0
- package/dist/cjs/context/index.js.map +1 -0
- package/dist/cjs/emitter/index.js +66 -0
- package/dist/cjs/emitter/index.js.map +1 -0
- package/dist/cjs/emitter/interface.js +3 -0
- package/dist/cjs/emitter/interface.js.map +1 -0
- package/dist/cjs/events/index.js +149 -0
- package/dist/cjs/events/index.js.map +1 -0
- package/dist/cjs/events/interfaces.js +3 -0
- package/dist/cjs/events/interfaces.js.map +1 -0
- package/dist/cjs/index.js +25 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/logger/index.js +62 -0
- package/dist/cjs/logger/index.js.map +1 -0
- package/dist/cjs/plugins/index.js +3 -0
- package/dist/cjs/plugins/index.js.map +1 -0
- package/dist/cjs/priority-queue/backoff.js +10 -0
- package/dist/cjs/priority-queue/backoff.js.map +1 -0
- package/dist/cjs/priority-queue/index.js +92 -0
- package/dist/cjs/priority-queue/index.js.map +1 -0
- package/dist/cjs/queue/delivery.js +69 -0
- package/dist/cjs/queue/delivery.js.map +1 -0
- package/dist/cjs/queue/event-queue.js +340 -0
- package/dist/cjs/queue/event-queue.js.map +1 -0
- package/dist/cjs/stats/index.js +96 -0
- package/dist/cjs/stats/index.js.map +1 -0
- package/dist/cjs/task/task-group.js +24 -0
- package/dist/cjs/task/task-group.js.map +1 -0
- package/dist/cjs/user/index.js +3 -0
- package/dist/cjs/user/index.js.map +1 -0
- package/dist/cjs/utils/bind-all.js +18 -0
- package/dist/cjs/utils/bind-all.js.map +1 -0
- package/dist/cjs/utils/environment.js +12 -0
- package/dist/cjs/utils/environment.js.map +1 -0
- package/dist/cjs/utils/get-global.js +21 -0
- package/dist/cjs/utils/get-global.js.map +1 -0
- package/dist/cjs/utils/group-by.js +28 -0
- package/dist/cjs/utils/group-by.js.map +1 -0
- package/dist/cjs/utils/has-properties.js +13 -0
- package/dist/cjs/utils/has-properties.js.map +1 -0
- package/dist/cjs/utils/is-plain-object.js +28 -0
- package/dist/cjs/utils/is-plain-object.js.map +1 -0
- package/dist/cjs/utils/is-thenable.js +15 -0
- package/dist/cjs/utils/is-thenable.js.map +1 -0
- package/dist/cjs/utils/p-while.js +25 -0
- package/dist/cjs/utils/p-while.js.map +1 -0
- package/dist/cjs/utils/pick.js +10 -0
- package/dist/cjs/utils/pick.js.map +1 -0
- package/dist/cjs/utils/ts-helpers.js +3 -0
- package/dist/cjs/utils/ts-helpers.js.map +1 -0
- package/dist/cjs/validation/assertions.js +41 -0
- package/dist/cjs/validation/assertions.js.map +1 -0
- package/dist/cjs/validation/helpers.js +26 -0
- package/dist/cjs/validation/helpers.js.map +1 -0
- package/dist/esm/analytics/dispatch.js +49 -0
- package/dist/esm/analytics/dispatch.js.map +1 -0
- package/dist/esm/analytics/index.js +2 -0
- package/dist/esm/analytics/index.js.map +1 -0
- package/dist/esm/callback/index.js +40 -0
- package/dist/esm/callback/index.js.map +1 -0
- package/dist/esm/connection/index.js +11 -0
- package/dist/esm/connection/index.js.map +1 -0
- package/dist/esm/context/index.js +84 -0
- package/dist/esm/context/index.js.map +1 -0
- package/dist/esm/emitter/index.js +63 -0
- package/dist/esm/emitter/index.js.map +1 -0
- package/dist/esm/emitter/interface.js +2 -0
- package/dist/esm/emitter/interface.js.map +1 -0
- package/dist/esm/events/index.js +146 -0
- package/dist/esm/events/index.js.map +1 -0
- package/dist/esm/events/interfaces.js +2 -0
- package/dist/esm/events/interfaces.js.map +1 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logger/index.js +59 -0
- package/dist/esm/logger/index.js.map +1 -0
- package/dist/esm/plugins/index.js +2 -0
- package/dist/esm/plugins/index.js.map +1 -0
- package/dist/esm/priority-queue/backoff.js +6 -0
- package/dist/esm/priority-queue/backoff.js.map +1 -0
- package/dist/esm/priority-queue/index.js +89 -0
- package/dist/esm/priority-queue/index.js.map +1 -0
- package/dist/esm/queue/delivery.js +64 -0
- package/dist/esm/queue/delivery.js.map +1 -0
- package/dist/esm/queue/event-queue.js +337 -0
- package/dist/esm/queue/event-queue.js.map +1 -0
- package/dist/esm/stats/index.js +93 -0
- package/dist/esm/stats/index.js.map +1 -0
- package/dist/esm/task/task-group.js +20 -0
- package/dist/esm/task/task-group.js.map +1 -0
- package/dist/esm/user/index.js +2 -0
- package/dist/esm/user/index.js.map +1 -0
- package/dist/esm/utils/bind-all.js +14 -0
- package/dist/esm/utils/bind-all.js.map +1 -0
- package/dist/esm/utils/environment.js +7 -0
- package/dist/esm/utils/environment.js.map +1 -0
- package/dist/esm/utils/get-global.js +17 -0
- package/dist/esm/utils/get-global.js.map +1 -0
- package/dist/esm/utils/group-by.js +24 -0
- package/dist/esm/utils/group-by.js.map +1 -0
- package/dist/esm/utils/has-properties.js +9 -0
- package/dist/esm/utils/has-properties.js.map +1 -0
- package/dist/esm/utils/is-plain-object.js +24 -0
- package/dist/esm/utils/is-plain-object.js.map +1 -0
- package/dist/esm/utils/is-thenable.js +11 -0
- package/dist/esm/utils/is-thenable.js.map +1 -0
- package/dist/esm/utils/p-while.js +21 -0
- package/dist/esm/utils/p-while.js.map +1 -0
- package/dist/esm/utils/pick.js +6 -0
- package/dist/esm/utils/pick.js.map +1 -0
- package/dist/esm/utils/ts-helpers.js +2 -0
- package/dist/esm/utils/ts-helpers.js.map +1 -0
- package/dist/esm/validation/assertions.js +37 -0
- package/dist/esm/validation/assertions.js.map +1 -0
- package/dist/esm/validation/helpers.js +18 -0
- package/dist/esm/validation/helpers.js.map +1 -0
- package/dist/types/analytics/dispatch.d.ts +20 -0
- package/dist/types/analytics/dispatch.d.ts.map +1 -0
- package/dist/types/analytics/index.d.ts +12 -0
- package/dist/types/analytics/index.d.ts.map +1 -0
- package/dist/types/callback/index.d.ts +11 -0
- package/dist/types/callback/index.d.ts.map +1 -0
- package/dist/types/connection/index.d.ts +3 -0
- package/dist/types/connection/index.d.ts.map +1 -0
- package/dist/types/context/index.d.ts +44 -0
- package/dist/types/context/index.d.ts.map +1 -0
- package/dist/types/emitter/index.d.ts +25 -0
- package/dist/types/emitter/index.d.ts.map +1 -0
- package/dist/types/emitter/interface.d.ts +27 -0
- package/dist/types/emitter/interface.d.ts.map +1 -0
- package/dist/types/events/index.d.ts +27 -0
- package/dist/types/events/index.d.ts.map +1 -0
- package/dist/types/events/interfaces.d.ts +373 -0
- package/dist/types/events/interfaces.d.ts.map +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/logger/index.d.ts +19 -0
- package/dist/types/logger/index.d.ts.map +1 -0
- package/dist/types/plugins/index.d.ts +25 -0
- package/dist/types/plugins/index.d.ts.map +1 -0
- package/dist/types/priority-queue/backoff.d.ts +13 -0
- package/dist/types/priority-queue/backoff.d.ts.map +1 -0
- package/dist/types/priority-queue/index.d.ts +25 -0
- package/dist/types/priority-queue/index.d.ts.map +1 -0
- package/dist/types/queue/delivery.d.ts +5 -0
- package/dist/types/queue/delivery.d.ts.map +1 -0
- package/dist/types/queue/event-queue.d.ts +43 -0
- package/dist/types/queue/event-queue.d.ts.map +1 -0
- package/dist/types/stats/index.d.ts +34 -0
- package/dist/types/stats/index.d.ts.map +1 -0
- package/dist/types/task/task-group.d.ts +6 -0
- package/dist/types/task/task-group.d.ts.map +1 -0
- package/dist/types/user/index.d.ts +6 -0
- package/dist/types/user/index.d.ts.map +1 -0
- package/dist/types/utils/bind-all.d.ts +4 -0
- package/dist/types/utils/bind-all.d.ts.map +1 -0
- package/dist/types/utils/environment.d.ts +3 -0
- package/dist/types/utils/environment.d.ts.map +1 -0
- package/dist/types/utils/get-global.d.ts +2 -0
- package/dist/types/utils/get-global.d.ts.map +1 -0
- package/dist/types/utils/group-by.d.ts +4 -0
- package/dist/types/utils/group-by.d.ts.map +1 -0
- package/dist/types/utils/has-properties.d.ts +4 -0
- package/dist/types/utils/has-properties.d.ts.map +1 -0
- package/dist/types/utils/is-plain-object.d.ts +2 -0
- package/dist/types/utils/is-plain-object.d.ts.map +1 -0
- package/dist/types/utils/is-thenable.d.ts +6 -0
- package/dist/types/utils/is-thenable.d.ts.map +1 -0
- package/dist/types/utils/p-while.d.ts +2 -0
- package/dist/types/utils/p-while.d.ts.map +1 -0
- package/dist/types/utils/pick.d.ts +2 -0
- package/dist/types/utils/pick.d.ts.map +1 -0
- package/dist/types/utils/ts-helpers.d.ts +13 -0
- package/dist/types/utils/ts-helpers.d.ts.map +1 -0
- package/dist/types/validation/assertions.d.ts +7 -0
- package/dist/types/validation/assertions.d.ts.map +1 -0
- package/dist/types/validation/helpers.d.ts +7 -0
- package/dist/types/validation/helpers.d.ts.map +1 -0
- package/package.json +39 -0
- package/src/analytics/dispatch.ts +58 -0
- package/src/analytics/index.ts +11 -0
- package/src/callback/index.ts +51 -0
- package/src/connection/index.ts +13 -0
- package/src/context/index.ts +123 -0
- package/src/emitter/index.ts +65 -0
- package/src/emitter/interface.ts +31 -0
- package/src/events/index.ts +280 -0
- package/src/events/interfaces.ts +447 -0
- package/src/index.ts +18 -0
- package/src/logger/index.ts +74 -0
- package/src/plugins/index.ts +43 -0
- package/src/priority-queue/backoff.ts +24 -0
- package/src/priority-queue/index.ts +103 -0
- package/src/queue/delivery.ts +73 -0
- package/src/queue/event-queue.ts +320 -0
- package/src/stats/index.ts +88 -0
- package/src/task/task-group.ts +31 -0
- package/src/user/index.ts +7 -0
- package/src/utils/bind-all.ts +19 -0
- package/src/utils/environment.ts +7 -0
- package/src/utils/get-global.ts +16 -0
- package/src/utils/group-by.ts +30 -0
- package/src/utils/has-properties.ts +7 -0
- package/src/utils/is-plain-object.ts +26 -0
- package/src/utils/is-thenable.ts +9 -0
- package/src/utils/p-while.ts +12 -0
- package/src/utils/pick.ts +8 -0
- package/src/utils/ts-helpers.ts +13 -0
- package/src/validation/assertions.ts +43 -0
- package/src/validation/helpers.ts +27 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { CoreCustomerioEvent } from '../events/interfaces'
|
|
2
|
+
|
|
3
|
+
import { v4 as uuid } from '@lukeed/uuid'
|
|
4
|
+
import { dset } from 'dset'
|
|
5
|
+
import { CoreLogger, LogLevel, LogMessage } from '../logger'
|
|
6
|
+
import { CoreStats, CoreMetric, NullStats } from '../stats'
|
|
7
|
+
|
|
8
|
+
export interface SerializedContext {
|
|
9
|
+
id: string
|
|
10
|
+
event: CoreCustomerioEvent
|
|
11
|
+
logs: LogMessage[]
|
|
12
|
+
metrics?: CoreMetric[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ContextFailedDelivery {
|
|
16
|
+
reason: unknown
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CancelationOptions {
|
|
20
|
+
retry?: boolean
|
|
21
|
+
reason?: string
|
|
22
|
+
type?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class ContextCancelation {
|
|
26
|
+
retry: boolean
|
|
27
|
+
type: string
|
|
28
|
+
reason?: string
|
|
29
|
+
|
|
30
|
+
constructor(options: CancelationOptions) {
|
|
31
|
+
this.retry = options.retry ?? true
|
|
32
|
+
this.type = options.type ?? 'plugin Error'
|
|
33
|
+
this.reason = options.reason ?? ''
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export abstract class CoreContext<
|
|
38
|
+
Event extends CoreCustomerioEvent = CoreCustomerioEvent
|
|
39
|
+
> {
|
|
40
|
+
event: Event
|
|
41
|
+
logger: CoreLogger
|
|
42
|
+
stats: CoreStats
|
|
43
|
+
attempts = 0
|
|
44
|
+
|
|
45
|
+
private _failedDelivery?: ContextFailedDelivery
|
|
46
|
+
private _id: string
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
event: Event,
|
|
50
|
+
id = uuid(),
|
|
51
|
+
stats: CoreStats = new NullStats(),
|
|
52
|
+
logger = new CoreLogger()
|
|
53
|
+
) {
|
|
54
|
+
this.event = event
|
|
55
|
+
this._id = id
|
|
56
|
+
this.logger = logger
|
|
57
|
+
this.stats = stats
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static system(): void {
|
|
61
|
+
// This should be overridden by the subclass to return an instance of the subclass.
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
isSame(other: CoreContext): boolean {
|
|
65
|
+
return other.id === this.id
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
cancel(error?: Error | ContextCancelation): never {
|
|
69
|
+
if (error) {
|
|
70
|
+
throw error
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw new ContextCancelation({ reason: 'Context Cancel' })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
log(level: LogLevel, message: string, extras?: object): void {
|
|
77
|
+
this.logger.log(level, message, extras)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get id(): string {
|
|
81
|
+
return this._id
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
updateEvent(path: string, val: unknown): Event {
|
|
85
|
+
// Don't allow integrations that are set to false to be overwritten with integration settings.
|
|
86
|
+
if (path.split('.')[0] === 'integrations') {
|
|
87
|
+
const integrationName = path.split('.')[1]
|
|
88
|
+
|
|
89
|
+
if (this.event.integrations?.[integrationName] === false) {
|
|
90
|
+
return this.event
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
dset(this.event, path, val)
|
|
95
|
+
return this.event
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
failedDelivery(): ContextFailedDelivery | undefined {
|
|
99
|
+
return this._failedDelivery
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setFailedDelivery(options: ContextFailedDelivery) {
|
|
103
|
+
this._failedDelivery = options
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
logs(): LogMessage[] {
|
|
107
|
+
return this.logger.logs
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
flush(): void {
|
|
111
|
+
this.logger.flush()
|
|
112
|
+
this.stats.flush()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
toJSON(): SerializedContext {
|
|
116
|
+
return {
|
|
117
|
+
id: this._id,
|
|
118
|
+
event: this.event,
|
|
119
|
+
logs: this.logger.logs,
|
|
120
|
+
metrics: this.stats.metrics,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
type EventName = string
|
|
2
|
+
type EventFnArgs = any[]
|
|
3
|
+
type EmitterContract = Record<EventName, EventFnArgs>
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Event Emitter that takes the expected contract as a generic
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* type Contract = {
|
|
10
|
+
* delivery_success: [DeliverySuccessResponse, Metrics],
|
|
11
|
+
* delivery_failure: [DeliveryError]
|
|
12
|
+
* }
|
|
13
|
+
* new Emitter<Contract>()
|
|
14
|
+
* .on('delivery_success', (res, metrics) => ...)
|
|
15
|
+
* .on('delivery_failure', (err) => ...)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export class Emitter<Contract extends EmitterContract = EmitterContract> {
|
|
19
|
+
private callbacks: Partial<Contract> = {}
|
|
20
|
+
on<EventName extends keyof Contract>(
|
|
21
|
+
event: EventName,
|
|
22
|
+
callback: (...args: Contract[EventName]) => void
|
|
23
|
+
): this {
|
|
24
|
+
if (!this.callbacks[event]) {
|
|
25
|
+
this.callbacks[event] = [callback] as Contract[EventName]
|
|
26
|
+
} else {
|
|
27
|
+
this.callbacks[event]!.push(callback)
|
|
28
|
+
}
|
|
29
|
+
return this
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
once<EventName extends keyof Contract>(
|
|
33
|
+
event: EventName,
|
|
34
|
+
callback: (...args: Contract[EventName]) => void
|
|
35
|
+
): this {
|
|
36
|
+
const on = (...args: Contract[EventName]): void => {
|
|
37
|
+
this.off(event, on)
|
|
38
|
+
callback.apply(this, args)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.on(event, on)
|
|
42
|
+
return this
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
off<EventName extends keyof Contract>(
|
|
46
|
+
event: EventName,
|
|
47
|
+
callback: (...args: Contract[EventName]) => void
|
|
48
|
+
): this {
|
|
49
|
+
const fns = this.callbacks[event] ?? []
|
|
50
|
+
const without = fns.filter((fn) => fn !== callback) as Contract[EventName]
|
|
51
|
+
this.callbacks[event] = without
|
|
52
|
+
return this
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
emit<EventName extends keyof Contract>(
|
|
56
|
+
event: EventName,
|
|
57
|
+
...args: Contract[EventName]
|
|
58
|
+
): this {
|
|
59
|
+
const callbacks = this.callbacks[event] ?? []
|
|
60
|
+
callbacks.forEach((callback) => {
|
|
61
|
+
callback.apply(this, args)
|
|
62
|
+
})
|
|
63
|
+
return this
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CoreContext } from '../context'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This is the base contract for all emitted errors. This interface may be extended.
|
|
5
|
+
*/
|
|
6
|
+
export interface CoreEmittedError<Ctx extends CoreContext> {
|
|
7
|
+
/**
|
|
8
|
+
* e.g. 'delivery_failure'
|
|
9
|
+
*/
|
|
10
|
+
code: string
|
|
11
|
+
/**
|
|
12
|
+
* Why the error occurred. This can be an actual error object or a just a message.
|
|
13
|
+
*/
|
|
14
|
+
reason?: unknown
|
|
15
|
+
ctx?: Ctx
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type CoreEmitterContract<
|
|
19
|
+
Ctx extends CoreContext,
|
|
20
|
+
Err extends CoreEmittedError<Ctx> = CoreEmittedError<Ctx>
|
|
21
|
+
> = {
|
|
22
|
+
alias: [ctx: Ctx]
|
|
23
|
+
track: [ctx: Ctx]
|
|
24
|
+
identify: [ctx: Ctx]
|
|
25
|
+
page: [ctx: Ctx]
|
|
26
|
+
screen: [ctx: Ctx]
|
|
27
|
+
group: [ctx: Ctx]
|
|
28
|
+
register: [pluginNames: string[]]
|
|
29
|
+
deregister: [pluginNames: string[]]
|
|
30
|
+
error: [error: Err]
|
|
31
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
export * from './interfaces'
|
|
2
|
+
import { dset } from 'dset'
|
|
3
|
+
import { ID, User } from '../user'
|
|
4
|
+
import {
|
|
5
|
+
Integrations,
|
|
6
|
+
EventProperties,
|
|
7
|
+
CoreCustomerioEvent,
|
|
8
|
+
CoreOptions,
|
|
9
|
+
CoreExtraContext,
|
|
10
|
+
UserTraits,
|
|
11
|
+
GroupTraits,
|
|
12
|
+
} from './interfaces'
|
|
13
|
+
import { pickBy } from '../utils/pick'
|
|
14
|
+
import { validateEvent } from '../validation/assertions'
|
|
15
|
+
import type { RemoveIndexSignature } from '../utils/ts-helpers'
|
|
16
|
+
|
|
17
|
+
interface EventFactorySettings {
|
|
18
|
+
createMessageId: () => string
|
|
19
|
+
user?: User
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class EventFactory {
|
|
23
|
+
createMessageId: EventFactorySettings['createMessageId']
|
|
24
|
+
user?: User
|
|
25
|
+
|
|
26
|
+
constructor(settings: EventFactorySettings) {
|
|
27
|
+
this.user = settings.user
|
|
28
|
+
this.createMessageId = settings.createMessageId
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
track(
|
|
32
|
+
event: string,
|
|
33
|
+
properties?: EventProperties,
|
|
34
|
+
options?: CoreOptions,
|
|
35
|
+
globalIntegrations?: Integrations
|
|
36
|
+
) {
|
|
37
|
+
return this.normalize({
|
|
38
|
+
...this.baseEvent(),
|
|
39
|
+
event,
|
|
40
|
+
type: 'track',
|
|
41
|
+
properties: properties ?? {}, // TODO: why is this not a shallow copy like everywhere else?
|
|
42
|
+
options: { ...options },
|
|
43
|
+
integrations: { ...globalIntegrations },
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
page(
|
|
48
|
+
category: string | null,
|
|
49
|
+
page: string | null,
|
|
50
|
+
properties?: EventProperties,
|
|
51
|
+
options?: CoreOptions,
|
|
52
|
+
globalIntegrations?: Integrations
|
|
53
|
+
): CoreCustomerioEvent {
|
|
54
|
+
const event: CoreCustomerioEvent = {
|
|
55
|
+
type: 'page',
|
|
56
|
+
properties: { ...properties },
|
|
57
|
+
options: { ...options },
|
|
58
|
+
integrations: { ...globalIntegrations },
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (category !== null) {
|
|
62
|
+
event.category = category
|
|
63
|
+
event.properties = event.properties ?? {}
|
|
64
|
+
event.properties.category = category
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (page !== null) {
|
|
68
|
+
event.name = page
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return this.normalize({
|
|
72
|
+
...this.baseEvent(),
|
|
73
|
+
...event,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
screen(
|
|
78
|
+
category: string | null,
|
|
79
|
+
screen: string | null,
|
|
80
|
+
properties?: EventProperties,
|
|
81
|
+
options?: CoreOptions,
|
|
82
|
+
globalIntegrations?: Integrations
|
|
83
|
+
): CoreCustomerioEvent {
|
|
84
|
+
const event: CoreCustomerioEvent = {
|
|
85
|
+
type: 'screen',
|
|
86
|
+
properties: { ...properties },
|
|
87
|
+
options: { ...options },
|
|
88
|
+
integrations: { ...globalIntegrations },
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (category !== null) {
|
|
92
|
+
event.category = category
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (screen !== null) {
|
|
96
|
+
event.name = screen
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return this.normalize({
|
|
100
|
+
...this.baseEvent(),
|
|
101
|
+
...event,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
identify(
|
|
106
|
+
userId: ID,
|
|
107
|
+
traits?: UserTraits,
|
|
108
|
+
options?: CoreOptions,
|
|
109
|
+
globalIntegrations?: Integrations
|
|
110
|
+
): CoreCustomerioEvent {
|
|
111
|
+
return this.normalize({
|
|
112
|
+
...this.baseEvent(),
|
|
113
|
+
type: 'identify',
|
|
114
|
+
userId,
|
|
115
|
+
traits: traits ?? {},
|
|
116
|
+
options: { ...options },
|
|
117
|
+
integrations: globalIntegrations,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
group(
|
|
122
|
+
groupId: ID,
|
|
123
|
+
traits?: GroupTraits,
|
|
124
|
+
options?: CoreOptions,
|
|
125
|
+
globalIntegrations?: Integrations
|
|
126
|
+
): CoreCustomerioEvent {
|
|
127
|
+
return this.normalize({
|
|
128
|
+
...this.baseEvent(),
|
|
129
|
+
type: 'group',
|
|
130
|
+
traits: traits ?? {},
|
|
131
|
+
options: { ...options }, // this spreading is intentional
|
|
132
|
+
integrations: { ...globalIntegrations }, //
|
|
133
|
+
groupId,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
alias(
|
|
138
|
+
to: string,
|
|
139
|
+
from: string | null, // TODO: can we make this undefined?
|
|
140
|
+
options?: CoreOptions,
|
|
141
|
+
globalIntegrations?: Integrations
|
|
142
|
+
): CoreCustomerioEvent {
|
|
143
|
+
const base: CoreCustomerioEvent = {
|
|
144
|
+
userId: to,
|
|
145
|
+
type: 'alias',
|
|
146
|
+
options: { ...options },
|
|
147
|
+
integrations: { ...globalIntegrations },
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (from !== null) {
|
|
151
|
+
base.previousId = from
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (to === undefined) {
|
|
155
|
+
return this.normalize({
|
|
156
|
+
...base,
|
|
157
|
+
...this.baseEvent(),
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return this.normalize({
|
|
162
|
+
...this.baseEvent(),
|
|
163
|
+
...base,
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private baseEvent(): Partial<CoreCustomerioEvent> {
|
|
168
|
+
const base: Partial<CoreCustomerioEvent> = {
|
|
169
|
+
integrations: {},
|
|
170
|
+
options: {},
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!this.user) return base
|
|
174
|
+
|
|
175
|
+
const user = this.user
|
|
176
|
+
|
|
177
|
+
if (user.id()) {
|
|
178
|
+
base.userId = user.id()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (user.anonymousId()) {
|
|
182
|
+
base.anonymousId = user.anonymousId()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return base
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Builds the context part of an event based on "foreign" keys that
|
|
190
|
+
* are provided in the `Options` parameter for an Event
|
|
191
|
+
*/
|
|
192
|
+
private context(
|
|
193
|
+
options: CoreOptions
|
|
194
|
+
): [CoreExtraContext, Partial<CoreCustomerioEvent>] {
|
|
195
|
+
type CoreOptionKeys = keyof RemoveIndexSignature<CoreOptions>
|
|
196
|
+
/**
|
|
197
|
+
* If the event options are known keys from this list, we move them to the top level of the event.
|
|
198
|
+
* Any other options are moved to context.
|
|
199
|
+
*/
|
|
200
|
+
const eventOverrideKeys: CoreOptionKeys[] = [
|
|
201
|
+
'userId',
|
|
202
|
+
'anonymousId',
|
|
203
|
+
'timestamp',
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
delete options['integrations']
|
|
207
|
+
const providedOptionsKeys = Object.keys(options) as Exclude<
|
|
208
|
+
CoreOptionKeys,
|
|
209
|
+
'integrations'
|
|
210
|
+
>[]
|
|
211
|
+
|
|
212
|
+
const context = options.context ?? {}
|
|
213
|
+
const eventOverrides = {}
|
|
214
|
+
|
|
215
|
+
providedOptionsKeys.forEach((key) => {
|
|
216
|
+
if (key === 'context') {
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (eventOverrideKeys.includes(key)) {
|
|
221
|
+
dset(eventOverrides, key, options[key])
|
|
222
|
+
} else {
|
|
223
|
+
dset(context, key, options[key])
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
return [context, eventOverrides]
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public normalize(event: CoreCustomerioEvent): CoreCustomerioEvent {
|
|
231
|
+
const integrationBooleans = Object.keys(event.integrations ?? {}).reduce(
|
|
232
|
+
(integrationNames, name) => {
|
|
233
|
+
return {
|
|
234
|
+
...integrationNames,
|
|
235
|
+
[name]: Boolean(event.integrations?.[name]),
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
{} as Record<string, boolean>
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
// filter out any undefined options
|
|
242
|
+
event.options = pickBy(event.options || {}, (_, value) => {
|
|
243
|
+
return value !== undefined
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
// This is pretty trippy, but here's what's going on:
|
|
247
|
+
// - a) We don't pass initial integration options as part of the event, only if they're true or false
|
|
248
|
+
// - b) We do accept per integration overrides (like integrations.Amplitude.sessionId) at the event level
|
|
249
|
+
// Hence the need to convert base integration options to booleans, but maintain per event integration overrides
|
|
250
|
+
const allIntegrations = {
|
|
251
|
+
// Base config integrations object as booleans
|
|
252
|
+
...integrationBooleans,
|
|
253
|
+
|
|
254
|
+
// Per event overrides, for things like amplitude sessionId, for example
|
|
255
|
+
...event.options?.integrations,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const [context, overrides] = event.options
|
|
259
|
+
? this.context(event.options)
|
|
260
|
+
: []
|
|
261
|
+
|
|
262
|
+
const { options, ...rest } = event
|
|
263
|
+
|
|
264
|
+
const body = {
|
|
265
|
+
timestamp: new Date(),
|
|
266
|
+
...rest,
|
|
267
|
+
integrations: allIntegrations,
|
|
268
|
+
context,
|
|
269
|
+
...overrides,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const evt: CoreCustomerioEvent = {
|
|
273
|
+
...body,
|
|
274
|
+
messageId: this.createMessageId(),
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
validateEvent(evt)
|
|
278
|
+
return evt
|
|
279
|
+
}
|
|
280
|
+
}
|