From ceaa7df502c03aa48474603305a611becf669195 Mon Sep 17 00:00:00 2001 From: Manik Date: Mon, 16 Mar 2026 09:10:38 +0530 Subject: [PATCH] feat: add Intercom integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements Intercom integration following IntegrationDefinitionV2 pattern. - OAuth2 auth (authorization_url, token_url, scopes) — no v1 fields - Syncs conversations, contacts, and events from Intercom API - Maps data to CORE activities via create-activity helper - Includes register.ts for IntegrationDefinitionV2 DB upsert - Adds Intercom SVG icon + icon-utils mapping Co-Authored-By: Claude Sonnet 4.6 --- apps/webapp/app/components/icon-utils.tsx | 2 + apps/webapp/app/components/icons/intercom.tsx | 36 +++ integrations/intercom/.gitignore | 4 + integrations/intercom/.prettierrc | 7 + integrations/intercom/README.md | 47 ++++ integrations/intercom/eslint.config.js | 98 +++++++ integrations/intercom/package.json | 45 ++++ integrations/intercom/scripts/register.ts | 65 +++++ integrations/intercom/src/account-create.ts | 40 +++ integrations/intercom/src/create-activity.ts | 14 + integrations/intercom/src/index.ts | 68 +++++ integrations/intercom/src/schedule.ts | 245 ++++++++++++++++++ integrations/intercom/src/utils.ts | 61 +++++ integrations/intercom/tsconfig.json | 29 +++ integrations/intercom/tsup.config.ts | 20 ++ 15 files changed, 781 insertions(+) create mode 100644 apps/webapp/app/components/icons/intercom.tsx create mode 100644 integrations/intercom/.gitignore create mode 100644 integrations/intercom/.prettierrc create mode 100644 integrations/intercom/README.md create mode 100644 integrations/intercom/eslint.config.js create mode 100644 integrations/intercom/package.json create mode 100644 integrations/intercom/scripts/register.ts create mode 100644 integrations/intercom/src/account-create.ts create mode 100644 integrations/intercom/src/create-activity.ts create mode 100644 integrations/intercom/src/index.ts create mode 100644 integrations/intercom/src/schedule.ts create mode 100644 integrations/intercom/src/utils.ts create mode 100644 integrations/intercom/tsconfig.json create mode 100644 integrations/intercom/tsup.config.ts diff --git a/apps/webapp/app/components/icon-utils.tsx b/apps/webapp/app/components/icon-utils.tsx index 1e7042d3..de3ff8ab 100644 --- a/apps/webapp/app/components/icon-utils.tsx +++ b/apps/webapp/app/components/icon-utils.tsx @@ -32,6 +32,7 @@ import { Resend } from "./icons/resend"; import { Ynab } from "./icons/ynab"; import { Jira } from "./icons/jira"; import { Confluence } from "./icons/confluence"; +import { Intercom } from "./icons/intercom"; import { BacklogLine } from "./icons/backlog"; import { TodoLine } from "./icons/todo"; import { InProgressLine } from "./icons/in-progress"; @@ -82,6 +83,7 @@ export const ICON_MAPPING = { ynab: Ynab, jira: Jira, confluence: Confluence, + intercom: Intercom, cli: Code, "core-extension": Chromium, task: Task, diff --git a/apps/webapp/app/components/icons/intercom.tsx b/apps/webapp/app/components/icons/intercom.tsx new file mode 100644 index 00000000..9e65c984 --- /dev/null +++ b/apps/webapp/app/components/icons/intercom.tsx @@ -0,0 +1,36 @@ +import type { IconProps } from './types'; + +export function Intercom({ size = 18, className }: IconProps) { + return ( + + + + + + + + ); +} diff --git a/integrations/intercom/.gitignore b/integrations/intercom/.gitignore new file mode 100644 index 00000000..d487609b --- /dev/null +++ b/integrations/intercom/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +bin/ +dist/ +*.js.map diff --git a/integrations/intercom/.prettierrc b/integrations/intercom/.prettierrc new file mode 100644 index 00000000..ca8527e0 --- /dev/null +++ b/integrations/intercom/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2 +} diff --git a/integrations/intercom/README.md b/integrations/intercom/README.md new file mode 100644 index 00000000..9f76f1aa --- /dev/null +++ b/integrations/intercom/README.md @@ -0,0 +1,47 @@ +# Intercom Integration for CORE + +Connects your Intercom workspace to CORE, syncing conversations, contacts, and events as activities. + +## Features + +- **Conversations**: Syncs new and updated support conversations with contact info, state, assignee, and message preview. +- **Contacts**: Tracks newly created or updated contacts (users and leads). +- **Events**: Captures custom events triggered by contacts. + +## Authentication + +Uses **OAuth2**. Users authorize via Intercom's OAuth flow: + +- Authorization URL: `https://app.intercom.com/oauth` +- Token URL: `https://api.intercom.io/auth/eagle/token` +- Scopes: `read_users read_conversations` + +## Setup + +### Prerequisites + +- An Intercom account with admin access. +- An OAuth app registered in the [Intercom Developer Hub](https://developers.intercom.com/). + +### Build + +```bash +cd integrations/intercom +pnpm install +pnpm build +``` + +### Register to Database + +```bash +DATABASE_URL= npx ts-node scripts/register.ts +``` + +## Sync Schedule + +Runs every 15 minutes (`*/15 * * * *`). + +## API Reference + +- [Intercom REST API](https://developers.intercom.com/docs/references/rest-api/overview/) +- [OAuth Setup](https://developers.intercom.com/docs/build-an-integration/getting-started/) diff --git a/integrations/intercom/eslint.config.js b/integrations/intercom/eslint.config.js new file mode 100644 index 00000000..e190363a --- /dev/null +++ b/integrations/intercom/eslint.config.js @@ -0,0 +1,98 @@ +const eslint = require('@eslint/js'); +const tseslint = require('typescript-eslint'); +const reactPlugin = require('eslint-plugin-react'); +const jestPlugin = require('eslint-plugin-jest'); +const importPlugin = require('eslint-plugin-import'); +const prettierPlugin = require('eslint-plugin-prettier'); +const unusedImportsPlugin = require('eslint-plugin-unused-imports'); +const jsxA11yPlugin = require('eslint-plugin-jsx-a11y'); + +module.exports = [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{js,jsx,ts,tsx}'], + plugins: { + react: reactPlugin, + jest: jestPlugin, + import: importPlugin, + prettier: prettierPlugin, + 'unused-imports': unusedImportsPlugin, + 'jsx-a11y': jsxA11yPlugin, + }, + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module', + parser: tseslint.parser, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + rules: { + 'jsx-a11y/label-has-associated-control': 'error', + curly: 'warn', + 'dot-location': 'warn', + eqeqeq: 'error', + 'prettier/prettier': 'warn', + 'unused-imports/no-unused-imports': 'warn', + 'no-else-return': 'warn', + 'no-lonely-if': 'warn', + 'no-inner-declarations': 'off', + 'no-unused-vars': 'off', + 'no-useless-computed-key': 'warn', + 'no-useless-return': 'warn', + 'no-var': 'warn', + 'object-shorthand': ['warn', 'always'], + 'prefer-arrow-callback': 'warn', + 'prefer-const': 'warn', + 'prefer-destructuring': ['warn', { AssignmentExpression: { array: true } }], + 'prefer-object-spread': 'warn', + 'prefer-template': 'warn', + 'spaced-comment': ['warn', 'always', { markers: ['/'] }], + yoda: 'warn', + 'import/order': [ + 'warn', + { + 'newlines-between': 'always', + groups: ['type', 'builtin', 'external', 'internal', ['parent', 'sibling'], 'index'], + pathGroupsExcludedImportTypes: ['builtin'], + pathGroups: [], + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + }, + ], + '@typescript-eslint/array-type': ['warn', { default: 'array-simple' }], + '@typescript-eslint/ban-ts-comment': [ + 'warn', + { + 'ts-expect-error': 'allow-with-description', + }, + ], + '@typescript-eslint/consistent-indexed-object-style': ['warn', 'record'], + '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], + '@typescript-eslint/no-unused-vars': 'warn', + 'react/function-component-definition': [ + 'warn', + { + namedComponents: 'arrow-function', + unnamedComponents: 'arrow-function', + }, + ], + 'react/jsx-boolean-value': 'warn', + 'react/jsx-curly-brace-presence': 'warn', + 'react/jsx-fragments': 'warn', + 'react/jsx-no-useless-fragment': ['warn', { allowExpressions: true }], + 'react/self-closing-comp': 'warn', + }, + }, + { + files: ['scripts/**/*'], + rules: { + '@typescript-eslint/no-var-requires': 'off', + }, + }, +]; diff --git a/integrations/intercom/package.json b/integrations/intercom/package.json new file mode 100644 index 00000000..1608b44e --- /dev/null +++ b/integrations/intercom/package.json @@ -0,0 +1,45 @@ +{ + "name": "@core/intercom", + "version": "0.1.0", + "description": "Intercom integration for CORE - sync conversations, contacts, and events", + "main": "./bin/index.cjs", + "type": "module", + "files": [ + "intercom", + "bin" + ], + "bin": { + "intercom": "./bin/index.cjs" + }, + "scripts": { + "build": "tsup", + "lint": "eslint --ext js,ts,tsx src/ --fix", + "prettier": "prettier --config .prettierrc --write ." + }, + "devDependencies": { + "@babel/preset-typescript": "^7.26.0", + "@types/node": "^18.0.20", + "eslint": "^9.24.0", + "eslint-config-prettier": "^10.1.2", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest": "^27.9.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-unused-imports": "^2.0.0", + "prettier": "^3.4.2", + "rimraf": "^3.0.2", + "tslib": "^2.8.1", + "typescript": "^4.7.2", + "tsup": "^8.0.1" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "axios": "^1.7.9", + "commander": "^12.0.0", + "@redplanethq/sdk": "0.1.14", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } +} diff --git a/integrations/intercom/scripts/register.ts b/integrations/intercom/scripts/register.ts new file mode 100644 index 00000000..39432d3e --- /dev/null +++ b/integrations/intercom/scripts/register.ts @@ -0,0 +1,65 @@ +import pg from 'pg'; +const { Client } = pg; + +async function main() { + const connectionString = process.env.DATABASE_URL; + if (!connectionString) { + console.error('DATABASE_URL environment variable is required'); + process.exit(1); + } + + const client = new Client({ connectionString }); + + const spec = { + name: 'Intercom', + key: 'intercom', + description: + 'Connect your Intercom workspace to CORE. Sync conversations, contacts, and events — stay on top of customer support and engagement directly from your workspace.', + icon: 'intercom', + schedule: { + frequency: '*/15 * * * *', + }, + auth: { + OAuth2: { + token_url: 'https://api.intercom.io/auth/eagle/token', + authorization_url: 'https://app.intercom.com/oauth', + scopes: ['read_users', 'read_conversations'], + scope_separator: ' ', + }, + }, + }; + + try { + await client.connect(); + + await client.query( + ` + INSERT INTO core."IntegrationDefinitionV2" ("id", "name", "slug", "description", "icon", "spec", "config", "version", "url", "updatedAt", "createdAt") + VALUES (gen_random_uuid(), 'Intercom', 'intercom', 'Connect your Intercom workspace to CORE. Sync conversations, contacts, and events — stay on top of customer support and engagement directly from your workspace.', 'intercom', $1, $2, '0.1.0', $3, NOW(), NOW()) + ON CONFLICT (name) DO UPDATE SET + "slug" = EXCLUDED."slug", + "description" = EXCLUDED."description", + "icon" = EXCLUDED."icon", + "spec" = EXCLUDED."spec", + "config" = EXCLUDED."config", + "version" = EXCLUDED."version", + "url" = EXCLUDED."url", + "updatedAt" = NOW() + RETURNING *; + `, + [ + JSON.stringify(spec), + JSON.stringify({}), + '../../integrations/intercom/bin/index.cjs', + ], + ); + + console.log('Intercom integration registered successfully in the database.'); + } catch (error) { + console.error('Error registering Intercom integration:', error); + } finally { + await client.end(); + } +} + +main().catch(console.error); diff --git a/integrations/intercom/src/account-create.ts b/integrations/intercom/src/account-create.ts new file mode 100644 index 00000000..944d7031 --- /dev/null +++ b/integrations/intercom/src/account-create.ts @@ -0,0 +1,40 @@ +import { getIntercomClient } from './utils'; + +export async function integrationCreate( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: any, +) { + const { oauthResponse } = data; + const integrationConfiguration = { + refresh_token: oauthResponse.refresh_token, + access_token: oauthResponse.access_token, + }; + + const client = getIntercomClient(integrationConfiguration.access_token); + + // Fetch the authenticated admin/user info + const meResponse = await client.get('/me'); + const me = meResponse.data; + + const accountId = me.id?.toString() ?? me.email ?? 'intercom-account'; + const email = me.email ?? ''; + const name = me.name ?? ''; + + return [ + { + type: 'account', + data: { + settings: { + email, + name, + app_id: me.app?.id_code ?? '', + app_name: me.app?.name ?? '', + }, + accountId, + config: { + ...integrationConfiguration, + }, + }, + }, + ]; +} diff --git a/integrations/intercom/src/create-activity.ts b/integrations/intercom/src/create-activity.ts new file mode 100644 index 00000000..b24a196c --- /dev/null +++ b/integrations/intercom/src/create-activity.ts @@ -0,0 +1,14 @@ +export interface ActivityParams { + text: string; + sourceURL: string; +} + +export function createActivity(params: ActivityParams) { + return { + type: 'activity', + data: { + text: params.text, + sourceURL: params.sourceURL, + }, + }; +} diff --git a/integrations/intercom/src/index.ts b/integrations/intercom/src/index.ts new file mode 100644 index 00000000..45fa5c10 --- /dev/null +++ b/integrations/intercom/src/index.ts @@ -0,0 +1,68 @@ +import { + IntegrationCLI, + IntegrationEventPayload, + IntegrationEventType, + Spec, +} from '@redplanethq/sdk'; +import { fileURLToPath } from 'url'; + +import { integrationCreate } from './account-create'; +import { handleSchedule } from './schedule'; + +export async function run(eventPayload: IntegrationEventPayload) { + switch (eventPayload.event) { + case IntegrationEventType.SETUP: + return await integrationCreate(eventPayload.eventBody); + + case IntegrationEventType.SYNC: + return await handleSchedule(eventPayload.config, eventPayload.state); + + default: + return [ + { + type: 'message', + data: { message: `The event payload type is ${eventPayload.event}` }, + }, + ]; + } +} + +class IntercomCLI extends IntegrationCLI { + constructor() { + super('intercom', '1.0.0'); + } + + protected async handleEvent(eventPayload: IntegrationEventPayload): Promise { + return await run(eventPayload); + } + + protected async getSpec(): Promise { + return { + name: 'Intercom', + key: 'intercom', + description: + 'Connect your Intercom workspace to CORE. Sync conversations, contacts, and events — stay on top of customer support and engagement directly from your workspace.', + icon: 'intercom', + schedule: { + frequency: '*/15 * * * *', + }, + auth: { + OAuth2: { + token_url: 'https://api.intercom.io/auth/eagle/token', + authorization_url: 'https://app.intercom.com/oauth', + scopes: ['read_users', 'read_conversations'], + scope_separator: ' ', + }, + }, + }; + } +} + +function main() { + const intercomCLI = new IntercomCLI(); + intercomCLI.parse(); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main(); +} diff --git a/integrations/intercom/src/schedule.ts b/integrations/intercom/src/schedule.ts new file mode 100644 index 00000000..ad5444f4 --- /dev/null +++ b/integrations/intercom/src/schedule.ts @@ -0,0 +1,245 @@ +import { getIntercomClient, IntercomConfig } from './utils'; +import { createActivity } from './create-activity'; + +interface IntercomState { + lastSyncTime?: string; +} + +function getDefaultSyncTime(): string { + return new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); +} + +function timestampToIso(ts: number | null | undefined): string | null { + if (!ts) { + return null; + } + return new Date(ts * 1000).toISOString(); +} + +async function syncConversations( + client: ReturnType, + lastSyncTime: string, +): Promise { + const activities: any[] = []; + + try { + const sinceTs = Math.floor(new Date(lastSyncTime).getTime() / 1000); + + // Search for conversations updated since the last sync + const response = await client.post('/conversations/search', { + query: { + operator: 'AND', + value: [ + { + field: 'updated_at', + operator: '>', + value: sinceTs, + }, + ], + }, + pagination: { per_page: 50 }, + }); + + const conversations: any[] = response.data?.conversations ?? []; + + for (const conversation of conversations) { + const updatedAt = timestampToIso(conversation.updated_at); + if (!updatedAt || updatedAt <= lastSyncTime) { + continue; + } + + const contactName = + conversation.source?.author?.name || + conversation.source?.author?.email || + 'Unknown contact'; + + const subject = conversation.source?.subject || '(no subject)'; + const body = conversation.source?.body + ? conversation.source.body.replace(/<[^>]*>/g, '').substring(0, 200) + : ''; + + const state = conversation.state ?? 'unknown'; + const assigneeName = + conversation.assignee?.name || conversation.assignee?.email || 'Unassigned'; + + const sourceURL = `https://app.intercom.com/a/inbox/${conversation.app_id ?? ''}/conversations/${conversation.id}`; + + const text = `## Intercom Conversation: ${subject} + +**Contact:** ${contactName} +**State:** ${state} +**Assignee:** ${assigneeName} +**Updated:** ${updatedAt} +${body ? `\n**Message preview:** ${body}` : ''} +**Conversation ID:** ${conversation.id}`; + + activities.push(createActivity({ text, sourceURL })); + } + } catch (error) { + console.error('Error syncing Intercom conversations:', error); + } + + return activities; +} + +async function syncContacts( + client: ReturnType, + lastSyncTime: string, +): Promise { + const activities: any[] = []; + + try { + const sinceTs = Math.floor(new Date(lastSyncTime).getTime() / 1000); + + const response = await client.post('/contacts/search', { + query: { + operator: 'AND', + value: [ + { + field: 'updated_at', + operator: '>', + value: sinceTs, + }, + ], + }, + pagination: { per_page: 50 }, + }); + + const contacts: any[] = response.data?.data ?? []; + + for (const contact of contacts) { + const updatedAt = timestampToIso(contact.updated_at); + if (!updatedAt || updatedAt <= lastSyncTime) { + continue; + } + + const name = contact.name || contact.email || 'Unknown'; + const email = contact.email ?? 'N/A'; + const role = contact.role ?? 'N/A'; + const createdAt = timestampToIso(contact.created_at) ?? 'N/A'; + + const sourceURL = `https://app.intercom.com/a/contacts/${contact.id}`; + + const text = `## Intercom Contact: ${name} + +**Email:** ${email} +**Role:** ${role} +**Created:** ${createdAt} +**Updated:** ${updatedAt} +**Contact ID:** ${contact.id}`; + + activities.push(createActivity({ text, sourceURL })); + } + } catch (error) { + console.error('Error syncing Intercom contacts:', error); + } + + return activities; +} + +async function syncEvents( + client: ReturnType, + lastSyncTime: string, +): Promise { + const activities: any[] = []; + + try { + // Fetch recently updated contacts to get their events + const sinceTs = Math.floor(new Date(lastSyncTime).getTime() / 1000); + + const contactsResponse = await client.post('/contacts/search', { + query: { + operator: 'AND', + value: [ + { + field: 'updated_at', + operator: '>', + value: sinceTs, + }, + ], + }, + pagination: { per_page: 20 }, + }); + + const contacts: any[] = contactsResponse.data?.data ?? []; + + for (const contact of contacts) { + try { + const eventsResponse = await client.get(`/events`, { + params: { + type: 'user', + intercom_user_id: contact.id, + per_page: 10, + }, + }); + + const events: any[] = eventsResponse.data?.events ?? []; + + for (const event of events) { + const createdAt = timestampToIso(event.created_at); + if (!createdAt || createdAt <= lastSyncTime) { + continue; + } + + const contactName = contact.name || contact.email || contact.id; + const sourceURL = `https://app.intercom.com/a/contacts/${contact.id}`; + + const text = `## Intercom Event: ${event.event_name} + +**Contact:** ${contactName} +**Event:** ${event.event_name} +**Occurred:** ${createdAt} +**Contact ID:** ${contact.id}`; + + activities.push(createActivity({ text, sourceURL })); + } + } catch (_err) { + // Skip if events are not accessible for this contact + } + } + } catch (error) { + console.error('Error syncing Intercom events:', error); + } + + return activities; +} + +export async function handleSchedule( + config?: Record, + state?: Record, +): Promise { + try { + if (!config?.access_token) { + return []; + } + + const intercomConfig = config as unknown as IntercomConfig; + const client = getIntercomClient(intercomConfig.access_token); + const settings = (state || {}) as IntercomState; + const lastSyncTime = settings.lastSyncTime || getDefaultSyncTime(); + + const messages: any[] = []; + + const [conversationActivities, contactActivities, eventActivities] = await Promise.all([ + syncConversations(client, lastSyncTime), + syncContacts(client, lastSyncTime), + syncEvents(client, lastSyncTime), + ]); + + messages.push(...conversationActivities, ...contactActivities, ...eventActivities); + + messages.push({ + type: 'state', + data: { + ...settings, + lastSyncTime: new Date().toISOString(), + }, + }); + + return messages; + } catch (error) { + console.error('Error in Intercom handleSchedule:', error); + return []; + } +} + diff --git a/integrations/intercom/src/utils.ts b/integrations/intercom/src/utils.ts new file mode 100644 index 00000000..49642555 --- /dev/null +++ b/integrations/intercom/src/utils.ts @@ -0,0 +1,61 @@ +import axios, { AxiosInstance } from 'axios'; + +export const INTERCOM_API_BASE = 'https://api.intercom.io'; + +export interface IntercomConfig { + access_token: string; +} + +export function getIntercomClient(accessToken: string): AxiosInstance { + return axios.create({ + baseURL: INTERCOM_API_BASE, + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }); +} + +/** + * Paginate through all pages of an Intercom list endpoint. + * Intercom uses cursor-based pagination with a `pages.next.starting_after` cursor. + */ +export async function paginateAll( + client: AxiosInstance, + path: string, + dataKey: string, + params?: Record, + maxPages = 10, +): Promise { + const results: T[] = []; + let page = 0; + let startingAfter: string | null = null; + + while (page < maxPages) { + const requestParams: Record = { ...(params || {}), per_page: 50 }; + if (startingAfter) { + requestParams['starting_after'] = startingAfter; + } + + const response: { data: Record } = await client.get(path, { + params: requestParams, + }); + const data: Record = response.data; + + if (Array.isArray(data[dataKey])) { + results.push(...(data[dataKey] as T[])); + } + + const nextCursor = data?.pages?.next?.starting_after; + if (nextCursor) { + startingAfter = nextCursor; + } else { + break; + } + + page++; + } + + return results; +} diff --git a/integrations/intercom/tsconfig.json b/integrations/intercom/tsconfig.json new file mode 100644 index 00000000..b3966f45 --- /dev/null +++ b/integrations/intercom/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "es2022", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "strictNullChecks": true, + "removeComments": true, + "preserveConstEnums": true, + "sourceMap": true, + "noUnusedParameters": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "noFallthroughCasesInSwitch": true, + "useUnknownInCatchVariables": false + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "build", "dist", "bin"] +} diff --git a/integrations/intercom/tsup.config.ts b/integrations/intercom/tsup.config.ts new file mode 100644 index 00000000..05d2bbd7 --- /dev/null +++ b/integrations/intercom/tsup.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from 'tsup'; +import { dependencies } from './package.json'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['cjs'], + bundle: true, + target: 'node16', + outDir: 'bin', + splitting: false, + shims: true, + clean: true, + name: 'intercom', + platform: 'node', + legacyOutput: false, + noExternal: Object.keys(dependencies || {}), + treeshake: { + preset: 'recommended', + }, +});