diff --git a/bun.lockb b/bun.lockb index fe6551c4d..2e8d85976 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/integrations/pendo/assets/icon.png b/integrations/pendo/assets/icon.png new file mode 100644 index 000000000..41b3af1f4 Binary files /dev/null and b/integrations/pendo/assets/icon.png differ diff --git a/integrations/pendo/assets/preview-1.png b/integrations/pendo/assets/preview-1.png new file mode 100644 index 000000000..388150479 Binary files /dev/null and b/integrations/pendo/assets/preview-1.png differ diff --git a/integrations/pendo/assets/preview-2.png b/integrations/pendo/assets/preview-2.png new file mode 100644 index 000000000..f9790f351 Binary files /dev/null and b/integrations/pendo/assets/preview-2.png differ diff --git a/integrations/pendo/gitbook-manifest.yaml b/integrations/pendo/gitbook-manifest.yaml new file mode 100644 index 000000000..80bb8d2c3 --- /dev/null +++ b/integrations/pendo/gitbook-manifest.yaml @@ -0,0 +1,37 @@ +name: pendo +title: Pendo +icon: ./assets/icon.png +previewImages: + - ./assets/preview-1.png + - ./assets/preview-2.png +description: Plug your GitBook site to your Pendo Analytics instance. +externalLinks: + - label: Documentation + url: https://www.gitbook.com/integrations/pendo +visibility: private +script: ./src/index.tsx +# The following scope(s) are available only to GitBook Staff +# See https://developer.gitbook.com/integrations/configurations#scopes +scopes: + - site:script:inject +organization: gitbook +contentSecurityPolicy: + script-src: https://cdn.eu.pendo.io https://cdn.pendo.io +summary: | + # Overview + This integration allows to add Pendo Analytics on your published GitBook site. + + # How it works + The integration injects the Pendo Analytics script on your page, using the configured API Key, + so that you can get analytics information from your GitBook site directly inside of Pendo. + + # Configure + Install the integration on the GitBook site of your choice. + Locate the Pendo API Key you want to use from Pendo and paste it in the GitBook integration's configuration screen + +categories: + - analytics +configurations: + site: + componentId: config +target: site diff --git a/integrations/pendo/package.json b/integrations/pendo/package.json new file mode 100644 index 000000000..471b8effa --- /dev/null +++ b/integrations/pendo/package.json @@ -0,0 +1,19 @@ +{ + "name": "@gitbook/integration-pendo", + "version": "0.0.1", + "private": true, + "dependencies": { + "@gitbook/api": "*", + "@gitbook/runtime": "*" + }, + "devDependencies": { + "@gitbook/cli": "workspace:*", + "@gitbook/tsconfig": "workspace:*" + }, + "scripts": { + "typecheck": "tsc --noEmit", + "publish-integrations-staging": "gitbook publish .", + "check": "gitbook check", + "publish-integrations": "gitbook publish ." + } +} diff --git a/integrations/pendo/src/index.tsx b/integrations/pendo/src/index.tsx new file mode 100644 index 000000000..1e8b12c17 --- /dev/null +++ b/integrations/pendo/src/index.tsx @@ -0,0 +1,148 @@ +import { + createIntegration, + FetchPublishScriptEventCallback, + RuntimeContext, + RuntimeEnvironment, + createComponent, +} from '@gitbook/runtime'; +import { IntegrationInstallationConfiguration } from '@gitbook/api'; + +import script from './script.raw.js'; + +type PendoRuntimeContext = RuntimeContext< + RuntimeEnvironment<{}, PendoSiteInstallationConfiguration> +>; +type PendoRuntimeEnvironment = RuntimeEnvironment<{}, PendoSiteInstallationConfiguration>; + +type PendoSiteInstallationConfiguration = { + api_key?: string; + is_eu_region?: boolean; +}; + +type PendoState = PendoSiteInstallationConfiguration; + +type PendoProps = { + installation: { + configuration?: IntegrationInstallationConfiguration; + }; + siteInstallation?: { + configuration?: PendoSiteInstallationConfiguration; + }; +}; +export type PendoAction = { action: 'save.config' }; + +const configBlock = createComponent({ + componentId: 'config', + initialState: (props) => { + const siteInstallation = props.siteInstallation; + return { + api_key: siteInstallation?.configuration?.api_key || '', + is_eu_region: siteInstallation?.configuration?.is_eu_region || false, + }; + }, + action: async (element, action, context) => { + switch (action.action) { + case 'save.config': + const { api, environment } = context; + const siteInstallation = assertSiteInstallation(environment); + + const configurationBody = { + ...siteInstallation.configuration, + api_key: element.state.api_key, + is_eu_region: element.state.is_eu_region, + }; + + await api.integrations.updateIntegrationSiteInstallation( + siteInstallation.integration, + siteInstallation.installation, + siteInstallation.site, + { + configuration: { + ...configurationBody, + }, + }, + ); + + return { type: 'complete' }; + } + }, + render: async (element, context) => { + return ( + + + + + The Pendo API Key.} + element={} + /> + + + + + } + /> + + + } + /> + + + ); + }, +}); + +function assertSiteInstallation(environment: PendoRuntimeEnvironment) { + const siteInstallation = environment.siteInstallation; + if (!siteInstallation) { + throw new Error('No site installation found'); + } + + return siteInstallation; +} + +export const handleFetchEvent: FetchPublishScriptEventCallback = async ( + event, + { environment }: PendoRuntimeContext, +) => { + const apiKey = environment.siteInstallation?.configuration?.api_key; + const isEURegion = environment.siteInstallation?.configuration?.is_eu_region; + if (!apiKey) { + throw new Error( + `The API key is missing from the configuration (ID: ${ + 'spaceId' in event ? event.spaceId : event.siteId + }).`, + ); + } + const region = isEURegion ? 'EU' : 'US'; + return new Response( + (script as string).replace('', apiKey).replace('', region), + { + headers: { + 'Content-Type': 'application/javascript', + 'Cache-Control': 'max-age=604800', + }, + }, + ); +}; + +export default createIntegration({ + fetch_published_script: handleFetchEvent, + components: [configBlock], +}); diff --git a/integrations/pendo/src/script.raw.js b/integrations/pendo/src/script.raw.js new file mode 100644 index 000000000..a73bc0f3b --- /dev/null +++ b/integrations/pendo/src/script.raw.js @@ -0,0 +1,25 @@ +(function (apiKey) { + (function (p, e, n, d, o) { + var v, w, x, y, z; + o = p[d] = p[d] || {}; + o._q = o._q || []; + v = ['initialize', 'identify', 'updateOptions', 'pageLoad', 'track']; + for (w = 0, x = v.length; w < x; ++w) + (function (m) { + o[m] = + o[m] || + function () { + o._q[m === v[0] ? 'unshift' : 'push']( + [m].concat([].slice.call(arguments, 0)), + ); + }; + })(v[w]); + y = e.createElement(n); + y.async = !0; + var cdn; + cdn = '' === 'EU' ? 'https://cdn.eu.pendo.io' : 'https://cdn.pendo.io'; + y.src = `${cdn}/agent/static/` + apiKey + '/pendo.js'; + z = e.getElementsByTagName(n)[0]; + z.parentNode.insertBefore(y, z); + })(window, document, 'script', 'pendo'); +})(''); diff --git a/integrations/pendo/tsconfig.json b/integrations/pendo/tsconfig.json new file mode 100644 index 000000000..1a48f875b --- /dev/null +++ b/integrations/pendo/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@gitbook/tsconfig/integration.json" +}