Merge branch 'develop' into sec/sandbox-dynamic-property

This commit is contained in:
kwburns-kong
2026-05-14 17:08:26 -04:00
committed by GitHub
98 changed files with 935 additions and 714 deletions

14
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"workspaces": [
"packages/insomnia-testing",
"packages/insomnia",
"packages/insomnia-analytics",
"packages/insomnia-api",
"packages/insomnia-inso",
"packages/insomnia-smoke-test",
@@ -18409,6 +18410,10 @@
"resolved": "packages/insomnia",
"link": true
},
"node_modules/insomnia-analytics": {
"resolved": "packages/insomnia-analytics",
"link": true
},
"node_modules/insomnia-api": {
"resolved": "packages/insomnia-api",
"link": true
@@ -29183,7 +29188,6 @@
"@rjsf/utils": "6.0.0-beta.15",
"@rjsf/validator-ajv8": "6.0.0-beta.15",
"@seald-io/nedb": "^4.1.1",
"@segment/analytics-node": "2.2.1",
"@sentry/electron": "^6.5.0",
"@stoplight/spectral-core": "^1.22.0",
"@stoplight/spectral-formats": "^1.8.2",
@@ -29336,6 +29340,13 @@
"@kong/insomnia-plugin-external-vault": "0.1.4-dev.20251224090833"
}
},
"packages/insomnia-analytics": {
"version": "12.5.1-alpha.0",
"license": "Apache-2.0",
"dependencies": {
"@segment/analytics-node": "2.2.1"
}
},
"packages/insomnia-api": {
"version": "12.5.1-alpha.0",
"license": "Apache-2.0",
@@ -29348,7 +29359,6 @@
"license": "Apache-2.0",
"dependencies": {
"@seald-io/nedb": "^4.1.1",
"@segment/analytics-node": "^2.2.1",
"@stoplight/spectral-core": "^1.22.0",
"@stoplight/spectral-formats": "^1.8.2",
"@stoplight/spectral-ruleset-bundler": "1.7.0",

View File

@@ -17,6 +17,7 @@
"workspaces": [
"packages/insomnia-testing",
"packages/insomnia",
"packages/insomnia-analytics",
"packages/insomnia-api",
"packages/insomnia-inso",
"packages/insomnia-smoke-test",

View File

@@ -0,0 +1,36 @@
{
"private": true,
"name": "insomnia-analytics",
"license": "Apache-2.0",
"version": "12.5.1-alpha.0",
"author": "Kong <office@konghq.com>",
"description": "Shared analytics client for the Insomnia desktop app and CLI",
"repository": {
"type": "git",
"url": "git+https://github.com/Kong/insomnia.git",
"directory": "packages/insomnia-analytics"
},
"bugs": {
"url": "https://github.com/kong/insomnia/issues"
},
"homepage": "https://github.com/Kong/insomnia#readme",
"sideEffects": false,
"exports": {
".": {
"import": "./src/index.ts",
"types": "./src/index.ts"
},
"./events": {
"import": "./src/events.ts",
"types": "./src/events.ts"
}
},
"scripts": {
"lint": "eslint . --ext .ts --cache",
"type-check": "tsc --noEmit --project tsconfig.json",
"test": "vitest run"
},
"dependencies": {
"@segment/analytics-node": "2.2.1"
}
}

View File

@@ -0,0 +1,73 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
const mockTrack = vi.fn();
const mockPage = vi.fn();
const mockCloseAndFlush = vi.fn().mockResolvedValue(void 0);
vi.mock('@segment/analytics-node', () => ({
Analytics: vi.fn(() => ({
track: mockTrack,
page: mockPage,
closeAndFlush: mockCloseAndFlush,
})),
}));
describe('InsomniaAnalytics', () => {
beforeEach(() => {
vi.resetModules();
mockTrack.mockReset();
mockPage.mockReset();
mockCloseAndFlush.mockReset().mockResolvedValue(void 0);
});
it('forwards track calls with platform-tagged properties and an app+os context', async () => {
const { InsomniaAnalytics } = await import('./analytics');
const analytics = new InsomniaAnalytics({
writeKey: 'test',
app: { appName: 'inso', appVersion: '1.2.3', osVersion: '14.0', platform: 'cli' },
});
analytics.track({ event: 'inso_run_test', properties: { suite: 'echo' }, anonymousId: 'anon-1' });
expect(mockTrack).toHaveBeenCalledTimes(1);
const [payload] = mockTrack.mock.calls[0];
expect(payload).toMatchObject({
event: 'inso_run_test',
anonymousId: 'anon-1',
userId: '',
properties: { suite: 'echo', platform: 'cli' },
context: { app: { name: 'inso', version: '1.2.3' }, os: { version: '14.0' } },
});
});
it('resolves osVersion lazily when provided as a function', async () => {
const { InsomniaAnalytics } = await import('./analytics');
let v = '1.0';
const analytics = new InsomniaAnalytics({
writeKey: 'test',
app: { appName: 'app', appVersion: '0.1', osVersion: () => v, platform: 'app' },
});
v = '2.0';
analytics.track({ event: 'App Started', anonymousId: 'a', userId: 'u' });
const [payload] = mockTrack.mock.calls[0];
expect(payload.context.os.version).toBe('2.0');
});
it('routes analytics errors through the onError handler', async () => {
const onError = vi.fn();
const { InsomniaAnalytics } = await import('./analytics');
const analytics = new InsomniaAnalytics({
writeKey: 'test',
app: { appName: 'app', appVersion: '0.1', osVersion: '1.0', platform: 'app' },
onError,
});
analytics.track({ event: 'App Started', anonymousId: 'a' });
const [, callback] = mockTrack.mock.calls[0];
callback(new Error('segment failure'));
expect(onError).toHaveBeenCalledWith(expect.any(Error));
});
});

View File

@@ -0,0 +1,103 @@
import { Analytics, type AnalyticsSettings } from '@segment/analytics-node';
export interface AppContext {
appName: string;
appVersion: string;
osVersion: string | (() => string);
platform: 'app' | 'cli';
}
export interface TrackOptions {
event: string;
properties?: Record<string, unknown>;
anonymousId: string;
userId?: string;
}
export interface PageOptions {
name: string;
anonymousId: string;
userId?: string;
}
export interface InsomniaAnalyticsOptions {
writeKey: string;
app: AppContext;
settings?: Omit<AnalyticsSettings, 'writeKey'>;
onError?: (error: unknown) => void;
}
export function getNormalizedOsName(platform: NodeJS.Platform | string): string {
switch (platform) {
case 'darwin': {
return 'mac';
}
case 'win32': {
return 'windows';
}
default: {
return platform;
}
}
}
export class InsomniaAnalytics {
private readonly client: Analytics;
private readonly app: AppContext;
private readonly onError: (error: unknown) => void;
constructor({ writeKey, app, settings, onError }: InsomniaAnalyticsOptions) {
this.client = new Analytics({ writeKey, ...settings });
this.app = app;
this.onError = onError ?? (() => {});
}
track({ event, properties, anonymousId, userId }: TrackOptions): void {
this.client.track(
{
event,
anonymousId,
userId: userId ?? '',
properties: { ...properties, platform: this.app.platform },
context: this.buildContext(),
},
error => {
if (error) {
this.onError(error);
}
},
);
}
page({ name, anonymousId, userId }: PageOptions): void {
this.client.page(
{
name,
anonymousId,
userId: userId ?? '',
context: this.buildContext(),
},
error => {
if (error) {
this.onError(error);
}
},
);
}
async closeAndFlush(timeoutMs = 5000): Promise<void> {
try {
await this.client.closeAndFlush({ timeout: timeoutMs });
} catch (error) {
this.onError(error);
}
}
private buildContext() {
const osVersion = typeof this.app.osVersion === 'function' ? this.app.osVersion() : this.app.osVersion;
return {
app: { name: this.app.appName, version: this.app.appVersion },
os: { name: getNormalizedOsName(process.platform), version: osVersion },
};
}
}

View File

@@ -0,0 +1,16 @@
import { describe, expect, it } from 'vitest';
import { AnalyticsEvent, InsoEvent } from './events';
describe('events', () => {
it('AnalyticsEvent has expected entries', () => {
expect(AnalyticsEvent.appStarted).toBe('App Started');
expect(AnalyticsEvent.unitTestRun).toBe('Ran Individual Unit Test');
expect(AnalyticsEvent.installPlugin).toBe('Plugin Installed');
});
it('InsoEvent prefixes with inso_', () => {
expect(InsoEvent.runTest).toBe('inso_run_test');
expect(InsoEvent.script).toBe('inso_script');
});
});

View File

@@ -0,0 +1,142 @@
export enum AnalyticsEvent {
appStarted = 'App Started',
analyticsDisabled = 'Analytics Disabled',
collectionCreate = 'Collection Created',
dataExport = 'Data Exported',
exportCompleted = 'Export Completed',
dataImport = 'Data Imported',
importStarted = 'Import Started',
importScanned = 'Import Scanned',
importCompleted = 'Import Completed',
importLoginRequired = 'Import Login Required',
importResumedAfterLogin = 'Import Resumed After Login',
importedRequestFirstSend = 'Imported Request First Send',
documentCreate = 'Document Created',
mockCreateModalOpened = 'Mock Server Create Modal Opened',
mockCreate = 'Mock Created',
mockEdit = 'Mock Server Edited',
mockDelete = 'Mock Server Deleted',
mockRouteCreate = 'Mock Route Created',
mockRouteEdit = 'Mock Route Edited',
mockRouteDelete = 'Mock Route Deleted',
generateCollection = 'Generated Collection',
generateCollectionFromMock = 'Generate Collection From Mock',
environmentCreate = 'Environment Created',
loginSuccess = 'Login Success',
inviteTrigger = 'Invite Triggered From App',
exportAllCollections = 'Exported All Collections',
kongConnected = 'Kong Connected',
kongSync = 'Kong Synced',
requestBodyTypeSelect = 'Request Body Type Selected',
requestCreated = 'Request Created',
requestExecuted = 'Request Executed',
requestEdit = 'Request Edited',
requestDeleted = 'Request Deleted',
requestRenamed = 'Request Renamed',
requestUrlCopied = 'Request URL Copied',
collectionRunExecute = 'Collection Run Executed',
projectLocalCreate = 'Local Project Created',
projectLocalDelete = 'Local Project Deleted',
selectScratchpad = 'Scratchpad Selected at Login',
syncConflictResolutionStart = 'Sync Conflict Resolution Started',
syncConflictResolutionCompleteMine = 'Sync Conflict Resolution Completed Mine',
syncConflictResolutionCompleteTheirs = 'Sync Conflict Resolution Completed Theirs',
testSuiteCreate = 'Test Suite Created',
testSuiteDelete = 'Test Suite Deleted',
unitTestCreate = 'Unit Test Created',
unitTestDelete = 'Unit Test Deleted',
unitTestRun = 'Ran Individual Unit Test',
unitTestRunAll = 'Ran All Unit Tests',
vcsSyncStart = 'VCS Sync Started',
vcsSyncComplete = 'VCS Sync Completed',
vcsAction = 'VCS Action Executed',
gitAuthenticationCompleted = 'Git Authentication Completed',
gitAuthenticationUpdated = 'Git Authentication Updated',
buttonClick = 'Button Clicked',
inviteMember = 'Invite Sent',
inviteResent = 'Invite Resent',
inviteRevoked = 'Invite Revoked',
projectCreated = 'Project Created',
projectUpdated = 'Project Updated',
exportStarted = 'Export Started',
exportRequestsChosen = 'Export Requests Chosen',
recommendCommitsGenerated = 'Recommend Commits Generated',
recommendCommitsSaved = 'Recommend Commits Saved',
recommendCommitsCancelled = 'Recommend Commits Cancelled',
recommendCommitsClicked = 'Recommend Commits Clicked',
mcpClientWorkspaceCreate = 'MCP Client Workspace Created',
mcpClientAdded = 'MCP Client Added',
mcpClientConnected = 'MCP Client Connected',
mcpClientDisconnected = 'MCP Client Disconnected',
mcpToolCalled = 'MCP Tool Called',
mcpResourceRead = 'MCP Resource Read',
mcpPromptCalled = 'MCP Prompt Called',
inviteNotPermitted = 'Invite Not Permitted',
responseToMockClicked = 'Response To Mock Clicked',
gitSyncButtonClicked = 'Git Sync Button Clicked',
preferencesViewed = 'Preferences Viewed',
copyAsCurl = 'Copied As cURL',
themeChanged = 'Theme Changed',
generateCodeClicked = 'Generate Code Clicked',
generateCodeLanguageChanged = 'Generate Code Language Changed',
filterCreatedHomePage = 'Filter Created From Home Page',
filterCreatedProjects = 'Filter Created Projects',
filterCreatedRequests = 'Filter Created Requests',
filterCreatedResponseBody = 'Filter Created Response Body',
aiFeatureEnabled = 'AI Feature Enabled',
aiFeatureDisabled = 'AI Feature Disabled',
installPlugin = 'Plugin Installed',
homepageFiltered = 'homepage-filtered',
quickSearchOpenedByKeyboard = 'quick-search-opened-by-keyboard',
quickSearchOpenedByMouse = 'quick-search-opened-by-mouse',
statusbarLeftbarToggled = 'statusbar-leftbar-toggled',
statusbarTopbarToggled = 'statusbar-topbar-toggled',
statusbarOrphanedProjectsClicked = 'statusbar-orphaned-projects-clicked',
designerGenerateMockClicked = 'designer-generate-mock-clicked',
designerPreviewToggled = 'designer-preview-toggled',
requestEnvironmentClicked = 'request-environment-clicked',
requestAddCookiesClicked = 'request-add-cookies-clicked',
requestAddCertificatesClicked = 'request-add-certificates-clicked',
requestListSortClicked = 'request-list-sort-clicked',
requestListExpandCollapseClicked = 'request-list-expand-collapse-clicked',
requestParamsDescriptionToggled = 'request-params-description-toggled',
requestParamsImportFromURLClicked = 'request-params-import-from-URL-clicked',
requestParamsBulkEditToggled = 'request-params-bulk-edit-toggled',
responsePreviewJSONPathEntered = 'response-preview-jsonpath-entered',
requestBodyBeautifyClicked = 'request-body-beautify-clicked',
requestHeadersDescriptionToggled = 'request-headers-description-toggled',
requestHeadersBulkEditToggled = 'request-headers-bulk-edit-toggled',
requestScriptsPreScriptSnippetAdded = 'request-scripts-prescript-snippet-added',
requestScriptsPostScriptSnippetAdded = 'request-scripts-postscript-snippet-added',
responseHeadersCopyAllClicked = 'response-headers-copy-all-clicked',
responseCookiesManageCookiesClicked = 'response-cookies-manage-cookies-clicked',
requestOpenInNewTabClicked = 'request-open-in-new-tab-clicked',
requestListMenuPinClicked = 'request-list-menu-pin-clicked',
requestListMenuDuplicateClicked = 'request-list-menu-duplicate-clicked',
requestListMenuRenameClicked = 'request-list-menu-rename-clicked',
requestListMenuSettingsClicked = 'request-list-menu-settings-clicked',
requestSendMenuGenerateCodeClicked = 'request-send-menu-generate-code-clicked',
requestSendMenuSendAfterDelayClicked = 'request-send-menu-send-after-delay-clicked',
requestSendMenuRepeatAfterIntervalClicked = 'request-send-menu-repeat-after-interval-clicked',
requestSendMenuDownloadAfterSendClicked = 'request-send-menu-download-after-send-clicked',
requestSendMenuSendAndDownloadClicked = 'request-send-menu-send-and-download-clicked',
mcpListExpandCollapseClicked = 'mcp-list-expand-collapse-clicked',
mcpListFiltered = 'mcp-list-filtered',
mcpRequestParamsBeautifyClicked = 'mcp-request-params-beautify-clicked',
mcpRequestHeadersDescriptionToggled = 'mcp-request-headers-description-toggled',
mcpRequestRootsNotifyClicked = 'mcp-request-roots-notify-clicked',
mcpResponseHeadersCopyAllClicked = 'mcp-response-headers-copy-all-clicked',
kongKonnectPatValidated = 'kong-konnect-pat-validated',
kongKonnectSyncCompleted = 'kong-konnect-sync-completed',
emptyStateSendRequestClicked = 'empty-state-send-request-clicked',
emptyStateCreateDocumentClicked = 'empty-state-create-document-clicked',
}
export enum InsoEvent {
runTest = 'inso_run_test',
runCollection = 'inso_run_collection',
lintSpec = 'inso_lint_spec',
exportSpec = 'inso_export_spec',
script = 'inso_script',
}

View File

@@ -0,0 +1,2 @@
export * from './events';
export * from './analytics';

View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2020",
"allowJs": false,
"resolveJsonModule": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"module": "ESNext",
"sourceMap": true,
"baseUrl": ".",
"rootDirs": ["."],
"lib": ["ES2023", "DOM"],
"types": [],
"strict": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useUnknownInCatchVariables": false,
"verbatimModuleSyntax": true,
"forceConsistentCasingInFileNames": true
}
}

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
},
});

View File

@@ -51,7 +51,6 @@
"shellwords": "^1.0.1"
},
"dependencies": {
"@segment/analytics-node": "^2.2.1",
"@seald-io/nedb": "^4.1.1",
"@stoplight/spectral-core": "^1.22.0",
"@stoplight/spectral-formats": "^1.8.2",

View File

@@ -1,7 +1,7 @@
import os from 'node:os';
import { Analytics } from '@segment/analytics-node';
import { getSegmentWriteKey } from 'insomnia/src/common/constants';
import { InsoEvent, InsomniaAnalytics } from 'insomnia-analytics';
import { v4 as uuidv4 } from 'uuid';
import type { Settings } from '~/insomnia-data';
@@ -10,15 +10,18 @@ import packageJson from '../package.json';
import neDbAdapter from './db/adapters/ne-db-adapter';
import { getAppDataDir, getDefaultProductName } from './util';
export enum InsoEvent {
runTest = 'inso_run_test',
runCollection = 'inso_run_collection',
lintSpec = 'inso_lint_spec',
exportSpec = 'inso_export_spec',
script = 'inso_script',
}
export { InsoEvent };
const analytics = new InsomniaAnalytics({
writeKey: getSegmentWriteKey(),
app: {
appName: 'inso',
appVersion: process.env.VERSION || packageJson.version,
osVersion: () => os.release(),
platform: 'cli',
},
});
const analyticsClient = new Analytics({ writeKey: getSegmentWriteKey() });
let deviceId: string | null = null;
let localSettings: Settings | null = null;
@@ -54,25 +57,16 @@ const getDeviceId = async (): Promise<string> => {
return deviceId;
};
const getOsName = (): string => {
switch (process.platform) {
case 'darwin': {
return 'mac';
}
case 'win32': {
return 'windows';
}
default: {
return process.platform;
}
}
};
export const trackInsoEvent = async (event: InsoEvent, properties?: Record<string, unknown>): Promise<void> => {
if (process.env.NODE_ENV === 'test') {
return;
}
// new for v13 - provide a way to disable analytics
if (process.env.INSO_TELEMETRY_DISABLED) {
return;
}
const settings = await getLocalSettings();
if (settings && !settings.enableAnalytics) {
return;
@@ -80,40 +74,10 @@ export const trackInsoEvent = async (event: InsoEvent, properties?: Record<strin
try {
const anonymousId = await getDeviceId();
const version = process.env.VERSION || packageJson.version;
analyticsClient.track(
{
event,
anonymousId,
properties: {
...properties,
platform: 'cli',
},
context: {
app: {
name: 'inso',
version,
},
os: {
name: getOsName(),
version: os.release(),
},
},
},
() => {
// Silently fail
},
);
} catch {
// Silently fail
}
analytics.track({ event, anonymousId, properties });
} catch {}
};
export const flushAnalytics = async (): Promise<void> => {
try {
await analyticsClient.closeAndFlush({ timeout: 5000 });
} catch {
// Silently fail
}
await analytics.closeAndFlush(5000);
};

View File

@@ -64,7 +64,6 @@
"@rjsf/utils": "6.0.0-beta.15",
"@rjsf/validator-ajv8": "6.0.0-beta.15",
"@seald-io/nedb": "^4.1.1",
"@segment/analytics-node": "2.2.1",
"@sentry/electron": "^6.5.0",
"@stoplight/spectral-core": "^1.22.0",
"@stoplight/spectral-formats": "^1.8.2",

View File

@@ -66,8 +66,8 @@ vi.mock('../main/install-plugin', async () => {
});
vi.mock('../main/analytics', () => ({
trackSegmentEvent: vi.fn(),
SegmentEvent: {
trackAnalyticsEvent: vi.fn(),
AnalyticsEvent: {
installPlugin: 'Plugin Installed',
},
}));

View File

@@ -21,7 +21,7 @@ import { registerLLMConfigServiceAPI } from '~/main/llm-config-service';
import { userDataFolder } from '../config/config.json';
import { getAppVersion, getProductName, isDevelopment } from './common/constants';
import { isMac } from './common/platform';
import { SegmentEvent, trackSegmentEvent } from './main/analytics';
import { AnalyticsEvent, trackAnalyticsEvent } from './main/analytics';
import { registerInsomniaProtocols } from './main/api.protocol';
import { backupIfNewerVersionAvailable } from './main/backup';
import { registerSyncHandlers } from './main/cloud-sync/ipc';
@@ -365,7 +365,7 @@ async function _trackStats() {
const settings = await services.settings.get();
trackSegmentEvent(SegmentEvent.appStarted, {
trackAnalyticsEvent(AnalyticsEvent.appStarted, {
localProjects,
remoteProjects,
createdRequests: stats.createdRequests,

View File

@@ -289,7 +289,7 @@ const main: Window['main'] = {
secretStorage,
electronStorage,
sync,
trackSegmentEvent: options => ipcRenderer.send('trackSegmentEvent', options),
trackAnalyticsEvent: options => ipcRenderer.send('trackAnalyticsEvent', options),
trackPageView: options => ipcRenderer.send('trackPageView', options),
setCurrentOrganizationId: organizationId => ipcRenderer.send('analytics.setOrganizationId', organizationId),
showNunjucksContextMenu: options => ipcRenderer.send('show-nunjucks-context-menu', options),

View File

@@ -1,7 +1,7 @@
/**
* Tests run against the in-memory NeDB initialized by setup-vitest.ts.
* fetch is mocked per-test to return shaped Konnect API responses.
* window.main is stubbed globally so trackSegmentEvent calls don't throw.
* window.main is stubbed globally so trackAnalyticsEvent calls don't throw.
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@ -101,13 +101,13 @@ const konnectRequests = (docs: any[]) => docs.filter((r: any) => r.konnectRouteK
const konnectWorkspaces = (docs: any[]) => docs.filter((w: any) => w.konnectServiceId != null);
const konnectProjects = (docs: any[]) => docs.filter((p: any) => p.konnectControlPlaneId != null);
const trackSegmentEvent = vi.fn();
const trackAnalyticsEvent = vi.fn();
beforeEach(async () => {
// Re-init with fresh in-memory NeDB buckets — clean slate for every test.
await initDatabase(mainDatabase, { inMemoryOnly: true }, true);
resetV4Counter();
vi.stubGlobal('window', { main: { trackSegmentEvent } });
vi.stubGlobal('window', { main: { trackAnalyticsEvent } });
});
afterEach(() => {

View File

@@ -1,8 +1,8 @@
import crypto from 'node:crypto';
import { Analytics } from '@segment/analytics-node';
import * as Sentry from '@sentry/electron/main';
import { net } from 'electron';
import { AnalyticsEvent, InsomniaAnalytics } from 'insomnia-analytics';
import { v4 as uuidv4 } from 'uuid';
import { services } from '~/insomnia-data';
@@ -15,7 +15,8 @@ import {
getSegmentWriteKey,
PLAYWRIGHT_TEST,
} from '../common/constants';
import { platform } from '../common/platform';
export { AnalyticsEvent };
let _currentOrganizationId: string | undefined;
@@ -23,18 +24,29 @@ export function setCurrentOrganizationId(id: string | undefined): void {
_currentOrganizationId = id;
}
const analytics = new Analytics({
const analytics = new InsomniaAnalytics({
writeKey: getSegmentWriteKey(),
httpClient: {
makeRequest(_options) {
return net.fetch(_options.url, {
method: _options.method,
headers: _options.headers,
body: _options.body,
signal: AbortSignal.timeout(_options.httpRequestTimeout),
});
app: {
appName: getProductName(),
appVersion: getAppVersion(),
osVersion: () => process.getSystemVersion(),
platform: 'app',
},
settings: {
httpClient: {
makeRequest(_options) {
return net.fetch(_options.url, {
method: _options.method,
headers: _options.headers,
body: _options.body,
signal: AbortSignal.timeout(_options.httpRequestTimeout),
});
},
},
},
onError: error => {
console.warn('[analytics] Error sending analytics event', error);
},
});
const getDeviceId = async () => {
@@ -42,48 +54,11 @@ const getDeviceId = async () => {
return settings.deviceId || (await services.settings.update(settings, { deviceId: uuidv4() })).deviceId;
};
export enum SegmentEvent {
appStarted = 'App Started',
collectionCreate = 'Collection Created',
dataExport = 'Data Exported',
dataImport = 'Data Imported',
loginSuccess = 'Login Success',
documentCreate = 'Document Created',
kongConnected = 'Kong Connected',
kongSync = 'Kong Synced',
requestBodyTypeSelect = 'Request Body Type Selected',
requestCreated = 'Request Created',
requestExecuted = 'Request Executed',
collectionRunExecute = 'Collection Run Executed',
projectLocalCreate = 'Local Project Created',
projectLocalDelete = 'Local Project Deleted',
testSuiteCreate = 'Test Suite Created',
testSuiteDelete = 'Test Suite Deleted',
unitTestCreate = 'Unit Test Created',
unitTestDelete = 'Unit Test Deleted',
unitTestRun = 'Ran Individual Unit Test',
unitTestRunAll = 'Ran All Unit Tests',
vcsSyncStart = 'VCS Sync Started',
vcsSyncComplete = 'VCS Sync Completed',
vcsAction = 'VCS Action Executed',
gitAuthenticationCompleted = 'Git Authentication Completed',
gitAuthenticationUpdated = 'Git Authentication Updated',
buttonClick = 'Button Clicked',
aiFeatureEnabled = 'AI Feature Enabled',
aiFeatureDisabled = 'AI Feature Disabled',
mcpClientConnected = 'MCP Client Connected',
mcpClientDisconnected = 'MCP Client Disconnected',
mcpToolCalled = 'MCP Tool Called',
mcpResourceRead = 'MCP Resource Read',
mcpPromptCalled = 'MCP Prompt Called',
installPlugin = 'Plugin Installed',
}
function hashString(input: string) {
return crypto.createHash('sha256').update(input).digest('hex');
}
export async function trackSegmentEvent(event: SegmentEvent, properties?: Record<string, any>) {
export async function trackAnalyticsEvent(event: AnalyticsEvent, properties?: Record<string, any>) {
if (PLAYWRIGHT_TEST) {
return;
}
@@ -93,46 +68,34 @@ export async function trackSegmentEvent(event: SegmentEvent, properties?: Record
userSession.hashedAccountId = userSession?.accountId ? hashString(userSession.accountId) : '';
}
const allowAnalytics = settings.enableAnalytics || userSession?.hashedAccountId;
if (allowAnalytics) {
try {
const anonymousId = (await getDeviceId()) ?? '';
const context = {
app: { name: getProductName(), version: getAppVersion() },
os: { name: _getOsName(), version: process.getSystemVersion() },
};
if (!allowAnalytics) {
return;
}
analytics.track(
{
event,
properties: {
...(_currentOrganizationId && { organization_id: _currentOrganizationId }),
...properties,
platform: 'app',
},
context,
anonymousId,
userId: userSession?.hashedAccountId || '',
try {
const anonymousId = (await getDeviceId()) ?? '';
analytics.track({
event,
properties: {
...(_currentOrganizationId && { organization_id: _currentOrganizationId }),
...properties,
},
anonymousId,
userId: userSession?.hashedAccountId || '',
});
} catch (error: unknown) {
console.warn('[analytics] Unexpected error while sending analytics event', error);
} finally {
if (!userSession?.hashedAccountId && [AnalyticsEvent.unitTestRun, AnalyticsEvent.unitTestRunAll].includes(event)) {
Sentry.captureException(`Run tests by anonymous`, {
tags: {
source: 'main/analytics',
},
error => {
if (error) {
console.warn('[analytics] Error sending segment event', error);
}
extra: {
organizationId: properties?.organizationId || '',
projectId: properties?.projectId || '',
},
);
} catch (error: unknown) {
console.warn('[analytics] Unexpected error while sending segment event', error);
} finally {
if (!userSession?.hashedAccountId && [SegmentEvent.unitTestRun, SegmentEvent.unitTestRunAll].includes(event)) {
Sentry.captureException(`Run tests by anonymous`, {
tags: {
source: 'main/analytics',
},
extra: {
organizationId: properties?.organizationId || '',
projectId: properties?.projectId || '',
},
});
}
});
}
}
}
@@ -148,48 +111,24 @@ export async function trackPageView(name: string) {
}
const allowAnalytics = settings.enableAnalytics || userSession?.hashedAccountId;
if (allowAnalytics) {
try {
const anonymousId = (await getDeviceId()) ?? '';
const context = {
app: { name: getProductName(), version: getAppVersion() },
os: { name: _getOsName(), version: process.getSystemVersion() },
};
if (!allowAnalytics) {
return;
}
analytics.page({ name, context, anonymousId, userId: userSession?.hashedAccountId }, error => {
if (error) {
console.warn('[analytics] Error sending segment event', error);
}
try {
const anonymousId = (await getDeviceId()) ?? '';
analytics.page({ name, anonymousId, userId: userSession?.hashedAccountId });
if (userSession?.id) {
net.fetch(getApiBaseURL() + '/v1/telemetry/', {
method: 'POST',
headers: new Headers({
'X-Session-Id': userSession?.id,
'X-Insomnia-Client': getClientString(),
}),
});
if (userSession?.id) {
net.fetch(getApiBaseURL() + '/v1/telemetry/', {
method: 'POST',
headers: new Headers({
'X-Session-Id': userSession?.id,
'X-Insomnia-Client': getClientString(),
}),
});
}
} catch (error: unknown) {
console.warn('[analytics] Unexpected error while sending segment event', error);
}
}
}
// ~~~~~~~~~~~~~~~~~ //
// Private Functions //
// ~~~~~~~~~~~~~~~~~ //
function _getOsName() {
switch (platform) {
case 'darwin': {
return 'mac';
}
case 'win32': {
return 'windows';
}
default: {
return platform;
}
} catch (error: unknown) {
console.warn('[analytics] Unexpected error while sending analytics event', error);
}
}

View File

@@ -66,7 +66,7 @@ import { routableFSClient } from '../sync/git/routable-fs-client';
import { shallowClone } from '../sync/git/shallow-clone';
import type { AutoResolvedConflict, MergeConflict } from '../sync/types';
import { invariant } from '../utils/invariant';
import { SegmentEvent, trackSegmentEvent } from './analytics';
import { AnalyticsEvent, trackAnalyticsEvent } from './analytics';
import { ipcMainHandle } from './ipc/electron';
// Initialize Git Remote Providers on module load
@@ -153,7 +153,7 @@ export function getErrorMessage(error: unknown): string {
// Non-Error objects
return 'Unknown Error';
}
export function vcsSegmentEventProperties(type: 'git', action: VCSAction, error?: string) {
export function vcsEventProperties(type: 'git', action: VCSAction, error?: string) {
return { type, action, error };
}
@@ -1097,8 +1097,8 @@ export const cloneGitRepoAction = async ({
}
if (!projectId) {
trackSegmentEvent(SegmentEvent.vcsSyncStart, {
...vcsSegmentEventProperties('git', 'clone'),
trackAnalyticsEvent(AnalyticsEvent.vcsSyncStart, {
...vcsEventProperties('git', 'clone'),
provider,
repoId: repoSettingsPatch._id,
});
@@ -1230,8 +1230,8 @@ export const cloneGitRepoAction = async ({
});
await database.flushChanges(bufferId);
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
...vcsSegmentEventProperties('git', 'clone'),
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
...vcsEventProperties('git', 'clone'),
providerName,
repoId: repoSettingsPatch._id,
});
@@ -1245,8 +1245,8 @@ export const cloneGitRepoAction = async ({
const project = await services.project.getById(projectId);
invariant(project, 'Project not found');
trackSegmentEvent(SegmentEvent.vcsSyncStart, {
...vcsSegmentEventProperties('git', 'clone'),
trackAnalyticsEvent(AnalyticsEvent.vcsSyncStart, {
...vcsEventProperties('git', 'clone'),
provider,
repoId: repoSettingsPatch._id,
});
@@ -1304,8 +1304,8 @@ export const cloneGitRepoAction = async ({
});
await services.apiSpec.getOrCreateForParentId(workspace._id);
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
...vcsSegmentEventProperties('git', 'clone', 'no directory found'),
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
...vcsEventProperties('git', 'clone', 'no directory found'),
providerName: provider,
repoId: repoSettingsPatch._id,
});
@@ -1323,8 +1323,8 @@ export const cloneGitRepoAction = async ({
const workspaces = await inMemoryFsClient.promises.readdir(workspaceBase);
if (workspaces.length === 0) {
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
...vcsSegmentEventProperties('git', 'clone', 'no workspaces found'),
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
...vcsEventProperties('git', 'clone', 'no workspaces found'),
providerName: provider,
repoId: repoSettingsPatch._id,
});
@@ -1335,8 +1335,8 @@ export const cloneGitRepoAction = async ({
}
if (workspaces.length > 1) {
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
...vcsSegmentEventProperties('git', 'clone', 'multiple workspaces found'),
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
...vcsEventProperties('git', 'clone', 'multiple workspaces found'),
providerName: provider,
repoId: repoSettingsPatch._id,
});
@@ -1424,8 +1424,8 @@ export const cloneGitRepoAction = async ({
// Flush DB changes
await database.flushChanges(bufferId);
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
...vcsSegmentEventProperties('git', 'clone'),
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
...vcsEventProperties('git', 'clone'),
providerName: provider,
repoId: repoSettingsPatch._id,
});
@@ -1601,8 +1601,8 @@ export const commitToGitRepoAction = async ({
providerName = credentials.provider;
}
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'commit'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'commit'),
providerName,
repoId: gitRepository._id,
});
@@ -1730,8 +1730,8 @@ export const commitAndPushToGitRepoAction = async ({
providerName = credentials.provider;
}
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'commit'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'commit'),
providerName,
repoId: repo._id,
});
@@ -1770,8 +1770,8 @@ export const commitAndPushToGitRepoAction = async ({
try {
await GitVCS.push(repo.credentialsId);
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'push'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'push'),
providerName,
repoId: repo._id,
});
@@ -1804,8 +1804,8 @@ export const commitAndPushToGitRepoAction = async ({
}
const errorMessage = getErrorMessage(err);
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'push', errorMessage),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'push', errorMessage),
providerName,
repoId: repo._id,
});
@@ -1846,8 +1846,8 @@ export const createNewGitBranchAction = async ({
providerName = credentials.provider;
}
await GitVCS.checkout(branch);
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'create_branch'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'create_branch'),
providerName,
repoId: gitRepository._id,
});
@@ -2021,8 +2021,8 @@ export const mergeGitBranch = async ({
await repoFileWatcherRegistry.importAllFiles(gitRepoId);
clearConflictSuppression(gitRepository._id);
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'merge_branch'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'merge_branch'),
providerName,
repoId: gitRepository._id,
});
@@ -2050,8 +2050,8 @@ export const mergeGitBranch = async ({
errorMessage = `${err.message}, ${err.data.response}`;
}
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'merge_branch', errorMessage),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'merge_branch', errorMessage),
providerName,
repoId: gitRepository._id,
});
@@ -2086,8 +2086,8 @@ export const deleteGitBranchAction = async ({
providerName = credentials.provider;
}
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'delete_branch'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'delete_branch'),
providerName,
repoId: repo._id,
});
@@ -2163,8 +2163,8 @@ export const pushToGitRemoteAction = async ({
const bufferId = await database.bufferChanges();
await GitVCS.push(gitRepository.credentialsId);
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', force ? 'force_push' : 'push'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', force ? 'force_push' : 'push'),
providerName,
repoId: gitRepository._id,
});
@@ -2208,8 +2208,8 @@ export const pushToGitRemoteAction = async ({
}
const errorMessage = getErrorMessage(err);
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'push', errorMessage),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'push', errorMessage),
providerName,
repoId: gitRepository._id,
});
@@ -2277,8 +2277,8 @@ export async function pullFromGitRemote({ projectId, workspaceId }: { projectId:
await repoFileWatcherRegistry.importAllFiles(gitRepository._id);
clearConflictSuppression(repoId);
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'pull'),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'pull'),
providerName: credentials.provider,
repoId: gitRepository._id,
});
@@ -2330,8 +2330,8 @@ export async function pullFromGitRemote({ projectId, workspaceId }: { projectId:
providerName = credentials.provider;
}
trackSegmentEvent(SegmentEvent.vcsAction, {
...vcsSegmentEventProperties('git', 'pull', errorMessage),
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
...vcsEventProperties('git', 'pull', errorMessage),
providerName,
repoId: gitRepository._id,
});
@@ -2788,9 +2788,9 @@ async function completeSignInToGitProvider({
}
if (isEditing) {
trackSegmentEvent(SegmentEvent.gitAuthenticationUpdated, { provider });
trackAnalyticsEvent(AnalyticsEvent.gitAuthenticationUpdated, { provider });
} else {
trackSegmentEvent(SegmentEvent.gitAuthenticationCompleted, { provider });
trackAnalyticsEvent(AnalyticsEvent.gitAuthenticationCompleted, { provider });
}
return {};

View File

@@ -7,7 +7,7 @@ import { promisify } from 'node:util';
import { app, net } from 'electron';
import { services } from '~/insomnia-data';
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
import { isDevelopment } from '../common/constants';
import { validatePluginName } from '../utils/plugin';
@@ -160,7 +160,7 @@ export default async function installPlugin(pluginName: string, allowScopedPacka
}),
);
trackSegmentEvent(SegmentEvent.installPlugin, {
trackAnalyticsEvent(AnalyticsEvent.installPlugin, {
pluginName: moduleName,
pluginVersion: info.version,
});

View File

@@ -192,7 +192,7 @@ export type MainOnChannels =
| 'socketIO.event.on'
| 'startExecution'
| 'trackPageView'
| 'trackSegmentEvent'
| 'trackAnalyticsEvent'
| 'updateLatestStepName'
| 'webSocket.close'
| 'webSocket.closeAll'

View File

@@ -37,8 +37,8 @@ import type {
import type { HiddenBrowserWindowBridgeAPI } from '../../entry.hidden-window';
import type { PluginTemplateTag, RenderedRequest } from '../../templating/types';
import type { SegmentEvent } from '../analytics';
import { setCurrentOrganizationId, trackPageView, trackSegmentEvent } from '../analytics';
import type { AnalyticsEvent } from '../analytics';
import { setCurrentOrganizationId, trackAnalyticsEvent, trackPageView } from '../analytics';
import {
authorizeUserInDefaultBrowser,
cancelAuthorizationInDefaultBrowser,
@@ -186,7 +186,7 @@ export interface RendererToMainBridgeAPI {
secretStorage: secretStorageBridgeAPI;
electronStorage: electronStorageBridgeAPI;
sync: SyncBridgeAPI;
trackSegmentEvent: (options: { event: string; properties?: Record<string, unknown> }) => void;
trackAnalyticsEvent: (options: { event: string; properties?: Record<string, unknown> }) => void;
trackPageView: (options: { name: string }) => void;
setCurrentOrganizationId: (organizationId: string | undefined) => void;
showNunjucksContextMenu: (options: {
@@ -413,8 +413,8 @@ export function registerMainHandlers() {
cancelCurlRequest(requestId);
});
ipcMainOn('trackSegmentEvent', (_, options: { event: SegmentEvent; properties?: Record<string, unknown> }): void => {
trackSegmentEvent(options.event, options.properties);
ipcMainOn('trackAnalyticsEvent', (_, options: { event: AnalyticsEvent; properties?: Record<string, unknown> }): void => {
trackAnalyticsEvent(options.event, options.properties);
});
ipcMainOn('trackPageView', (_, options: { name: string }): void => {
trackPageView(options.name);

View File

@@ -4,7 +4,7 @@ import { app } from 'electron';
import { LLM_BACKENDS } from '~/common/constants';
import { services } from '~/insomnia-data';
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
import { ipcMainHandle } from '~/main/ipc/electron';
const LLM_PLUGIN_NAME = 'insomnia-llm';
@@ -123,7 +123,7 @@ export const getAIFeatureEnabled = async (feature: AIFeatureNames): Promise<bool
export const setAIFeatureEnabled = async (feature: AIFeatureNames, enabled: boolean): Promise<void> => {
await services.pluginData.upsertByKey(LLM_PLUGIN_NAME, `feature.${feature}`, String(enabled));
trackSegmentEvent(enabled ? SegmentEvent.aiFeatureEnabled : SegmentEvent.aiFeatureDisabled, {
trackAnalyticsEvent(enabled ? AnalyticsEvent.aiFeatureEnabled : AnalyticsEvent.aiFeatureDisabled, {
feature: feature,
set_for: 'user',
});

View File

@@ -11,7 +11,7 @@ import {
} from '@modelcontextprotocol/sdk/types.js';
import { METHOD_SUBSCRIBE_RESOURCE, METHOD_UNSUBSCRIBE_RESOURCE } from '~/common/mcp-utils';
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
import { getActiveMcpClient, getReadyActiveMcpConnectionContext, writeEventLogAndNotify } from '~/main/mcp/common';
import type { CommonMcpOptions, McpMessageEventWithoutBase } from '~/main/mcp/types';
@@ -29,7 +29,7 @@ export const callTool = async (options: CommonMcpOptions & CallToolRequest['para
const mcpClient = getActiveMcpClient(requestId);
if (mcpClient) {
const response = await mcpClient.callTool(params, CompatibilityCallToolResultSchema);
trackSegmentEvent(SegmentEvent.mcpToolCalled);
trackAnalyticsEvent(AnalyticsEvent.mcpToolCalled);
return response.content;
}
return null;
@@ -49,7 +49,7 @@ export const getPrompt = async (options: CommonMcpOptions & GetPromptRequest['pa
const mcpClient = getActiveMcpClient(options.requestId);
if (mcpClient) {
const prompt = await mcpClient.getPrompt(params);
trackSegmentEvent(SegmentEvent.mcpPromptCalled);
trackAnalyticsEvent(AnalyticsEvent.mcpPromptCalled);
return prompt;
}
return null;
@@ -80,7 +80,7 @@ export const readResource = async (options: CommonMcpOptions & ReadResourceReque
const mcpClient = getActiveMcpClient(requestId);
if (mcpClient) {
const resource = await mcpClient.readResource(params);
trackSegmentEvent(SegmentEvent.mcpResourceRead);
trackAnalyticsEvent(AnalyticsEvent.mcpResourceRead);
return resource;
}
return null;

View File

@@ -21,7 +21,7 @@ import electron from 'electron';
import { getAppVersion, getProductName, REALTIME_EVENTS_CHANNELS } from '~/common/constants';
import { getMcpMethodFromMessage, METHOD_NOTIFICATION_CANCELLED } from '~/common/mcp-utils';
import { models, services } from '~/insomnia-data';
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
import {
callTool,
getPrompt,
@@ -257,7 +257,7 @@ const createTransportAndConnect = async (context: ConnectionContext, mcpClient:
}
const authDisabled = 'disabled' in mcpRequest.authentication && mcpRequest.authentication.disabled;
const isFirstConnection = !mcpRequest.connected;
trackSegmentEvent(SegmentEvent.mcpClientConnected, {
trackAnalyticsEvent(AnalyticsEvent.mcpClientConnected, {
transportType: connectionOptions.transportType,
firstTime: isFirstConnection,
...(connectionOptions.transportType === models.mcpRequest.TRANSPORT_TYPES.HTTP
@@ -455,7 +455,7 @@ const closeMcpConnection = async (options: CommonMcpOptions) => {
// Execute clear resource subscription in main process rather than UI to make sure closeAllMcpConnections method will clear subscriptions
await services.mcpRequest.clearResourceSubscriptions(requestId);
}
trackSegmentEvent(SegmentEvent.mcpClientDisconnected);
trackAnalyticsEvent(AnalyticsEvent.mcpClientDisconnected);
};
const closeAllMcpConnections = () => {

View File

@@ -35,7 +35,7 @@ import {
GIT_PROVIDER_COMPLETE_SIGN_IN_FETCHER_KEY,
useGitProviderCompleteSignInFetcher,
} from '~/routes/git-credentials.complete-sign-in';
import { PENDING_IMPORT_ATTRIBUTION_KEY, SegmentEvent, trackImportEvent } from '~/ui/analytics';
import { AnalyticsEvent, PENDING_IMPORT_ATTRIBUTION_KEY, trackImportEvent } from '~/ui/analytics';
import { getLoginUrl } from '~/ui/auth-session-provider.client';
import { CopyButton } from '~/ui/components/base/copy-button';
import { Link } from '~/ui/components/base/link';
@@ -203,6 +203,11 @@ export const Layout = ({ children }: { children: React.ReactNode }) => {
*
insomnia://*
;
frame-src
blob:
*
insomnia://*
;
script-src
'self'
'unsafe-eval'
@@ -386,10 +391,10 @@ const Root = () => {
if (!userSession.id) {
window.sessionStorage.setItem('pendingDeepLinkAfterAuthorize', url);
window.localStorage.setItem('logoutMessage', 'Please log in to import this resource.');
trackImportEvent(SegmentEvent.importLoginRequired);
trackImportEvent(AnalyticsEvent.importLoginRequired);
return navigate(href('/auth/login'));
}
trackImportEvent(SegmentEvent.importStarted, { source: 'import-url' });
trackImportEvent(AnalyticsEvent.importStarted, { source: 'import-url' });
if (params.uri) {
return setImportObject({
@@ -635,7 +640,7 @@ const Root = () => {
if (pendingDeepLink && organizationId && organizationId !== models.organization.SCRATCHPAD_ORGANIZATION_ID) {
window.sessionStorage.removeItem('pendingDeepLinkAfterAuthorize');
window.sessionStorage.setItem('suppressWelcomeModals', 'true');
trackImportEvent(SegmentEvent.importResumedAfterLogin);
trackImportEvent(AnalyticsEvent.importResumedAfterLogin);
window.main.openDeepLink(pendingDeepLink);
}
}, [organizationId]);

View File

@@ -1,6 +1,6 @@
import { href } from 'react-router';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { showToast } from '~/ui/components/toast-notification';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -38,8 +38,8 @@ export async function clientAction(args: Route.ClientActionArgs) {
.join('\n'),
});
window.main.trackSegmentEvent({
event: SegmentEvent.recommendCommitsGenerated,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.recommendCommitsGenerated,
properties: {
file_count: commits?.map(commit => commit.files?.length || 0)?.reduce((a, b) => a + b, 0),
group_count: commits?.length || 0,

View File

@@ -4,7 +4,7 @@ import { Button, Heading } from 'react-aria-components';
import { href, redirect, useFetchers, useNavigate } from 'react-router';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { getLoginUrl, submitAuthCode } from '~/ui/auth-session-provider.client';
import { Icon } from '~/ui/components/icon';
import { validateVaultKey } from '~/ui/vault-key.client';
@@ -32,8 +32,8 @@ export async function clientAction({ request }: Route.ClientActionArgs) {
};
}
console.log('Login successful');
window.main.trackSegmentEvent({
event: SegmentEvent.loginSuccess,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.loginSuccess,
});
window.localStorage.setItem('hasUserLoggedInBefore', 'true');
const userSession = await services.userSession.getOrCreate();

View File

@@ -3,7 +3,7 @@ import { Button } from 'react-aria-components';
import { href, redirect, useNavigate } from 'react-router';
import { models } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { getLoginUrl } from '~/ui/auth-session-provider.client';
import { Icon } from '~/ui/components/icon';
import { Tooltip } from '~/ui/components/tooltip';
@@ -152,8 +152,8 @@ const Component = () => {
<Button
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.selectScratchpad,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.selectScratchpad,
});
navigate(
href('/organization/:organizationId/project/:projectId/workspace/:workspaceId/debug', {

View File

@@ -9,7 +9,7 @@ import {
scanResources,
} from '~/common/import';
import type { ImportEntry } from '~/main/importers/entities';
import { SegmentEvent, trackImportEvent } from '~/ui/analytics';
import { AnalyticsEvent, trackImportEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -27,7 +27,7 @@ export const scanImportResources = async (data: {
invariant(typeof source === 'string', 'Source is required.');
invariant(IMPORT_SOURCE_TYPES.includes(source), 'Unsupported import type');
trackImportEvent(SegmentEvent.importScanned, { source });
trackImportEvent(AnalyticsEvent.importScanned, { source });
const contentList: ImportEntry[] = [];

View File

@@ -51,7 +51,7 @@ import { useOrganizationLoaderData } from '~/routes/organization';
import { useInsomniaSyncPullRemoteFileActionFetcher } from '~/routes/organization.$organizationId.insomnia-sync.pull-remote-file';
import { useWorkspaceNewActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.new';
import { useStorageRulesLoaderFetcher } from '~/routes/organization.$organizationId.storage-rules';
import { SegmentEvent, trackOnceDaily } from '~/ui/analytics';
import { AnalyticsEvent, trackOnceDaily } from '~/ui/analytics';
import { AvatarGroup } from '~/ui/components/avatar';
import { CloudSyncProjectBar } from '~/ui/components/dropdowns/cloud-sync-project-bar';
import { GitProjectSyncDropdown } from '~/ui/components/dropdowns/git-project-sync-dropdown';
@@ -970,7 +970,7 @@ const Component = () => {
onChange={filter => {
setWorkspaceListFilter(filter);
if (filter.trim() !== '') {
trackOnceDaily(SegmentEvent.homepageFiltered);
trackOnceDaily(AnalyticsEvent.homepageFiltered);
}
}}
>
@@ -1066,8 +1066,8 @@ const Component = () => {
<Button
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.importStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.importStarted,
properties: {
source: 'project',
},

View File

@@ -6,7 +6,7 @@ import { projectLock } from '~/common/project';
import type { WorkspaceMeta } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { reportGitProjectCount } from '~/routes/organization.$organizationId.project.new';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { showToast } from '~/ui/components/toast-notification';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -106,8 +106,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
sessionId,
});
window.main.trackSegmentEvent({
event: SegmentEvent.projectUpdated,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.projectUpdated,
properties: {
storage: 'local',
},
@@ -158,8 +158,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
name,
});
window.main.trackSegmentEvent({
event: SegmentEvent.projectUpdated,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.projectUpdated,
properties: {
storage: 'remote',
},
@@ -222,8 +222,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
sessionId,
});
window.main.trackSegmentEvent({
event: SegmentEvent.projectUpdated,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.projectUpdated,
properties: {
storage: 'git',
},
@@ -376,8 +376,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
// local project rename
await services.project.update(project, { name });
window.main.trackSegmentEvent({
event: SegmentEvent.projectUpdated,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.projectUpdated,
properties: {
storage: 'local',
},

View File

@@ -6,12 +6,14 @@ import { v4 as uuidv4 } from 'uuid';
import { getContentDispositionHeader } from '~/common/misc';
import type {
Environment,
Request,
RequestGroup,
RequestMeta,
ResponseInfo,
RunnerResultPerRequestPerIteration,
UserUploadEnvironment,
} from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { database as db, models, services } from '~/insomnia-data';
import type { ResponsePatch } from '~/main/network/libcurl-promise';
import type { TimingStep } from '~/main/network/request-timing';
import {
@@ -25,7 +27,7 @@ import {
tryToInterpolateRequest,
tryToTransformRequestWithPlugins,
} from '~/network/network';
import { type ImportAttribution, importAttributionKey, SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent, type ImportAttribution, importAttributionKey } from '~/ui/analytics';
import { parseGraphQLReqeustBody } from '~/utils/graph-ql';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -334,7 +336,7 @@ export const sendActionImplementation = async (options: {
};
export async function clientAction({ request, params }: Route.ClientActionArgs) {
const { requestId } = params;
const { requestId, workspaceId } = params;
const { shouldPromptForPathAfterResponse, ignoreUndefinedEnvVariable } = (await request.json()) as SendActionParams;
try {
@@ -353,8 +355,22 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
const activeRequest = await services.request.getById(requestId);
if (activeRequest) {
window.main.trackSegmentEvent({
event: SegmentEvent.requestExecuted,
const [requestAndAncestors, clientCertificates] = await Promise.all([
db.withAncestors<Request | RequestGroup>(
activeRequest as Request,
[models.request.type, models.requestGroup.type],
),
services.clientCertificate.findByParentId(workspaceId),
]);
const docsWithScripts = requestAndAncestors.filter(
(doc): doc is Request | RequestGroup =>
models.request.isRequest(doc) || models.requestGroup.isRequestGroup(doc),
);
const allPreScripts = docsWithScripts.map(doc => doc.preRequestScript).filter((s): s is string => !!s);
const allPostScripts = docsWithScripts.map(doc => doc.afterResponseScript).filter((s): s is string => !!s);
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestExecuted,
properties: {
preferredHttpVersion: settings.preferredHttpVersion,
// @ts-expect-error -- who cares
@@ -365,6 +381,14 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
count_headers: response.headers.length,
count_cookies: response.headers.find(h => h.name === 'set-cookie')?.value.split(',').length || 0,
count_tests: response.requestTestResults?.length || 0,
has_prescript: allPreScripts.length > 0,
has_postscript: allPostScripts.length > 0,
count_prescript_lines: allPreScripts.reduce((sum, s) => sum + s.split('\n').length, 0),
count_postscript_lines: allPostScripts.reduce((sum, s) => sum + s.split('\n').length, 0),
count_query_parameters: activeRequest.parameters?.length ?? 0,
count_path_parameters: activeRequest.pathParameters?.length ?? 0,
has_docs: !!activeRequest.description,
count_certificates: clientCertificates.length,
},
});
@@ -373,8 +397,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
if (jsonImportAttribution) {
try {
const importAttribution = JSON.parse(jsonImportAttribution) as ImportAttribution;
window.main.trackSegmentEvent({
event: SegmentEvent.importedRequestFirstSend,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.importedRequestFirstSend,
properties: {
...importAttribution,
protocol: activeRequest.type,

View File

@@ -2,7 +2,7 @@ import { href } from 'react-router';
import type { WebSocketRequest } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { updateMimeType } from '~/ui/components/dropdowns/content-type-dropdown';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -45,8 +45,8 @@ export async function clientAction({ params, request }: Route.ClientActionArgs)
await services.helpers.updateRequest(req, patch);
if (req.name !== patch.name) {
window.main.trackSegmentEvent({
event: SegmentEvent.requestRenamed,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestRenamed,
});
}

View File

@@ -1,7 +1,7 @@
import { href, redirect } from 'react-router';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -19,8 +19,8 @@ export async function clientAction({ params, request }: Route.ClientActionArgs)
const workspaceMeta = await services.workspaceMeta.getByParentId(workspaceId);
invariant(workspaceMeta, 'Workspace meta not found');
window.main.trackSegmentEvent({
event: SegmentEvent.requestDeleted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestDeleted,
});
if (workspaceMeta.activeRequestId === id) {

View File

@@ -10,7 +10,7 @@ import {
} from '~/common/constants';
import type { Request, RequestBody, RequestParameter } from '~/insomnia-data';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import type { CreateRequestType } from '~/ui/hooks/use-request';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -126,8 +126,8 @@ export async function clientAction({ params, request }: Route.ClientActionArgs)
const certificates = await services.clientCertificate.findByParentId(workspaceId);
window.main.trackSegmentEvent({
event: SegmentEvent.requestCreated,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestCreated,
properties: {
requestType,
protocol: requestType,

View File

@@ -32,7 +32,7 @@ import { useRootLoaderData } from '~/root';
import { useOrganizationLoaderData } from '~/routes/organization';
import type { CollectionRunnerContext } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.send';
import { sendActionImplementation } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.send';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { Dropdown, DropdownItem, ItemContent } from '~/ui/components/base/dropdown';
import { ErrorBoundary } from '~/ui/components/error-boundary';
import { HelpTooltip } from '~/ui/components/help-tooltip';
@@ -270,8 +270,8 @@ export const Runner: FC = () => {
}
setIsRunning(true);
window.main.trackSegmentEvent({
event: SegmentEvent.collectionRunExecute,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.collectionRunExecute,
properties: { plan: organizationData?.currentPlan?.type || 'scratchpad', iterations: iterationCount },
});

View File

@@ -72,7 +72,7 @@ import Tutorial, {
scratchPadTutorialList,
} from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.tutorial.$panel';
import { useToggleExpandAllActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.toggle-expand-all';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { DropdownHint } from '~/ui/components/base/dropdown/dropdown-hint';
import { DocumentTab } from '~/ui/components/document-tab';
import { RequestActionsDropdown } from '~/ui/components/dropdowns/request-actions-dropdown';
@@ -848,8 +848,8 @@ const Debug = () => {
onOpenChange={isOpen => {
setIsEnvironmentPickerOpen(isOpen);
if (isOpen) {
window.main.trackSegmentEvent({
event: SegmentEvent.requestEnvironmentClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestEnvironmentClicked,
});
}
}}
@@ -858,8 +858,8 @@ const Debug = () => {
</div>
<Button
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.requestAddCookiesClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestAddCookiesClicked,
});
setIsCookieModalOpen(true);
}}
@@ -873,8 +873,8 @@ const Debug = () => {
</Button>
<Button
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.requestAddCertificatesClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestAddCertificatesClicked,
});
setCertificatesModalOpen(true);
}}
@@ -901,8 +901,8 @@ const Debug = () => {
setFilter(value);
if (value.trim() !== '') {
window.main.trackSegmentEvent({
event: SegmentEvent.filterCreatedRequests,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.filterCreatedRequests,
});
}
}}
@@ -923,8 +923,8 @@ const Debug = () => {
selectedKey={sortOrder}
onSelectionChange={order => {
if (order) {
window.main.trackSegmentEvent({
event: SegmentEvent.requestListSortClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestListSortClicked,
});
setSearchParams({
...Object.fromEntries(searchParams.entries()),
@@ -976,8 +976,8 @@ const Debug = () => {
defaultSelected={allExpanded}
onChange={() => {
setAllExpanded(!allExpanded);
window.main.trackSegmentEvent({
event: SegmentEvent.requestListExpandCollapseClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestListExpandCollapseClicked,
});
toggleExpandAllFetcher.submit({
organizationId,

View File

@@ -1,7 +1,7 @@
import { href } from 'react-router';
import { EnvironmentType, services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -22,8 +22,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
isPrivate,
});
window.main.trackSegmentEvent({
event: SegmentEvent.environmentCreate,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.environmentCreate,
properties: { type: isPrivate ? 'private' : 'global' },
});

View File

@@ -2,8 +2,8 @@ import { href } from 'react-router';
import { database } from '~/common/database';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { getSyncItems, remoteCompareCache, vcsSegmentEventProperties } from '~/ui/sync-utils';
import { AnalyticsEvent } from '~/ui/analytics';
import { getSyncItems, remoteCompareCache, vcsEventProperties } from '~/ui/sync-utils';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -24,9 +24,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
projectId: project._id,
});
window.main.trackSegmentEvent({
event: SegmentEvent.vcsAction,
properties: vcsSegmentEventProperties('remote', 'pull'),
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.vcsAction,
properties: vcsEventProperties('remote', 'pull'),
});
// This is to synchronize the local database with the branch changes
await database.batchModifyDocs(delta);
@@ -38,9 +38,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error while pulling from remote.';
window.main.trackSegmentEvent({
event: SegmentEvent.vcsAction,
properties: vcsSegmentEventProperties('remote', 'pull', errorMessage),
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.vcsAction,
properties: vcsEventProperties('remote', 'pull', errorMessage),
});
return {

View File

@@ -1,8 +1,8 @@
import { href } from 'react-router';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { remoteCompareCache, vcsSegmentEventProperties } from '~/ui/sync-utils';
import { AnalyticsEvent } from '~/ui/analytics';
import { remoteCompareCache, vcsEventProperties } from '~/ui/sync-utils';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -21,9 +21,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
teamProjectId: project.remoteId,
});
window.main.trackSegmentEvent({
event: SegmentEvent.vcsAction,
properties: vcsSegmentEventProperties('remote', 'push'),
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.vcsAction,
properties: vcsEventProperties('remote', 'push'),
});
delete remoteCompareCache[workspaceId];
@@ -34,9 +34,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error while pushing to remote.';
window.main.trackSegmentEvent({
event: SegmentEvent.vcsAction,
properties: vcsSegmentEventProperties('remote', 'push', errorMessage),
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.vcsAction,
properties: vcsEventProperties('remote', 'push', errorMessage),
});
return {

View File

@@ -2,7 +2,7 @@ import { href, redirect } from 'react-router';
import { getMockServiceBinURL } from '~/common/constants';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -62,8 +62,8 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
});
}
window.main.trackSegmentEvent({
event: SegmentEvent.generateCollectionFromMock,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.generateCollectionFromMock,
properties: {
count_requests: mockRoutes.length,
},

View File

@@ -1,7 +1,7 @@
import { href, redirect } from 'react-router';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -16,8 +16,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
await services.mockRoute.remove(mockRoute);
window.main.trackSegmentEvent({
event: SegmentEvent.mockRouteDelete,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockRouteDelete,
});
if (isSelected) {

View File

@@ -22,7 +22,7 @@ import { models, services } from '~/insomnia-data';
import { useRootLoaderData } from '~/root';
import { useRequestNewMockSendActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.new-mock-send';
import { useMockRouteUpdateActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.mock-server.mock-route.$mockRouteId.update';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { CodeEditor } from '~/ui/components/.client/codemirror/code-editor';
import { Dropdown, DropdownItem, ItemContent } from '~/ui/components/base/dropdown';
import { MockResponseHeadersEditor } from '~/ui/components/editors/mock-response-headers-editor';
@@ -304,8 +304,8 @@ export const MockRouteRoute = () => {
noLint={mockRoute.body?.includes('{{') && mockRoute.body?.includes('}}')}
updateFilter={filter => {
if (filter) {
window.main.trackSegmentEvent({
event: SegmentEvent.filterCreatedResponseBody,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.filterCreatedResponseBody,
});
}
}}

View File

@@ -2,7 +2,7 @@ import { href } from 'react-router';
import type { MockRoute } from '~/insomnia-data';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -47,8 +47,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
await services.mockRoute.update(mockRoute, patch);
window.main.trackSegmentEvent({
event: SegmentEvent.mockRouteEdit,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockRouteEdit,
});
return null;

View File

@@ -2,7 +2,7 @@ import { href, redirect } from 'react-router';
import type { MockRoute } from '~/insomnia-data';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -57,8 +57,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
delete patch.mockServerName;
const mockRoute = await services.mockRoute.create({ ...patch, parentId: newMockServer._id });
window.main.trackSegmentEvent({
event: SegmentEvent.mockRouteCreate,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockRouteCreate,
});
return redirect(
@@ -78,8 +78,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
invariant(mockServer, 'Mock server not found');
const mockRoute = await services.mockRoute.create(patch);
window.main.trackSegmentEvent({
event: SegmentEvent.mockRouteCreate,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockRouteCreate,
properties: {
source: 'from_response',
},

View File

@@ -3,7 +3,7 @@ import { href, redirect } from 'react-router';
import { importResourcesToWorkspace, scanResources } from '~/common/import';
import { models, services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -53,8 +53,8 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
workspaceId,
});
window.main.trackSegmentEvent({
event: SegmentEvent.generateCollection,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.generateCollection,
properties: {
count_requests: scannedResources.map(r => r.requests?.length ?? 0).reduce((a, b) => a + b, 0),
},

View File

@@ -34,7 +34,7 @@ import { useWorkspaceLoaderData } from '~/routes/organization.$organizationId.pr
import { useSpecGenerateRequestCollectionActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.generate-request-collection';
import { useSpecUpdateActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.update';
import { useStorageRulesLoaderFetcher } from '~/routes/organization.$organizationId.storage-rules';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { CodeEditor, type CodeEditorHandle } from '~/ui/components/.client/codemirror/code-editor';
import { DesignEmptyState } from '~/ui/components/design-empty-state';
import { DocumentTab } from '~/ui/components/document-tab';
@@ -404,8 +404,8 @@ const Component = ({ params }: Route.ComponentProps) => {
name: 'Toggle preview',
icon: <Icon className="w-3" icon={isSpecPaneOpen ? 'eye' : 'eye-slash'} />,
action: () => {
window.main.trackSegmentEvent({
event: SegmentEvent.designerPreviewToggled,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.designerPreviewToggled,
properties: {
status: !isSpecPaneOpen ? 'open' : 'collapsed',
},
@@ -542,8 +542,8 @@ const Component = ({ params }: Route.ComponentProps) => {
{isGenerateMockServersWithAIEnabled && (
<Button
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.designerGenerateMockClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.designerGenerateMockClicked,
});
setNewMockServerModalOpen(true);
}}
@@ -560,8 +560,8 @@ const Component = ({ params }: Route.ComponentProps) => {
className="flex h-full items-center justify-center gap-2 rounded-xs px-2 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
onChange={value => {
setIsSpecPaneOpen(value);
window.main.trackSegmentEvent({
event: SegmentEvent.designerPreviewToggled,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.designerPreviewToggled,
properties: {
status: !value ? 'open' : 'collapsed',
},

View File

@@ -1,7 +1,7 @@
import { href, redirect } from 'react-router';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -16,7 +16,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
await services.unitTestSuite.remove(unitTestSuite);
window.main.trackSegmentEvent({ event: SegmentEvent.testSuiteDelete });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.testSuiteDelete });
return redirect(
href(`/organization/:organizationId/project/:projectId/workspace/:workspaceId/test`, {

View File

@@ -5,7 +5,7 @@ import { database } from '~/common/database';
import type { UnitTest } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { getSendRequestCallback } from '~/network/unit-test-feature';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -52,7 +52,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
results,
parentId: workspaceId,
});
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRunAll, properties: { organizationId, projectId } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRunAll, properties: { organizationId, projectId } });
return redirect(
href(
@@ -93,7 +93,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
results,
parentId: workspaceId,
});
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRunAll, properties: { organizationId, projectId } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRunAll, properties: { organizationId, projectId } });
return redirect(
href(

View File

@@ -3,7 +3,7 @@ import { href } from 'react-router';
import { database } from '~/common/database';
import type { UnitTest } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -18,7 +18,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
invariant(unitTest, 'Test not found');
await services.unitTest.remove(unitTest);
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestDelete });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestDelete });
return null;
}

View File

@@ -5,7 +5,7 @@ import { database } from '~/common/database';
import type { UnitTest } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { getSendRequestCallback } from '~/network/unit-test-feature';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -53,7 +53,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
results,
parentId: unitTest.parentId,
});
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRun, properties: { organizationId, projectId } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRun, properties: { organizationId, projectId } });
return redirect(
href(
@@ -94,7 +94,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
results,
parentId: unitTest.parentId,
});
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRun, properties: { organizationId, projectId } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRun, properties: { organizationId, projectId } });
return redirect(
href(

View File

@@ -1,7 +1,7 @@
import { href } from 'react-router';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -22,7 +22,7 @@ expect(response1.status).to.equal(200);`,
name,
});
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestCreate });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestCreate });
return null;
}

View File

@@ -1,7 +1,7 @@
import { href, redirect } from 'react-router';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -19,7 +19,7 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
name,
});
window.main.trackSegmentEvent({ event: SegmentEvent.testSuiteCreate });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.testSuiteCreate });
return redirect(
href('/organization/:organizationId/project/:projectId/workspace/:workspaceId/test/test-suite/:testSuiteId', {

View File

@@ -2,7 +2,7 @@ import { href, redirect } from 'react-router';
import type { Project, Workspace } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -49,8 +49,8 @@ async function deleteWorkspace(workspace: Workspace | null, project: Project | n
await deleteWorkspaceFromLocal(workspace);
if (workspace.scope === 'mock-server') {
window.main.trackSegmentEvent({
event: SegmentEvent.mockDelete,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockDelete,
});
}

View File

@@ -7,7 +7,7 @@ import type { MockRoute, MockServer, WorkspaceScope } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import type { MockRouteData } from '~/plugins/types';
import { safeToUseInsomniaFileNameWithExt } from '~/sync/git/insomnia-filename';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { showToast } from '~/ui/components/toast-notification';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -144,8 +144,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
description: '',
});
window.main.trackSegmentEvent({
event: SegmentEvent.mcpClientAdded,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mcpClientAdded,
});
}
@@ -168,20 +168,20 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
});
}
let event = SegmentEvent.documentCreate;
let event = AnalyticsEvent.documentCreate;
let environmentType: string | undefined;
if (models.workspace.isCollection(workspace)) {
event = SegmentEvent.collectionCreate;
event = AnalyticsEvent.collectionCreate;
} else if (models.workspace.isEnvironment(workspace)) {
event = SegmentEvent.environmentCreate;
event = AnalyticsEvent.environmentCreate;
const environment = await services.environment.getById(workspace._id);
environmentType = environment?.isPrivate ? 'private' : 'global';
} else if (scope === 'mcp') {
event = SegmentEvent.mcpClientWorkspaceCreate;
event = AnalyticsEvent.mcpClientWorkspaceCreate;
}
window.main.trackSegmentEvent({
window.main.trackAnalyticsEvent({
event: event,
...(environmentType && {
properties: {
@@ -212,7 +212,7 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
})
)._id;
window.main.trackSegmentEvent({ event: SegmentEvent.requestCreated, properties: { requestType: 'HTTP' } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestCreated, properties: { requestType: 'HTTP' } });
return redirect(
href(`/organization/:organizationId/project/:projectId/workspace/:workspaceId/debug/request/:requestId`, {
@@ -351,8 +351,8 @@ async function createMockServer(
});
}
window.main.trackSegmentEvent({
event: SegmentEvent.mockCreate,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockCreate,
properties: {
provider: (modelConfig && modelConfig.backend) || '',
model: (modelConfig && modelConfig.model) || '',

View File

@@ -2,7 +2,7 @@ import { href } from 'react-router';
import { models, services } from '~/insomnia-data';
import { safeToUseInsomniaFileNameWithExt } from '~/sync/git/insomnia-filename';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -53,8 +53,8 @@ export async function clientAction({ request }: Route.ClientActionArgs) {
url: mockServerUrl,
});
window.main.trackSegmentEvent({
event: SegmentEvent.mockEdit,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockEdit,
});
}

View File

@@ -6,7 +6,7 @@ import { isNotNullOrUndefined } from '~/common/misc';
import { projectLock } from '~/common/project';
import type { Project } from '~/insomnia-data';
import { models, services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { showToast } from '~/ui/components/toast-notification';
import { invariant } from '~/utils/invariant';
import { createFetcherSubmitHook } from '~/utils/router';
@@ -141,8 +141,8 @@ export const createProject = async (organizationId: string, newProjectData: Crea
}
}
window.main.trackSegmentEvent({
event: SegmentEvent.projectCreated,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.projectCreated,
properties: {
storage: newProjectData.storageType,
git_provider,

View File

@@ -21,7 +21,7 @@ import { useRootLoaderData } from '~/root';
import { useWorkspaceLoaderData } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId';
import { useSyncOrganizationsAndProjectsActionFetcher } from '~/routes/organization.sync-organizations-and-projects';
import { useUntrackedProjectsLoaderFetcher } from '~/routes/untracked-projects';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { getLoginUrl } from '~/ui/auth-session-provider.client';
import { CommandPalette } from '~/ui/components/command-palette';
import { GitHubStarsButton } from '~/ui/components/github-stars-button';
@@ -417,8 +417,8 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
className="h-[10px] w-[10px] grow-0 gap-2 text-xs text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset"
onChange={value => {
setIsOrganizationSidebarOpen(value);
window.main.trackSegmentEvent({
event: SegmentEvent.statusbarLeftbarToggled,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.statusbarLeftbarToggled,
properties: {
status: value ? 'open' : 'collapsed',
},
@@ -461,8 +461,8 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
className="h-[10px] w-[10px] grow-0 rotate-90 gap-2 text-xs text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset"
onChange={flag => {
setIsMinimal(!flag);
window.main.trackSegmentEvent({
event: SegmentEvent.statusbarTopbarToggled,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.statusbarTopbarToggled,
properties: {
status: !flag ? 'minimal' : 'expanded',
},
@@ -525,8 +525,8 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
<Button
className="flex h-full items-center justify-center gap-2 px-4 py-1 text-xs text-(--color-warning) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.statusbarOrphanedProjectsClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.statusbarOrphanedProjectsClicked,
});
showModal(SettingsModal, { tab: 'data' });
}}
@@ -541,8 +541,8 @@ const Component = ({ loaderData }: Route.ComponentProps) => {
<Button
className="flex h-full items-center justify-center gap-2 px-4 py-1 text-xs text-(--color-warning) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.statusbarOrphanedProjectsClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.statusbarOrphanedProjectsClicked,
});
showModal(SettingsModal, { tab: 'data' });
}}

View File

@@ -1,6 +1,6 @@
import type { Settings } from '~/insomnia-data';
import { services } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { createFetcherSubmitHook } from '~/utils/router';
import type { Route } from './+types/settings.update';
@@ -8,7 +8,7 @@ import type { Route } from './+types/settings.update';
export async function clientAction({ request }: Route.ClientActionArgs) {
const patch = (await request.json()) as Partial<Settings>;
if ('enableAnalytics' in patch && !patch.enableAnalytics) {
window.main.trackSegmentEvent({ event: SegmentEvent.analyticsDisabled });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.analyticsDisabled });
}
await services.settings.patch(patch);
return null;

View File

@@ -1,150 +1,6 @@
export enum SegmentEvent {
appStarted = 'App Started',
analyticsDisabled = 'Analytics Disabled',
collectionCreate = 'Collection Created',
dataExport = 'Data Exported',
exportCompleted = 'Export Completed',
dataImport = 'Data Imported',
importStarted = 'Import Started',
importScanned = 'Import Scanned',
importCompleted = 'Import Completed',
importLoginRequired = 'Import Login Required',
importResumedAfterLogin = 'Import Resumed After Login',
importedRequestFirstSend = 'Imported Request First Send',
documentCreate = 'Document Created',
mockCreateModalOpened = 'Mock Server Create Modal Opened',
mockCreate = 'Mock Created',
mockEdit = 'Mock Server Edited',
mockDelete = 'Mock Server Deleted',
mockRouteCreate = 'Mock Route Created',
mockRouteEdit = 'Mock Route Edited',
mockRouteDelete = 'Mock Route Deleted',
generateCollection = 'Generated Collection',
generateCollectionFromMock = 'Generate Collection From Mock',
environmentCreate = 'Environment Created',
loginSuccess = 'Login Success',
inviteTrigger = 'Invite Triggered From App',
exportAllCollections = 'Exported All Collections',
kongConnected = 'Kong Connected',
kongSync = 'Kong Synced',
requestBodyTypeSelect = 'Request Body Type Selected',
requestCreated = 'Request Created',
requestExecuted = 'Request Executed',
requestEdit = 'Request Edited',
requestDeleted = 'Request Deleted',
requestRenamed = 'Request Renamed',
requestUrlCopied = 'Request URL Copied',
collectionRunExecute = 'Collection Run Executed',
projectLocalCreate = 'Local Project Created',
projectLocalDelete = 'Local Project Deleted',
selectScratchpad = 'Scratchpad Selected at Login',
syncConflictResolutionStart = 'Sync Conflict Resolution Started',
syncConflictResolutionCompleteMine = 'Sync Conflict Resolution Completed Mine',
syncConflictResolutionCompleteTheirs = 'Sync Conflict Resolution Completed Theirs',
testSuiteCreate = 'Test Suite Created',
testSuiteDelete = 'Test Suite Deleted',
unitTestCreate = 'Unit Test Created',
unitTestDelete = 'Unit Test Deleted',
unitTestRun = 'Ran Individual Unit Test',
unitTestRunAll = 'Ran All Unit Tests',
vcsSyncStart = 'VCS Sync Started',
vcsSyncComplete = 'VCS Sync Completed',
vcsAction = 'VCS Action Executed',
buttonClick = 'Button Clicked',
inviteMember = 'Invite Sent',
inviteResent = 'Invite Resent',
inviteRevoked = 'Invite Revoked',
projectCreated = 'Project Created',
projectUpdated = 'Project Updated',
exportStarted = 'Export Started',
exportRequestsChosen = 'Export Requests Chosen',
recommendCommitsGenerated = 'Recommend Commits Generated',
recommendCommitsSaved = 'Recommend Commits Saved',
recommendCommitsCancelled = 'Recommend Commits Cancelled',
recommendCommitsClicked = 'Recommend Commits Clicked',
mcpClientWorkspaceCreate = 'MCP Client Workspace Created',
mcpClientAdded = 'MCP Client Added',
inviteNotPermitted = 'Invite Not Permitted',
responseToMockClicked = 'Response To Mock Clicked',
gitSyncButtonClicked = 'Git Sync Button Clicked',
preferencesViewed = 'Preferences Viewed',
copyAsCurl = 'Copied As cURL',
themeChanged = 'Theme Changed',
generateCodeClicked = 'Generate Code Clicked',
generateCodeLanguageChanged = 'Generate Code Language Changed',
filterCreatedHomePage = 'Filter Created From Home Page',
filterCreatedProjects = 'Filter Created Projects',
filterCreatedRequests = 'Filter Created Requests',
filterCreatedResponseBody = 'Filter Created Response Body',
import type { AnalyticsEvent } from 'insomnia-analytics/events';
// INS-2120: Segment events to track common actions
homepageFiltered = 'homepage-filtered',
quickSearchOpenedByKeyboard = 'quick-search-opened-by-keyboard',
quickSearchOpenedByMouse = 'quick-search-opened-by-mouse',
statusbarLeftbarToggled = 'statusbar-leftbar-toggled',
statusbarTopbarToggled = 'statusbar-topbar-toggled',
statusbarOrphanedProjectsClicked = 'statusbar-orphaned-projects-clicked',
designerGenerateMockClicked = 'designer-generate-mock-clicked',
designerPreviewToggled = 'designer-preview-toggled',
requestEnvironmentClicked = 'request-environment-clicked',
requestAddCookiesClicked = 'request-add-cookies-clicked',
requestAddCertificatesClicked = 'request-add-certificates-clicked',
requestListSortClicked = 'request-list-sort-clicked',
requestListExpandCollapseClicked = 'request-list-expand-collapse-clicked',
requestParamsDescriptionToggled = 'request-params-description-toggled',
requestParamsImportFromURLClicked = 'request-params-import-from-URL-clicked',
requestParamsBulkEditToggled = 'request-params-bulk-edit-toggled',
responsePreviewJSONPathEntered = 'response-preview-jsonpath-entered',
requestBodyBeautifyClicked = 'request-body-beautify-clicked',
requestHeadersDescriptionToggled = 'request-headers-description-toggled',
requestHeadersBulkEditToggled = 'request-headers-bulk-edit-toggled',
requestScriptsPreScriptSnippetAdded = 'request-scripts-prescript-snippet-added',
requestScriptsPostScriptSnippetAdded = 'request-scripts-postscript-snippet-added',
responseHeadersCopyAllClicked = 'response-headers-copy-all-clicked',
responseCookiesManageCookiesClicked = 'response-cookies-manage-cookies-clicked',
requestOpenInNewTabClicked = 'request-open-in-new-tab-clicked',
requestListMenuPinClicked = 'request-list-menu-pin-clicked',
requestListMenuDuplicateClicked = 'request-list-menu-duplicate-clicked',
requestListMenuRenameClicked = 'request-list-menu-rename-clicked',
requestListMenuSettingsClicked = 'request-list-menu-settings-clicked',
requestSendMenuGenerateCodeClicked = 'request-send-menu-generate-code-clicked',
requestSendMenuSendAfterDelayClicked = 'request-send-menu-send-after-delay-clicked',
requestSendMenuRepeatAfterIntervalClicked = 'request-send-menu-repeat-after-interval-clicked',
requestSendMenuDownloadAfterSendClicked = 'request-send-menu-download-after-send-clicked',
requestSendMenuSendAndDownloadClicked = 'request-send-menu-send-and-download-clicked',
mcpListExpandCollapseClicked = 'mcp-list-expand-collapse-clicked',
mcpListFiltered = 'mcp-list-filtered',
mcpRequestParamsBeautifyClicked = 'mcp-request-params-beautify-clicked',
mcpRequestHeadersDescriptionToggled = 'mcp-request-headers-description-toggled',
mcpRequestRootsNotifyClicked = 'mcp-request-roots-notify-clicked',
mcpResponseHeadersCopyAllClicked = 'mcp-response-headers-copy-all-clicked',
kongKonnectPatValidated = 'kong-konnect-pat-validated',
kongKonnectSyncCompleted = 'kong-konnect-sync-completed',
emptyStateSendRequestClicked = 'empty-state-send-request-clicked',
emptyStateCreateDocumentClicked = 'empty-state-create-document-clicked',
}
type PushPull = 'push' | 'pull';
type VCSAction =
| PushPull
| `force_${PushPull}`
| 'create_branch'
| 'merge_branch'
| 'delete_branch'
| 'checkout_branch'
| 'commit'
| 'stage_all'
| 'stage'
| 'unstage_all'
| 'unstage'
| 'rollback'
| 'rollback_all'
| 'update'
| 'setup'
| 'clone';
export function vcsSegmentEventProperties(type: 'git', action: VCSAction, error?: string) {
return { type, action, error };
}
export { AnalyticsEvent } from 'insomnia-analytics/events';
function getTodayDateString(): string {
return new Date().toISOString().split('T')[0];
@@ -159,11 +15,11 @@ export function markTrackedToday(key: string): void {
localStorage.setItem(key, getTodayDateString());
}
export function trackOnceDaily(event: SegmentEvent, properties?: Record<string, unknown>): void {
export function trackOnceDaily(event: AnalyticsEvent, properties?: Record<string, unknown>): void {
if (hasTrackedToday(event)) {
return;
}
window.main.trackSegmentEvent({ event, properties });
window.main.trackAnalyticsEvent({ event, properties });
markTrackedToday(event);
}
@@ -185,8 +41,8 @@ export function readPendingImportAttribution(): ImportAttribution {
}
}
export function trackImportEvent(event: SegmentEvent, properties: Record<string, unknown> = {}): void {
window.main.trackSegmentEvent({
export function trackImportEvent(event: AnalyticsEvent, properties: Record<string, unknown> = {}): void {
window.main.trackAnalyticsEvent({
event,
properties: { ...readPendingImportAttribution(), ...properties },
});

View File

@@ -26,7 +26,7 @@ import { useRootLoaderData } from '~/root';
import { getTagDefinitions } from '~/templating/index';
import { type NunjucksParsedTag, type nunjucksTagContextMenuOptions } from '~/templating/types';
import { extractNunjucksTagFromCoords } from '~/templating/utils';
import { SegmentEvent, trackOnceDaily } from '~/ui/analytics';
import { AnalyticsEvent, trackOnceDaily } from '~/ui/analytics';
import { Icon } from '~/ui/components/icon';
import { createKeybindingsHandler, useDocBodyKeyboardShortcuts } from '~/ui/components/keydown-binder';
import { FilterHelpModal } from '~/ui/components/modals/filter-help-modal';
@@ -793,7 +793,7 @@ export const CodeEditor = memo(
defaultValue={filter || ''}
placeholder={mode?.includes('json') ? '$.store.books[*].author' : '/store/books/author'}
onFocus={() => {
trackOnceDaily(SegmentEvent.responsePreviewJSONPathEntered);
trackOnceDaily(AnalyticsEvent.responsePreviewJSONPathEntered);
}}
onKeyDown={createKeybindingsHandler({
Enter: () => {

View File

@@ -28,7 +28,7 @@ import { useCommandsLoaderFetcher } from '~/routes/commands';
import { useInsomniaSyncPullRemoteFileActionFetcher } from '~/routes/organization.$organizationId.insomnia-sync.pull-remote-file';
import { useSetActiveEnvironmentFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.environment.set-active';
import { useRemoteFilesLoaderFetcher } from '~/routes/remote-files';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { AvatarGroup } from '~/ui/components/avatar';
import { Icon } from '~/ui/components/icon';
import { useDocBodyKeyboardShortcuts } from '~/ui/components/keydown-binder';
@@ -49,8 +49,8 @@ export const CommandPalette = memo(function CommandPalette({ style = {} }: { sty
useDocBodyKeyboardShortcuts({
request_quickSwitch: () => {
setIsOpen(true);
window.main.trackSegmentEvent({
event: SegmentEvent.quickSearchOpenedByKeyboard,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.quickSearchOpenedByKeyboard,
});
},
});
@@ -62,8 +62,8 @@ export const CommandPalette = memo(function CommandPalette({ style = {} }: { sty
onOpenChange={isOpen => {
setIsOpen(isOpen);
if (isOpen) {
window.main.trackSegmentEvent({
event: SegmentEvent.quickSearchOpenedByMouse,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.quickSearchOpenedByMouse,
});
}
}}

View File

@@ -34,7 +34,7 @@ import {
useRequestLoaderData,
} from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
import { deconstructQueryStringToParams } from '../../../utils/url/querystring';
import { SegmentEvent } from '../../analytics';
import { AnalyticsEvent } from '../../analytics';
import { useRequestPatcher } from '../../hooks/use-request';
import { Icon } from '../icon';
import { showModal } from '../modals';
@@ -76,12 +76,12 @@ export const ContentTypeDropdown: FC = () => {
addCancel: true,
onConfirm: async () => {
patchRequest(requestId, { body: { mimeType } });
window.main.trackSegmentEvent({ event: SegmentEvent.requestBodyTypeSelect, properties: { type: mimeType } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestBodyTypeSelect, properties: { type: mimeType } });
},
});
} else {
patchRequest(requestId, { body: { mimeType } });
window.main.trackSegmentEvent({ event: SegmentEvent.requestBodyTypeSelect, properties: { type: mimeType } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestBodyTypeSelect, properties: { type: mimeType } });
}
};

View File

@@ -24,7 +24,7 @@ import { useGitProjectRepoFetcher } from '~/routes/git.repo';
import { useGitProjectStatusActionFetcher } from '~/routes/git.status';
import { useStorageRulesLoaderFetcher } from '~/routes/organization.$organizationId.storage-rules';
import { GitVCSOperationErrors } from '~/sync/git/git-vcs-operation-errors';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { ProjectModal } from '~/ui/components/modals/project-modal';
import { showSettingsModal } from '~/ui/components/modals/settings-modal';
import { useGitCredentials } from '~/ui/hooks/use-git-credentials';
@@ -664,8 +664,8 @@ export const GitProjectSyncDropdown: FC<Props> = ({ gitRepository, activeProject
<Button
onPress={() => {
setIsUpdateProjectModalOpen(true);
window.main.trackSegmentEvent({
event: SegmentEvent.gitSyncButtonClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.gitSyncButtonClicked,
});
}}
className="flex h-[25px] items-center justify-center gap-2 rounded-md border border-solid border-(--hl-md) bg-(--color-surprise) px-4 py-2 text-sm font-semibold text-(--color-font-surprise) ring-1 ring-transparent transition-all hover:bg-(--color-surprise)/80 focus:ring-(--hl-md) focus:ring-inset aria-pressed:opacity-80"

View File

@@ -16,7 +16,7 @@ import { useRootLoaderData } from '~/root';
import { useWorkspaceLoaderData } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId';
import { useRequestDuplicateActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.duplicate';
import { useRequestDeleteActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.delete';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { useTabNavigate } from '~/ui/hooks/use-insomnia-tab';
import { exportHarRequest } from '../../../common/har';
@@ -77,7 +77,7 @@ export const RequestActionsDropdown = ({
const tabNavigate = useTabNavigate();
const openInNewTab = async () => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestOpenInNewTabClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestOpenInNewTabClicked });
tabNavigate(
{
organization: organizationId,
@@ -101,7 +101,7 @@ export const RequestActionsDropdown = ({
if (!request) {
return;
}
window.main.trackSegmentEvent({ event: SegmentEvent.requestListMenuDuplicateClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestListMenuDuplicateClicked });
showModal(PromptModal, {
title: 'Duplicate Request',
@@ -141,8 +141,8 @@ export const RequestActionsDropdown = ({
const generateCode = () => {
if (isRequest(request)) {
window.main.trackSegmentEvent({
event: SegmentEvent.generateCodeClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.generateCodeClicked,
});
showModal(GenerateCodeModal, { request });
@@ -160,8 +160,8 @@ export const RequestActionsDropdown = ({
window.clipboard.writeText(cmd);
}
window.main.trackSegmentEvent({
event: SegmentEvent.copyAsCurl,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.copyAsCurl,
});
} catch (err) {
showModal(AlertModal, {
@@ -172,7 +172,7 @@ export const RequestActionsDropdown = ({
};
const togglePin = () => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestListMenuPinClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestListMenuPinClicked });
patchRequestMeta(request._id, { pinned: !isPinned });
};
@@ -279,7 +279,7 @@ export const RequestActionsDropdown = ({
id: 'Rename',
name: 'Rename',
action: () => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestListMenuRenameClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestListMenuRenameClicked });
onRename();
},
icon: 'edit',
@@ -297,7 +297,7 @@ export const RequestActionsDropdown = ({
icon: 'gear',
hint: hotKeyRegistry.request_showSettings,
action: () => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestListMenuSettingsClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestListMenuSettingsClicked });
setIsSettingsModalOpen(true);
},
},

View File

@@ -21,7 +21,7 @@ import { getDocumentActions } from '../../../plugins';
import * as pluginApp from '../../../plugins/context/app';
import * as pluginData from '../../../plugins/context/data';
import * as pluginStore from '../../../plugins/context/store';
import { SegmentEvent } from '../../analytics';
import { AnalyticsEvent } from '../../analytics';
import { useLoadingRecord } from '../../hooks/use-loading-record';
import { Dropdown, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
import { Icon } from '../icon';
@@ -178,8 +178,8 @@ export const WorkspaceCardDropdown: FC<Props> = props => {
label="Import"
icon="file-import"
onClick={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.importStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.importStarted,
properties: {
source: `${workspace.scope}-list`,
},
@@ -195,8 +195,8 @@ export const WorkspaceCardDropdown: FC<Props> = props => {
label="Export"
icon="file-export"
onClick={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.exportStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.exportStarted,
properties: {
source: `${workspace.scope}-list`,
},

View File

@@ -42,7 +42,7 @@ import * as pluginStore from '../../../plugins/context/store';
import { useWorkspaceLoaderData } from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId';
import { useMockServerGenerateRequestCollectionActionFetcher } from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.mock-server.generate-request-collection';
import { invariant } from '../../../utils/invariant';
import { SegmentEvent } from '../../analytics';
import { AnalyticsEvent } from '../../analytics';
import { DropdownHint } from '../base/dropdown/dropdown-hint';
import { Icon } from '../icon';
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
@@ -142,8 +142,8 @@ export const WorkspaceDropdown: FC<{}> = () => {
name: 'Import',
icon: <Icon icon="file-import" />,
action: () => {
window.main.trackSegmentEvent({
event: SegmentEvent.importStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.importStarted,
properties: {
source: `scratchpad-${activeWorkspace.scope}-menu`,
},
@@ -157,8 +157,8 @@ export const WorkspaceDropdown: FC<{}> = () => {
name: 'Export',
icon: <Icon icon="file-export" />,
action: () => {
window.main.trackSegmentEvent({
event: SegmentEvent.exportStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.exportStarted,
properties: {
source: `scratchpad-${activeWorkspace.scope}-menu`,
},
@@ -203,8 +203,8 @@ export const WorkspaceDropdown: FC<{}> = () => {
name: 'From File',
icon: <Icon icon="file-import" />,
action: () => {
window.main.trackSegmentEvent({
event: SegmentEvent.importStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.importStarted,
properties: {
source: `${activeWorkspace.scope}-menu`,
},
@@ -271,8 +271,8 @@ export const WorkspaceDropdown: FC<{}> = () => {
name: 'Export',
icon: <Icon icon="file-export" />,
action: () => {
window.main.trackSegmentEvent({
event: SegmentEvent.exportStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.exportStarted,
properties: {
source: `${activeWorkspace.scope}-menu`,
},

View File

@@ -2,7 +2,7 @@ import React, { type FC, Fragment } from 'react';
import { CodeEditor } from '~/ui/components/.client/codemirror/code-editor';
import { SegmentEvent } from '../../../../ui/analytics';
import { AnalyticsEvent } from '../../../../ui/analytics';
interface Props {
onChange: (value: string) => void;
@@ -25,7 +25,7 @@ export const RawEditor: FC<Props> = ({ className, content, contentType, onChange
mode={contentType}
placeholder="..."
onPrettify={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestBodyBeautifyClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestBodyBeautifyClicked });
}}
/>
</Fragment>

View File

@@ -3,7 +3,7 @@ import { Link } from 'react-aria-components';
import * as reactUse from 'react-use';
import { getGitHubRestApiUrl } from '../../common/constants';
import { SegmentEvent } from '../analytics';
import { AnalyticsEvent } from '../analytics';
import { Icon } from './icon';
const LOCALSTORAGE_GITHUB_STARS_KEY = 'insomnia:github-stars';
@@ -50,8 +50,8 @@ export const GitHubStarsButton = () => {
});
const starClick = useCallback(() => {
window.main.trackSegmentEvent({
event: SegmentEvent.buttonClick,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.buttonClick,
properties: {
type: 'GitHub stars',
action: 'clicked star',
@@ -60,8 +60,8 @@ export const GitHubStarsButton = () => {
}, []);
const counterClick = useCallback(() => {
window.main.trackSegmentEvent({
event: SegmentEvent.buttonClick,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.buttonClick,
properties: {
type: 'GitHub stars',
action: 'clicked stargazers',

View File

@@ -5,7 +5,7 @@ import { Button, Heading, Link, Radio, RadioGroup } from 'react-aria-components'
import { getCurrentSessionId } from '~/account/session';
import { Modal } from '~/basic-components/modal';
import { getAppWebsiteBaseURL } from '~/common/constants';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { Tooltip } from '~/ui/components/tooltip';
import { Icon } from './icon';
@@ -109,8 +109,8 @@ const MissingSomeoneModal = ({ isOpen, onClose }: any) => {
const [reason, setReason] = useState<string | null>(null);
const handleClose = () => {
if (reason) {
window.main.trackSegmentEvent({
event: SegmentEvent.inviteNotPermitted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.inviteNotPermitted,
properties: {
collaboration_type: reason,
},

View File

@@ -36,7 +36,7 @@ import {
type McpRequestLoaderData,
useRequestLoaderData,
} from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
import { SegmentEvent, trackOnceDaily } from '~/ui/analytics';
import { AnalyticsEvent, trackOnceDaily } from '~/ui/analytics';
import { McpActionsDropdown } from '~/ui/components/dropdowns/mcp-actions-dropdown';
import { WorkspaceDropdown } from '~/ui/components/dropdowns/workspace-dropdown';
import { WorkspaceSyncDropdown } from '~/ui/components/dropdowns/workspace-sync-dropdown';
@@ -446,7 +446,7 @@ export const McpPane = () => {
onChange={value => {
setFilter(value);
if (value) {
trackOnceDaily(SegmentEvent.mcpListFiltered);
trackOnceDaily(AnalyticsEvent.mcpListFiltered);
}
}}
>
@@ -472,7 +472,7 @@ export const McpPane = () => {
setCollapsedPrimitives(['tools', 'resources', 'prompts']);
}
setAllExpanded(newState);
window.main.trackSegmentEvent({ event: SegmentEvent.mcpListExpandCollapseClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.mcpListExpandCollapseClicked });
}}
className="flex aspect-square h-full items-center justify-center rounded-xs text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset"
>

View File

@@ -20,7 +20,7 @@ import {
type McpRequestLoaderData,
useRequestLoaderData,
} from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
import { SegmentEvent } from '../../../ui/analytics';
import { AnalyticsEvent } from '../../../ui/analytics';
import { useRequestPatcher, useRequestPayloadPatcher } from '../../hooks/use-request';
import { CodeEditor, type CodeEditorHandle } from '../.client/codemirror/code-editor';
import { AuthWrapper } from '../editors/auth/auth-wrapper';
@@ -373,7 +373,7 @@ export const McpRequestPane: FC<Props> = ({
mode="json"
placeholder=""
onPrettify={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.mcpRequestParamsBeautifyClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.mcpRequestParamsBeautifyClicked });
}}
/>
</div>
@@ -404,7 +404,7 @@ export const McpRequestPane: FC<Props> = ({
isDisabled={!isDisconnected}
requestType="McpRequest"
onDescriptionToggle={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.mcpRequestHeadersDescriptionToggled });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.mcpRequestHeadersDescriptionToggled });
}}
/>
</TabPanel>

View File

@@ -4,7 +4,7 @@ import { Button, Heading, ListBox, ListBoxItem, Toolbar } from 'react-aria-compo
import type { McpRequest } from '~/insomnia-data';
import type { McpReadyState } from '~/main/mcp/types';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { PromptButton } from '~/ui/components/base/prompt-button';
import { useRequestPatcher } from '~/ui/hooks/use-request';
@@ -54,7 +54,7 @@ export const McpRootsPanel = ({ request, readyState }: McpRootsPanelProps) => {
className="rounded-sm bg-(--color-surprise) px-(--padding-md) text-center text-(--color-font-surprise)"
onClick={() => {
window.main.mcp.notification.rootListChange({ requestId });
window.main.trackSegmentEvent({ event: SegmentEvent.mcpRequestRootsNotifyClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.mcpRequestRootsNotifyClicked });
}}
isDisabled={!isConnected}
>

View File

@@ -11,7 +11,7 @@ import {
useWorkspaceLoaderFetcher,
type WorkspaceLoaderData,
} from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId';
import { SegmentEvent } from '../../analytics';
import { AnalyticsEvent } from '../../analytics';
import { Icon } from '../icon';
import { getMethodShortHand } from '../tags/method-tag';
@@ -358,8 +358,8 @@ export const ExportRequestsModal = ({
<Button
onPress={() => {
if (state?.treeRoot) {
window.main.trackSegmentEvent({
event: SegmentEvent.exportRequestsChosen,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.exportRequestsChosen,
properties: {
totalRequests: state.treeRoot.totalRequests,
exported_requests: state.treeRoot.selectedRequests,

View File

@@ -3,7 +3,7 @@ import { forwardRef, useCallback, useImperativeHandle, useRef, useState } from '
import { Button } from 'react-aria-components';
import type { Request } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { CodeEditor, type CodeEditorHandle } from '~/ui/components/.client/codemirror/code-editor';
import { exportHarWithRequest } from '../../../common/har';
@@ -94,8 +94,8 @@ export const GenerateCodeModal = forwardRef<GenerateCodeModalHandle, Props>((pro
setSnippet(cmd);
}
window.main.trackSegmentEvent({
event: SegmentEvent.generateCodeLanguageChanged,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.generateCodeLanguageChanged,
properties: {
language: target?.title,
},

View File

@@ -50,7 +50,7 @@ import {
import type { GitFileType } from '~/sync/git/git-vcs';
import { GitVCSOperationErrors } from '~/sync/git/git-vcs-operation-errors';
import type { GitProviderOption } from '~/sync/git/providers/types';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { Badge } from '~/ui/components/base/badge';
import { GitOauthAuthBanner } from '~/ui/components/git/git-oauth-auth-banner';
import { isGitRepoLoadAuthHttp40Error } from '~/ui/components/git/git-oauth-auth-utils';
@@ -395,8 +395,8 @@ const GeneratedCommitsForm: FC<GeneratedCommitsFormProps> = ({
}))
.filter(commit => commit.id !== DO_NOT_COMMIT_ID && commit.files.length > 0);
window.main.trackSegmentEvent({
event: SegmentEvent.recommendCommitsSaved,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.recommendCommitsSaved,
properties: {
group_count: commits.length,
file_excluded_count: commitsSections.getItem(DO_NOT_COMMIT_ID)?.value?.files?.length || 0,
@@ -1303,12 +1303,12 @@ const OriginalGitProjectStagingModal: FC<
const handleGenerateCommits = React.useCallback(() => {
if (commitGenerationCompleted) {
window.main.trackSegmentEvent({ event: SegmentEvent.recommendCommitsCancelled });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.recommendCommitsCancelled });
setCommitGenerationKey(commitGenerationKey + 1);
return;
}
window.main.trackSegmentEvent({ event: SegmentEvent.recommendCommitsClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.recommendCommitsClicked });
generateCommitsFetcher.submit({
projectId,
});

View File

@@ -22,10 +22,10 @@ import {
} from '../../../../common/import';
import { invariant } from '../../../../utils/invariant';
import {
AnalyticsEvent,
importAttributionKey,
PENDING_IMPORT_ATTRIBUTION_KEY,
readPendingImportAttribution,
SegmentEvent,
trackImportEvent,
} from '../../../analytics';
import { Modal, type ModalHandle, type ModalProps } from '../../base/modal';
@@ -275,7 +275,7 @@ export const ImportModal: FC<ImportModalProps> = ({
// Track the import completion event, redirect to the new workspace and close the modal
useEffect(() => {
if (importFetcher?.data?.done === true && scanResourcesFetcherData?.length) {
trackImportEvent(SegmentEvent.importCompleted, {
trackImportEvent(AnalyticsEvent.importCompleted, {
workspaces: scanResourcesFetcherData.map(scanResult => scanResult.workspaces?.length || 0),
requests: scanResourcesFetcherData.map(scanResult => scanResult.requests?.length || 0),
});
@@ -405,7 +405,7 @@ export const ImportModal: FC<ImportModalProps> = ({
.filter(({ errors }) => errors.length === 0)
.forEach(scanResult => {
const type = scanResult.type?.id ?? 'unknown';
trackImportEvent(SegmentEvent.dataImport, { 'data-import-type': type });
trackImportEvent(AnalyticsEvent.dataImport, { 'data-import-type': type });
});
}}
/>

View File

@@ -21,7 +21,7 @@ import { models } from '~/insomnia-data';
import { useRootLoaderData } from '~/root';
import { useOrganizationLoaderData } from '~/routes/organization';
import { useCollaboratorsSearchLoaderFetcher } from '~/routes/organization.$organizationId.collaborators-search';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { Icon } from '~/ui/components/icon';
import { useIsLightTheme } from '~/ui/hooks/theme';
@@ -380,8 +380,8 @@ export const InviteForm = ({
})
.then(
() => {
window.main.trackSegmentEvent({
event: SegmentEvent.inviteMember,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.inviteMember,
properties: {
numberOfInvites: emailsToInvite.length,
numberOfTeams: groupsToInvite.length,

View File

@@ -37,7 +37,7 @@ import { useInviteFetcher } from '~/routes/organization.$organizationId.collabor
import { useReinviteFetcher } from '~/routes/organization.$organizationId.collaborators.invites.$invitationId.reinvite';
import { useCollaboratorsCheckSeatsLoaderFetcher } from '~/routes/organization.$organizationId.collaborators-check-seats';
import { useOrganizationMemberRolesActionFetcher } from '~/routes/organization.$organizationId.members.$userId.roles';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { PromptButton } from '~/ui/components/base/prompt-button';
import { Icon } from '~/ui/components/icon';
import { AlertModal } from '~/ui/components/modals/alert-modal';
@@ -408,7 +408,7 @@ const MemberListItem: FC<{
organizationId,
invitationId: member.metadata.invitationId,
});
window.main.trackSegmentEvent({ event: SegmentEvent.inviteResent });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.inviteResent });
}
}}
className="flex min-w-[75px] items-center gap-2 px-2 py-1 text-sm font-semibold text-(--color-font) transition-all aria-pressed:bg-(--hl-sm)"
@@ -519,7 +519,7 @@ const MemberListItem: FC<{
.then(() => {
onResetCurrentPage();
onRemoveMember();
window.main.trackSegmentEvent({ event: SegmentEvent.inviteRevoked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.inviteRevoked });
})
.catch(error => {
onError(error.message);
@@ -665,7 +665,7 @@ export const InviteModalContainer: FC<{
// track event when modal is opened
useEffect(() => {
if (isOpen) {
window.main.trackSegmentEvent({ event: SegmentEvent.inviteTrigger });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.inviteTrigger });
}
}, [isOpen]);

View File

@@ -29,7 +29,7 @@ import { Badge } from '~/ui/components/base/badge';
import { useAIFeatureStatus } from '~/ui/hooks/use-organization-features';
import { safeToUseInsomniaFileName, safeToUseInsomniaFileNameWithExt } from '../../../sync/git/insomnia-filename';
import { SegmentEvent } from '../../analytics';
import { AnalyticsEvent } from '../../analytics';
import { Icon } from '../icon';
const titleByScope: Record<WorkspaceScope, string> = {
@@ -142,8 +142,8 @@ export const NewWorkspaceModal = ({
useEffect(() => {
if (isOpen && scope === models.workspace.WorkspaceScopeKeys.mockServer) {
window.main.trackSegmentEvent({
event: SegmentEvent.mockCreateModalOpened,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.mockCreateModalOpened,
});
}
}, [isOpen, scope]);

View File

@@ -7,7 +7,7 @@ import { AI_PLUGIN_NAME, isKonnectSyncEnabled } from '~/common/constants';
import { models } from '~/insomnia-data';
import { getBundlePlugins } from '~/plugins';
import { useRootLoaderData } from '~/root';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { AISettings } from '~/ui/components/settings/ai-settings';
import { CredentialsSettings } from '~/ui/components/settings/credentials';
import { KonnectSettings } from '~/ui/components/settings/konnect-settings';
@@ -105,8 +105,8 @@ export const SettingsModal = forwardRef<SettingsModalHandle, ModalProps>((props,
onSelectionChange={key => {
setDefaultTabKey(key.toString());
window.main.trackSegmentEvent({
event: SegmentEvent.preferencesViewed,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.preferencesViewed,
properties: { tab: key.toString() },
});
}}
@@ -260,8 +260,8 @@ SettingsModal.displayName = 'SettingsModal';
export const showSettingsModal = (options?: { tab?: SettingsModalTabKey }) => {
showModal(SettingsModal, options);
window.main.trackSegmentEvent({
event: SegmentEvent.preferencesViewed,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.preferencesViewed,
properties: { tab: options?.tab || 'general' },
});
};

View File

@@ -20,7 +20,7 @@ import { showModal } from '~/ui/components/modals';
import { AlertModal } from '~/ui/components/modals/alert-modal';
import { type MergeConflict, RESOLUTION_SOURCE } from '../../../sync/types';
import { SegmentEvent } from '../../analytics';
import { AnalyticsEvent } from '../../analytics';
import { MergeEditor } from '../.client/codemirror/merge-editor';
import { DiffEditor } from '../diff-view-editor';
import { Icon } from '../icon';
@@ -137,8 +137,8 @@ export const SyncMergeModal = forwardRef<SyncMergeModalHandle>((_, ref) => {
onCancelUnresolvedRef.current = onCancelUnresolved;
setIsOpen(true);
window.main.trackSegmentEvent({
event: SegmentEvent.syncConflictResolutionStart,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.syncConflictResolutionStart,
});
},
}),
@@ -309,14 +309,14 @@ export const SyncMergeModal = forwardRef<SyncMergeModalHandle>((_, ref) => {
);
// if at least one conflict.choose is theirsBlob, track conflict resolution complete as theirs
if (conflicts?.some(conflict => conflict.choose === conflict.theirsBlob)) {
window.main.trackSegmentEvent({
event: SegmentEvent.syncConflictResolutionCompleteTheirs,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.syncConflictResolutionCompleteTheirs,
});
}
// if at least one conflict.choose is mine, track conflict resolution complete as mine
if (conflicts?.some(conflict => conflict.choose === conflict.mineBlob)) {
window.main.trackSegmentEvent({
event: SegmentEvent.syncConflictResolutionCompleteMine,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.syncConflictResolutionCompleteMine,
});
}

View File

@@ -15,7 +15,7 @@ import {
type RequestLoaderData,
useRequestLoaderData,
} from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
import { SegmentEvent } from '../../../ui/analytics';
import { AnalyticsEvent } from '../../../ui/analytics';
import { deconstructQueryStringToParams, extractQueryStringFromUrl } from '../../../utils/url/querystring';
import { useRequestPatcher, useSettingsPatcher } from '../../hooks/use-request';
import { useGitVCSVersion } from '../../hooks/use-vcs-version';
@@ -206,7 +206,7 @@ export const RequestPane: FC<Props> = ({ environmentId, settings, onPaste }) =>
isDisabled={!urlHasQueryParameters}
onPress={() => {
handleImportQueryFromUrl();
window.main.trackSegmentEvent({ event: SegmentEvent.requestParamsImportFromURLClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestParamsImportFromURLClicked });
}}
className="flex h-full w-[14ch] shrink-0 items-center justify-start gap-2 rounded-xs px-2 py-1 text-sm text-(--color-font) ring-1 ring-transparent transition-colors hover:bg-(--hl-xs) focus:bg-(--hl-sm) focus:ring-(--hl-md) focus:ring-inset aria-selected:bg-(--hl-xs) aria-selected:hover:bg-(--hl-sm) aria-selected:focus:bg-(--hl-sm) data-pressed:bg-(--hl-sm)"
>
@@ -217,7 +217,7 @@ export const RequestPane: FC<Props> = ({ environmentId, settings, onPaste }) =>
patchSettings({
useBulkParametersEditor: isSelected,
});
window.main.trackSegmentEvent({ event: SegmentEvent.requestParamsBulkEditToggled });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestParamsBulkEditToggled });
}}
isSelected={settings.useBulkParametersEditor}
className="flex h-full w-[14ch] shrink-0 items-center justify-start gap-2 rounded-xs px-2 py-1 text-sm text-(--color-font) ring-1 ring-transparent transition-colors hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset"
@@ -239,7 +239,7 @@ export const RequestPane: FC<Props> = ({ environmentId, settings, onPaste }) =>
key={contentType}
bulk={settings.useBulkParametersEditor}
onDescriptionToggle={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestParamsDescriptionToggled });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestParamsDescriptionToggled });
}}
/>
</ErrorBoundary>
@@ -307,7 +307,7 @@ export const RequestPane: FC<Props> = ({ environmentId, settings, onPaste }) =>
headers={activeRequest.headers}
requestType="Request"
onDescriptionToggle={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestHeadersDescriptionToggled });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestHeadersDescriptionToggled });
}}
/>
</div>
@@ -320,7 +320,7 @@ export const RequestPane: FC<Props> = ({ environmentId, settings, onPaste }) =>
patchSettings({
useBulkHeaderEditor: !settings.useBulkHeaderEditor,
});
window.main.trackSegmentEvent({ event: SegmentEvent.requestHeadersBulkEditToggled });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestHeadersBulkEditToggled });
}}
>
{settings.useBulkHeaderEditor ? 'Regular Edit' : 'Bulk Edit'}
@@ -370,8 +370,8 @@ export const RequestPane: FC<Props> = ({ environmentId, settings, onPaste }) =>
onChange={preRequestScript => patchRequest(requestId, { preRequestScript })}
settings={settings}
onSnippetAdded={snippetName => {
window.main.trackSegmentEvent({
event: SegmentEvent.requestScriptsPreScriptSnippetAdded,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestScriptsPreScriptSnippetAdded,
properties: { snippetName },
});
}}
@@ -386,8 +386,8 @@ export const RequestPane: FC<Props> = ({ environmentId, settings, onPaste }) =>
onChange={afterResponseScript => patchRequest(requestId, { afterResponseScript })}
settings={settings}
onSnippetAdded={snippetName => {
window.main.trackSegmentEvent({
event: SegmentEvent.requestScriptsPostScriptSnippetAdded,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestScriptsPostScriptSnippetAdded,
properties: { snippetName },
});
}}

View File

@@ -4,7 +4,7 @@ import { Tab, TabList, TabPanel, Tabs, Toolbar } from 'react-aria-components';
import { services } from '~/insomnia-data';
import type { ResponseTimelineEntry } from '~/main/network/libcurl-promise';
import { useRootLoaderData } from '~/root';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { PREVIEW_MODE_SOURCE } from '../../../common/constants';
import { getSetCookieHeaders } from '../../../common/misc';
@@ -147,8 +147,8 @@ export const ResponsePane: FC<Props> = ({ activeRequestId }) => {
className="flex h-full w-full flex-1 flex-col"
onSelectionChange={key => {
if (key === 'mock-response') {
window.main.trackSegmentEvent({
event: SegmentEvent.responseToMockClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.responseToMockClicked,
properties: {
source: 'Response Pane Tab',
},
@@ -248,7 +248,7 @@ export const ResponsePane: FC<Props> = ({ activeRequestId }) => {
<ResponseHeadersViewer
headers={activeResponse.headers}
onCopyAll={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.responseHeadersCopyAllClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.responseHeadersCopyAllClicked });
}}
/>
</ErrorBoundary>

View File

@@ -1,7 +1,7 @@
import React, { type FC } from 'react';
import { Button } from 'react-aria-components';
import { SegmentEvent } from '../../analytics';
import { AnalyticsEvent } from '../../analytics';
import { Icon } from '../icon';
interface Props {
@@ -24,8 +24,8 @@ export const ProjectEmptyView: FC<Props> = ({
aria-label="Create request collection"
className="flex w-full max-w-[180px] flex-col items-center justify-center gap-(--padding-xs) rounded-md border border-solid border-(--hl-sm) px-12 py-8 text-(--font-size-sm) shadow-xs transition-all duration-100 hover:bg-(--color-bg) sm:gap-(--padding-sm)"
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.emptyStateSendRequestClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.emptyStateSendRequestClicked,
});
onCreateRequestCollectionWithRequest();
}}
@@ -37,8 +37,8 @@ export const ProjectEmptyView: FC<Props> = ({
aria-label="Create document"
className="flex w-full max-w-[180px] flex-col items-center justify-center gap-(--padding-xs) rounded-md border border-solid border-(--hl-sm) px-12 py-8 text-(--font-size-sm) shadow-xs transition-all duration-100 hover:bg-(--color-bg) sm:gap-(--padding-sm)"
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.emptyStateCreateDocumentClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.emptyStateCreateDocumentClicked,
});
onCreateDesignDocument();
}}
@@ -50,8 +50,8 @@ export const ProjectEmptyView: FC<Props> = ({
aria-label="Import"
className="flex w-full max-w-[180px] flex-col items-center justify-center gap-(--padding-xs) rounded-md border border-solid border-(--hl-sm) px-12 py-8 text-(--font-size-sm) shadow-xs transition-all duration-100 hover:bg-(--color-bg) sm:gap-(--padding-sm)"
onPress={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.importStarted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.importStarted,
properties: {
source: 'home-page',
},

View File

@@ -7,7 +7,7 @@ import * as reactUse from 'react-use';
import type { GitRepository, Project } from '~/insomnia-data';
import { models } from '~/insomnia-data';
import type { SyncResult } from '~/konnect/sync';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { useKonnectSync } from '../../hooks/use-konnect-sync';
import { AvatarGroup } from '../avatar';
@@ -270,7 +270,7 @@ export const ProjectListSidebar = ({
onChange={value => {
setProjectListFilter(value);
if (value.trim() !== '') {
window.main.trackSegmentEvent({ event: SegmentEvent.filterCreatedProjects });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.filterCreatedProjects });
}
}}
/>

View File

@@ -11,7 +11,7 @@ import type {
WebSocketRequest,
} from '~/insomnia-data';
import { models } from '~/insomnia-data';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { showSettingsModal } from '~/ui/components/modals/settings-modal';
import { database as db } from '../../common/database';
@@ -141,8 +141,8 @@ export const RenderedQueryString: FC<Props> = ({ request }) => {
message: `Your URL is quite long, so only the first ${MAX_URL_LENGTH} characters were copied.`,
});
} else {
window.main.trackSegmentEvent({
event: SegmentEvent.requestUrlCopied,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestUrlCopied,
});
}
}, [tooLong]);

View File

@@ -25,7 +25,7 @@ import {
type RequestLoaderData,
useRequestLoaderData,
} from '../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
import { SegmentEvent } from '../../ui/analytics';
import { AnalyticsEvent } from '../../ui/analytics';
import { tryToInterpolateRequestOrShowRenderErrorModal } from '../../utils/try-interpolate';
import { buildQueryStringFromParams, joinUrlAndQueryString } from '../../utils/url/querystring';
import { useInsomniaTabContext } from '../context/app/insomnia-tab-context';
@@ -374,7 +374,7 @@ export const RequestUrlBar = forwardRef<RequestUrlBarHandle, Props>(
icon="code"
label="Generate Client Code"
onClick={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestSendMenuGenerateCodeClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestSendMenuGenerateCodeClicked });
showModal(GenerateCodeModal, { request: activeRequest });
}}
/>
@@ -386,7 +386,7 @@ export const RequestUrlBar = forwardRef<RequestUrlBarHandle, Props>(
icon="clock-o"
label="Send After Delay"
onClick={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.requestSendMenuSendAfterDelayClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.requestSendMenuSendAfterDelayClicked });
showModal(PromptModal, {
inputType: 'decimal',
title: 'Send After Delay',
@@ -404,8 +404,8 @@ export const RequestUrlBar = forwardRef<RequestUrlBarHandle, Props>(
icon="repeat"
label="Repeat on Interval"
onClick={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.requestSendMenuRepeatAfterIntervalClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestSendMenuRepeatAfterIntervalClicked,
});
showModal(PromptModal, {
inputType: 'decimal',
@@ -436,8 +436,8 @@ export const RequestUrlBar = forwardRef<RequestUrlBarHandle, Props>(
icon="download"
label="Download After Send"
onClick={async () => {
window.main.trackSegmentEvent({
event: SegmentEvent.requestSendMenuDownloadAfterSendClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestSendMenuDownloadAfterSendClicked,
});
const { canceled, filePaths } = await window.dialog.showOpenDialog({
title: 'Select Download Location',
@@ -457,8 +457,8 @@ export const RequestUrlBar = forwardRef<RequestUrlBarHandle, Props>(
icon="download"
label="Send And Download"
onClick={() => {
window.main.trackSegmentEvent({
event: SegmentEvent.requestSendMenuSendAndDownloadClicked,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.requestSendMenuSendAndDownloadClicked,
});
sendOrConnect(true);
}}

View File

@@ -5,7 +5,7 @@ import { exportRequestsHAR, exportWorkspacesHAR } from 'insomnia/src/common/har'
import { getInsomniaV5DataExport } from 'insomnia/src/common/insomnia-v5';
import { isNotNullOrUndefined } from 'insomnia/src/common/misc';
import { strings } from 'insomnia/src/common/strings';
import { SegmentEvent } from 'insomnia/src/ui/analytics';
import { AnalyticsEvent } from 'insomnia/src/ui/analytics';
import { Icon } from 'insomnia/src/ui/components/icon';
import { showError, showModal } from 'insomnia/src/ui/components/modals';
import { AskModal } from 'insomnia/src/ui/components/modals/ask-modal';
@@ -212,7 +212,7 @@ export const exportProjectToFile = (activeProjectName: string, workspacesForActi
throw new Error(`selected export format "${selectedFormat}" is invalid`);
}
}
window.main.trackSegmentEvent({ event: SegmentEvent.exportCompleted });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.exportCompleted });
} catch (err) {
showError({
title: 'Export Failed',
@@ -240,8 +240,8 @@ export const exportMockServerToFile = async (workspace: Workspace) => {
includePrivateEnvironments: false,
});
await writeExportedFileToFileSystem(fileName, stringifiedExport);
window.main.trackSegmentEvent({
event: SegmentEvent.dataExport,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.dataExport,
properties: { type: 'yaml', scope: 'mock-server' },
});
} catch (err) {
@@ -282,8 +282,8 @@ export const exportGlobalEnvironmentToFile = async (workspace: Workspace) => {
includePrivateEnvironments: shouldExportPrivateEnvironments,
});
await writeExportedFileToFileSystem(fileName, stringifiedExport);
window.main.trackSegmentEvent({
event: SegmentEvent.dataExport,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.dataExport,
properties: { type: 'yaml', scope: 'environment' },
});
} catch (err) {
@@ -350,7 +350,7 @@ export const exportRequestsToFile = (workspaceId: string, requestIds: string[])
}
}
await writeExportedFileToFileSystem(fileName, stringifiedExport);
window.main.trackSegmentEvent({ event: SegmentEvent.dataExport, properties: { type: selectedFormat } });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.dataExport, properties: { type: selectedFormat } });
} catch (err) {
showError({
title: 'Export Failed',
@@ -378,8 +378,8 @@ export const exportMcpClientToFile = async (workspace: Workspace) => {
includePrivateEnvironments: false,
});
await writeExportedFileToFileSystem(fileName, stringifiedExport);
window.main.trackSegmentEvent({
event: SegmentEvent.dataExport,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.dataExport,
properties: { type: 'yaml', scope: 'mcp' },
});
} catch (err) {
@@ -733,8 +733,8 @@ export const ImportExport: FC<Props> = ({ hideSettingsModal, onModalChange }) =>
title: 'Export Complete',
message: 'All your data have been successfully exported',
});
window.main.trackSegmentEvent({
event: SegmentEvent.exportAllCollections,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.exportAllCollections,
});
}}
aria-label="Export all data"
@@ -802,8 +802,8 @@ export const ImportExport: FC<Props> = ({ hideSettingsModal, onModalChange }) =>
title: 'Export Complete',
message: 'All your data have been successfully exported',
});
window.main.trackSegmentEvent({
event: SegmentEvent.exportAllCollections,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.exportAllCollections,
});
}}
aria-label="Export all data"

View File

@@ -3,7 +3,7 @@ import { Button } from 'react-aria-components';
import { validatePat } from '~/konnect/api';
import { useRootLoaderData } from '~/root';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { useSettingsPatcher } from '../../hooks/use-request';
@@ -28,7 +28,7 @@ export const KonnectSettings = () => {
await window.main.secretStorage.setSecret('konnectPat', trimmed);
patchSettings({ hasKonnectPat: true });
setPat('');
window.main.trackSegmentEvent({ event: SegmentEvent.kongKonnectPatValidated });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.kongKonnectPatValidated });
} else {
setValidationError(result.error ?? 'PAT is invalid or could not connect to Konnect.');
}

View File

@@ -1,7 +1,7 @@
import React, { type FC, useState } from 'react';
import { Cookie } from 'tough-cookie';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { CookiesModal } from '~/ui/components/modals/cookies-modal';
interface Props {
@@ -64,7 +64,7 @@ export const ResponseCookiesViewer: FC<Props> = props => {
className="pull-right btn btn--clicky"
onClick={() => {
setIsCookieModalOpen(true);
window.main.trackSegmentEvent({ event: SegmentEvent.responseCookiesManageCookiesClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.responseCookiesManageCookiesClicked });
}}
>
Manage Cookies

View File

@@ -1,9 +1,29 @@
import { useEffect, useState } from 'react';
interface Props {
body: Buffer;
}
export const ResponsePDFViewer = (props: Props) => {
const url = `data:application/pdf;base64,${props.body.toString('base64')}`;
export const ResponsePDFViewer = ({ body }: Props) => {
const [url, setUrl] = useState<string>('');
return <webview data-testid="ResponsePDFView" src={url} />;
useEffect(() => {
const blob = new Blob([body], { type: 'application/pdf' });
const objectUrl = URL.createObjectURL(blob);
setUrl(objectUrl);
return () => URL.revokeObjectURL(objectUrl);
}, [body]);
if (!url) {
return null;
}
return (
<iframe
data-testid="ResponsePDFView"
src={url}
title="PDF response preview"
style={{ width: '100%', height: '100%', border: 0, backgroundColor: '#fff' }}
/>
);
};

View File

@@ -1,7 +1,7 @@
import iconv from 'iconv-lite';
import { Fragment, useCallback, useRef, useState } from 'react';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { CodeEditor, type CodeEditorHandle } from '~/ui/components/.client/codemirror/code-editor';
import {
@@ -248,8 +248,8 @@ export const ResponseViewer = ({
updateFilter?.(filter);
if (filter) {
window.main.trackSegmentEvent({
event: SegmentEvent.filterCreatedResponseBody,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.filterCreatedResponseBody,
});
}
}}
@@ -375,8 +375,8 @@ export const ResponseViewer = ({
updateFilter?.(filter);
if (filter) {
window.main.trackSegmentEvent({
event: SegmentEvent.filterCreatedResponseBody,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.filterCreatedResponseBody,
});
}
}}

View File

@@ -16,7 +16,7 @@ import type { ResponseTimelineEntry } from '../../../main/network/libcurl-promis
import type { SocketIOEvent } from '../../../main/network/socket-io';
import type { WebSocketEvent } from '../../../main/network/websocket';
import { useRequestLoaderData } from '../../../routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId';
import { SegmentEvent } from '../../../ui/analytics';
import { AnalyticsEvent } from '../../../ui/analytics';
import { deserializeNDJSON } from '../../../utils/ndjson';
import { useReadyState } from '../../hooks/use-ready-state';
import { useRealtimeConnectionEvents } from '../../hooks/use-realtime-connection-events';
@@ -415,7 +415,7 @@ const RealtimeActiveResponsePane: FC<RealtimeActiveResponsePaneProps & { readySt
<ResponseHeadersViewer
headers={response.headers}
onCopyAll={() => {
window.main.trackSegmentEvent({ event: SegmentEvent.mcpResponseHeadersCopyAllClicked });
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.mcpResponseHeadersCopyAllClicked });
}}
/>
</ErrorBoundary>

View File

@@ -3,7 +3,7 @@ import * as reactUse from 'react-use';
import type { ThemeSettings } from '~/insomnia-data';
import { useRootLoaderData } from '~/root';
import { SegmentEvent } from '~/ui/analytics';
import { AnalyticsEvent } from '~/ui/analytics';
import { type ColorScheme, getThemes } from '../../plugins';
import { applyColorScheme, getColorScheme, type PluginTheme } from '../../plugins/misc';
@@ -55,8 +55,8 @@ export const useThemes = () => {
// Activate the theme for the selected color scheme
const activate = useCallback(
async (themeName: string, colorScheme: ColorScheme) => {
window.main.trackSegmentEvent({
event: SegmentEvent.themeChanged,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.themeChanged,
properties: { themeName, colorScheme },
});

View File

@@ -3,7 +3,7 @@ import { useRevalidator } from 'react-router';
import type { SyncResult } from '../../konnect/sync';
import { syncKonnect } from '../../konnect/sync';
import { SegmentEvent } from '../analytics';
import { AnalyticsEvent } from '../analytics';
const REVALIDATE_DEBOUNCE_MS = 500;
@@ -63,8 +63,8 @@ export function useKonnectSync(): UseKonnectSyncResult {
error: !result.success && !cancelled ? (result.error ?? 'Sync failed') : null,
});
window.main.trackSegmentEvent({
event: SegmentEvent.kongKonnectSyncCompleted,
window.main.trackAnalyticsEvent({
event: AnalyticsEvent.kongKonnectSyncCompleted,
properties: {
success: result.success,
cancelled,

View File

@@ -37,7 +37,7 @@ type VCSAction =
| 'setup'
| 'clone';
export function vcsSegmentEventProperties(type: 'remote', action: VCSAction, error?: string) {
export function vcsEventProperties(type: 'remote', action: VCSAction, error?: string) {
return { type, action, error };
}