Files
twenty/packages/twenty-server/src/engine/core-modules/audit
Charles BochetandGitHub d37ed7e07c Optimize merge queue to only run E2E and integrate prettier into lint (#18459)
## Summary

- **Merge queue optimization**: Created a dedicated
`ci-merge-queue.yaml` workflow that only runs Playwright E2E tests on
`ubuntu-latest-8-cores`. Removed `merge_group` trigger from all 7
existing CI workflows (front, server, shared, website, sdk, zapier,
docker-compose). The merge queue goes from ~30+ parallel jobs to a
single focused E2E job.
- **Label-based merge queue simulation**: Added `run-merge-queue` label
support so developers can trigger the exact merge queue E2E pipeline on
any open PR before it enters the queue.
- **Prettier in lint**: Chained `prettier --check` into `lint` and
`prettier --write` into `lint --configuration=fix` across `nx.json`
defaults, `twenty-front`, and `twenty-server`. Prettier formatting
errors are now caught by `lint` and fixed by `lint:fix` /
`lint:diff-with-main --configuration=fix`.

## After merge (manual repo settings)

Update GitHub branch protection required status checks:
1. Remove old per-workflow merge queue checks (`ci-front-status-check`,
`ci-e2e-status-check`, `ci-server-status-check`, etc.)
2. Add `ci-merge-queue-status-check` as the required check for the merge
queue
2026-03-06 13:20:57 +01:00
..

Analytics Module

This module provides analytics tracking functionality for the Twenty application.

Usage

Tracking Events

The AuditService provides a createContext method that returns an object with three methods:

  • insertWorkspaceEvent: For tracking workspace-level events
  • createObjectEvent: For tracking object-level events that include record and metadata IDs
  • createPageviewEvent: For tracking page views
import { Injectable } from '@nestjs/common';
import { AuditService } from 'src/engine/core-modules/audit/services/audit.service';
import { CUSTOM_DOMAIN_ACTIVATED_EVENT } from 'src/engine/core-modules/audit/utils/events/track/custom-domain/custom-domain-activated';

@Injectable()
export class MyService {
  constructor(private readonly auditService: AuditService) {}

  async doSomething() {
    // Create an analytics context
    const auditService = this.auditService.createContext({
      workspaceId: 'workspace-id',
      userId: 'user-id',
    });

    // Track a workspace event
    auditService.insertWorkspaceEvent(CUSTOM_DOMAIN_ACTIVATED_EVENT, {});

    // Track an object event
    auditService.createObjectEvent(OBJECT_RECORD_CREATED_EVENT, {
      recordId: 'record-id',
      objectMetadataId: 'object-metadata-id',
      // other properties
    });

    // Track a pageview
    auditService.createPageviewEvent('page-name', {
      href: '/path',
      locale: 'en-US',
      // other properties
    });
  }
}

Adding New Events

To add a new event:

  1. Create a new file in the src/engine/core-modules/analytics/utils/events/track directory
  2. Define the event name, schema, and type
  3. Register the event using the registerEvent function
  4. Update the TrackEventName and TrackEventProperties types in src/engine/core-modules/analytics/utils/events/event-types.ts

Example:

// src/engine/core-modules/analytics/utils/events/track/my-feature/my-event.ts
import { z } from 'zod';

import { registerEvent } from 'src/engine/core-modules/analytics/utils/events/track/track';

export const MY_EVENT = 'My Event' as const;
export const myEventSchema = z.object({
  event: z.literal(MY_EVENT),
  properties: z.object({
    myProperty: z.string(),
  }),
});

export type MyEventTrackEvent = z.infer<typeof myEventSchema>;

registerEvent(MY_EVENT, myEventSchema);

Then update the events.type.ts file:

// src/engine/core-modules/analytics/types/events.type.ts
import {
  MY_EVENT,
  MyEventTrackEvent,
} from '../utils/events/track/my-feature/my-event';

// Add to the union type
export type TrackEventName = typeof MY_EVENT;
// ... other event names;

// Add to the TrackEvents interface
export interface TrackEvents {
  [MY_EVENT]: MyEventTrackEvent;
  // ... other event types
}

// The TrackEventProperties type will automatically use the new event
export type TrackEventProperties<T extends TrackEventName> =
  T extends keyof TrackEvents ? TrackEvents[T]['properties'] : object;

API

AuditService

createContext(context?)

Creates an analytics context with the given user ID and workspace ID.

  • context (optional): An object with userId and workspaceId properties

Returns an object with the following methods:

  • insertWorkspaceEvent<T extends TrackEventName>(event: T, properties: TrackEventProperties<T>): Tracks a workspace-level event
  • createObjectEvent<T extends TrackEventName>(event: T, properties: TrackEventProperties<T> & { recordId: string; objectMetadataId: string }): Tracks an object-level event
  • createPageviewEvent(name: string, properties: Partial<PageviewProperties>): Tracks a pageview

Types

TrackEventName

A union type of all registered event names, plus string for backward compatibility.

TrackEventProperties

A mapped type that maps each event name to its corresponding properties type. It uses the TrackEvents interface to provide a more maintainable and type-safe way to map event names to their properties.

// Define the mapping between event names and their event types
export interface TrackEvents {
  [EVENT_NAME_1]: Event1Type;
  [EVENT_NAME_2]: Event2Type;
  // ... other event types
}

// Use the mapping to extract properties for each event type
export type TrackEventProperties<T extends TrackEventName> =
  T extends keyof TrackEvents ? TrackEvents[T]['properties'] : object;

This approach makes it easier to add new events without having to modify a complex nested conditional type.

PageviewProperties

Properties for pageview events, including href, locale, pathname, referrer, sessionId, timeZone, and userAgent.