twenty-app-intake 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +292 -7
- package/manifest.json +389 -15
- package/package.json +1 -1
- package/src/front-components/settings-panel.mjs +7 -7
- package/src/front-components/settings-panel.mjs.map +2 -2
- package/src/front-components/settings-panel.tsx +49 -0
- package/src/logic-functions/check-silence.mjs +263 -0
- package/src/logic-functions/check-silence.mjs.map +7 -0
- package/src/logic-functions/check-silence.ts +52 -0
- package/src/logic-functions/contract.mjs +507 -0
- package/src/logic-functions/contract.mjs.map +7 -0
- package/src/logic-functions/contract.ts +172 -0
- package/src/logic-functions/health.mjs +125 -5
- package/src/logic-functions/health.mjs.map +3 -3
- package/src/logic-functions/health.ts +41 -5
- package/src/logic-functions/quarantine-discard.mjs +380 -0
- package/src/logic-functions/quarantine-discard.mjs.map +7 -0
- package/src/logic-functions/quarantine-discard.ts +65 -0
- package/src/logic-functions/quarantine-list.mjs +211 -0
- package/src/logic-functions/quarantine-list.mjs.map +7 -0
- package/src/logic-functions/quarantine-list.ts +93 -0
- package/src/logic-functions/quarantine-release.mjs +1974 -0
- package/src/logic-functions/quarantine-release.mjs.map +7 -0
- package/src/logic-functions/quarantine-release.ts +94 -0
- package/src/logic-functions/register.mjs +34 -1
- package/src/logic-functions/register.mjs.map +2 -2
- package/src/logic-functions/replay-bulk.mjs +2040 -0
- package/src/logic-functions/replay-bulk.mjs.map +7 -0
- package/src/logic-functions/replay-bulk.ts +83 -0
- package/src/logic-functions/replay-log.mjs +1987 -0
- package/src/logic-functions/replay-log.mjs.map +7 -0
- package/src/logic-functions/replay-log.ts +54 -0
- package/src/logic-functions/retry.mjs +1001 -248
- package/src/logic-functions/retry.mjs.map +4 -4
- package/src/logic-functions/retry.ts +45 -68
- package/src/logic-functions/test-ingest.mjs +616 -28
- package/src/logic-functions/test-ingest.mjs.map +4 -4
- package/src/logic-functions/test-ingest.ts +132 -28
- package/src/logic-functions/webhook.mjs +965 -209
- package/src/logic-functions/webhook.mjs.map +4 -4
- package/src/logic-functions/webhook.ts +23 -48
- package/src/post-install.mjs +34 -1
- package/src/post-install.mjs.map +2 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["twenty-sdk-define-stub:__twenty-sdk-define-stub__", "../../../../src/constants/universal-identifiers.ts", "../../../../src/constants/version.ts", "../../../../src/constants/field-map.ts", "../../../../src/lib/rest-client.ts", "../../../../src/lib/diff.ts", "../../../../src/lib/settings.ts", "../../../../src/logic-functions/contract.ts"],
|
|
4
|
+
"sourcesContent": ["\n// Auto-generated stub for twenty-sdk/define injected by the SDK CLI build.\n// Real implementations would pull in zod, twenty-shared and ~1MB of code; at\n// runtime only `default.config.handler` is consumed, so tiny no-ops suffice.\nconst __defineFactoryStub = (config) => ({\n success: true,\n config,\n errors: [],\n});\n\nconst __anyHandler = {\n get(_target, prop) {\n if (prop === '__esModule') return true;\n if (prop === Symbol.toPrimitive) return () => '';\n if (typeof prop === 'symbol') return undefined;\n return new Proxy(() => undefined, __anyHandler);\n },\n apply() {\n return new Proxy(() => undefined, __anyHandler);\n },\n};\nconst __anyStub = new Proxy(() => undefined, __anyHandler);\n\nexport const createValidationResult = __defineFactoryStub;\nexport const defineAgent = __defineFactoryStub;\nexport const defineApplication = __defineFactoryStub;\nexport const defineApplicationRole = __defineFactoryStub;\nexport const defineCommandMenuItem = __defineFactoryStub;\nexport const defineConnectionProvider = __defineFactoryStub;\nexport const defineField = __defineFactoryStub;\nexport const defineFrontComponent = __defineFactoryStub;\nexport const defineIndex = __defineFactoryStub;\nexport const defineLogicFunction = __defineFactoryStub;\nexport const defineNavigationMenuItem = __defineFactoryStub;\nexport const defineObject = __defineFactoryStub;\nexport const definePageLayout = __defineFactoryStub;\nexport const definePageLayoutTab = __defineFactoryStub;\nexport const definePermissionFlag = __defineFactoryStub;\nexport const definePostInstallLogicFunction = __defineFactoryStub;\nexport const definePreInstallLogicFunction = __defineFactoryStub;\nexport const defineRole = __defineFactoryStub;\nexport const defineSettingsFrontComponent = __defineFactoryStub;\nexport const defineSkill = __defineFactoryStub;\nexport const defineUninstallLogicFunction = __defineFactoryStub;\nexport const defineView = __defineFactoryStub;\nexport const defineViewField = __defineFactoryStub;\nexport const AggregateOperations = __anyStub;\nexport const DateDisplayFormat = __anyStub;\nexport const FieldMetadataSettingsOnClickAction = __anyStub;\nexport const FieldType = __anyStub;\nexport const HTTPMethod = __anyStub;\nexport const NavigationMenuItemType = __anyStub;\nexport const NumberDataType = __anyStub;\nexport const ObjectOpenRecordIn = __anyStub;\nexport const ObjectRecordGroupByDateGranularity = __anyStub;\nexport const OnDeleteAction = __anyStub;\nexport const PageLayoutTabLayoutMode = __anyStub;\nexport const PageLayoutType = __anyStub;\nexport const RelationType = __anyStub;\nexport const RowLevelPermissionPredicateGroupLogicalOperator = __anyStub;\nexport const RowLevelPermissionPredicateOperand = __anyStub;\nexport const STANDARD_OBJECT = __anyStub;\nexport const STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS = __anyStub;\nexport const STANDARD_PAGE_LAYOUT = __anyStub;\nexport const STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS = __anyStub;\nexport const SystemPermissionFlag = __anyStub;\nexport const ViewCalendarLayout = __anyStub;\nexport const ViewFilterGroupLogicalOperator = __anyStub;\nexport const ViewFilterOperand = __anyStub;\nexport const ViewKey = __anyStub;\nexport const ViewOpenRecordIn = __anyStub;\nexport const ViewSortDirection = __anyStub;\nexport const ViewType = __anyStub;\nexport const ViewVisibility = __anyStub;\nexport const canAccessFullAdminPanel = __anyStub;\nexport const canImpersonate = __anyStub;\nexport const every = __anyStub;\nexport const everyDefined = __anyStub;\nexport const everyEquals = __anyStub;\nexport const favoriteRecordIds = __anyStub;\nexport const featureFlags = __anyStub;\nexport const getFieldUniversalIdentifier = __anyStub;\nexport const getSystemRelationFieldUniversalIdentifier = __anyStub;\nexport const getSystemViewFieldUniversalIdentifier = __anyStub;\nexport const getSystemViewUniversalIdentifier = __anyStub;\nexport const hasAnySoftDeleteFilterOnView = __anyStub;\nexport const includes = __anyStub;\nexport const includesEvery = __anyStub;\nexport const isDashboardPageLayoutInEditMode = __anyStub;\nexport const isDefined = __anyStub;\nexport const isInSidePanel = __anyStub;\nexport const isLayoutCustomizationModeEnabled = __anyStub;\nexport const isNonEmptyString = __anyStub;\nexport const isSelectAll = __anyStub;\nexport const none = __anyStub;\nexport const noneDefined = __anyStub;\nexport const noneEquals = __anyStub;\nexport const numberOfSelectedRecords = __anyStub;\nexport const objectMetadataItem = __anyStub;\nexport const objectMetadataLabel = __anyStub;\nexport const objectPermissions = __anyStub;\nexport const pageType = __anyStub;\nexport const selectedRecords = __anyStub;\nexport const some = __anyStub;\nexport const someDefined = __anyStub;\nexport const someEquals = __anyStub;\nexport const someNonEmptyString = __anyStub;\nexport const targetObjectReadPermissions = __anyStub;\nexport const targetObjectWritePermissions = __anyStub;\nexport const validateFields = __anyStub;\n", "export const IDS = {\n APPLICATION: '96a389dd-384a-43a1-a376-9d3cf01b6ab7',\n DEFAULT_ROLE: '3dd2cdb0-32c7-44b4-9f7b-0a9c667ec249',\n\n // Objects\n INTAKE_SOURCE_OBJECT: '8c1c227c-db7d-4834-b328-cacbe8af0bfc',\n INTAKE_LOG_OBJECT: 'b77a2439-1503-4ef6-ab8b-8f4293bc19da',\n INTAKE_FIELD_RULE_OBJECT: 'e74df817-ff25-4bfb-bdf0-651662ca327e',\n\n // IntakeSource fields\n INTAKE_SOURCE_NAME: '0deb7e4b-bb90-4c86-b903-5208b42041e2',\n INTAKE_SOURCE_SLUG: '18f7645b-2dce-4145-b1b6-9cc3332c0578',\n INTAKE_SOURCE_TARGET_OBJECT: '65f3d7f7-a601-4375-9c67-684d0044620b',\n INTAKE_SOURCE_SECRET: '4c919402-07f5-4041-ade6-a42402f1881f',\n INTAKE_SOURCE_STATUS: '6f1fa6fe-02b4-48ab-b1ee-97c600bbf483',\n INTAKE_SOURCE_CREATE_OPP: '67ae59aa-2275-4ef2-b594-137f6bfafd99',\n INTAKE_SOURCE_OPP_TEMPLATE: '3907b346-77a6-4c79-a4d7-6e1cc20bb82b',\n INTAKE_SOURCE_TOTAL_INGESTED: '69655a93-bc70-48bd-874e-aeccc63ee1a8',\n INTAKE_SOURCE_TOTAL_FAILED: '27b3a499-183e-4cc3-8e40-938f953a986d',\n INTAKE_SOURCE_LAST_INGESTED: 'e167f628-9865-468e-894a-b4b6150cec26',\n\n // IntakeLog fields\n INTAKE_LOG_SOURCE_RELATION: 'b1c623ea-7a9a-47a5-bee4-35fdd668a637',\n INTAKE_LOG_STATUS: '8b6d26d1-f89c-486c-a617-9f9f6a9ca36d',\n INTAKE_LOG_PAYLOAD_HASH: 'c43bb1ad-e78a-4c60-bd95-02475ede7f44',\n INTAKE_LOG_RECORD_ID: '77a55116-a64b-4954-8369-341022dd5bdd',\n INTAKE_LOG_RECORD_TYPE: '02154e96-5808-48ed-8838-51083f174550',\n INTAKE_LOG_FIELDS_CREATED: '2295ca2b-b559-4b38-9771-12dd8d3d76d7',\n INTAKE_LOG_FIELDS_MATCHED: '83d07a0f-5bcd-4fb1-9759-f9b04b9381d8',\n INTAKE_LOG_OVERFLOW_COUNT: '4090acb1-f48a-4957-bb91-c642f8f434a7',\n INTAKE_LOG_PROCESSING_MS: 'a9191f27-4b40-42e9-8cce-20c75900e12c',\n INTAKE_LOG_ERROR: '8d9979f1-9774-43a8-b958-d1b37bd4ecc7',\n INTAKE_LOG_PROCESSED_AT: 'a8d99fbd-722e-4987-8afe-b830ac026f0a',\n\n // IntakeFieldRule fields\n INTAKE_FIELD_RULE_SOURCE_RELATION: '396dd1f1-3873-4efa-a6bb-7b41eda14d83',\n INTAKE_FIELD_RULE_INPUT_PATTERN: '757201a0-9f64-47b9-bf3a-a793074bf84e',\n INTAKE_FIELD_RULE_CANONICAL_NAME: 'fafebc55-a2cc-4c9f-975b-050c2676659d',\n INTAKE_FIELD_RULE_FIELD_TYPE: '72d1f6d8-d63e-419e-a244-8127d7b4705f',\n INTAKE_FIELD_RULE_PRIORITY: '911df299-940b-44f9-92f7-3fd01fde01a7',\n INTAKE_FIELD_RULE_IS_ACTIVE: '6f9cd96d-aecd-437f-a7b5-d45548782771',\n INTAKE_FIELD_RULE_TARGET_OBJECT: '7b8411a7-5e1d-40f2-a20c-683c07f67926',\n\n // Logic Functions\n WEBHOOK_LOGIC_FUNCTION: '8323c257-48fe-4bb6-b1dd-166844f575c0',\n HEALTH_LOGIC_FUNCTION: '4ece4e39-286b-4820-81f4-b7beee71d092',\n REGISTER_LOGIC_FUNCTION: '56d46e18-6cae-4d99-9893-b7d70740c9db',\n\n // IntakeSource reverse relation fields\n INTAKE_SOURCE_LOGS_REVERSE: '6ecfa369-22e0-4812-bb75-4338cea8be65',\n INTAKE_SOURCE_RULES_REVERSE: '776fefaf-0eee-4ec0-93e0-d11bd80c1e20',\n\n // v0.2.0 \u2014 rawPayload + retry + post-install + UI\n INTAKE_LOG_RAW_PAYLOAD: 'b65c9e1c-f2c1-4c52-a5b3-1e1ac3139692',\n RETRY_LOGIC_FUNCTION: '90f20396-a69a-4ae4-af20-6be65c565bb5',\n POST_INSTALL_LOGIC_FUNCTION: 'e836fdd6-fd54-4aa4-ba6d-87d5a02148ef',\n LOG_RETRY_FRONT_COMPONENT: '0cb97f6d-30e2-48c8-90e1-241b82870b4d',\n FIELD_RULES_TABLE_FRONT_COMPONENT: 'c35faad9-3468-4746-9c1c-b13d73e5e09b',\n SOURCE_PAGE_LAYOUT: '8c894ba8-5c8c-45ed-b0af-1d22ede93eb9',\n SOURCE_LAYOUT_TAB_RULES: '80f5c49a-083c-4062-8bd5-ae158abb6c33',\n SOURCE_LAYOUT_TAB_RULES_WIDGET: '26a9ebb7-1d59-4b03-a018-56968f6df224',\n LOG_PAGE_LAYOUT: 'b0972802-ede5-463c-9f59-cb05f04957db',\n LOG_LAYOUT_TAB_RETRY: '6b109695-ba3a-43de-a31a-23119ccf87ba',\n LOG_LAYOUT_TAB_RETRY_WIDGET: '11727c1a-5145-46de-9742-88d07d64c72e',\n SAMPLE_RULE_PHONE: '8440da8d-8ac4-4f4b-baa2-4d62936cde35',\n SAMPLE_RULE_EMAIL: 'd123cde4-05aa-44e1-86f6-454be1d7048b',\n SAMPLE_RULE_FULLNAME: '00e49fe8-1b48-4f5b-aaf4-414d0923c712',\n\n // Views & Navigation\n SOURCES_VIEW: '4435cd43-5c13-472f-b4f9-b686c0f46285',\n LOGS_VIEW: 'a2cd0a93-9778-4ba5-99d9-b8b89c4fb39d',\n INTAKE_NAV_ITEM: 'e81bf288-5686-4713-86ae-e2947b124cab',\n\n // \u2500\u2500 v0.4.0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // IntakeLog fields \u2014 quarantine, idempotency, replay lineage\n INTAKE_LOG_IDEMPOTENCY_KEY: 'bf91e4c0-6477-41f2-a9bd-fb159d3091d4',\n INTAKE_LOG_SPAM_SCORE: '4e250af2-925f-4724-85f6-f353a6bb4cfd',\n INTAKE_LOG_QUARANTINE_REASON: '323b3c3e-a111-45d1-a211-cf70efa6cd9e',\n INTAKE_LOG_REPLAY_OF: 'b68876b1-0045-4eaa-a4e7-f0634e110ef1',\n\n // IntakeSource fields \u2014 silence detection, spam, merge policy\n INTAKE_SOURCE_EXPECTED_CADENCE: 'a97c3906-5555-4e60-a6d6-84155a4e00a6',\n INTAKE_SOURCE_HEALTH_STATUS: '2ad13b20-5bd7-450a-b52d-bcf208373cf5',\n INTAKE_SOURCE_SILENT_SINCE: 'd3395601-f29f-4e57-98eb-561ab011cc72',\n INTAKE_SOURCE_ALERT_WEBHOOK: '5a066d62-0426-41c1-b34c-1a7e881ce854',\n INTAKE_SOURCE_LAST_ALERT_AT: 'fc64a598-ccbe-44e3-bd42-716e32d626a2',\n INTAKE_SOURCE_HONEYPOT_FIELD: '5c145686-50de-496c-bb45-a30c8b39034c',\n INTAKE_SOURCE_MERGE_POLICY: '5a946f85-b1c2-493e-92c6-31c50e637a7e',\n\n // IntakeFieldRule fields\n INTAKE_FIELD_RULE_MERGE_STRATEGY: '40c6fdcb-527a-4691-8518-c40a3d3e34c6',\n\n // Logic Functions\n CONTRACT_LOGIC_FUNCTION: '2eea4152-1b08-4b8c-903c-f4c7ee7275d0',\n REPLAY_LOG_LOGIC_FUNCTION: '8ff98390-7358-4a91-84f3-435f758182a3',\n REPLAY_BULK_LOGIC_FUNCTION: 'b82ed4c4-07bc-4095-8ee5-611200626dc0',\n QUARANTINE_LIST_LOGIC_FUNCTION: '0203643c-2a97-4e7c-b4a5-b76d721cf23a',\n QUARANTINE_RELEASE_LOGIC_FUNCTION: '5a10d4fe-c807-45c6-9e61-c191d4e37b4e',\n QUARANTINE_DISCARD_LOGIC_FUNCTION: '1454a56e-6197-4dfd-9b41-5cc3b1436af7',\n CHECK_SILENCE_LOGIC_FUNCTION: 'e88cd5f2-ab96-4f89-ac55-78db66b32d66',\n\n // Application variables\n VAR_MERGE_POLICY: 'a84fe9fd-3d7c-41d9-b0e8-dfdc1a87f348',\n VAR_SPAM_FILTER_ENABLED: '570d3ba8-1904-4bad-a5ce-27e208e6414a',\n VAR_SPAM_SCORE_THRESHOLD: '4395574d-24b4-4e0e-afa3-1e90fcd4f470',\n VAR_HONEYPOT_FIELD: '2cf56c56-f5cb-47c5-aa43-84f301060a95',\n VAR_RAW_PAYLOAD_RETENTION: 'f3314ad6-05b9-4127-bb88-cea5f2018dc9',\n VAR_RAW_PAYLOAD_MAX_BYTES: 'b002ca76-df3d-47b4-8ac5-d3f562239c48',\n VAR_REPLAY_MAX_BATCH: 'cf55956f-177e-4afd-8d9f-7f42c614209c',\n} as const;\n", "/**\n * Reported by GET /s/intake/contract so an integrator can tell which behaviour\n * a workspace is running. Keep in step with package.json on every release.\n */\nexport const APP_VERSION = '0.4.0';\n", "export type TwentyFieldType =\n | 'TEXT'\n | 'NUMBER'\n | 'BOOLEAN'\n | 'DATE'\n | 'DATE_TIME'\n | 'EMAILS'\n | 'PHONES'\n | 'LINKS'\n | 'FULL_NAME'\n | 'ADDRESS'\n | 'SELECT'\n | 'CURRENCY'\n | 'RATING'\n | 'RICH_TEXT'\n | 'RAW_JSON';\n\nexport type FieldAction = 'field' | 'note' | 'skip';\n\nexport type FieldMapEntry = {\n canonicalName: string;\n twentyType: TwentyFieldType;\n action: FieldAction;\n};\n\n/**\n * Normalise an incoming key to its lookup form:\n * camelCase \u2192 snake_case, hyphens/spaces \u2192 underscores, lowercase.\n * e.g. \"phoneNumber\" \u2192 \"phone_number\", \"E-Mail\" \u2192 \"e_mail\"\n */\nexport function normaliseKey(key: string): string {\n return key\n .replace(/([A-Z])/g, '_$1')\n .toLowerCase()\n .replace(/[-\\s.]/g, '_')\n .replace(/_{2,}/g, '_');\n}\n\nexport const FIELD_MAP: Record<string, FieldMapEntry> = {\n // \u2500\u2500 Phone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n phone: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n phone_number: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n tel: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n telephone: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n mobile: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n mobile_number: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n cell: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n cell_phone: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n contact_phone: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n phone1: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n primary_phone: { canonicalName: 'phone', twentyType: 'PHONES', action: 'field' },\n\n // \u2500\u2500 Email \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n email: { canonicalName: 'email', twentyType: 'EMAILS', action: 'field' },\n email_address: { canonicalName: 'email', twentyType: 'EMAILS', action: 'field' },\n e_mail: { canonicalName: 'email', twentyType: 'EMAILS', action: 'field' },\n contact_email: { canonicalName: 'email', twentyType: 'EMAILS', action: 'field' },\n email1: { canonicalName: 'email', twentyType: 'EMAILS', action: 'field' },\n user_email: { canonicalName: 'email', twentyType: 'EMAILS', action: 'field' },\n primary_email: { canonicalName: 'email', twentyType: 'EMAILS', action: 'field' },\n\n // \u2500\u2500 First name \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n first_name: { canonicalName: 'firstName', twentyType: 'TEXT', action: 'field' },\n fname: { canonicalName: 'firstName', twentyType: 'TEXT', action: 'field' },\n given_name: { canonicalName: 'firstName', twentyType: 'TEXT', action: 'field' },\n forename: { canonicalName: 'firstName', twentyType: 'TEXT', action: 'field' },\n\n // \u2500\u2500 Last name \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n last_name: { canonicalName: 'lastName', twentyType: 'TEXT', action: 'field' },\n lname: { canonicalName: 'lastName', twentyType: 'TEXT', action: 'field' },\n surname: { canonicalName: 'lastName', twentyType: 'TEXT', action: 'field' },\n family_name: { canonicalName: 'lastName', twentyType: 'TEXT', action: 'field' },\n\n // \u2500\u2500 Full name (will be split on ingest) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n name: { canonicalName: 'fullName', twentyType: 'FULL_NAME', action: 'field' },\n full_name: { canonicalName: 'fullName', twentyType: 'FULL_NAME', action: 'field' },\n contact_name: { canonicalName: 'fullName', twentyType: 'FULL_NAME', action: 'field' },\n your_name: { canonicalName: 'fullName', twentyType: 'FULL_NAME', action: 'field' },\n\n // \u2500\u2500 Company \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n company: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n company_name: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n business: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n business_name: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n organization: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n organisation: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n employer: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n firm: { canonicalName: 'companyName', twentyType: 'TEXT', action: 'field' },\n\n // \u2500\u2500 Website / Domain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n website: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n website_url: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n url: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n domain: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n homepage: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n web: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n site: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n site_url: { canonicalName: 'domainName', twentyType: 'LINKS', action: 'field' },\n\n // \u2500\u2500 Address \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n street: { canonicalName: 'addressStreet1', twentyType: 'TEXT', action: 'field' },\n street_address: { canonicalName: 'addressStreet1', twentyType: 'TEXT', action: 'field' },\n address_line_1: { canonicalName: 'addressStreet1', twentyType: 'TEXT', action: 'field' },\n address1: { canonicalName: 'addressStreet1', twentyType: 'TEXT', action: 'field' },\n address_line_2: { canonicalName: 'addressStreet2', twentyType: 'TEXT', action: 'field' },\n address2: { canonicalName: 'addressStreet2', twentyType: 'TEXT', action: 'field' },\n city: { canonicalName: 'addressCity', twentyType: 'TEXT', action: 'field' },\n address_city: { canonicalName: 'addressCity', twentyType: 'TEXT', action: 'field' },\n town: { canonicalName: 'addressCity', twentyType: 'TEXT', action: 'field' },\n state: { canonicalName: 'addressState', twentyType: 'TEXT', action: 'field' },\n province: { canonicalName: 'addressState', twentyType: 'TEXT', action: 'field' },\n region: { canonicalName: 'addressState', twentyType: 'TEXT', action: 'field' },\n address_state: { canonicalName: 'addressState', twentyType: 'TEXT', action: 'field' },\n zip: { canonicalName: 'addressPostcode', twentyType: 'TEXT', action: 'field' },\n zip_code: { canonicalName: 'addressPostcode', twentyType: 'TEXT', action: 'field' },\n postal_code: { canonicalName: 'addressPostcode', twentyType: 'TEXT', action: 'field' },\n postcode: { canonicalName: 'addressPostcode', twentyType: 'TEXT', action: 'field' },\n country: { canonicalName: 'addressCountry', twentyType: 'TEXT', action: 'field' },\n address_country: { canonicalName: 'addressCountry', twentyType: 'TEXT', action: 'field' },\n\n // \u2500\u2500 Social \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n linkedin: { canonicalName: 'linkedInLink', twentyType: 'LINKS', action: 'field' },\n linkedin_url: { canonicalName: 'linkedInLink', twentyType: 'LINKS', action: 'field' },\n linkedin_profile: { canonicalName: 'linkedInLink', twentyType: 'LINKS', action: 'field' },\n twitter: { canonicalName: 'xLink', twentyType: 'LINKS', action: 'field' },\n twitter_url: { canonicalName: 'xLink', twentyType: 'LINKS', action: 'field' },\n x_profile: { canonicalName: 'xLink', twentyType: 'LINKS', action: 'field' },\n\n // \u2500\u2500 Pipeline-specific (Google / business intel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n google_places_url: { canonicalName: 'extGooglePlacesUrl', twentyType: 'LINKS', action: 'field' },\n places_url: { canonicalName: 'extGooglePlacesUrl', twentyType: 'LINKS', action: 'field' },\n google_maps_url: { canonicalName: 'extGooglePlacesUrl', twentyType: 'LINKS', action: 'field' },\n maps_url: { canonicalName: 'extGooglePlacesUrl', twentyType: 'LINKS', action: 'field' },\n rating: { canonicalName: 'extGoogleRating', twentyType: 'NUMBER', action: 'field' },\n google_rating: { canonicalName: 'extGoogleRating', twentyType: 'NUMBER', action: 'field' },\n stars: { canonicalName: 'extGoogleRating', twentyType: 'NUMBER', action: 'field' },\n review_count: { canonicalName: 'extReviewCount', twentyType: 'NUMBER', action: 'field' },\n reviews_count: { canonicalName: 'extReviewCount', twentyType: 'NUMBER', action: 'field' },\n total_reviews: { canonicalName: 'extReviewCount', twentyType: 'NUMBER', action: 'field' },\n num_reviews: { canonicalName: 'extReviewCount', twentyType: 'NUMBER', action: 'field' },\n marketing_score: { canonicalName: 'extMarketingScore', twentyType: 'NUMBER', action: 'field' },\n lead_score: { canonicalName: 'extLeadScore', twentyType: 'NUMBER', action: 'field' },\n analyzed_url: { canonicalName: 'extAnalyzedUrl', twentyType: 'LINKS', action: 'field' },\n\n // \u2500\u2500 UTM \u2014 always captured in note \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n utm_source: { canonicalName: 'utm_source', twentyType: 'TEXT', action: 'note' },\n utm_medium: { canonicalName: 'utm_medium', twentyType: 'TEXT', action: 'note' },\n utm_campaign: { canonicalName: 'utm_campaign', twentyType: 'TEXT', action: 'note' },\n utm_content: { canonicalName: 'utm_content', twentyType: 'TEXT', action: 'note' },\n utm_term: { canonicalName: 'utm_term', twentyType: 'TEXT', action: 'note' },\n ref: { canonicalName: 'ref', twentyType: 'TEXT', action: 'note' },\n referrer: { canonicalName: 'referrer', twentyType: 'TEXT', action: 'note' },\n source_page: { canonicalName: 'source_page', twentyType: 'TEXT', action: 'note' },\n landing_page: { canonicalName: 'landing_page', twentyType: 'TEXT', action: 'note' },\n\n // \u2500\u2500 Known note fields \u2014 always prose \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n message: { canonicalName: 'message', twentyType: 'RICH_TEXT', action: 'note' },\n description: { canonicalName: 'description', twentyType: 'RICH_TEXT', action: 'note' },\n analysis: { canonicalName: 'analysis', twentyType: 'RICH_TEXT', action: 'note' },\n notes: { canonicalName: 'notes', twentyType: 'RICH_TEXT', action: 'note' },\n comments: { canonicalName: 'comments', twentyType: 'RICH_TEXT', action: 'note' },\n summary: { canonicalName: 'summary', twentyType: 'RICH_TEXT', action: 'note' },\n inquiry: { canonicalName: 'inquiry', twentyType: 'RICH_TEXT', action: 'note' },\n body: { canonicalName: 'body', twentyType: 'RICH_TEXT', action: 'note' },\n details: { canonicalName: 'details', twentyType: 'RICH_TEXT', action: 'note' },\n content: { canonicalName: 'content', twentyType: 'RICH_TEXT', action: 'note' },\n text: { canonicalName: 'text', twentyType: 'RICH_TEXT', action: 'note' },\n feedback: { canonicalName: 'feedback', twentyType: 'RICH_TEXT', action: 'note' },\n about: { canonicalName: 'about', twentyType: 'RICH_TEXT', action: 'note' },\n additional_info: { canonicalName: 'additional_info', twentyType: 'RICH_TEXT', action: 'note' },\n\n // \u2500\u2500 System / internal \u2014 skip entirely \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n _id: { canonicalName: '_id', twentyType: 'TEXT', action: 'skip' },\n __v: { canonicalName: '__v', twentyType: 'TEXT', action: 'skip' },\n created_at: { canonicalName: 'createdAt', twentyType: 'DATE_TIME', action: 'skip' },\n updated_at: { canonicalName: 'updatedAt', twentyType: 'DATE_TIME', action: 'skip' },\n form_id: { canonicalName: 'form_id', twentyType: 'TEXT', action: 'skip' },\n submission_id: { canonicalName: 'submission_id', twentyType: 'TEXT', action: 'skip' },\n token: { canonicalName: 'token', twentyType: 'TEXT', action: 'skip' },\n password: { canonicalName: 'password', twentyType: 'TEXT', action: 'skip' },\n secret: { canonicalName: 'secret', twentyType: 'TEXT', action: 'skip' },\n} as const;\n", "/**\n * Minimal REST client for Twenty's Core and Metadata APIs.\n * Uses raw fetch so it works with dynamically created custom fields\n * that the typed SDK client doesn't know about at build time.\n */\n\nconst apiUrl = () => process.env['TWENTY_API_URL'] ?? '';\nconst token = () => process.env['TWENTY_APP_ACCESS_TOKEN'] ?? '';\n\nasync function request<T>(\n base: string,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T> {\n const res = await fetch(`${base}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token()}`,\n 'Content-Type': 'application/json',\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`${method} ${path} \u2192 ${res.status}: ${text}`);\n }\n return JSON.parse(text) as T;\n}\n\nexport const coreApi = {\n get: <T>(path: string) => request<T>(`${apiUrl()}/rest`, 'GET', path),\n post: <T>(path: string, body: unknown) => request<T>(`${apiUrl()}/rest`, 'POST', path, body),\n patch: <T>(path: string, body: unknown) => request<T>(`${apiUrl()}/rest`, 'PATCH', path, body),\n};\n\nexport const metaApi = {\n get: <T>(path: string) => request<T>(`${apiUrl()}/rest/metadata`, 'GET', path),\n post: <T>(path: string, body: unknown) => request<T>(`${apiUrl()}/rest/metadata`, 'POST', path, body),\n};\n\n/** Run a raw GraphQL query against the /graphql endpoint. */\nexport async function gql<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n): Promise<T> {\n return request<T>(`${apiUrl()}`, 'POST', '/graphql', { query, variables });\n}\n", "/**\n * What an ingestion would do, worked out without doing it.\n *\n * The old dry-run answered in prose: a list of field names and a rendered note.\n * It read well and could not be acted on \u2014 nothing in it distinguished a field\n * that would be created from one that already existed, or a new contact from an\n * update to somebody's existing record, which are the two questions worth asking\n * before pointing live traffic at a source.\n *\n * This returns the same decisions the pipeline makes, as data. The lookups are\n * the ones the upsert stage uses, so a preview that says \"update\" is describing\n * the record the real run would actually touch, and the merge policy is applied\n * to the values it finds, so a preview can show that an incoming name is going\n * to be refused because a better one is already there.\n *\n * Read-only throughout: no field is created, no record is written.\n */\n\nimport type { NormalizedField } from './normalizer';\nimport { assembleComposites } from './normalizer';\nimport { mergePatch, type FieldMergeStrategy } from './merge';\nimport type { MergePolicy } from './settings';\nimport { findPersonByEmail, findCompanyByDomain, primaryEmailOf, primaryDomainOf } from './lookup';\nimport { metaApi } from './rest-client';\n\nexport type DiffFieldStatus =\n | 'create' // written to a brand new record\n | 'fill-empty' // the record exists and this field is blank\n | 'overwrite' // the record exists, holds a different value, and policy allows replacing it\n | 'preserved' // the record exists, holds a value, and policy keeps it\n | 'unchanged'; // already exactly this value\n\nexport type DiffField = {\n name: string;\n from: 'builtin' | 'rule' | 'passthrough' | 'derived';\n incoming: unknown;\n existing?: unknown;\n status: DiffFieldStatus;\n /** True when the field itself does not exist on the object yet. */\n fieldWouldBeCreated: boolean;\n twentyType?: string;\n};\n\nexport type DiffObject = {\n object: 'person' | 'company' | 'opportunity' | 'note';\n operation: 'create' | 'update' | 'skip';\n recordId: string | null;\n matchedBy: { field: string; value: string } | null;\n fields: DiffField[];\n fieldsToCreate: Array<{ name: string; type: string }>;\n};\n\nexport type IntakeDiff = {\n objects: DiffObject[];\n noteFields: string[];\n skipped: string[];\n warnings: string[];\n};\n\nexport type DiffInput = {\n crmFields: NormalizedField[];\n noteFields: NormalizedField[];\n skipped: NormalizedField[];\n structuredCompany?: Record<string, unknown> | undefined;\n structuredPerson?: Record<string, unknown> | undefined;\n createOpportunity: boolean;\n targetObject: 'PERSON' | 'COMPANY' | 'AUTO';\n policy: MergePolicy;\n warnings: string[];\n};\n\nconst STANDARD_BY_OBJECT: Record<string, Set<string>> = {\n person: new Set(['name', 'emails', 'phones', 'firstName', 'lastName', 'fullName', 'email', 'phone',\n 'linkedInLink', 'xLink', 'jobTitle', 'city', 'companyName', 'domainName', 'address']),\n company: new Set(['name', 'domainName', 'address', 'companyName', 'linkedInLink', 'xLink',\n 'employees', 'annualRecurringRevenue', 'idealCustomerProfile', 'city']),\n opportunity: new Set(['name', 'stage', 'amount', 'closeDate']),\n};\n\nexport async function buildDiff(input: DiffInput): Promise<IntakeDiff> {\n const clean = input.crmFields.filter((f) => !f.originalKey.startsWith('__'));\n const { assembled, remainder } = assembleComposites(clean);\n for (const f of remainder) assembled[f.canonicalName] = f.value;\n\n const objects: DiffObject[] = [];\n const existingFields = await objectFieldNames();\n\n const strategyFor = (field: string): FieldMergeStrategy =>\n clean.find((f) => f.canonicalName === field)?.mergeStrategy ?? 'INHERIT';\n\n // \u2500\u2500 Company \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const companyName = (input.structuredCompany?.['name'] as string | undefined)\n ?? (assembled['companyName'] as string | undefined);\n\n if (companyName || input.targetObject === 'COMPANY') {\n const standard: Record<string, unknown> = { ...(input.structuredCompany ?? {}) };\n if (companyName && !standard['name']) standard['name'] = companyName;\n if (!standard['domainName'] && assembled['domainName']) standard['domainName'] = assembled['domainName'];\n if (!standard['address'] && assembled['address']) standard['address'] = assembled['address'];\n\n const domain = primaryDomainOf(standard);\n const existing = domain ? await findCompanyByDomain(domain) : null;\n\n objects.push(describe(\n 'company',\n existing,\n domain ? { field: 'domainName', value: domain } : null,\n { ...standard, ...customFor(clean, 'company') },\n clean,\n existingFields['company'] ?? new Set(),\n input.policy,\n strategyFor,\n ));\n }\n\n // \u2500\u2500 Person \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const hasPersonData = assembled['emails'] || assembled['name'] || input.structuredPerson;\n if (hasPersonData || input.targetObject === 'PERSON') {\n const standard: Record<string, unknown> = { ...(input.structuredPerson ?? {}) };\n if (!standard['name'] && assembled['name']) standard['name'] = assembled['name'];\n if (!standard['emails'] && assembled['emails']) standard['emails'] = assembled['emails'];\n if (!standard['phones'] && assembled['phones']) standard['phones'] = assembled['phones'];\n\n const email = primaryEmailOf(standard) || primaryEmailOf(assembled);\n const existing = email ? await findPersonByEmail(email) : null;\n\n objects.push(describe(\n 'person',\n existing,\n email ? { field: 'emails.primaryEmail', value: email } : null,\n { ...standard, ...customFor(clean, 'person') },\n clean,\n existingFields['person'] ?? new Set(),\n input.policy,\n strategyFor,\n ));\n }\n\n // \u2500\u2500 Opportunity \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Always created when the source is configured to, never matched against an\n // existing deal \u2014 the pipeline does not dedupe opportunities, and a preview\n // that implied otherwise would be lying about what happens next.\n if (input.createOpportunity && objects.some((o) => o.operation !== 'skip')) {\n const oppCustom = customFor(clean, 'opportunity');\n const oppStandard: Record<string, unknown> = {};\n for (const f of clean) {\n if (f.targetObject === 'opportunity' && (f.canonicalName === 'amount' || f.canonicalName === 'closeDate')) {\n oppStandard[f.canonicalName] = f.value;\n }\n }\n objects.push(describe(\n 'opportunity',\n null,\n null,\n { ...oppStandard, ...oppCustom },\n clean,\n existingFields['opportunity'] ?? new Set(),\n input.policy,\n strategyFor,\n ));\n }\n\n return {\n objects,\n noteFields: input.noteFields.map((f) => f.canonicalName),\n skipped: input.skipped.map((f) => f.originalKey),\n warnings: input.warnings,\n };\n}\n\nfunction customFor(fields: NormalizedField[], object: string): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const f of fields) {\n const target = f.targetObject === 'auto' ? impliedPrimary(fields) : f.targetObject;\n if (target !== object) continue;\n if (STANDARD_BY_OBJECT[object]?.has(f.canonicalName)) continue;\n out[f.canonicalName] = f.value;\n }\n return out;\n}\n\n/** Mirrors SyncSchemaStage's primaryTarget for fields still marked `auto`. */\nfunction impliedPrimary(fields: NormalizedField[]): 'person' | 'company' {\n const hasPerson = fields.some((f) =>\n ['firstName', 'lastName', 'email', 'phone', 'fullName', 'emails', 'phones', 'name'].includes(f.canonicalName));\n return hasPerson ? 'person' : 'company';\n}\n\nfunction describe(\n object: DiffObject['object'],\n existing: Record<string, unknown> | null,\n matchedBy: { field: string; value: string } | null,\n incoming: Record<string, unknown>,\n normalized: NormalizedField[],\n objectFields: Set<string>,\n policy: MergePolicy,\n strategyFor: (field: string) => FieldMergeStrategy,\n): DiffObject {\n const { decisions } = existing\n ? mergePatch(existing, incoming, policy, strategyFor)\n : { decisions: {} as Record<string, { write: boolean; status: string }> };\n\n const fields: DiffField[] = [];\n const fieldsToCreate: Array<{ name: string; type: string }> = [];\n\n for (const [name, value] of Object.entries(incoming)) {\n const source = normalized.find((f) => f.canonicalName === name);\n const isStandard = STANDARD_BY_OBJECT[object]?.has(name) ?? false;\n const wouldCreate = !isStandard && !objectFields.has(name);\n\n if (wouldCreate) fieldsToCreate.push({ name, type: source?.twentyType ?? 'TEXT' });\n\n let status: DiffFieldStatus = 'create';\n if (existing) {\n const decided = decisions[name]?.status;\n status = decided === 'filled-empty' ? 'fill-empty'\n : decided === 'unchanged' ? 'unchanged'\n : decided === 'set' ? 'overwrite'\n : 'preserved';\n }\n\n fields.push({\n name,\n from: source?.source ?? 'derived',\n incoming: value,\n ...(existing ? { existing: existing[name] } : {}),\n status,\n fieldWouldBeCreated: wouldCreate,\n twentyType: source?.twentyType,\n });\n }\n\n return {\n object,\n operation: existing ? 'update' : 'create',\n recordId: (existing?.['id'] as string | undefined) ?? null,\n matchedBy,\n fields,\n fieldsToCreate,\n };\n}\n\n/**\n * Which fields each object already has. One metadata read serves every object in\n * the diff; a failure degrades to \"assume nothing exists\", which overstates the\n * fields that would be created rather than hiding any.\n */\nexport async function objectFieldNames(): Promise<Record<string, Set<string>>> {\n try {\n const res = await metaApi.get<Array<{ nameSingular: string; fields: Array<{ name: string }> }>>('/objects');\n const list = Array.isArray(res) ? res : ((res as { data?: typeof res }).data ?? []);\n const out: Record<string, Set<string>> = {};\n for (const node of list) {\n out[node.nameSingular] = new Set((node.fields ?? []).map((f) => f.name));\n }\n return out;\n } catch {\n return {};\n }\n}\n", "/**\n * Every workspace setting Intake reads, in one place.\n *\n * The pipeline used to reach for `process.env` inline at each use site, which\n * meant the default for a setting lived wherever it happened to be read and the\n * contract endpoint had no way to report what was actually in force. Reading\n * them here keeps the defaults in one list and lets `GET /s/intake/contract`\n * answer \"what is this app configured to do right now\" honestly.\n */\n\nexport type MergePolicy = 'PRESERVE' | 'NEWEST_WINS';\nexport type RetentionMode = 'FULL' | 'NONE';\n\nfunction str(key: string, fallback: string): string {\n const v = process.env[key];\n return v === undefined || v === '' ? fallback : v;\n}\n\nfunction int(key: string, fallback: number): number {\n const parsed = parseInt(process.env[key] ?? '', 10);\n return Number.isFinite(parsed) ? parsed : fallback;\n}\n\nfunction bool(key: string, fallback: boolean): boolean {\n const v = process.env[key];\n if (v === undefined || v === '') return fallback;\n return v.toLowerCase() === 'true';\n}\n\nexport function appLabel(): string {\n return str('INTAKE_APP_LABEL', 'Intake');\n}\n\nexport function defaultOpportunityStage(): string {\n return str('INTAKE_DEFAULT_OPP_STAGE', 'NEW');\n}\n\nexport function fieldCreationEnabled(): boolean {\n return process.env['INTAKE_FIELD_CREATION_ENABLED'] !== 'false';\n}\n\nexport function maxExtFields(): number {\n return int('INTAKE_MAX_EXT_FIELDS', 50);\n}\n\nexport function dedupWindowMinutes(): number {\n return int('INTAKE_DEDUP_WINDOW_MINUTES', 5);\n}\n\nexport function requireHmac(): boolean {\n return bool('INTAKE_REQUIRE_HMAC', false);\n}\n\n/**\n * How an update treats a field that already holds a value.\n *\n * PRESERVE fills blanks and leaves anything already written alone. It is the\n * default because the alternative loses data with no record of what was there:\n * a returning lead who types their name in lowercase should not overwrite the\n * corrected spelling a salesperson entered by hand. NEWEST_WINS restores the\n * last-write-wins behaviour of 0.3.0 and earlier.\n */\nexport function mergePolicy(): MergePolicy {\n return str('INTAKE_MERGE_POLICY', 'PRESERVE').toUpperCase() === 'NEWEST_WINS'\n ? 'NEWEST_WINS'\n : 'PRESERVE';\n}\n\nexport function spamFilterEnabled(): boolean {\n return bool('INTAKE_SPAM_FILTER_ENABLED', false);\n}\n\nexport function spamScoreThreshold(): number {\n return int('INTAKE_SPAM_SCORE_THRESHOLD', 5);\n}\n\n/** Workspace-wide honeypot key. A per-source value overrides it. */\nexport function honeypotField(): string {\n return str('INTAKE_HONEYPOT_FIELD', '');\n}\n\nexport function rawPayloadRetention(): RetentionMode {\n return str('INTAKE_RAW_PAYLOAD_RETENTION', 'FULL').toUpperCase() === 'NONE'\n ? 'NONE'\n : 'FULL';\n}\n\nexport function rawPayloadMaxBytes(): number {\n return int('INTAKE_RAW_PAYLOAD_MAX_BYTES', 65000);\n}\n\nexport function replayMaxBatch(): number {\n return Math.min(int('INTAKE_REPLAY_MAX_BATCH', 50), 500);\n}\n\n/** Everything above, resolved, for the contract endpoint. */\nexport function snapshot(): Record<string, string | number | boolean> {\n return {\n INTAKE_APP_LABEL: appLabel(),\n INTAKE_DEFAULT_OPP_STAGE: defaultOpportunityStage(),\n INTAKE_FIELD_CREATION_ENABLED: fieldCreationEnabled(),\n INTAKE_MAX_EXT_FIELDS: maxExtFields(),\n INTAKE_DEDUP_WINDOW_MINUTES: dedupWindowMinutes(),\n INTAKE_REQUIRE_HMAC: requireHmac(),\n INTAKE_MERGE_POLICY: mergePolicy(),\n INTAKE_SPAM_FILTER_ENABLED: spamFilterEnabled(),\n INTAKE_SPAM_SCORE_THRESHOLD: spamScoreThreshold(),\n INTAKE_HONEYPOT_FIELD: honeypotField(),\n INTAKE_RAW_PAYLOAD_RETENTION: rawPayloadRetention(),\n INTAKE_RAW_PAYLOAD_MAX_BYTES: rawPayloadMaxBytes(),\n INTAKE_REPLAY_MAX_BATCH: replayMaxBatch(),\n };\n}\n", "import { defineLogicFunction } from 'twenty-sdk/define';\nimport { IDS } from '../constants/universal-identifiers';\nimport { APP_VERSION } from '../constants/version';\nimport { FIELD_MAP } from '../constants/field-map';\nimport { coreApi } from '../lib/rest-client';\nimport { objectFieldNames } from '../lib/diff';\nimport { snapshot } from '../lib/settings';\n\n/**\n * Everything this app accepts and everything it currently is, in one call.\n *\n * The alternative, for anyone integrating, is reading the source for the field\n * map, querying the rules table for the overrides, and introspecting Twenty's\n * metadata API for the custom fields that already exist \u2014 three places, none of\n * which say how the settings are configured. An agent or a script should be able\n * to learn the contract without any of that, and then send a payload that lands\n * where it expects.\n *\n * Secrets never appear. A source reports whether it has a signing secret, not\n * what it is.\n */\nconst handler = async () => {\n const [sources, rules, objectFields] = await Promise.all([\n loadSources(),\n loadRules(),\n objectFieldNames(),\n ]);\n\n // Group the built-in map by destination so the answer is \"these keys all mean\n // email\" rather than a flat list the caller has to invert.\n const canonical = new Map<string, { accepts: string[]; type: string; action: string }>();\n for (const [key, entry] of Object.entries(FIELD_MAP)) {\n const existing = canonical.get(entry.canonicalName);\n if (existing) {\n existing.accepts.push(key);\n } else {\n canonical.set(entry.canonicalName, {\n accepts: [key],\n type: entry.twentyType,\n action: entry.action,\n });\n }\n }\n\n return {\n statusCode: 200,\n body: {\n app: {\n name: 'twenty-app-intake',\n version: APP_VERSION,\n requiredServerVersion: '>=2.26.0',\n },\n\n endpoints: ENDPOINTS,\n\n targetObjects: ['person', 'company', 'opportunity', 'note'],\n\n /** How a payload key becomes a field, in the order the pipeline tries. */\n resolutionOrder: [\n 'Built-in field map (canonicalFields below)',\n 'Field rules configured in the workspace (rules below)',\n 'Passthrough \u2014 the key is camel-cased, prefixed ext, and the type is detected from the value',\n ],\n\n canonicalFields: [...canonical.entries()].map(([name, v]) => ({\n canonicalName: name,\n accepts: v.accepts,\n twentyType: v.type,\n action: v.action,\n })),\n\n /**\n * Custom fields Intake has already created or can already write to. This\n * is the live schema, not a guess \u2014 a caller can check whether the field\n * it wants exists before sending anything.\n */\n extFields: {\n person: extOnly(objectFields['person']),\n company: extOnly(objectFields['company']),\n opportunity: extOnly(objectFields['opportunity']),\n },\n\n rules,\n sources,\n settings: snapshot(),\n\n routing: {\n note: 'A field reaches the Opportunity only when a rule says so or the key carries an explicit prefix. Nothing is routed to a deal by inference.',\n opportunityKeyPrefixes: ['opportunity_', 'opp_', 'deal_'],\n },\n },\n };\n};\n\nconst ENDPOINTS = [\n { method: 'POST', path: '/s/intake/:slug', auth: false, description: 'Ingest a payload.' },\n { method: 'POST', path: '/s/intake/:slug/test', auth: false, description: 'Dry run \u2014 returns a structured diff, writes nothing.' },\n { method: 'GET', path: '/s/intake/health', auth: false, description: 'Health check, plus a summary of silent sources.' },\n { method: 'GET', path: '/s/intake/contract', auth: true, description: 'This document.' },\n { method: 'POST', path: '/s/intake/sources/register', auth: true, description: 'Register a source and get a webhook URL and secret.' },\n { method: 'POST', path: '/s/intake/sources/check-silence', auth: true, description: 'Check every source against its expected cadence.' },\n { method: 'POST', path: '/s/intake/logs/:logId/retry', auth: true, description: 'Retry a failed ingestion from its stored payload.' },\n { method: 'POST', path: '/s/intake/logs/:logId/replay', auth: true, description: 'Re-run any stored payload through the current rules.' },\n { method: 'POST', path: '/s/intake/replay', auth: true, description: 'Bulk replay a selection of stored payloads.' },\n { method: 'GET', path: '/s/intake/quarantine', auth: true, description: 'List held payloads and why they were held.' },\n { method: 'POST', path: '/s/intake/quarantine/:logId/release', auth: true, description: 'Ingest a held payload, overruling the filter.' },\n { method: 'POST', path: '/s/intake/quarantine/:logId/discard', auth: true, description: 'Mark a held payload as junk.' },\n];\n\nfunction extOnly(fields: Set<string> | undefined): string[] {\n return [...(fields ?? [])].filter((f) => /^ext[A-Z]/.test(f)).sort();\n}\n\nasync function loadSources(): Promise<unknown[]> {\n try {\n const res = await coreApi.get<{\n data: { intakeSources: { edges: Array<{ node: Record<string, unknown> }> } };\n }>('/intakeSources?limit=200');\n\n return (res.data?.intakeSources?.edges ?? []).map(({ node }) => ({\n slug: node['slug'],\n name: node['name'],\n status: node['status'],\n targetObject: node['targetObject'],\n createOpportunity: node['createOpportunity'],\n // Whether requests must be signed \u2014 never the secret itself.\n requiresSignature: Boolean(node['webhookSecret']),\n honeypotField: node['honeypotField'] ?? null,\n mergePolicy: node['mergePolicy'] ?? null,\n expectedCadenceHours: node['expectedCadenceHours'] ?? null,\n healthStatus: node['healthStatus'] ?? null,\n lastIngestedAt: node['lastIngestedAt'] ?? null,\n totalIngested: node['totalIngested'] ?? 0,\n }));\n } catch {\n return [];\n }\n}\n\nasync function loadRules(): Promise<unknown[]> {\n try {\n const res = await coreApi.get<{\n data: { intakeFieldRules: { edges: Array<{ node: Record<string, unknown> }> } };\n }>('/intakeFieldRules?limit=200&orderBy=priority[DescNullsLast]');\n\n return (res.data?.intakeFieldRules?.edges ?? []).map(({ node }) => ({\n inputPattern: node['inputPattern'],\n canonicalName: node['canonicalName'],\n fieldType: node['fieldType'],\n targetObject: node['targetObject'] ?? 'AUTO',\n mergeStrategy: node['mergeStrategy'] ?? 'INHERIT',\n priority: node['priority'] ?? 0,\n isActive: node['isActive'] ?? true,\n scope: node['intakeSourceId'] ? 'source' : 'global',\n }));\n } catch {\n return [];\n }\n}\n\nexport default defineLogicFunction({\n universalIdentifier: IDS.CONTRACT_LOGIC_FUNCTION,\n name: 'intake-contract',\n description: 'Describes what Intake accepts and how it is currently configured, in one call.',\n timeoutSeconds: 20,\n handler,\n httpRouteTriggerSettings: {\n path: '/intake/contract',\n httpMethod: 'GET',\n isAuthRequired: true,\n },\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { defineLogicFunction } from 'twenty-sdk/define';
|
|
2
|
+
import { IDS } from '../constants/universal-identifiers';
|
|
3
|
+
import { APP_VERSION } from '../constants/version';
|
|
4
|
+
import { FIELD_MAP } from '../constants/field-map';
|
|
5
|
+
import { coreApi } from '../lib/rest-client';
|
|
6
|
+
import { objectFieldNames } from '../lib/diff';
|
|
7
|
+
import { snapshot } from '../lib/settings';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Everything this app accepts and everything it currently is, in one call.
|
|
11
|
+
*
|
|
12
|
+
* The alternative, for anyone integrating, is reading the source for the field
|
|
13
|
+
* map, querying the rules table for the overrides, and introspecting Twenty's
|
|
14
|
+
* metadata API for the custom fields that already exist — three places, none of
|
|
15
|
+
* which say how the settings are configured. An agent or a script should be able
|
|
16
|
+
* to learn the contract without any of that, and then send a payload that lands
|
|
17
|
+
* where it expects.
|
|
18
|
+
*
|
|
19
|
+
* Secrets never appear. A source reports whether it has a signing secret, not
|
|
20
|
+
* what it is.
|
|
21
|
+
*/
|
|
22
|
+
const handler = async () => {
|
|
23
|
+
const [sources, rules, objectFields] = await Promise.all([
|
|
24
|
+
loadSources(),
|
|
25
|
+
loadRules(),
|
|
26
|
+
objectFieldNames(),
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
// Group the built-in map by destination so the answer is "these keys all mean
|
|
30
|
+
// email" rather than a flat list the caller has to invert.
|
|
31
|
+
const canonical = new Map<string, { accepts: string[]; type: string; action: string }>();
|
|
32
|
+
for (const [key, entry] of Object.entries(FIELD_MAP)) {
|
|
33
|
+
const existing = canonical.get(entry.canonicalName);
|
|
34
|
+
if (existing) {
|
|
35
|
+
existing.accepts.push(key);
|
|
36
|
+
} else {
|
|
37
|
+
canonical.set(entry.canonicalName, {
|
|
38
|
+
accepts: [key],
|
|
39
|
+
type: entry.twentyType,
|
|
40
|
+
action: entry.action,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
statusCode: 200,
|
|
47
|
+
body: {
|
|
48
|
+
app: {
|
|
49
|
+
name: 'twenty-app-intake',
|
|
50
|
+
version: APP_VERSION,
|
|
51
|
+
requiredServerVersion: '>=2.26.0',
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
endpoints: ENDPOINTS,
|
|
55
|
+
|
|
56
|
+
targetObjects: ['person', 'company', 'opportunity', 'note'],
|
|
57
|
+
|
|
58
|
+
/** How a payload key becomes a field, in the order the pipeline tries. */
|
|
59
|
+
resolutionOrder: [
|
|
60
|
+
'Built-in field map (canonicalFields below)',
|
|
61
|
+
'Field rules configured in the workspace (rules below)',
|
|
62
|
+
'Passthrough — the key is camel-cased, prefixed ext, and the type is detected from the value',
|
|
63
|
+
],
|
|
64
|
+
|
|
65
|
+
canonicalFields: [...canonical.entries()].map(([name, v]) => ({
|
|
66
|
+
canonicalName: name,
|
|
67
|
+
accepts: v.accepts,
|
|
68
|
+
twentyType: v.type,
|
|
69
|
+
action: v.action,
|
|
70
|
+
})),
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Custom fields Intake has already created or can already write to. This
|
|
74
|
+
* is the live schema, not a guess — a caller can check whether the field
|
|
75
|
+
* it wants exists before sending anything.
|
|
76
|
+
*/
|
|
77
|
+
extFields: {
|
|
78
|
+
person: extOnly(objectFields['person']),
|
|
79
|
+
company: extOnly(objectFields['company']),
|
|
80
|
+
opportunity: extOnly(objectFields['opportunity']),
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
rules,
|
|
84
|
+
sources,
|
|
85
|
+
settings: snapshot(),
|
|
86
|
+
|
|
87
|
+
routing: {
|
|
88
|
+
note: 'A field reaches the Opportunity only when a rule says so or the key carries an explicit prefix. Nothing is routed to a deal by inference.',
|
|
89
|
+
opportunityKeyPrefixes: ['opportunity_', 'opp_', 'deal_'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const ENDPOINTS = [
|
|
96
|
+
{ method: 'POST', path: '/s/intake/:slug', auth: false, description: 'Ingest a payload.' },
|
|
97
|
+
{ method: 'POST', path: '/s/intake/:slug/test', auth: false, description: 'Dry run — returns a structured diff, writes nothing.' },
|
|
98
|
+
{ method: 'GET', path: '/s/intake/health', auth: false, description: 'Health check, plus a summary of silent sources.' },
|
|
99
|
+
{ method: 'GET', path: '/s/intake/contract', auth: true, description: 'This document.' },
|
|
100
|
+
{ method: 'POST', path: '/s/intake/sources/register', auth: true, description: 'Register a source and get a webhook URL and secret.' },
|
|
101
|
+
{ method: 'POST', path: '/s/intake/sources/check-silence', auth: true, description: 'Check every source against its expected cadence.' },
|
|
102
|
+
{ method: 'POST', path: '/s/intake/logs/:logId/retry', auth: true, description: 'Retry a failed ingestion from its stored payload.' },
|
|
103
|
+
{ method: 'POST', path: '/s/intake/logs/:logId/replay', auth: true, description: 'Re-run any stored payload through the current rules.' },
|
|
104
|
+
{ method: 'POST', path: '/s/intake/replay', auth: true, description: 'Bulk replay a selection of stored payloads.' },
|
|
105
|
+
{ method: 'GET', path: '/s/intake/quarantine', auth: true, description: 'List held payloads and why they were held.' },
|
|
106
|
+
{ method: 'POST', path: '/s/intake/quarantine/:logId/release', auth: true, description: 'Ingest a held payload, overruling the filter.' },
|
|
107
|
+
{ method: 'POST', path: '/s/intake/quarantine/:logId/discard', auth: true, description: 'Mark a held payload as junk.' },
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
function extOnly(fields: Set<string> | undefined): string[] {
|
|
111
|
+
return [...(fields ?? [])].filter((f) => /^ext[A-Z]/.test(f)).sort();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function loadSources(): Promise<unknown[]> {
|
|
115
|
+
try {
|
|
116
|
+
const res = await coreApi.get<{
|
|
117
|
+
data: { intakeSources: { edges: Array<{ node: Record<string, unknown> }> } };
|
|
118
|
+
}>('/intakeSources?limit=200');
|
|
119
|
+
|
|
120
|
+
return (res.data?.intakeSources?.edges ?? []).map(({ node }) => ({
|
|
121
|
+
slug: node['slug'],
|
|
122
|
+
name: node['name'],
|
|
123
|
+
status: node['status'],
|
|
124
|
+
targetObject: node['targetObject'],
|
|
125
|
+
createOpportunity: node['createOpportunity'],
|
|
126
|
+
// Whether requests must be signed — never the secret itself.
|
|
127
|
+
requiresSignature: Boolean(node['webhookSecret']),
|
|
128
|
+
honeypotField: node['honeypotField'] ?? null,
|
|
129
|
+
mergePolicy: node['mergePolicy'] ?? null,
|
|
130
|
+
expectedCadenceHours: node['expectedCadenceHours'] ?? null,
|
|
131
|
+
healthStatus: node['healthStatus'] ?? null,
|
|
132
|
+
lastIngestedAt: node['lastIngestedAt'] ?? null,
|
|
133
|
+
totalIngested: node['totalIngested'] ?? 0,
|
|
134
|
+
}));
|
|
135
|
+
} catch {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function loadRules(): Promise<unknown[]> {
|
|
141
|
+
try {
|
|
142
|
+
const res = await coreApi.get<{
|
|
143
|
+
data: { intakeFieldRules: { edges: Array<{ node: Record<string, unknown> }> } };
|
|
144
|
+
}>('/intakeFieldRules?limit=200&orderBy=priority[DescNullsLast]');
|
|
145
|
+
|
|
146
|
+
return (res.data?.intakeFieldRules?.edges ?? []).map(({ node }) => ({
|
|
147
|
+
inputPattern: node['inputPattern'],
|
|
148
|
+
canonicalName: node['canonicalName'],
|
|
149
|
+
fieldType: node['fieldType'],
|
|
150
|
+
targetObject: node['targetObject'] ?? 'AUTO',
|
|
151
|
+
mergeStrategy: node['mergeStrategy'] ?? 'INHERIT',
|
|
152
|
+
priority: node['priority'] ?? 0,
|
|
153
|
+
isActive: node['isActive'] ?? true,
|
|
154
|
+
scope: node['intakeSourceId'] ? 'source' : 'global',
|
|
155
|
+
}));
|
|
156
|
+
} catch {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default defineLogicFunction({
|
|
162
|
+
universalIdentifier: IDS.CONTRACT_LOGIC_FUNCTION,
|
|
163
|
+
name: 'intake-contract',
|
|
164
|
+
description: 'Describes what Intake accepts and how it is currently configured, in one call.',
|
|
165
|
+
timeoutSeconds: 20,
|
|
166
|
+
handler,
|
|
167
|
+
httpRouteTriggerSettings: {
|
|
168
|
+
path: '/intake/contract',
|
|
169
|
+
httpMethod: 'GET',
|
|
170
|
+
isAuthRequired: true,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
@@ -62,6 +62,7 @@ var IDS = {
|
|
|
62
62
|
INTAKE_FIELD_RULE_FIELD_TYPE: "72d1f6d8-d63e-419e-a244-8127d7b4705f",
|
|
63
63
|
INTAKE_FIELD_RULE_PRIORITY: "911df299-940b-44f9-92f7-3fd01fde01a7",
|
|
64
64
|
INTAKE_FIELD_RULE_IS_ACTIVE: "6f9cd96d-aecd-437f-a7b5-d45548782771",
|
|
65
|
+
INTAKE_FIELD_RULE_TARGET_OBJECT: "7b8411a7-5e1d-40f2-a20c-683c07f67926",
|
|
65
66
|
// Logic Functions
|
|
66
67
|
WEBHOOK_LOGIC_FUNCTION: "8323c257-48fe-4bb6-b1dd-166844f575c0",
|
|
67
68
|
HEALTH_LOGIC_FUNCTION: "4ece4e39-286b-4820-81f4-b7beee71d092",
|
|
@@ -87,15 +88,134 @@ var IDS = {
|
|
|
87
88
|
// Views & Navigation
|
|
88
89
|
SOURCES_VIEW: "4435cd43-5c13-472f-b4f9-b686c0f46285",
|
|
89
90
|
LOGS_VIEW: "a2cd0a93-9778-4ba5-99d9-b8b89c4fb39d",
|
|
90
|
-
INTAKE_NAV_ITEM: "e81bf288-5686-4713-86ae-e2947b124cab"
|
|
91
|
+
INTAKE_NAV_ITEM: "e81bf288-5686-4713-86ae-e2947b124cab",
|
|
92
|
+
// ── v0.4.0 ────────────────────────────────────────────────────────────────
|
|
93
|
+
// IntakeLog fields — quarantine, idempotency, replay lineage
|
|
94
|
+
INTAKE_LOG_IDEMPOTENCY_KEY: "bf91e4c0-6477-41f2-a9bd-fb159d3091d4",
|
|
95
|
+
INTAKE_LOG_SPAM_SCORE: "4e250af2-925f-4724-85f6-f353a6bb4cfd",
|
|
96
|
+
INTAKE_LOG_QUARANTINE_REASON: "323b3c3e-a111-45d1-a211-cf70efa6cd9e",
|
|
97
|
+
INTAKE_LOG_REPLAY_OF: "b68876b1-0045-4eaa-a4e7-f0634e110ef1",
|
|
98
|
+
// IntakeSource fields — silence detection, spam, merge policy
|
|
99
|
+
INTAKE_SOURCE_EXPECTED_CADENCE: "a97c3906-5555-4e60-a6d6-84155a4e00a6",
|
|
100
|
+
INTAKE_SOURCE_HEALTH_STATUS: "2ad13b20-5bd7-450a-b52d-bcf208373cf5",
|
|
101
|
+
INTAKE_SOURCE_SILENT_SINCE: "d3395601-f29f-4e57-98eb-561ab011cc72",
|
|
102
|
+
INTAKE_SOURCE_ALERT_WEBHOOK: "5a066d62-0426-41c1-b34c-1a7e881ce854",
|
|
103
|
+
INTAKE_SOURCE_LAST_ALERT_AT: "fc64a598-ccbe-44e3-bd42-716e32d626a2",
|
|
104
|
+
INTAKE_SOURCE_HONEYPOT_FIELD: "5c145686-50de-496c-bb45-a30c8b39034c",
|
|
105
|
+
INTAKE_SOURCE_MERGE_POLICY: "5a946f85-b1c2-493e-92c6-31c50e637a7e",
|
|
106
|
+
// IntakeFieldRule fields
|
|
107
|
+
INTAKE_FIELD_RULE_MERGE_STRATEGY: "40c6fdcb-527a-4691-8518-c40a3d3e34c6",
|
|
108
|
+
// Logic Functions
|
|
109
|
+
CONTRACT_LOGIC_FUNCTION: "2eea4152-1b08-4b8c-903c-f4c7ee7275d0",
|
|
110
|
+
REPLAY_LOG_LOGIC_FUNCTION: "8ff98390-7358-4a91-84f3-435f758182a3",
|
|
111
|
+
REPLAY_BULK_LOGIC_FUNCTION: "b82ed4c4-07bc-4095-8ee5-611200626dc0",
|
|
112
|
+
QUARANTINE_LIST_LOGIC_FUNCTION: "0203643c-2a97-4e7c-b4a5-b76d721cf23a",
|
|
113
|
+
QUARANTINE_RELEASE_LOGIC_FUNCTION: "5a10d4fe-c807-45c6-9e61-c191d4e37b4e",
|
|
114
|
+
QUARANTINE_DISCARD_LOGIC_FUNCTION: "1454a56e-6197-4dfd-9b41-5cc3b1436af7",
|
|
115
|
+
CHECK_SILENCE_LOGIC_FUNCTION: "e88cd5f2-ab96-4f89-ac55-78db66b32d66",
|
|
116
|
+
// Application variables
|
|
117
|
+
VAR_MERGE_POLICY: "a84fe9fd-3d7c-41d9-b0e8-dfdc1a87f348",
|
|
118
|
+
VAR_SPAM_FILTER_ENABLED: "570d3ba8-1904-4bad-a5ce-27e208e6414a",
|
|
119
|
+
VAR_SPAM_SCORE_THRESHOLD: "4395574d-24b4-4e0e-afa3-1e90fcd4f470",
|
|
120
|
+
VAR_HONEYPOT_FIELD: "2cf56c56-f5cb-47c5-aa43-84f301060a95",
|
|
121
|
+
VAR_RAW_PAYLOAD_RETENTION: "f3314ad6-05b9-4127-bb88-cea5f2018dc9",
|
|
122
|
+
VAR_RAW_PAYLOAD_MAX_BYTES: "b002ca76-df3d-47b4-8ac5-d3f562239c48",
|
|
123
|
+
VAR_REPLAY_MAX_BATCH: "cf55956f-177e-4afd-8d9f-7f42c614209c"
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/constants/version.ts
|
|
127
|
+
var APP_VERSION = "0.4.0";
|
|
128
|
+
|
|
129
|
+
// src/lib/rest-client.ts
|
|
130
|
+
var apiUrl = () => process.env["TWENTY_API_URL"] ?? "";
|
|
131
|
+
var token = () => process.env["TWENTY_APP_ACCESS_TOKEN"] ?? "";
|
|
132
|
+
async function request(base, method, path, body) {
|
|
133
|
+
const res = await fetch(`${base}${path}`, {
|
|
134
|
+
method,
|
|
135
|
+
headers: {
|
|
136
|
+
Authorization: `Bearer ${token()}`,
|
|
137
|
+
"Content-Type": "application/json"
|
|
138
|
+
},
|
|
139
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
140
|
+
});
|
|
141
|
+
const text = await res.text();
|
|
142
|
+
if (!res.ok) {
|
|
143
|
+
throw new Error(`${method} ${path} \u2192 ${res.status}: ${text}`);
|
|
144
|
+
}
|
|
145
|
+
return JSON.parse(text);
|
|
146
|
+
}
|
|
147
|
+
var coreApi = {
|
|
148
|
+
get: (path) => request(`${apiUrl()}/rest`, "GET", path),
|
|
149
|
+
post: (path, body) => request(`${apiUrl()}/rest`, "POST", path, body),
|
|
150
|
+
patch: (path, body) => request(`${apiUrl()}/rest`, "PATCH", path, body)
|
|
91
151
|
};
|
|
92
152
|
|
|
153
|
+
// src/lib/silence.ts
|
|
154
|
+
var HOUR_MS = 60 * 60 * 1e3;
|
|
155
|
+
function assess(source, now = Date.now()) {
|
|
156
|
+
const base = {
|
|
157
|
+
slug: source.slug,
|
|
158
|
+
name: source.name,
|
|
159
|
+
expectedCadenceHours: source.expectedCadenceHours
|
|
160
|
+
};
|
|
161
|
+
if (source.status === "PAUSED" || !source.expectedCadenceHours || source.expectedCadenceHours <= 0) {
|
|
162
|
+
return { ...base, health: "NOT_MONITORED", hoursSinceLastIngest: hoursSince(source.lastIngestedAt, now), newlySilent: false };
|
|
163
|
+
}
|
|
164
|
+
const wasSilent = source.healthStatus === "SILENT" || source.healthStatus === "NEVER_RECEIVED";
|
|
165
|
+
if (!source.lastIngestedAt) {
|
|
166
|
+
return { ...base, health: "NEVER_RECEIVED", hoursSinceLastIngest: null, newlySilent: !wasSilent };
|
|
167
|
+
}
|
|
168
|
+
const hours = hoursSince(source.lastIngestedAt, now);
|
|
169
|
+
if (hours === null || hours > source.expectedCadenceHours) {
|
|
170
|
+
return { ...base, health: "SILENT", hoursSinceLastIngest: hours, newlySilent: !wasSilent };
|
|
171
|
+
}
|
|
172
|
+
return { ...base, health: "HEALTHY", hoursSinceLastIngest: hours, newlySilent: false };
|
|
173
|
+
}
|
|
174
|
+
function hoursSince(iso, now) {
|
|
175
|
+
if (!iso) return null;
|
|
176
|
+
const then = new Date(iso).getTime();
|
|
177
|
+
if (!Number.isFinite(then)) return null;
|
|
178
|
+
return Math.round((now - then) / HOUR_MS * 10) / 10;
|
|
179
|
+
}
|
|
180
|
+
async function loadSources() {
|
|
181
|
+
try {
|
|
182
|
+
const res = await coreApi.get(
|
|
183
|
+
"/intakeSources?limit=200"
|
|
184
|
+
);
|
|
185
|
+
return (res.data?.intakeSources?.edges ?? []).map((e) => e.node);
|
|
186
|
+
} catch {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
93
191
|
// src/logic-functions/health.ts
|
|
94
|
-
var handler = async () => {
|
|
192
|
+
var handler = async (params) => {
|
|
193
|
+
const strict = (params.queryParameters ?? {})["strict"] === "true";
|
|
95
194
|
try {
|
|
96
195
|
const client = new CoreApiClient();
|
|
97
196
|
await client.query({ intakeSources: { edges: { node: { id: true } } } });
|
|
98
|
-
|
|
197
|
+
const verdicts = (await loadSources()).map((s) => assess(s));
|
|
198
|
+
const silent = verdicts.filter((v) => v.health === "SILENT" || v.health === "NEVER_RECEIVED");
|
|
199
|
+
return {
|
|
200
|
+
statusCode: strict && silent.length > 0 ? 503 : 200,
|
|
201
|
+
body: {
|
|
202
|
+
status: strict && silent.length > 0 ? "degraded" : "ok",
|
|
203
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
204
|
+
version: APP_VERSION,
|
|
205
|
+
sources: {
|
|
206
|
+
total: verdicts.length,
|
|
207
|
+
monitored: verdicts.filter((v) => v.health !== "NOT_MONITORED").length,
|
|
208
|
+
silent: silent.length,
|
|
209
|
+
silentSources: silent.map((v) => ({
|
|
210
|
+
slug: v.slug,
|
|
211
|
+
name: v.name,
|
|
212
|
+
health: v.health,
|
|
213
|
+
hoursSinceLastIngest: v.hoursSinceLastIngest,
|
|
214
|
+
expectedCadenceHours: v.expectedCadenceHours
|
|
215
|
+
}))
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
99
219
|
} catch (e) {
|
|
100
220
|
return { statusCode: 503, body: { status: "error", message: String(e) } };
|
|
101
221
|
}
|
|
@@ -103,8 +223,8 @@ var handler = async () => {
|
|
|
103
223
|
var health_default = defineLogicFunction({
|
|
104
224
|
universalIdentifier: IDS.HEALTH_LOGIC_FUNCTION,
|
|
105
225
|
name: "intake-health",
|
|
106
|
-
description: "Health check for the Intake app.",
|
|
107
|
-
timeoutSeconds:
|
|
226
|
+
description: "Health check for the Intake app, including sources that have fallen silent.",
|
|
227
|
+
timeoutSeconds: 15,
|
|
108
228
|
handler,
|
|
109
229
|
httpRouteTriggerSettings: {
|
|
110
230
|
path: "/intake/health",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["twenty-sdk-define-stub:__twenty-sdk-define-stub__", "../../../../src/logic-functions/health.ts", "../../../../src/constants/universal-identifiers.ts"],
|
|
4
|
-
"sourcesContent": ["\n// Auto-generated stub for twenty-sdk/define injected by the SDK CLI build.\n// Real implementations would pull in zod, twenty-shared and ~1MB of code; at\n// runtime only `default.config.handler` is consumed, so tiny no-ops suffice.\nconst __defineFactoryStub = (config) => ({\n success: true,\n config,\n errors: [],\n});\n\nconst __anyHandler = {\n get(_target, prop) {\n if (prop === '__esModule') return true;\n if (prop === Symbol.toPrimitive) return () => '';\n if (typeof prop === 'symbol') return undefined;\n return new Proxy(() => undefined, __anyHandler);\n },\n apply() {\n return new Proxy(() => undefined, __anyHandler);\n },\n};\nconst __anyStub = new Proxy(() => undefined, __anyHandler);\n\nexport const createValidationResult = __defineFactoryStub;\nexport const defineAgent = __defineFactoryStub;\nexport const defineApplication = __defineFactoryStub;\nexport const defineApplicationRole = __defineFactoryStub;\nexport const defineCommandMenuItem = __defineFactoryStub;\nexport const defineConnectionProvider = __defineFactoryStub;\nexport const defineField = __defineFactoryStub;\nexport const defineFrontComponent = __defineFactoryStub;\nexport const defineIndex = __defineFactoryStub;\nexport const defineLogicFunction = __defineFactoryStub;\nexport const defineNavigationMenuItem = __defineFactoryStub;\nexport const defineObject = __defineFactoryStub;\nexport const definePageLayout = __defineFactoryStub;\nexport const definePageLayoutTab = __defineFactoryStub;\nexport const definePermissionFlag = __defineFactoryStub;\nexport const definePostInstallLogicFunction = __defineFactoryStub;\nexport const definePreInstallLogicFunction = __defineFactoryStub;\nexport const defineRole = __defineFactoryStub;\nexport const defineSettingsFrontComponent = __defineFactoryStub;\nexport const defineSkill = __defineFactoryStub;\nexport const defineUninstallLogicFunction = __defineFactoryStub;\nexport const defineView = __defineFactoryStub;\nexport const defineViewField = __defineFactoryStub;\nexport const AggregateOperations = __anyStub;\nexport const DateDisplayFormat = __anyStub;\nexport const FieldMetadataSettingsOnClickAction = __anyStub;\nexport const FieldType = __anyStub;\nexport const HTTPMethod = __anyStub;\nexport const NavigationMenuItemType = __anyStub;\nexport const NumberDataType = __anyStub;\nexport const ObjectOpenRecordIn = __anyStub;\nexport const ObjectRecordGroupByDateGranularity = __anyStub;\nexport const OnDeleteAction = __anyStub;\nexport const PageLayoutTabLayoutMode = __anyStub;\nexport const PageLayoutType = __anyStub;\nexport const RelationType = __anyStub;\nexport const RowLevelPermissionPredicateGroupLogicalOperator = __anyStub;\nexport const RowLevelPermissionPredicateOperand = __anyStub;\nexport const STANDARD_OBJECT = __anyStub;\nexport const STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS = __anyStub;\nexport const STANDARD_PAGE_LAYOUT = __anyStub;\nexport const STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS = __anyStub;\nexport const SystemPermissionFlag = __anyStub;\nexport const ViewCalendarLayout = __anyStub;\nexport const ViewFilterGroupLogicalOperator = __anyStub;\nexport const ViewFilterOperand = __anyStub;\nexport const ViewKey = __anyStub;\nexport const ViewOpenRecordIn = __anyStub;\nexport const ViewSortDirection = __anyStub;\nexport const ViewType = __anyStub;\nexport const ViewVisibility = __anyStub;\nexport const canAccessFullAdminPanel = __anyStub;\nexport const canImpersonate = __anyStub;\nexport const every = __anyStub;\nexport const everyDefined = __anyStub;\nexport const everyEquals = __anyStub;\nexport const favoriteRecordIds = __anyStub;\nexport const featureFlags = __anyStub;\nexport const getFieldUniversalIdentifier = __anyStub;\nexport const getSystemRelationFieldUniversalIdentifier = __anyStub;\nexport const getSystemViewFieldUniversalIdentifier = __anyStub;\nexport const getSystemViewUniversalIdentifier = __anyStub;\nexport const hasAnySoftDeleteFilterOnView = __anyStub;\nexport const includes = __anyStub;\nexport const includesEvery = __anyStub;\nexport const isDashboardPageLayoutInEditMode = __anyStub;\nexport const isDefined = __anyStub;\nexport const isInSidePanel = __anyStub;\nexport const isLayoutCustomizationModeEnabled = __anyStub;\nexport const isNonEmptyString = __anyStub;\nexport const isSelectAll = __anyStub;\nexport const none = __anyStub;\nexport const noneDefined = __anyStub;\nexport const noneEquals = __anyStub;\nexport const numberOfSelectedRecords = __anyStub;\nexport const objectMetadataItem = __anyStub;\nexport const objectMetadataLabel = __anyStub;\nexport const objectPermissions = __anyStub;\nexport const pageType = __anyStub;\nexport const selectedRecords = __anyStub;\nexport const some = __anyStub;\nexport const someDefined = __anyStub;\nexport const someEquals = __anyStub;\nexport const someNonEmptyString = __anyStub;\nexport const targetObjectReadPermissions = __anyStub;\nexport const targetObjectWritePermissions = __anyStub;\nexport const validateFields = __anyStub;\n", "import { defineLogicFunction } from 'twenty-sdk/define';\nimport { CoreApiClient } from 'twenty-client-sdk/core';\nimport { IDS } from '../constants/universal-identifiers';\n\nconst handler = async () => {\n try {\n const client = new CoreApiClient();\n await client.query({ intakeSources: { edges: { node: { id: true } } } });\n return { statusCode: 200, body: { status: 'ok', timestamp: new Date().toISOString() } };\n } catch (e) {\n return { statusCode: 503, body: { status: 'error', message: String(e) } };\n }\n};\n\nexport default defineLogicFunction({\n universalIdentifier: IDS.HEALTH_LOGIC_FUNCTION,\n name: 'intake-health',\n description: 'Health check for the Intake app.',\n timeoutSeconds: 5,\n handler,\n httpRouteTriggerSettings: {\n path: '/intake/health',\n httpMethod: 'GET',\n isAuthRequired: false,\n },\n});\n", "export const IDS = {\n APPLICATION: '96a389dd-384a-43a1-a376-9d3cf01b6ab7',\n DEFAULT_ROLE: '3dd2cdb0-32c7-44b4-9f7b-0a9c667ec249',\n\n // Objects\n INTAKE_SOURCE_OBJECT: '8c1c227c-db7d-4834-b328-cacbe8af0bfc',\n INTAKE_LOG_OBJECT: 'b77a2439-1503-4ef6-ab8b-8f4293bc19da',\n INTAKE_FIELD_RULE_OBJECT: 'e74df817-ff25-4bfb-bdf0-651662ca327e',\n\n // IntakeSource fields\n INTAKE_SOURCE_NAME: '0deb7e4b-bb90-4c86-b903-5208b42041e2',\n INTAKE_SOURCE_SLUG: '18f7645b-2dce-4145-b1b6-9cc3332c0578',\n INTAKE_SOURCE_TARGET_OBJECT: '65f3d7f7-a601-4375-9c67-684d0044620b',\n INTAKE_SOURCE_SECRET: '4c919402-07f5-4041-ade6-a42402f1881f',\n INTAKE_SOURCE_STATUS: '6f1fa6fe-02b4-48ab-b1ee-97c600bbf483',\n INTAKE_SOURCE_CREATE_OPP: '67ae59aa-2275-4ef2-b594-137f6bfafd99',\n INTAKE_SOURCE_OPP_TEMPLATE: '3907b346-77a6-4c79-a4d7-6e1cc20bb82b',\n INTAKE_SOURCE_TOTAL_INGESTED: '69655a93-bc70-48bd-874e-aeccc63ee1a8',\n INTAKE_SOURCE_TOTAL_FAILED: '27b3a499-183e-4cc3-8e40-938f953a986d',\n INTAKE_SOURCE_LAST_INGESTED: 'e167f628-9865-468e-894a-b4b6150cec26',\n\n // IntakeLog fields\n INTAKE_LOG_SOURCE_RELATION: 'b1c623ea-7a9a-47a5-bee4-35fdd668a637',\n INTAKE_LOG_STATUS: '8b6d26d1-f89c-486c-a617-9f9f6a9ca36d',\n INTAKE_LOG_PAYLOAD_HASH: 'c43bb1ad-e78a-4c60-bd95-02475ede7f44',\n INTAKE_LOG_RECORD_ID: '77a55116-a64b-4954-8369-341022dd5bdd',\n INTAKE_LOG_RECORD_TYPE: '02154e96-5808-48ed-8838-51083f174550',\n INTAKE_LOG_FIELDS_CREATED: '2295ca2b-b559-4b38-9771-12dd8d3d76d7',\n INTAKE_LOG_FIELDS_MATCHED: '83d07a0f-5bcd-4fb1-9759-f9b04b9381d8',\n INTAKE_LOG_OVERFLOW_COUNT: '4090acb1-f48a-4957-bb91-c642f8f434a7',\n INTAKE_LOG_PROCESSING_MS: 'a9191f27-4b40-42e9-8cce-20c75900e12c',\n INTAKE_LOG_ERROR: '8d9979f1-9774-43a8-b958-d1b37bd4ecc7',\n INTAKE_LOG_PROCESSED_AT: 'a8d99fbd-722e-4987-8afe-b830ac026f0a',\n\n // IntakeFieldRule fields\n INTAKE_FIELD_RULE_SOURCE_RELATION: '396dd1f1-3873-4efa-a6bb-7b41eda14d83',\n INTAKE_FIELD_RULE_INPUT_PATTERN: '757201a0-9f64-47b9-bf3a-a793074bf84e',\n INTAKE_FIELD_RULE_CANONICAL_NAME: 'fafebc55-a2cc-4c9f-975b-050c2676659d',\n INTAKE_FIELD_RULE_FIELD_TYPE: '72d1f6d8-d63e-419e-a244-8127d7b4705f',\n INTAKE_FIELD_RULE_PRIORITY: '911df299-940b-44f9-92f7-3fd01fde01a7',\n INTAKE_FIELD_RULE_IS_ACTIVE: '6f9cd96d-aecd-437f-a7b5-d45548782771',\n\n // Logic Functions\n WEBHOOK_LOGIC_FUNCTION: '8323c257-48fe-4bb6-b1dd-166844f575c0',\n HEALTH_LOGIC_FUNCTION: '4ece4e39-286b-4820-81f4-b7beee71d092',\n REGISTER_LOGIC_FUNCTION: '56d46e18-6cae-4d99-9893-b7d70740c9db',\n\n // IntakeSource reverse relation fields\n INTAKE_SOURCE_LOGS_REVERSE: '6ecfa369-22e0-4812-bb75-4338cea8be65',\n INTAKE_SOURCE_RULES_REVERSE: '776fefaf-0eee-4ec0-93e0-d11bd80c1e20',\n\n // v0.2.0 \u2014 rawPayload + retry + post-install + UI\n INTAKE_LOG_RAW_PAYLOAD: 'b65c9e1c-f2c1-4c52-a5b3-1e1ac3139692',\n RETRY_LOGIC_FUNCTION: '90f20396-a69a-4ae4-af20-6be65c565bb5',\n POST_INSTALL_LOGIC_FUNCTION: 'e836fdd6-fd54-4aa4-ba6d-87d5a02148ef',\n LOG_RETRY_FRONT_COMPONENT: '0cb97f6d-30e2-48c8-90e1-241b82870b4d',\n FIELD_RULES_TABLE_FRONT_COMPONENT: 'c35faad9-3468-4746-9c1c-b13d73e5e09b',\n SOURCE_PAGE_LAYOUT: '8c894ba8-5c8c-45ed-b0af-1d22ede93eb9',\n SOURCE_LAYOUT_TAB_RULES: '80f5c49a-083c-4062-8bd5-ae158abb6c33',\n SOURCE_LAYOUT_TAB_RULES_WIDGET: '26a9ebb7-1d59-4b03-a018-56968f6df224',\n LOG_PAGE_LAYOUT: 'b0972802-ede5-463c-9f59-cb05f04957db',\n LOG_LAYOUT_TAB_RETRY: '6b109695-ba3a-43de-a31a-23119ccf87ba',\n LOG_LAYOUT_TAB_RETRY_WIDGET: '11727c1a-5145-46de-9742-88d07d64c72e',\n SAMPLE_RULE_PHONE: '8440da8d-8ac4-4f4b-baa2-4d62936cde35',\n SAMPLE_RULE_EMAIL: 'd123cde4-05aa-44e1-86f6-454be1d7048b',\n SAMPLE_RULE_FULLNAME: '00e49fe8-1b48-4f5b-aaf4-414d0923c712',\n\n // Views & Navigation\n SOURCES_VIEW: '4435cd43-5c13-472f-b4f9-b686c0f46285',\n LOGS_VIEW: 'a2cd0a93-9778-4ba5-99d9-b8b89c4fb39d',\n INTAKE_NAV_ITEM: 'e81bf288-5686-4713-86ae-e2947b124cab',\n} as const;\n"],
|
|
5
|
-
"mappings": ";;;;AAIA,IAAM,sBAAsB,CAAC,YAAY;AAAA,EACvC,SAAS;AAAA,EACT;AAAA,EACA,QAAQ,CAAC;AACX;AAEA,IAAM,eAAe;AAAA,EACnB,IAAI,SAAS,MAAM;AACjB,QAAI,SAAS,aAAc,QAAO;AAClC,QAAI,SAAS,OAAO,YAAa,QAAO,MAAM;AAC9C,QAAI,OAAO,SAAS,SAAU,QAAO;AACrC,WAAO,IAAI,MAAM,MAAM,QAAW,YAAY;AAAA,EAChD;AAAA,EACA,QAAQ;AACN,WAAO,IAAI,MAAM,MAAM,QAAW,YAAY;AAAA,EAChD;AACF;AACA,IAAM,YAAY,IAAI,MAAM,MAAM,QAAW,YAAY;AAWlD,IAAM,sBAAsB;;;AC/BnC,SAAS,qBAAqB;;;ACDvB,IAAM,MAAM;AAAA,EACjB,aAAa;AAAA,EACb,cAAc;AAAA;AAAA,EAGd,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,0BAA0B;AAAA;AAAA,EAG1B,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,6BAA6B;AAAA,EAC7B,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,0BAA0B;AAAA,EAC1B,4BAA4B;AAAA,EAC5B,8BAA8B;AAAA,EAC9B,4BAA4B;AAAA,EAC5B,6BAA6B;AAAA;AAAA,EAG7B,4BAA4B;AAAA,EAC5B,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,sBAAsB;AAAA,EACtB,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,2BAA2B;AAAA,EAC3B,2BAA2B;AAAA,EAC3B,0BAA0B;AAAA,EAC1B,kBAAkB;AAAA,EAClB,yBAAyB;AAAA;AAAA,EAGzB,mCAAmC;AAAA,EACnC,iCAAiC;AAAA,EACjC,kCAAkC;AAAA,EAClC,8BAA8B;AAAA,EAC9B,4BAA4B;AAAA,EAC5B,6BAA6B;AAAA;AAAA,
|
|
3
|
+
"sources": ["twenty-sdk-define-stub:__twenty-sdk-define-stub__", "../../../../src/logic-functions/health.ts", "../../../../src/constants/universal-identifiers.ts", "../../../../src/constants/version.ts", "../../../../src/lib/rest-client.ts", "../../../../src/lib/silence.ts"],
|
|
4
|
+
"sourcesContent": ["\n// Auto-generated stub for twenty-sdk/define injected by the SDK CLI build.\n// Real implementations would pull in zod, twenty-shared and ~1MB of code; at\n// runtime only `default.config.handler` is consumed, so tiny no-ops suffice.\nconst __defineFactoryStub = (config) => ({\n success: true,\n config,\n errors: [],\n});\n\nconst __anyHandler = {\n get(_target, prop) {\n if (prop === '__esModule') return true;\n if (prop === Symbol.toPrimitive) return () => '';\n if (typeof prop === 'symbol') return undefined;\n return new Proxy(() => undefined, __anyHandler);\n },\n apply() {\n return new Proxy(() => undefined, __anyHandler);\n },\n};\nconst __anyStub = new Proxy(() => undefined, __anyHandler);\n\nexport const createValidationResult = __defineFactoryStub;\nexport const defineAgent = __defineFactoryStub;\nexport const defineApplication = __defineFactoryStub;\nexport const defineApplicationRole = __defineFactoryStub;\nexport const defineCommandMenuItem = __defineFactoryStub;\nexport const defineConnectionProvider = __defineFactoryStub;\nexport const defineField = __defineFactoryStub;\nexport const defineFrontComponent = __defineFactoryStub;\nexport const defineIndex = __defineFactoryStub;\nexport const defineLogicFunction = __defineFactoryStub;\nexport const defineNavigationMenuItem = __defineFactoryStub;\nexport const defineObject = __defineFactoryStub;\nexport const definePageLayout = __defineFactoryStub;\nexport const definePageLayoutTab = __defineFactoryStub;\nexport const definePermissionFlag = __defineFactoryStub;\nexport const definePostInstallLogicFunction = __defineFactoryStub;\nexport const definePreInstallLogicFunction = __defineFactoryStub;\nexport const defineRole = __defineFactoryStub;\nexport const defineSettingsFrontComponent = __defineFactoryStub;\nexport const defineSkill = __defineFactoryStub;\nexport const defineUninstallLogicFunction = __defineFactoryStub;\nexport const defineView = __defineFactoryStub;\nexport const defineViewField = __defineFactoryStub;\nexport const AggregateOperations = __anyStub;\nexport const DateDisplayFormat = __anyStub;\nexport const FieldMetadataSettingsOnClickAction = __anyStub;\nexport const FieldType = __anyStub;\nexport const HTTPMethod = __anyStub;\nexport const NavigationMenuItemType = __anyStub;\nexport const NumberDataType = __anyStub;\nexport const ObjectOpenRecordIn = __anyStub;\nexport const ObjectRecordGroupByDateGranularity = __anyStub;\nexport const OnDeleteAction = __anyStub;\nexport const PageLayoutTabLayoutMode = __anyStub;\nexport const PageLayoutType = __anyStub;\nexport const RelationType = __anyStub;\nexport const RowLevelPermissionPredicateGroupLogicalOperator = __anyStub;\nexport const RowLevelPermissionPredicateOperand = __anyStub;\nexport const STANDARD_OBJECT = __anyStub;\nexport const STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS = __anyStub;\nexport const STANDARD_PAGE_LAYOUT = __anyStub;\nexport const STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS = __anyStub;\nexport const SystemPermissionFlag = __anyStub;\nexport const ViewCalendarLayout = __anyStub;\nexport const ViewFilterGroupLogicalOperator = __anyStub;\nexport const ViewFilterOperand = __anyStub;\nexport const ViewKey = __anyStub;\nexport const ViewOpenRecordIn = __anyStub;\nexport const ViewSortDirection = __anyStub;\nexport const ViewType = __anyStub;\nexport const ViewVisibility = __anyStub;\nexport const canAccessFullAdminPanel = __anyStub;\nexport const canImpersonate = __anyStub;\nexport const every = __anyStub;\nexport const everyDefined = __anyStub;\nexport const everyEquals = __anyStub;\nexport const favoriteRecordIds = __anyStub;\nexport const featureFlags = __anyStub;\nexport const getFieldUniversalIdentifier = __anyStub;\nexport const getSystemRelationFieldUniversalIdentifier = __anyStub;\nexport const getSystemViewFieldUniversalIdentifier = __anyStub;\nexport const getSystemViewUniversalIdentifier = __anyStub;\nexport const hasAnySoftDeleteFilterOnView = __anyStub;\nexport const includes = __anyStub;\nexport const includesEvery = __anyStub;\nexport const isDashboardPageLayoutInEditMode = __anyStub;\nexport const isDefined = __anyStub;\nexport const isInSidePanel = __anyStub;\nexport const isLayoutCustomizationModeEnabled = __anyStub;\nexport const isNonEmptyString = __anyStub;\nexport const isSelectAll = __anyStub;\nexport const none = __anyStub;\nexport const noneDefined = __anyStub;\nexport const noneEquals = __anyStub;\nexport const numberOfSelectedRecords = __anyStub;\nexport const objectMetadataItem = __anyStub;\nexport const objectMetadataLabel = __anyStub;\nexport const objectPermissions = __anyStub;\nexport const pageType = __anyStub;\nexport const selectedRecords = __anyStub;\nexport const some = __anyStub;\nexport const someDefined = __anyStub;\nexport const someEquals = __anyStub;\nexport const someNonEmptyString = __anyStub;\nexport const targetObjectReadPermissions = __anyStub;\nexport const targetObjectWritePermissions = __anyStub;\nexport const validateFields = __anyStub;\n", "import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';\nimport { CoreApiClient } from 'twenty-client-sdk/core';\nimport { IDS } from '../constants/universal-identifiers';\nimport { APP_VERSION } from '../constants/version';\nimport { loadSources, assess } from '../lib/silence';\n\n/**\n * Health, plus the sources that have gone quiet.\n *\n * The existing `status` and `timestamp` keys and the 200/503 behaviour are\n * untouched, because monitors are already pointed at this and an upgrade must\n * not start paging anyone. The silence summary is added alongside, and only\n * changes the status code when the caller asks for it with `?strict=true` \u2014\n * which is the form to point a monitor at if you want a dead form to page you.\n */\nconst handler = async (params: RoutePayload) => {\n const strict = (params.queryParameters ?? {})['strict'] === 'true';\n\n try {\n const client = new CoreApiClient();\n await client.query({ intakeSources: { edges: { node: { id: true } } } });\n\n const verdicts = (await loadSources()).map((s) => assess(s));\n const silent = verdicts.filter((v) => v.health === 'SILENT' || v.health === 'NEVER_RECEIVED');\n\n return {\n statusCode: strict && silent.length > 0 ? 503 : 200,\n body: {\n status: strict && silent.length > 0 ? 'degraded' : 'ok',\n timestamp: new Date().toISOString(),\n version: APP_VERSION,\n sources: {\n total: verdicts.length,\n monitored: verdicts.filter((v) => v.health !== 'NOT_MONITORED').length,\n silent: silent.length,\n silentSources: silent.map((v) => ({\n slug: v.slug,\n name: v.name,\n health: v.health,\n hoursSinceLastIngest: v.hoursSinceLastIngest,\n expectedCadenceHours: v.expectedCadenceHours,\n })),\n },\n },\n };\n } catch (e) {\n return { statusCode: 503, body: { status: 'error', message: String(e) } };\n }\n};\n\nexport default defineLogicFunction({\n universalIdentifier: IDS.HEALTH_LOGIC_FUNCTION,\n name: 'intake-health',\n description: 'Health check for the Intake app, including sources that have fallen silent.',\n timeoutSeconds: 15,\n handler,\n httpRouteTriggerSettings: {\n path: '/intake/health',\n httpMethod: 'GET',\n isAuthRequired: false,\n },\n});\n", "export const IDS = {\n APPLICATION: '96a389dd-384a-43a1-a376-9d3cf01b6ab7',\n DEFAULT_ROLE: '3dd2cdb0-32c7-44b4-9f7b-0a9c667ec249',\n\n // Objects\n INTAKE_SOURCE_OBJECT: '8c1c227c-db7d-4834-b328-cacbe8af0bfc',\n INTAKE_LOG_OBJECT: 'b77a2439-1503-4ef6-ab8b-8f4293bc19da',\n INTAKE_FIELD_RULE_OBJECT: 'e74df817-ff25-4bfb-bdf0-651662ca327e',\n\n // IntakeSource fields\n INTAKE_SOURCE_NAME: '0deb7e4b-bb90-4c86-b903-5208b42041e2',\n INTAKE_SOURCE_SLUG: '18f7645b-2dce-4145-b1b6-9cc3332c0578',\n INTAKE_SOURCE_TARGET_OBJECT: '65f3d7f7-a601-4375-9c67-684d0044620b',\n INTAKE_SOURCE_SECRET: '4c919402-07f5-4041-ade6-a42402f1881f',\n INTAKE_SOURCE_STATUS: '6f1fa6fe-02b4-48ab-b1ee-97c600bbf483',\n INTAKE_SOURCE_CREATE_OPP: '67ae59aa-2275-4ef2-b594-137f6bfafd99',\n INTAKE_SOURCE_OPP_TEMPLATE: '3907b346-77a6-4c79-a4d7-6e1cc20bb82b',\n INTAKE_SOURCE_TOTAL_INGESTED: '69655a93-bc70-48bd-874e-aeccc63ee1a8',\n INTAKE_SOURCE_TOTAL_FAILED: '27b3a499-183e-4cc3-8e40-938f953a986d',\n INTAKE_SOURCE_LAST_INGESTED: 'e167f628-9865-468e-894a-b4b6150cec26',\n\n // IntakeLog fields\n INTAKE_LOG_SOURCE_RELATION: 'b1c623ea-7a9a-47a5-bee4-35fdd668a637',\n INTAKE_LOG_STATUS: '8b6d26d1-f89c-486c-a617-9f9f6a9ca36d',\n INTAKE_LOG_PAYLOAD_HASH: 'c43bb1ad-e78a-4c60-bd95-02475ede7f44',\n INTAKE_LOG_RECORD_ID: '77a55116-a64b-4954-8369-341022dd5bdd',\n INTAKE_LOG_RECORD_TYPE: '02154e96-5808-48ed-8838-51083f174550',\n INTAKE_LOG_FIELDS_CREATED: '2295ca2b-b559-4b38-9771-12dd8d3d76d7',\n INTAKE_LOG_FIELDS_MATCHED: '83d07a0f-5bcd-4fb1-9759-f9b04b9381d8',\n INTAKE_LOG_OVERFLOW_COUNT: '4090acb1-f48a-4957-bb91-c642f8f434a7',\n INTAKE_LOG_PROCESSING_MS: 'a9191f27-4b40-42e9-8cce-20c75900e12c',\n INTAKE_LOG_ERROR: '8d9979f1-9774-43a8-b958-d1b37bd4ecc7',\n INTAKE_LOG_PROCESSED_AT: 'a8d99fbd-722e-4987-8afe-b830ac026f0a',\n\n // IntakeFieldRule fields\n INTAKE_FIELD_RULE_SOURCE_RELATION: '396dd1f1-3873-4efa-a6bb-7b41eda14d83',\n INTAKE_FIELD_RULE_INPUT_PATTERN: '757201a0-9f64-47b9-bf3a-a793074bf84e',\n INTAKE_FIELD_RULE_CANONICAL_NAME: 'fafebc55-a2cc-4c9f-975b-050c2676659d',\n INTAKE_FIELD_RULE_FIELD_TYPE: '72d1f6d8-d63e-419e-a244-8127d7b4705f',\n INTAKE_FIELD_RULE_PRIORITY: '911df299-940b-44f9-92f7-3fd01fde01a7',\n INTAKE_FIELD_RULE_IS_ACTIVE: '6f9cd96d-aecd-437f-a7b5-d45548782771',\n INTAKE_FIELD_RULE_TARGET_OBJECT: '7b8411a7-5e1d-40f2-a20c-683c07f67926',\n\n // Logic Functions\n WEBHOOK_LOGIC_FUNCTION: '8323c257-48fe-4bb6-b1dd-166844f575c0',\n HEALTH_LOGIC_FUNCTION: '4ece4e39-286b-4820-81f4-b7beee71d092',\n REGISTER_LOGIC_FUNCTION: '56d46e18-6cae-4d99-9893-b7d70740c9db',\n\n // IntakeSource reverse relation fields\n INTAKE_SOURCE_LOGS_REVERSE: '6ecfa369-22e0-4812-bb75-4338cea8be65',\n INTAKE_SOURCE_RULES_REVERSE: '776fefaf-0eee-4ec0-93e0-d11bd80c1e20',\n\n // v0.2.0 \u2014 rawPayload + retry + post-install + UI\n INTAKE_LOG_RAW_PAYLOAD: 'b65c9e1c-f2c1-4c52-a5b3-1e1ac3139692',\n RETRY_LOGIC_FUNCTION: '90f20396-a69a-4ae4-af20-6be65c565bb5',\n POST_INSTALL_LOGIC_FUNCTION: 'e836fdd6-fd54-4aa4-ba6d-87d5a02148ef',\n LOG_RETRY_FRONT_COMPONENT: '0cb97f6d-30e2-48c8-90e1-241b82870b4d',\n FIELD_RULES_TABLE_FRONT_COMPONENT: 'c35faad9-3468-4746-9c1c-b13d73e5e09b',\n SOURCE_PAGE_LAYOUT: '8c894ba8-5c8c-45ed-b0af-1d22ede93eb9',\n SOURCE_LAYOUT_TAB_RULES: '80f5c49a-083c-4062-8bd5-ae158abb6c33',\n SOURCE_LAYOUT_TAB_RULES_WIDGET: '26a9ebb7-1d59-4b03-a018-56968f6df224',\n LOG_PAGE_LAYOUT: 'b0972802-ede5-463c-9f59-cb05f04957db',\n LOG_LAYOUT_TAB_RETRY: '6b109695-ba3a-43de-a31a-23119ccf87ba',\n LOG_LAYOUT_TAB_RETRY_WIDGET: '11727c1a-5145-46de-9742-88d07d64c72e',\n SAMPLE_RULE_PHONE: '8440da8d-8ac4-4f4b-baa2-4d62936cde35',\n SAMPLE_RULE_EMAIL: 'd123cde4-05aa-44e1-86f6-454be1d7048b',\n SAMPLE_RULE_FULLNAME: '00e49fe8-1b48-4f5b-aaf4-414d0923c712',\n\n // Views & Navigation\n SOURCES_VIEW: '4435cd43-5c13-472f-b4f9-b686c0f46285',\n LOGS_VIEW: 'a2cd0a93-9778-4ba5-99d9-b8b89c4fb39d',\n INTAKE_NAV_ITEM: 'e81bf288-5686-4713-86ae-e2947b124cab',\n\n // \u2500\u2500 v0.4.0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // IntakeLog fields \u2014 quarantine, idempotency, replay lineage\n INTAKE_LOG_IDEMPOTENCY_KEY: 'bf91e4c0-6477-41f2-a9bd-fb159d3091d4',\n INTAKE_LOG_SPAM_SCORE: '4e250af2-925f-4724-85f6-f353a6bb4cfd',\n INTAKE_LOG_QUARANTINE_REASON: '323b3c3e-a111-45d1-a211-cf70efa6cd9e',\n INTAKE_LOG_REPLAY_OF: 'b68876b1-0045-4eaa-a4e7-f0634e110ef1',\n\n // IntakeSource fields \u2014 silence detection, spam, merge policy\n INTAKE_SOURCE_EXPECTED_CADENCE: 'a97c3906-5555-4e60-a6d6-84155a4e00a6',\n INTAKE_SOURCE_HEALTH_STATUS: '2ad13b20-5bd7-450a-b52d-bcf208373cf5',\n INTAKE_SOURCE_SILENT_SINCE: 'd3395601-f29f-4e57-98eb-561ab011cc72',\n INTAKE_SOURCE_ALERT_WEBHOOK: '5a066d62-0426-41c1-b34c-1a7e881ce854',\n INTAKE_SOURCE_LAST_ALERT_AT: 'fc64a598-ccbe-44e3-bd42-716e32d626a2',\n INTAKE_SOURCE_HONEYPOT_FIELD: '5c145686-50de-496c-bb45-a30c8b39034c',\n INTAKE_SOURCE_MERGE_POLICY: '5a946f85-b1c2-493e-92c6-31c50e637a7e',\n\n // IntakeFieldRule fields\n INTAKE_FIELD_RULE_MERGE_STRATEGY: '40c6fdcb-527a-4691-8518-c40a3d3e34c6',\n\n // Logic Functions\n CONTRACT_LOGIC_FUNCTION: '2eea4152-1b08-4b8c-903c-f4c7ee7275d0',\n REPLAY_LOG_LOGIC_FUNCTION: '8ff98390-7358-4a91-84f3-435f758182a3',\n REPLAY_BULK_LOGIC_FUNCTION: 'b82ed4c4-07bc-4095-8ee5-611200626dc0',\n QUARANTINE_LIST_LOGIC_FUNCTION: '0203643c-2a97-4e7c-b4a5-b76d721cf23a',\n QUARANTINE_RELEASE_LOGIC_FUNCTION: '5a10d4fe-c807-45c6-9e61-c191d4e37b4e',\n QUARANTINE_DISCARD_LOGIC_FUNCTION: '1454a56e-6197-4dfd-9b41-5cc3b1436af7',\n CHECK_SILENCE_LOGIC_FUNCTION: 'e88cd5f2-ab96-4f89-ac55-78db66b32d66',\n\n // Application variables\n VAR_MERGE_POLICY: 'a84fe9fd-3d7c-41d9-b0e8-dfdc1a87f348',\n VAR_SPAM_FILTER_ENABLED: '570d3ba8-1904-4bad-a5ce-27e208e6414a',\n VAR_SPAM_SCORE_THRESHOLD: '4395574d-24b4-4e0e-afa3-1e90fcd4f470',\n VAR_HONEYPOT_FIELD: '2cf56c56-f5cb-47c5-aa43-84f301060a95',\n VAR_RAW_PAYLOAD_RETENTION: 'f3314ad6-05b9-4127-bb88-cea5f2018dc9',\n VAR_RAW_PAYLOAD_MAX_BYTES: 'b002ca76-df3d-47b4-8ac5-d3f562239c48',\n VAR_REPLAY_MAX_BATCH: 'cf55956f-177e-4afd-8d9f-7f42c614209c',\n} as const;\n", "/**\n * Reported by GET /s/intake/contract so an integrator can tell which behaviour\n * a workspace is running. Keep in step with package.json on every release.\n */\nexport const APP_VERSION = '0.4.0';\n", "/**\n * Minimal REST client for Twenty's Core and Metadata APIs.\n * Uses raw fetch so it works with dynamically created custom fields\n * that the typed SDK client doesn't know about at build time.\n */\n\nconst apiUrl = () => process.env['TWENTY_API_URL'] ?? '';\nconst token = () => process.env['TWENTY_APP_ACCESS_TOKEN'] ?? '';\n\nasync function request<T>(\n base: string,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T> {\n const res = await fetch(`${base}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token()}`,\n 'Content-Type': 'application/json',\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`${method} ${path} \u2192 ${res.status}: ${text}`);\n }\n return JSON.parse(text) as T;\n}\n\nexport const coreApi = {\n get: <T>(path: string) => request<T>(`${apiUrl()}/rest`, 'GET', path),\n post: <T>(path: string, body: unknown) => request<T>(`${apiUrl()}/rest`, 'POST', path, body),\n patch: <T>(path: string, body: unknown) => request<T>(`${apiUrl()}/rest`, 'PATCH', path, body),\n};\n\nexport const metaApi = {\n get: <T>(path: string) => request<T>(`${apiUrl()}/rest/metadata`, 'GET', path),\n post: <T>(path: string, body: unknown) => request<T>(`${apiUrl()}/rest/metadata`, 'POST', path, body),\n};\n\n/** Run a raw GraphQL query against the /graphql endpoint. */\nexport async function gql<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n): Promise<T> {\n return request<T>(`${apiUrl()}`, 'POST', '/graphql', { query, variables });\n}\n", "/**\n * Noticing that nothing arrived.\n *\n * Every other failure in this app announces itself: a bad signature returns 401,\n * a rejected record writes a FAILED log. Silence returns nothing, logs nothing\n * and looks exactly like a quiet week. A form that breaks in February is found\n * in August, and the only evidence is leads that were never there.\n *\n * The check is deliberately small. A source states how long it may go quiet;\n * anything longer is a fault, recorded on the source and posted once to a\n * webhook if one is configured. There is no scheduler here \u2014 a Twenty App has\n * nowhere dependable to run one \u2014 so the endpoint is driven by whatever already\n * pings on a timer, which for most workspaces is an uptime monitor they have.\n */\n\nimport { coreApi } from './rest-client';\n\nexport type SourceHealth = {\n id: string;\n name: string;\n slug: string;\n status: string;\n expectedCadenceHours: number | null;\n lastIngestedAt: string | null;\n healthStatus: string | null;\n silentSince: string | null;\n lastAlertAt: string | null;\n alertWebhookUrl: string | null;\n};\n\nexport type SilenceVerdict = {\n slug: string;\n name: string;\n health: 'HEALTHY' | 'SILENT' | 'NEVER_RECEIVED' | 'NOT_MONITORED';\n hoursSinceLastIngest: number | null;\n expectedCadenceHours: number | null;\n /** True the first time a source is seen silent, which is when alerting fires. */\n newlySilent: boolean;\n};\n\nconst HOUR_MS = 60 * 60 * 1000;\n\nexport function assess(source: SourceHealth, now = Date.now()): SilenceVerdict {\n const base = {\n slug: source.slug,\n name: source.name,\n expectedCadenceHours: source.expectedCadenceHours,\n };\n\n // A paused source is quiet because someone made it quiet.\n if (source.status === 'PAUSED' || !source.expectedCadenceHours || source.expectedCadenceHours <= 0) {\n return { ...base, health: 'NOT_MONITORED', hoursSinceLastIngest: hoursSince(source.lastIngestedAt, now), newlySilent: false };\n }\n\n const wasSilent = source.healthStatus === 'SILENT' || source.healthStatus === 'NEVER_RECEIVED';\n\n if (!source.lastIngestedAt) {\n // A source that has never received anything is a different problem from one\n // that stopped: usually the webhook was never wired up on the sending side.\n return { ...base, health: 'NEVER_RECEIVED', hoursSinceLastIngest: null, newlySilent: !wasSilent };\n }\n\n const hours = hoursSince(source.lastIngestedAt, now);\n\n // A timestamp we cannot read must not be rounded down to \"arrived just now\" \u2014\n // that is a source reporting healthy forever, which is the exact failure this\n // check exists to catch. Unreadable counts as silent.\n if (hours === null || hours > source.expectedCadenceHours) {\n return { ...base, health: 'SILENT', hoursSinceLastIngest: hours, newlySilent: !wasSilent };\n }\n\n return { ...base, health: 'HEALTHY', hoursSinceLastIngest: hours, newlySilent: false };\n}\n\nfunction hoursSince(iso: string | null, now: number): number | null {\n if (!iso) return null;\n const then = new Date(iso).getTime();\n if (!Number.isFinite(then)) return null;\n return Math.round(((now - then) / HOUR_MS) * 10) / 10;\n}\n\nexport async function loadSources(): Promise<SourceHealth[]> {\n try {\n const res = await coreApi.get<{ data: { intakeSources: { edges: Array<{ node: SourceHealth }> } } }>(\n '/intakeSources?limit=200',\n );\n return (res.data?.intakeSources?.edges ?? []).map((e) => e.node);\n } catch {\n return [];\n }\n}\n\n/**\n * Write the verdict back to the source.\n *\n * `silentSince` is only stamped on the transition, so it keeps meaning \"when we\n * first noticed\" rather than sliding forward on every check.\n */\nexport async function persist(source: SourceHealth, verdict: SilenceVerdict): Promise<void> {\n if (verdict.health === 'NOT_MONITORED') return;\n if (source.healthStatus === verdict.health && !verdict.newlySilent) return;\n\n const patch: Record<string, unknown> = { healthStatus: verdict.health };\n if (verdict.health === 'HEALTHY') {\n patch['silentSince'] = null;\n } else if (verdict.newlySilent) {\n patch['silentSince'] = new Date().toISOString();\n }\n\n await coreApi.patch(`/intakeSources/${source.id}`, patch).catch(() => null);\n}\n\n/**\n * Post one alert.\n *\n * Fired only on the transition into silence, so a source that stays broken for a\n * month produces one message rather than one per check \u2014 an alert channel that\n * repeats itself is an alert channel people mute.\n */\nexport async function sendAlert(source: SourceHealth, verdict: SilenceVerdict): Promise<boolean> {\n if (!source.alertWebhookUrl || !verdict.newlySilent) return false;\n\n const text = verdict.health === 'NEVER_RECEIVED'\n ? `Intake: source \"${source.name}\" (${source.slug}) has never received a payload, and a cadence of ${verdict.expectedCadenceHours}h is configured. The sending side is probably not wired up.`\n : `Intake: source \"${source.name}\" (${source.slug}) has received nothing for ${verdict.hoursSinceLastIngest}h, past its expected cadence of ${verdict.expectedCadenceHours}h.`;\n\n try {\n const res = await fetch(source.alertWebhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n // `text` is what Slack, Discord and Teams incoming webhooks all read, so\n // the common case needs no transformation; the rest is there for anything\n // consuming the payload properly.\n body: JSON.stringify({\n text,\n source: { slug: source.slug, name: source.name },\n health: verdict.health,\n hoursSinceLastIngest: verdict.hoursSinceLastIngest,\n expectedCadenceHours: verdict.expectedCadenceHours,\n }),\n });\n if (!res.ok) return false;\n } catch {\n return false;\n }\n\n await coreApi.patch(`/intakeSources/${source.id}`, {\n lastAlertAt: new Date().toISOString(),\n }).catch(() => null);\n\n return true;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|