feat: group analytics

* wip

* wip

* wip

* wip

* wip

* add buffer

* wip

* wip

* fixes

* fix

* wip

* group validation

* fix group issues

* docs: add groups
This commit is contained in:
Carl-Gerhard Lindesvärd
2026-03-20 10:46:09 +01:00
committed by GitHub
parent 88a2d876ce
commit 11e9ecac1a
99 changed files with 5944 additions and 1432 deletions

View File

@@ -2,7 +2,9 @@
import type {
IAliasPayload as AliasPayload,
IAssignGroupPayload as AssignGroupPayload,
IDecrementPayload as DecrementPayload,
IGroupPayload as GroupPayload,
IIdentifyPayload as IdentifyPayload,
IIncrementPayload as IncrementPayload,
ITrackHandlerPayload as TrackHandlerPayload,
@@ -12,7 +14,9 @@ import { Api } from './api';
export type {
AliasPayload,
AssignGroupPayload,
DecrementPayload,
GroupPayload,
IdentifyPayload,
IncrementPayload,
TrackHandlerPayload,
@@ -22,8 +26,11 @@ export type {
export interface TrackProperties {
[key: string]: unknown;
profileId?: string;
groups?: string[];
}
export type UpsertGroupPayload = GroupPayload;
export interface OpenPanelOptions {
clientId: string;
clientSecret?: string;
@@ -45,6 +52,7 @@ export class OpenPanel {
api: Api;
options: OpenPanelOptions;
profileId?: string;
groups: string[] = [];
deviceId?: string;
sessionId?: string;
global?: Record<string, unknown>;
@@ -142,14 +150,19 @@ export class OpenPanel {
track(name: string, properties?: TrackProperties) {
this.log('track event', name, properties);
const { groups: groupsOverride, profileId, ...rest } = properties ?? {};
const mergedGroups = [
...new Set([...this.groups, ...(groupsOverride ?? [])]),
];
return this.send({
type: 'track',
payload: {
name,
profileId: properties?.profileId ?? this.profileId,
profileId: profileId ?? this.profileId,
groups: mergedGroups.length > 0 ? mergedGroups : undefined,
properties: {
...(this.global ?? {}),
...(properties ?? {}),
...rest,
},
},
});
@@ -176,6 +189,40 @@ export class OpenPanel {
}
}
upsertGroup(payload: UpsertGroupPayload) {
this.log('upsert group', payload);
return this.send({
type: 'group',
payload,
});
}
setGroup(groupId: string) {
this.log('set group', groupId);
if (!this.groups.includes(groupId)) {
this.groups = [...this.groups, groupId];
}
return this.send({
type: 'assign_group',
payload: {
groupIds: [groupId],
profileId: this.profileId,
},
});
}
setGroups(groupIds: string[]) {
this.log('set groups', groupIds);
this.groups = [...new Set([...this.groups, ...groupIds])];
return this.send({
type: 'assign_group',
payload: {
groupIds,
profileId: this.profileId,
},
});
}
/**
* @deprecated This method is deprecated and will be removed in a future version.
*/
@@ -227,10 +274,46 @@ export class OpenPanel {
clear() {
this.profileId = undefined;
this.groups = [];
this.deviceId = undefined;
this.sessionId = undefined;
}
private buildFlushPayload(
item: TrackHandlerPayload
): TrackHandlerPayload['payload'] {
if (item.type === 'replay') {
return item.payload;
}
if (item.type === 'track') {
const queuedGroups =
'groups' in item.payload ? (item.payload.groups ?? []) : [];
const mergedGroups = [...new Set([...this.groups, ...queuedGroups])];
return {
...item.payload,
profileId: item.payload.profileId ?? this.profileId,
groups: mergedGroups.length > 0 ? mergedGroups : undefined,
};
}
if (
item.type === 'identify' ||
item.type === 'increment' ||
item.type === 'decrement'
) {
return {
...item.payload,
profileId: item.payload.profileId ?? this.profileId,
} as TrackHandlerPayload['payload'];
}
if (item.type === 'assign_group') {
return {
...item.payload,
profileId: item.payload.profileId ?? this.profileId,
};
}
return item.payload;
}
flush() {
const remaining: TrackHandlerPayload[] = [];
for (const item of this.queue) {
@@ -238,16 +321,7 @@ export class OpenPanel {
remaining.push(item);
continue;
}
const payload =
item.type === 'replay'
? item.payload
: {
...item.payload,
profileId:
'profileId' in item.payload
? (item.payload.profileId ?? this.profileId)
: this.profileId,
};
const payload = this.buildFlushPayload(item);
this.send({ ...item, payload } as TrackHandlerPayload);
}
this.queue = remaining;