UNPKG

@segment/analytics-next

Version:

Analytics Next (aka Analytics 2.0) is the latest version of Segment’s JavaScript SDK - enabling you to send your data to any tool without having to learn, test, or use a new API every time.

201 lines (176 loc) 5.27 kB
import { isFunction, isPlainObject, isString, isNumber, } from '@segment/analytics-core' import { Context } from '../context' import { Callback, JSONObject, Options, EventProperties, SegmentEvent, Traits, GroupTraits, UserTraits, } from '../events' import { ID, WithId } from '../user' /** * Helper for the track method */ export function resolveArguments( eventName: string | SegmentEvent, properties?: EventProperties | Callback, options?: Options | Callback, callback?: Callback ): [string, EventProperties | Callback, Options, Callback | undefined] { const args = [eventName, properties, options, callback] const name = isPlainObject(eventName) ? eventName.event : eventName if (!name || !isString(name)) { throw new Error('Event missing') } const data = isPlainObject(eventName) ? eventName.properties ?? {} : isPlainObject(properties) ? properties : {} let opts: Options = {} if (!isFunction(options)) { opts = options ?? {} } if (isPlainObject(eventName) && !isFunction(properties)) { opts = properties ?? {} } const cb = args.find(isFunction) as Callback | undefined return [name, data, opts, cb] } /** * Helper for page, screen methods */ export function resolvePageArguments( category?: string | object, name?: string | object | Callback, properties?: EventProperties | Options | Callback | null, options?: Options | Callback, callback?: Callback ): [ string | null, string | null, EventProperties, Options, Callback | undefined ] { let resolvedCategory: string | undefined | null = null let resolvedName: string | undefined | null = null const args = [category, name, properties, options, callback] const strings = args.filter(isString) if (strings[0] !== undefined && strings[1] !== undefined) { resolvedCategory = strings[0] resolvedName = strings[1] } if (strings.length === 1) { resolvedCategory = null resolvedName = strings[0] } const resolvedCallback = args.find(isFunction) as Callback | undefined const objects = args.filter((obj) => { if (resolvedName === null) { return isPlainObject(obj) } return isPlainObject(obj) || obj === null }) as Array<JSONObject | null> const resolvedProperties = (objects[0] ?? {}) as EventProperties const resolvedOptions = (objects[1] ?? {}) as Options return [ resolvedCategory, resolvedName, resolvedProperties, resolvedOptions, resolvedCallback, ] } /** * Helper for group, identify methods */ export const resolveUserArguments = <T extends Traits, U extends WithId>( user: U ): ResolveUser<T> => { return (...args): ReturnType<ResolveUser<T>> => { const values: { id?: ID traits?: T | null options?: Options callback?: Callback } = {} // It's a stack so it's reversed so that we go through each of the expected arguments const orderStack: Array<keyof typeof values> = [ 'callback', 'options', 'traits', 'id', ] // Read each argument and eval the possible values here for (const arg of args) { let current = orderStack.pop() if (current === 'id') { if (isString(arg) || isNumber(arg)) { values.id = arg.toString() continue } if (arg === null || arg === undefined) { continue } // First argument should always be the id, if it is not a valid value we can skip it current = orderStack.pop() } // Traits and Options if ( (current === 'traits' || current === 'options') && (arg === null || arg === undefined || isPlainObject(arg)) ) { values[current] = arg as T } // Callback if (isFunction(arg)) { values.callback = arg as Callback break // This is always the last argument } } return [ values.id ?? user.id(), (values.traits ?? {}) as T, values.options ?? {}, values.callback, ] } } /** * Helper for alias method */ export function resolveAliasArguments( to: string | number, from?: string | number | Options, options?: Options | Callback, callback?: Callback ): [string, string | null, Options, Callback | undefined] { if (isNumber(to)) to = to.toString() // Legacy behaviour - allow integers for alias calls if (isNumber(from)) from = from.toString() const args = [to, from, options, callback] const [aliasTo = to, aliasFrom = null] = args.filter(isString) const [opts = {}] = args.filter(isPlainObject) const resolvedCallback = args.find(isFunction) as Callback | undefined return [aliasTo, aliasFrom, opts, resolvedCallback] } type ResolveUser<T extends Traits> = ( id?: ID | object, traits?: T | Callback | null, options?: Options | Callback, callback?: Callback ) => [ID, T, Options, Callback | undefined] export type IdentifyParams = Parameters<ResolveUser<UserTraits>> export type GroupParams = Parameters<ResolveUser<GroupTraits>> export type EventParams = Parameters<typeof resolveArguments> export type PageParams = Parameters<typeof resolvePageArguments> export type AliasParams = Parameters<typeof resolveAliasArguments> export type DispatchedEvent = Context