mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-30 19:12:57 -04:00
feat: integrate customer-io in-app messages (#9510)
This commit is contained in:
139
package-lock.json
generated
139
package-lock.json
generated
@@ -1901,6 +1901,57 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@customerio/cdp-analytics-browser": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@customerio/cdp-analytics-browser/-/cdp-analytics-browser-0.3.9.tgz",
|
||||
"integrity": "sha512-Hx2VwlqXXDejM0kVg1TJYp5nWuPC9yYflH0t0UJE/J7jEtDiaOSPdEkbHk39XmkVxkaKAgxAX5SdtuUUi5C61w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@customerio/cdp-analytics-core": "0.3.9",
|
||||
"@lukeed/uuid": "^2.0.0",
|
||||
"@segment/analytics.js-video-plugins": "^0.2.1",
|
||||
"@segment/facade": "^3.4.9",
|
||||
"customerio-gist-web": "3.18.0",
|
||||
"dset": "^3.1.2",
|
||||
"js-cookie": "3.0.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"spark-md5": "^3.0.1",
|
||||
"tslib": "^2.4.1",
|
||||
"unfetch": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@customerio/cdp-analytics-browser/node_modules/js-cookie": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz",
|
||||
"integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@customerio/cdp-analytics-browser/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/@customerio/cdp-analytics-core": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@customerio/cdp-analytics-core/-/cdp-analytics-core-0.3.9.tgz",
|
||||
"integrity": "sha512-AjMB48tTu8JN4NAgmbOoGAS1veE7AvLUyZD+qKXZLD/muWy6Vou4MEfXLVzhQP/cIbf3VBPGUxMnugjwZFc6Sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lukeed/uuid": "^2.0.0",
|
||||
"dset": "^3.1.2",
|
||||
"tslib": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@customerio/cdp-analytics-core/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/@dependents/detective-less": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-5.0.1.tgz",
|
||||
@@ -8617,6 +8668,48 @@
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/@segment/analytics.js-video-plugins": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@segment/analytics.js-video-plugins/-/analytics.js-video-plugins-0.2.1.tgz",
|
||||
"integrity": "sha512-lZwCyEXT4aaHBLNK433okEKdxGAuyrVmop4BpQqQSJuRz0DglPZgd9B/XjiiWs1UyOankg2aNYMN3VcS8t4eSQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"unfetch": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@segment/analytics.js-video-plugins/node_modules/unfetch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-3.1.2.tgz",
|
||||
"integrity": "sha512-L0qrK7ZeAudGiKYw6nzFjnJ2D5WHblUBwmHIqtPS6oKUd+Hcpk7/hKsSmcHsTlpd1TbTNsiRBUKRq3bHLNIqIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@segment/facade": {
|
||||
"version": "3.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@segment/facade/-/facade-3.4.10.tgz",
|
||||
"integrity": "sha512-xVQBbB/lNvk/u8+ey0kC/+g8pT3l0gCT8O2y9Z+StMMn3KAFAQ9w8xfgef67tJybktOKKU7pQGRPolRM1i1pdA==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@segment/isodate-traverse": "^1.1.1",
|
||||
"inherits": "^2.0.4",
|
||||
"new-date": "^1.0.3",
|
||||
"obj-case": "0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@segment/isodate": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@segment/isodate/-/isodate-1.0.3.tgz",
|
||||
"integrity": "sha512-BtanDuvJqnACFkeeYje7pWULVv8RgZaqKHWwGFnL/g/TH/CcZjkIVTfGDp/MAxmilYHUkrX70SqwnYSTNEaN7A==",
|
||||
"license": "SEE LICENSE IN LICENSE"
|
||||
},
|
||||
"node_modules/@segment/isodate-traverse": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@segment/isodate-traverse/-/isodate-traverse-1.1.1.tgz",
|
||||
"integrity": "sha512-+G6e1SgAUkcq0EDMi+SRLfT48TNlLPF3QnSgFGVs0V9F3o3fq/woQ2rHFlW20W0yy5NnCUH0QGU3Am2rZy/E3w==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@segment/isodate": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/browser-utils": {
|
||||
"version": "9.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.11.0.tgz",
|
||||
@@ -14826,6 +14919,24 @@
|
||||
"integrity": "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/customerio-gist-web": {
|
||||
"version": "3.18.0",
|
||||
"resolved": "https://registry.npmjs.org/customerio-gist-web/-/customerio-gist-web-3.18.0.tgz",
|
||||
"integrity": "sha512-3m48BGbiWxxoAYgHYPTBDwpOJ7q2byFEL5lhFfy5PjMujkz4Huqhzd/0lZxDNLEPALquX/HwOCxiWcvk+J0mvw==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/customerio-gist-web/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
@@ -23284,6 +23395,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/new-date": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/new-date/-/new-date-1.0.3.tgz",
|
||||
"integrity": "sha512-0fsVvQPbo2I18DT2zVHpezmeeNYV2JaJSrseiHLc17GNOxJzUdx5mvSigPu8LtIfZSij5i1wXnXFspEs2CD6hA==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@segment/isodate": "1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/nimma": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.3.tgz",
|
||||
@@ -24528,6 +24648,12 @@
|
||||
"integrity": "sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/obj-case": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/obj-case/-/obj-case-0.2.1.tgz",
|
||||
"integrity": "sha512-PquYBBTy+Y6Ob/O2574XHhDtHJlV1cJHMCgW+rDRc9J5hhmRelJB3k5dTK/3cVmFVtzvAKuENeuLpoyTzMzkOg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -27989,6 +28115,12 @@
|
||||
"deprecated": "Please use @jridgewell/sourcemap-codec instead",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/spark-md5": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz",
|
||||
"integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==",
|
||||
"license": "(WTFPL OR MIT)"
|
||||
},
|
||||
"node_modules/spdx-correct": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
|
||||
@@ -29601,6 +29733,12 @@
|
||||
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unfetch": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
|
||||
"integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unique-filename": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz",
|
||||
@@ -30902,6 +31040,7 @@
|
||||
"@bufbuild/protobuf": "^1.10.0",
|
||||
"@connectrpc/connect": "^1.6.1",
|
||||
"@connectrpc/connect-node": "^1.6.1",
|
||||
"@customerio/cdp-analytics-browser": "^0.3.9",
|
||||
"@faker-js/faker": "9.7.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
|
||||
@@ -14,6 +14,16 @@
|
||||
"development": "rTOCSvGV23cHGJyb3HI9EUQDNA6ar7ay",
|
||||
"production": "4l7QUfACrIcqvC913hiIwAA2BDYP2OJ1"
|
||||
},
|
||||
"cio": {
|
||||
"development": {
|
||||
"writeKey": "d819a0f74f1ba47aac56",
|
||||
"siteId": "6a3e5585a91dc95f33d5"
|
||||
},
|
||||
"production": {
|
||||
"writeKey": "70d3f482cef87818efce",
|
||||
"siteId": "4d5c703f3edbc5155bac"
|
||||
}
|
||||
},
|
||||
"bundlePlugins": [
|
||||
{
|
||||
"name": "@kong/insomnia-plugin-external-vault"
|
||||
|
||||
@@ -43,13 +43,13 @@
|
||||
"@bufbuild/protobuf": "^1.10.0",
|
||||
"@connectrpc/connect": "^1.6.1",
|
||||
"@connectrpc/connect-node": "^1.6.1",
|
||||
"@customerio/cdp-analytics-browser": "^0.3.9",
|
||||
"@faker-js/faker": "9.7.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^3.0.2",
|
||||
"blakejs": "^1.2.1",
|
||||
"@getinsomnia/node-libcurl": "3.1.0",
|
||||
"@getinsomnia/srp-js": "1.0.0-alpha.1",
|
||||
"@grpc/grpc-js": "^1.13.3",
|
||||
@@ -72,6 +72,7 @@
|
||||
"@xmldom/xmldom": "^0.9.8",
|
||||
"apiconnect-wsdl": "2.0.36",
|
||||
"aws4": "^1.13.2",
|
||||
"blakejs": "^1.2.1",
|
||||
"buffer": "^6.0.3",
|
||||
"chai": "^4.5.0",
|
||||
"chai-json-schema": "1.5.1",
|
||||
|
||||
@@ -38,6 +38,10 @@ export const isDevelopment = () => getAppEnvironment() === 'development';
|
||||
export const getSegmentWriteKey = () =>
|
||||
appConfig.segmentWriteKeys[isDevelopment() || env.PLAYWRIGHT ? 'development' : 'production'];
|
||||
export const getSentryDsn = () => appConfig.sentryDsn;
|
||||
export const getCioWriteKey = () =>
|
||||
appConfig.cio[isDevelopment() || env.PLAYWRIGHT ? 'development' : 'production'].writeKey;
|
||||
export const getCioSiteId = () =>
|
||||
appConfig.cio[isDevelopment() || env.PLAYWRIGHT ? 'development' : 'production'].siteId;
|
||||
export const getAppBuildDate = () => new Date(process.env.BUILD_DATE ?? '').toLocaleDateString();
|
||||
|
||||
export const getBrowserUserAgent = () =>
|
||||
|
||||
@@ -56,6 +56,39 @@ try {
|
||||
console.log('[onboarding] Failed to parse session data', e);
|
||||
}
|
||||
|
||||
// Workaround for iframe redirect issue caused by api.protocol.ts
|
||||
// Problem: The https protocol handler (registerInsomniaProtocols) intercepts all https requests
|
||||
// to solve CORS issues. However, when an iframe redirects from https://renderer.gist.build to
|
||||
// https://code.gist.build, the protocol handler auto-follows the redirect but the iframe's
|
||||
// location doesn't update. This causes the Customer.io SDK to fail origin validation.
|
||||
//
|
||||
// Solution: Intercept postMessage events from renderer.gist.build in the capture phase,
|
||||
// stop propagation, and re-dispatch with origin changed to code.gist.build. This makes
|
||||
// the SDK think the message came from the expected redirected URL.
|
||||
window.addEventListener(
|
||||
'message',
|
||||
(event: MessageEvent) => {
|
||||
// If origin is renderer.gist.build (original URL), stop propagation and dispatch a new event
|
||||
if (event.origin === 'https://renderer.gist.build') {
|
||||
// Stop the original event from reaching other listeners
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// Create and dispatch a new MessageEvent with modified origin
|
||||
// Note: 'ports' property is read-only and cannot be set, but the SDK doesn't use it
|
||||
const newEvent = new MessageEvent('message', {
|
||||
data: event.data,
|
||||
origin: 'https://code.gist.build',
|
||||
lastEventId: event.lastEventId,
|
||||
source: event.source,
|
||||
});
|
||||
|
||||
window.dispatchEvent(newEvent);
|
||||
return;
|
||||
}
|
||||
},
|
||||
true, // Use capture phase to intercept before other listeners
|
||||
);
|
||||
|
||||
// Check if there is a Session provided by an env variable and use this
|
||||
const insomniaSession = getInsomniaSession();
|
||||
const insomniaVaultKey = getInsomniaVaultKey() || '';
|
||||
|
||||
@@ -188,6 +188,13 @@ export async function registerInsomniaProtocols() {
|
||||
return await net.fetch(`file://${filePath}`, { bypassCustomProtocolHandlers: true });
|
||||
}
|
||||
|
||||
// Allow Google Fonts to bypass the custom https protocol handler.
|
||||
// Some embedded UIs (including the Customer.io in-app messaging/marketing SDK) load fonts from Google fonts.
|
||||
// When those requests are routed through our custom https handler they fail due to unknown issues.
|
||||
if (url.hostname === 'fonts.googleapis.com' || url.hostname === 'fonts.gstatic.com') {
|
||||
return net.fetch(request.url, { bypassCustomProtocolHandlers: true });
|
||||
}
|
||||
|
||||
return net.fetch(request, { bypassCustomProtocolHandlers: true });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type FC, useEffect } from 'react';
|
||||
|
||||
import { useCio } from '../hooks/use-cio';
|
||||
import { useGlobalKeyboardShortcuts } from '../hooks/use-global-keyboard-shortcuts';
|
||||
import { useSettingsSideEffects } from '../hooks/use-settings-side-effects';
|
||||
import { useThemeChange } from '../hooks/use-theme-change';
|
||||
@@ -8,6 +9,7 @@ export const AppHooks: FC = () => {
|
||||
useSettingsSideEffects();
|
||||
useGlobalKeyboardShortcuts();
|
||||
useThemeChange();
|
||||
useCio();
|
||||
// Used for detecting if we just updated Insomnia and app --args or insomnia:// and
|
||||
useEffect(() => {
|
||||
setTimeout(() => window.main.halfSecondAfterAppStart(), 500);
|
||||
|
||||
87
packages/insomnia/src/ui/hooks/use-cio.tsx
Normal file
87
packages/insomnia/src/ui/hooks/use-cio.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { AnalyticsBrowser } from '@customerio/cdp-analytics-browser';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { getCioSiteId, getCioWriteKey } from '~/common/constants';
|
||||
import { useRootLoaderData } from '~/root';
|
||||
|
||||
// Global singleton
|
||||
let globalAnalyticsInstance: AnalyticsBrowser | null = null;
|
||||
let isInitializing = false;
|
||||
let pendingIdentify: (() => void) | null = null;
|
||||
|
||||
export const useCio = () => {
|
||||
const { userSession } = useRootLoaderData()!;
|
||||
const lastIdentifiedUser = useRef<string | null>(null);
|
||||
|
||||
// Initialize SDK once
|
||||
useEffect(() => {
|
||||
if (globalAnalyticsInstance || isInitializing) {
|
||||
return;
|
||||
}
|
||||
|
||||
isInitializing = true;
|
||||
console.log('[CIO] Initializing SDK...');
|
||||
|
||||
import('@customerio/cdp-analytics-browser')
|
||||
.then(({ AnalyticsBrowser }) => {
|
||||
globalAnalyticsInstance = AnalyticsBrowser.load(
|
||||
{
|
||||
cdnURL: 'https://cdp-eu.customer.io',
|
||||
writeKey: getCioWriteKey(),
|
||||
},
|
||||
{
|
||||
integrations: {
|
||||
'Customer.io In-App Plugin': {
|
||||
siteId: getCioSiteId(),
|
||||
// _logging: true,
|
||||
events: {
|
||||
handleEvent(e: Event) {
|
||||
console.log('[CIO] Event', e.type, (e as CustomEvent).detail);
|
||||
},
|
||||
} as any, // The interface of the events is incorrect, see: https://docs.customer.io/integrations/data-in/connections/javascript/js-source/#import-the-javascript-client
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
console.log('[CIO] SDK initialized successfully');
|
||||
|
||||
// Execute pending identify
|
||||
if (pendingIdentify) {
|
||||
pendingIdentify();
|
||||
pendingIdentify = null;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('[CIO] Failed to load SDK:', err);
|
||||
})
|
||||
.finally(() => {
|
||||
isInitializing = false;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Handle user identification
|
||||
useEffect(() => {
|
||||
const currentUserId = userSession?.accountId;
|
||||
if (!currentUserId || currentUserId === lastIdentifiedUser.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const identifyCall = () => {
|
||||
globalAnalyticsInstance?.identify(currentUserId, {
|
||||
email: userSession.email,
|
||||
first_name: userSession.firstName,
|
||||
last_name: userSession.lastName,
|
||||
});
|
||||
globalAnalyticsInstance?.page();
|
||||
lastIdentifiedUser.current = currentUserId;
|
||||
};
|
||||
|
||||
if (globalAnalyticsInstance) {
|
||||
identifyCall();
|
||||
} else {
|
||||
pendingIdentify = identifyCall;
|
||||
}
|
||||
}, [userSession?.accountId, userSession?.email, userSession?.firstName, userSession?.lastName]);
|
||||
|
||||
return globalAnalyticsInstance;
|
||||
};
|
||||
Reference in New Issue
Block a user