{
  "version": 3,
  "sources": ["twenty-sdk-define-stub:__twenty-sdk-define-stub__", "../../../../src/constants/universal-identifiers.ts", "../../../../src/lib/rest-client.ts", "../../../../src/lib/ingest.ts", "../../../../src/lib/pipeline.ts", "../../../../src/lib/result.ts", "../../../../src/lib/stages/load-source.stage.ts", "../../../../src/lib/stages/verify-signature.stage.ts", "../../../../src/lib/idempotency.ts", "../../../../src/lib/settings.ts", "../../../../src/lib/stages/idempotency.stage.ts", "../../../../src/lib/flattener.ts", "../../../../src/lib/stages/flatten.stage.ts", "../../../../src/constants/field-map.ts", "../../../../src/lib/type-detector.ts", "../../../../src/lib/classifier.ts", "../../../../src/lib/merge.ts", "../../../../src/lib/normalizer.ts", "../../../../src/lib/stages/normalize.stage.ts", "../../../../src/lib/spam.ts", "../../../../src/lib/stages/quarantine.stage.ts", "../../../../src/lib/stages/sync-schema.stage.ts", "../../../../src/lib/note-formatter.ts", "../../../../src/constants/free-email-domains.ts", "../../../../src/lib/lookup.ts", "../../../../src/lib/stages/upsert-record.stage.ts", "../../../../src/lib/retention.ts", "../../../../src/lib/intake-log-writer.ts", "../../../../src/lib/stages/write-log.stage.ts", "../../../../src/lib/pipeline-factory.ts", "../../../../src/lib/replay.ts", "../../../../src/logic-functions/quarantine-release.ts"],
  "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 * 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 * One way to run an ingestion, shared by every entry point.\n *\n * The webhook, a replay, a retry and a quarantine release all run the same\n * pipeline and all have to deal with the same terminal outcomes. Keeping that in\n * one place is what stops the endpoints drifting apart \u2014 before this, only the\n * happy path wrote an IntakeLog, so a rejected signature or a failed record\n * write left nothing behind at all and `totalFailed` never moved off zero.\n */\n\nimport { CoreApiClient } from 'twenty-client-sdk/core';\nimport { MetadataApiClient } from 'twenty-client-sdk/metadata';\nimport { createPipeline } from './pipeline-factory';\nimport type { PipelineContext } from './pipeline';\nimport type { AppError } from './result';\nimport { writeIntakeLog, bumpSourceCounters, type LogStatus } from './intake-log-writer';\n\nexport type IngestOptions = {\n  sourceSlug: string;\n  rawPayload: Record<string, unknown>;\n  rawBody?: string | undefined;\n  headers?: Record<string, string | undefined>;\n  idempotencyKey?: string | undefined;\n  skipDedup?: boolean;\n  skipSpamFilter?: boolean;\n  replayOfLogId?: string | undefined;\n};\n\nexport type IngestOutcome =\n  | { ok: true; ctx: PipelineContext }\n  | { ok: false; error: AppError; ctx: PipelineContext };\n\nexport async function runIngestion(options: IngestOptions): Promise<IngestOutcome> {\n  const ctx: PipelineContext = {\n    sourceSlug: options.sourceSlug,\n    rawBody: options.rawBody,\n    rawPayload: options.rawPayload,\n    headers: options.headers ?? {},\n    startedAt: Date.now(),\n    fieldsCreated: 0,\n    fieldsMatched: 0,\n    warnings: [],\n    events: [],\n    ...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),\n    ...(options.skipDedup ? { skipDedup: true } : {}),\n    ...(options.skipSpamFilter ? { skipSpamFilter: true } : {}),\n    ...(options.replayOfLogId ? { replayOfLogId: options.replayOfLogId } : {}),\n  };\n\n  const pipeline = createPipeline(new CoreApiClient(), new MetadataApiClient());\n  const result = await pipeline.run(ctx);\n\n  if (result.ok) return { ok: true, ctx: result.value };\n\n  await recordFailure(ctx, result.error);\n  return { ok: false, error: result.error, ctx };\n}\n\n/**\n * Log a terminal outcome.\n *\n * Two cases deliberately write nothing. An unknown slug has no source to attach\n * a log to, and logging it anyway would let anyone fill the workspace with rows\n * by POSTing to invented URLs. A duplicate is already represented by the log of\n * the request that succeeded; recording the retry as well would double-count\n * every ingestion in the dashboard.\n */\nasync function recordFailure(ctx: PipelineContext, error: AppError): Promise<void> {\n  if (error.code === 'SOURCE_NOT_FOUND') return;\n  if (error.code === 'DUPLICATE_PAYLOAD') return;\n\n  const details = (error.details ?? {}) as { spamScore?: number; reason?: string };\n  const quarantined = error.code === 'QUARANTINED';\n  const status: LogStatus = quarantined ? 'QUARANTINED' : 'FAILED';\n\n  await writeIntakeLog({\n    ctx,\n    status,\n    error: `${error.code}: ${error.message}`,\n    spamScore: details.spamScore ?? null,\n    quarantineReason: quarantined ? (details.reason ?? error.message) : null,\n  });\n\n  // A quarantine is the filter working, not the source breaking. Counting it as\n  // a failure would make a well-defended form look like a broken one.\n  if (ctx.source && !quarantined) await bumpSourceCounters(ctx.source.id, 'failed');\n}\n\n/**\n * The HTTP status for a terminal error.\n *\n * A quarantine answers 202: the request was well-formed and has been accepted\n * for review. Returning an error instead would tell a bot which of its attempts\n * were detected, and would show a real person a failure on a form that in fact\n * went through and is waiting for someone to look at it.\n */\nexport function statusCodeFor(code: string): number {\n  switch (code) {\n    case 'INVALID_SIGNATURE': return 401;\n    case 'SOURCE_NOT_FOUND': return 404;\n    case 'SOURCE_PAUSED': return 423;\n    case 'DUPLICATE_PAYLOAD': return 200;\n    case 'QUARANTINED': return 202;\n    case 'NO_STORED_PAYLOAD': return 400;\n    default: return 500;\n  }\n}\n\n/** Pull a caller-supplied idempotency key off the header or the body. */\nexport function idempotencyKeyFrom(\n  headers: Record<string, string | undefined> | undefined,\n  body: Record<string, unknown>,\n): string | undefined {\n  const header = headers?.['idempotency-key'] ?? headers?.['Idempotency-Key'];\n  const fromBody = body['idempotencyKey'];\n  const key = header ?? (typeof fromBody === 'string' ? fromBody : undefined);\n  const trimmed = key?.trim();\n  if (!trimmed) return undefined;\n  return trimmed.slice(0, 255);\n}\n\n/** The success body shared by the webhook and every replay-shaped endpoint. */\nexport function successBody(ctx: PipelineContext, startedAt: number): Record<string, unknown> {\n  return {\n    success: true,\n    recordId: ctx.personId ?? ctx.companyId,\n    recordType: ctx.recordType,\n    personId: ctx.personId,\n    companyId: ctx.companyId,\n    opportunityId: ctx.opportunityId,\n    noteId: ctx.noteId,\n    fieldsCreated: ctx.fieldsCreated,\n    fieldsMatched: ctx.fieldsMatched,\n    overflowCount: ctx.noteFields?.length ?? 0,\n    spamScore: ctx.spam?.score ?? null,\n    mergeDecisions: ctx.mergeDecisions ?? null,\n    processingMs: Date.now() - startedAt,\n    warnings: ctx.warnings,\n  };\n}\n", "import type { Result, AppError } from './result';\nimport type { NormalizedField } from './normalizer';\nimport type { MergePolicy } from './settings';\nimport type { SpamVerdict } from './spam';\nimport type { MergeDecision } from './merge';\n\nexport type PipelineContext = {\n  // \u2500\u2500 Entry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  sourceSlug: string;\n  rawBody: string | undefined;\n  rawPayload: Record<string, unknown>;\n  headers: Record<string, string | undefined>;\n  startedAt: number;\n\n  // \u2500\u2500 Resolved source config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  source?: {\n    id: string;\n    name: string;\n    slug: string;\n    targetObject: 'PERSON' | 'COMPANY' | 'AUTO';\n    webhookSecret: string | null;\n    status: 'ACTIVE' | 'PAUSED';\n    createOpportunity: boolean;\n    opportunityNameTemplate: string;\n    /** Per-source honeypot key. Falls back to INTAKE_HONEYPOT_FIELD. */\n    honeypotField: string | null;\n    /** Per-source merge policy. Null inherits INTAKE_MERGE_POLICY. */\n    mergePolicy: MergePolicy | null;\n  };\n\n  // \u2500\u2500 Idempotency \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  payloadHash?: string;\n  /**\n   * Client-supplied key from the Idempotency-Key header or an `idempotencyKey`\n   * body field. Unlike the content hash it is honoured with no time limit \u2014\n   * the caller has promised these are the same submission, so a retry an hour\n   * later is still that one submission.\n   */\n  idempotencyKey?: string;\n\n  // \u2500\u2500 Spam \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  spam?: SpamVerdict;\n\n  // \u2500\u2500 Transformation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  flat?: Record<string, unknown>;\n  structuredCompany?: Record<string, unknown>;\n  structuredPerson?: Record<string, unknown>;\n  normalizedFields?: NormalizedField[];\n  crmFields?: NormalizedField[];\n  noteFields?: NormalizedField[];\n\n  // \u2500\u2500 Schema sync \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  fieldsCreated: number;\n  fieldsMatched: number;\n\n  // \u2500\u2500 Output \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  personId?: string;\n  companyId?: string;\n  opportunityId?: string;\n  noteId?: string;\n  recordType?: 'PERSON' | 'COMPANY';\n\n  // \u2500\u2500 Flags \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  skipDedup?: boolean;\n  /**\n   * Set when an operator releases a quarantined payload. The scoring still runs\n   * and is still recorded, but a human has overruled it.\n   */\n  skipSpamFilter?: boolean;\n  /** The log this run was replayed from, for lineage on the new log. */\n  replayOfLogId?: string;\n\n  // \u2500\u2500 Diagnostics \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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  /** What the merge policy did to each field, keyed by object then field. */\n  mergeDecisions?: Record<string, Record<string, MergeDecision>>;\n  warnings: string[];\n  events: PipelineEvent[];\n};\n\nexport type PipelineEvent = {\n  stage: string;\n  message: string;\n  data?: unknown;\n};\n\nexport interface PipelineStage {\n  readonly name: string;\n  execute(ctx: PipelineContext): Promise<Result<PipelineContext>>;\n}\n\nexport class Pipeline {\n  private readonly stages: PipelineStage[] = [];\n\n  use(stage: PipelineStage): this {\n    this.stages.push(stage);\n    return this;\n  }\n\n  async run(initial: PipelineContext): Promise<Result<PipelineContext, AppError>> {\n    let ctx = initial;\n\n    for (const stage of this.stages) {\n      const result = await stage.execute(ctx);\n\n      if (!result.ok) {\n        return {\n          ok: false,\n          error: { ...result.error, stage: stage.name },\n        };\n      }\n\n      ctx = result.value;\n      ctx.events.push({ stage: stage.name, message: 'completed' });\n    }\n\n    return { ok: true, value: ctx };\n  }\n}\n", "export type AppError = {\n  code: ErrorCode;\n  message: string;\n  stage?: string;\n  details?: unknown;\n};\n\nexport const ErrorCode = {\n  INVALID_SIGNATURE: 'INVALID_SIGNATURE',\n  INVALID_PAYLOAD: 'INVALID_PAYLOAD',\n  SOURCE_NOT_FOUND: 'SOURCE_NOT_FOUND',\n  SOURCE_PAUSED: 'SOURCE_PAUSED',\n  DUPLICATE_PAYLOAD: 'DUPLICATE_PAYLOAD',\n  /** Held by the spam filter \u2014 recorded, not written, and releasable. */\n  QUARANTINED: 'QUARANTINED',\n  /** Retention is off or the payload was too large, so there is nothing to replay. */\n  NO_STORED_PAYLOAD: 'NO_STORED_PAYLOAD',\n  FIELD_CREATE_FAILED: 'FIELD_CREATE_FAILED',\n  RECORD_UPSERT_FAILED: 'RECORD_UPSERT_FAILED',\n  NOTE_CREATE_FAILED: 'NOTE_CREATE_FAILED',\n  SCHEMA_FETCH_FAILED: 'SCHEMA_FETCH_FAILED',\n  INTERNAL_ERROR: 'INTERNAL_ERROR',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nexport type Ok<T> = { ok: true; value: T };\nexport type Err<E extends AppError = AppError> = { ok: false; error: E };\nexport type Result<T, E extends AppError = AppError> = Ok<T> | Err<E>;\n\nexport const ok = <T>(value: T): Ok<T> => ({ ok: true, value });\n\nexport const err = <E extends AppError>(\n  code: E['code'],\n  message: string,\n  details?: unknown,\n): Err<E> =>\n  ({ ok: false, error: { code, message, details } as E });\n\nexport function isOk<T>(r: Result<T>): r is Ok<T> {\n  return r.ok;\n}\n\nexport function unwrapOr<T>(r: Result<T>, fallback: T): T {\n  return r.ok ? r.value : fallback;\n}\n\nexport function mapResult<T, U>(\n  r: Result<T>,\n  fn: (v: T) => U,\n): Result<U> {\n  return r.ok ? ok(fn(r.value)) : r;\n}\n", "import type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { Result } from '../result';\nimport { ok, err, ErrorCode } from '../result';\nimport type { MergePolicy } from '../settings';\nimport type { CoreApiClient } from 'twenty-client-sdk/core';\n\nconst BASE_FIELDS = {\n  id: true,\n  name: true,\n  slug: true,\n  targetObject: true,\n  webhookSecret: true,\n  status: true,\n  createOpportunity: true,\n  opportunityNameTemplate: true,\n} as const;\n\nconst V4_FIELDS = {\n  ...BASE_FIELDS,\n  honeypotField: true,\n  mergePolicy: true,\n} as const;\n\ntype SourceNode = {\n  id: string;\n  name: string;\n  slug: string;\n  targetObject: string;\n  webhookSecret: string | null;\n  status: string;\n  createOpportunity: boolean | null;\n  opportunityNameTemplate: string | null;\n  honeypotField?: string | null;\n  mergePolicy?: string | null;\n};\n\nexport class LoadSourceStage implements PipelineStage {\n  readonly name = 'load-source';\n\n  constructor(private readonly client: CoreApiClient) {}\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    const node = await this.fetch(ctx.sourceSlug);\n\n    if (!node) {\n      return err(ErrorCode.SOURCE_NOT_FOUND, `No Intake source found with slug \"${ctx.sourceSlug}\"`);\n    }\n\n    if (node.status === 'PAUSED') {\n      return err(ErrorCode.SOURCE_PAUSED, `Source \"${node.name}\" is paused`);\n    }\n\n    return ok({\n      ...ctx,\n      source: {\n        id: node.id,\n        name: node.name,\n        slug: node.slug,\n        targetObject: node.targetObject as 'PERSON' | 'COMPANY' | 'AUTO',\n        webhookSecret: node.webhookSecret ?? null,\n        status: node.status as 'ACTIVE' | 'PAUSED',\n        createOpportunity: node.createOpportunity ?? true,\n        opportunityNameTemplate: node.opportunityNameTemplate ?? '{{source}} \u2014 {{firstName}} {{lastName}}',\n        honeypotField: node.honeypotField ?? null,\n        mergePolicy: parseMergePolicy(node.mergePolicy),\n      },\n    });\n  }\n\n  /**\n   * Ask for the 0.4.0 columns, and fall back to the original set if they are not\n   * there yet. A workspace that upgraded the package but has not re-synced its\n   * schema would otherwise fail the query and reject every request as an unknown\n   * source \u2014 an upgrade that takes the webhook offline, which is not acceptable\n   * for something sitting in front of someone's contact form.\n   */\n  private async fetch(slug: string): Promise<SourceNode | null> {\n    const args = { filter: { slug: { eq: slug } } };\n\n    try {\n      const result = await this.client.query({\n        intakeSources: { __args: args, edges: { node: V4_FIELDS } },\n      });\n      return result.intakeSources?.edges?.[0]?.node ?? null;\n    } catch {\n      try {\n        const result = await this.client.query({\n          intakeSources: { __args: args, edges: { node: BASE_FIELDS } },\n        });\n        return result.intakeSources?.edges?.[0]?.node ?? null;\n      } catch {\n        return null;\n      }\n    }\n  }\n}\n\nfunction parseMergePolicy(raw: string | null | undefined): MergePolicy | null {\n  switch ((raw ?? '').toUpperCase()) {\n    case 'PRESERVE': return 'PRESERVE';\n    case 'NEWEST_WINS': return 'NEWEST_WINS';\n    default: return null;\n  }\n}\n", "import { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { Result } from '../result';\nimport { ok, err, ErrorCode } from '../result';\n\nexport class VerifySignatureStage implements PipelineStage {\n  readonly name = 'verify-signature';\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    const secret = ctx.source?.webhookSecret;\n\n    // If INTAKE_REQUIRE_HMAC is enabled globally, reject sources without a secret\n    const requireHmac = process.env['INTAKE_REQUIRE_HMAC'] === 'true';\n    if (!secret) {\n      if (requireHmac) return err(ErrorCode.INVALID_SIGNATURE, 'This source has no webhook secret \u2014 INTAKE_REQUIRE_HMAC is enabled');\n      return ok(ctx);\n    }\n\n    const signature = ctx.headers['x-webhook-signature'];\n    if (!signature) {\n      return err(ErrorCode.INVALID_SIGNATURE, 'Missing X-Webhook-Signature header');\n    }\n\n    if (!ctx.rawBody) {\n      ctx.warnings.push('rawBody unavailable \u2014 skipping HMAC verification');\n      return ok(ctx);\n    }\n\n    const expected = createHmac('sha256', secret)\n      .update(ctx.rawBody)\n      .digest('hex');\n\n    const sigBuf = Buffer.from(signature.replace(/^sha256=/, ''));\n    const expBuf = Buffer.from(expected);\n\n    if (sigBuf.length !== expBuf.length || !timingSafeEqual(sigBuf, expBuf)) {\n      return err(ErrorCode.INVALID_SIGNATURE, 'Webhook signature verification failed');\n    }\n\n    return ok(ctx);\n  }\n}\n", "import { createHash } from 'node:crypto';\n\n/**\n * Compute a stable SHA-256 hash of a payload.\n * Keys are sorted before hashing so that { a:1, b:2 } and { b:2, a:1 }\n * produce the same hash \u2014 safe to retry from any client.\n */\nexport function computePayloadHash(payload: unknown): string {\n  const stable = stableStringify(payload);\n  return createHash('sha256').update(stable).digest('hex');\n}\n\nfunction stableStringify(value: unknown): string {\n  if (value === null || value === undefined) return String(value);\n  if (typeof value !== 'object') return JSON.stringify(value);\n  if (Array.isArray(value)) {\n    return '[' + value.map(stableStringify).join(',') + ']';\n  }\n  const obj = value as Record<string, unknown>;\n  const sorted = Object.keys(obj)\n    .sort()\n    .map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)\n    .join(',');\n  return '{' + sorted + '}';\n}\n\n/** Derive a human-readable idempotency window label. */\nexport function withinWindow(processedAt: Date, windowMs = 5 * 60 * 1000): boolean {\n  return Date.now() - processedAt.getTime() < windowMs;\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 type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { Result } from '../result';\nimport { ok, err, ErrorCode } from '../result';\nimport { computePayloadHash, withinWindow } from '../idempotency';\nimport { dedupWindowMinutes } from '../settings';\nimport type { CoreApiClient } from 'twenty-client-sdk/core';\n\n/**\n * Two mechanisms, answering two different questions.\n *\n * The content hash asks \"did we just see this exact body?\" and only trusts the\n * answer for a few minutes, because two genuine enquiries with identical content\n * are possible and a form that is legitimately submitted twice a week is not a\n * duplicate.\n *\n * An idempotency key asks something stronger: the caller states that this is one\n * submission, whatever the retry schedule looks like. That claim gets no time\n * window at all. A double-tapped submit button, a client retry after a timeout,\n * a queue redelivering an hour later \u2014 all of them carry the same key and all of\n * them resolve to the record made the first time, rather than a second\n * opportunity in someone's pipeline.\n */\nexport class IdempotencyStage implements PipelineStage {\n  readonly name = 'idempotency';\n\n  constructor(private readonly client: CoreApiClient) {}\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    const hash = computePayloadHash(ctx.rawPayload);\n    const next = { ...ctx, payloadHash: hash };\n\n    // Replay and retry deliberately re-run a payload that has been seen.\n    if (ctx.skipDedup) return ok(next);\n\n    if (ctx.idempotencyKey) {\n      const prior = await this.findByKey(ctx.idempotencyKey);\n      if (prior) {\n        return err(\n          ErrorCode.DUPLICATE_PAYLOAD,\n          `Idempotency key already processed (log ${prior.id})`,\n          { priorLogId: prior.id, priorRecordId: prior.recordId, idempotencyKey: ctx.idempotencyKey },\n        );\n      }\n    }\n\n    const windowMins = dedupWindowMinutes();\n    if (windowMins <= 0) return ok(next);\n\n    const prior = await this.findByHash(hash);\n    if (prior?.processedAt && withinWindow(new Date(prior.processedAt), windowMins * 60 * 1000)) {\n      return err(\n        ErrorCode.DUPLICATE_PAYLOAD,\n        `Duplicate payload \u2014 already processed ${String(prior.status).toLowerCase()} (log ${prior.id})`,\n        { priorLogId: prior.id, priorRecordId: prior.recordId },\n      );\n    }\n\n    return ok(next);\n  }\n\n  private async findByKey(key: string): Promise<PriorLog | null> {\n    try {\n      const res = await this.client.query({\n        intakeLogs: {\n          __args: {\n            filter: { idempotencyKey: { eq: key } },\n            orderBy: { processedAt: 'DescNullsLast' },\n            first: 1,\n          },\n          edges: { node: { id: true, status: true, recordId: true, processedAt: true } },\n        },\n      });\n      const node = res.intakeLogs?.edges?.[0]?.node ?? null;\n      // A payload that was held or thrown away was never acted on, so the key\n      // has not really been used and the caller deserves a fresh attempt.\n      if (node && (node.status === 'QUARANTINED' || node.status === 'DISCARDED')) return null;\n      return node;\n    } catch {\n      // The field is missing on workspaces that installed before 0.4.0 and have\n      // not re-synced. Falling through to hash dedup keeps them working exactly\n      // as they did, rather than failing every request.\n      return null;\n    }\n  }\n\n  private async findByHash(hash: string): Promise<PriorLog | null> {\n    try {\n      const res = await this.client.query({\n        intakeLogs: {\n          __args: {\n            filter: { payloadHash: { eq: hash } },\n            orderBy: { processedAt: 'DescNullsLast' },\n            first: 1,\n          },\n          edges: { node: { id: true, status: true, recordId: true, processedAt: true } },\n        },\n      });\n      return res.intakeLogs?.edges?.[0]?.node ?? null;\n    } catch {\n      return null;\n    }\n  }\n}\n\ntype PriorLog = {\n  id: string;\n  status: string;\n  recordId: string | null;\n  processedAt: string;\n};\n", "type FlatRecord = Record<string, unknown>;\n\nconst MAX_DEPTH = 8;\n\n/**\n * Recursively flatten a nested object into a single-level record.\n * Arrays are kept as-is (classified as notes downstream).\n * Circular references are detected and dropped.\n *\n * e.g. { user: { phone: \"x\" } } \u2192 { userPhone: \"x\" }\n */\nexport function flatten(\n  input: unknown,\n  prefix = '',\n  depth = 0,\n  seen = new WeakSet<object>(),\n): FlatRecord {\n  if (depth > MAX_DEPTH) return {};\n  if (input === null || input === undefined) return {};\n  if (typeof input !== 'object') {\n    return prefix ? { [prefix]: input } : {};\n  }\n  if (Array.isArray(input)) {\n    return prefix ? { [prefix]: input } : {};\n  }\n\n  const obj = input as Record<string, unknown>;\n\n  if (seen.has(obj)) return {};\n  seen.add(obj);\n\n  const result: FlatRecord = {};\n\n  for (const [key, value] of Object.entries(obj)) {\n    const newKey = prefix ? `${prefix}${toPascalSegment(key)}` : toCamelSegment(key);\n\n    if (\n      value !== null &&\n      typeof value === 'object' &&\n      !Array.isArray(value)\n    ) {\n      const nested = flatten(value, newKey, depth + 1, seen);\n      Object.assign(result, nested);\n    } else {\n      result[newKey] = value;\n    }\n  }\n\n  return result;\n}\n\n/** Convert a snake_case or kebab-case segment to camelCase. */\nfunction toCamelSegment(s: string): string {\n  return s.replace(/[-_]([a-zA-Z0-9])/g, (_, c: string) => c.toUpperCase());\n}\n\n/** Convert a snake_case or kebab-case segment to PascalCase for joining. */\nfunction toPascalSegment(s: string): string {\n  const camel = toCamelSegment(s);\n  return camel.charAt(0).toUpperCase() + camel.slice(1);\n}\n\n/**\n * Detect whether a top-level payload is \"structured\" (pipeline-app style\n * with nested `company` / `person` object keys) or \"flat\" (contact-form style).\n */\nexport type PayloadStructure =\n  | { type: 'structured'; company: FlatRecord | null; person: FlatRecord | null; extra: FlatRecord }\n  | { type: 'flat'; data: FlatRecord };\n\nexport function detectStructure(raw: unknown): PayloadStructure {\n  if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n    return { type: 'flat', data: {} };\n  }\n\n  const obj = raw as Record<string, unknown>;\n  const hasObjectCompany =\n    'company' in obj && typeof obj['company'] === 'object' && obj['company'] !== null && !Array.isArray(obj['company']);\n  const hasObjectPerson =\n    'person' in obj && typeof obj['person'] === 'object' && obj['person'] !== null && !Array.isArray(obj['person']);\n\n  if (!hasObjectCompany && !hasObjectPerson) {\n    return { type: 'flat', data: flatten(obj) };\n  }\n\n  // Keep company and person in their original nested form \u2014 they are already\n  // in Twenty's native composite format (domainName, address, emails, phones, etc.)\n  const company = hasObjectCompany ? (obj['company'] as FlatRecord) : null;\n  const person = hasObjectPerson ? (obj['person'] as FlatRecord) : null;\n\n  const extra: FlatRecord = {};\n  for (const [k, v] of Object.entries(obj)) {\n    if (k === 'company' || k === 'person') continue;\n    const nested = flatten({ [k]: v });\n    Object.assign(extra, nested);\n  }\n\n  return { type: 'structured', company, person, extra };\n}\n", "import type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { Result } from '../result';\nimport { ok } from '../result';\nimport { detectStructure, flatten } from '../flattener';\n\nexport class FlattenStage implements PipelineStage {\n  readonly name = 'flatten';\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    const structure = detectStructure(ctx.rawPayload);\n\n    if (structure.type === 'structured') {\n      // For structured payloads (pipeline app format), company and person are\n      // already in Twenty's native format. Only normalize the extra fields\n      // (pipeline-specific data: ratings, scores, analysis, etc.)\n      const extraFlat = flatten(structure.extra ?? {});\n\n      return ok({\n        ...ctx,\n        structuredCompany: structure.company ?? undefined,\n        structuredPerson: structure.person ?? undefined,\n        flat: extraFlat,\n      });\n    }\n\n    // Flat format \u2014 already a top-level key\u2192value record\n    return ok({ ...ctx, flat: structure.data });\n  }\n}\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\", \"Email\" \u2192 \"email\"\n *\n * The underscore is inserted *between* characters, never before the first one.\n * Prefixing every capital unconditionally turned a key that merely started with\n * one \u2014 `Email`, `Phone`, `First Name`, the default labels of most form builders\n * \u2014 into `_email`, which matches nothing in the map. Those payloads fell through\n * to passthrough and produced a person with an `extEmail` column and no email\n * address on the record: unreachable, and re-created from scratch on every\n * submission because dedup runs on `emails.primaryEmail`.\n *\n * Two passes handle the two shapes of a capital: one that follows a lowercase\n * letter or digit (`phoneNumber`), and a run of capitals followed by a word\n * (`XMLHttpRequest`). An all-caps key like `EMAIL` has neither and lowercases\n * cleanly. Leading underscores that were in the key to begin with survive, so\n * the `_id` skip entry still matches.\n */\nexport function normaliseKey(key: string): string {\n  return key\n    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n    .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\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", "import type { TwentyFieldType } from '../constants/field-map';\n\nconst URL_RE = /^https?:\\/\\/[^\\s/$.?#].[^\\s]*$/i;\nconst EMAIL_RE = /^[^@\\s]+@[^@\\s]+\\.[^@\\s]{2,}$/;\nconst PHONE_RE = /^[+]?[(]?[0-9]{1,4}[)]?[-\\s.]?[(]?[0-9]{1,4}[)]?[-\\s.]?[0-9]{3,9}([-\\s.][0-9]{1,9})?$/;\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})?)?)?$/;\n\nexport function detectType(value: unknown): TwentyFieldType {\n  if (value === null || value === undefined) return 'TEXT';\n  if (typeof value === 'boolean') return 'BOOLEAN';\n  if (typeof value === 'number') return 'NUMBER';\n\n  if (typeof value === 'string') {\n    const s = value.trim();\n    if (!s) return 'TEXT';\n    if (URL_RE.test(s)) return 'LINKS';\n    if (EMAIL_RE.test(s)) return 'EMAILS';\n    if (PHONE_RE.test(s)) return 'PHONES';\n    if (DATE_RE.test(s)) return 'DATE_TIME';\n    if (!isNaN(Number(s)) && s !== '') return 'NUMBER';\n    return 'TEXT';\n  }\n\n  if (Array.isArray(value)) return 'RAW_JSON';\n  if (typeof value === 'object') return 'RAW_JSON';\n\n  return 'TEXT';\n}\n", "const PROSE_KEYS = new Set([\n  'message', 'description', 'analysis', 'notes', 'comments',\n  'summary', 'inquiry', 'body', 'details', 'content', 'text',\n  'feedback', 'about', 'additional_info', 'info', 'context',\n  'requirements', 'goals', 'challenges', 'question', 'request',\n]);\n\nconst NOTE_KEY_PREFIXES = ['utm_', 'ga_', 'fb_', 'gclid', 'msclkid'];\n\nconst SENTENCE_RE = /[.!?]\\s+[A-Z]/g;\nconst LINE_BREAK_RE = /\\n|\\r/;\n\nexport type Classification = 'field' | 'note' | 'skip';\n\n/**\n * Decide whether a key+value pair belongs in a CRM field or the overflow note.\n *\n * Rules (in priority order):\n * 1. Arrays / objects \u2192 note  (can't be a single field)\n * 2. Known note key names \u2192 note\n * 3. UTM / tracking prefix \u2192 note\n * 4. Contains line breaks \u2192 note\n * 5. Looks like prose (2+ sentences) \u2192 note\n * 6. Everything else \u2192 field\n */\nexport function classify(key: string, value: unknown): Classification {\n  const normKey = key.toLowerCase().replace(/[-\\s]/g, '_');\n\n  if (Array.isArray(value)) return 'note';\n  if (value !== null && typeof value === 'object') return 'note';\n\n  if (PROSE_KEYS.has(normKey)) return 'note';\n  if (NOTE_KEY_PREFIXES.some((p) => normKey.startsWith(p))) return 'note';\n\n  if (typeof value === 'string') {\n    if (LINE_BREAK_RE.test(value)) return 'note';\n    const matches = value.match(SENTENCE_RE);\n    if (matches && matches.length >= 2) return 'note';\n  }\n\n  return 'field';\n}\n", "/**\n * What an update does to a field that already holds a value.\n *\n * Before 0.4.0 every update was a blind PATCH: whatever arrived overwrote\n * whatever was there. That is fine the first time a lead is seen and lossy every\n * time after. A returning enquiry with the name box left blank would replace a\n * salesperson's corrected spelling with nothing, and there is no undo in a CRM\n * for a field that was quietly overwritten by a webhook at 3am.\n *\n * PRESERVE is therefore the default: fill what is empty, leave what is written.\n * NEWEST_WINS is the old behaviour, kept for anyone who relied on it.\n *\n * The decision is per field, so a rule can mark a genuinely volatile attribute\n * \u2014 a lead score, a last-seen page \u2014 NEWEST_WINS while names and emails stay\n * protected by the workspace default.\n */\n\nimport type { MergePolicy } from './settings';\n\nexport type FieldMergeStrategy = 'INHERIT' | 'PRESERVE' | 'NEWEST_WINS';\n\nexport type MergeDecision = {\n  /** Whether the incoming value is written. */\n  write: boolean;\n  /** Why, for the dry-run diff and the log's warnings. */\n  status: 'set' | 'filled-empty' | 'preserved' | 'unchanged';\n};\n\n/**\n * Decide whether `incoming` replaces `existing`.\n *\n * `existing` is the value already on the record \u2014 undefined when the record is\n * new or has never held this field.\n */\nexport function decideMerge(\n  existing: unknown,\n  incoming: unknown,\n  policy: MergePolicy,\n  strategy: FieldMergeStrategy = 'INHERIT',\n): MergeDecision {\n  // Never write a blank over anything. An absent value in a payload means the\n  // sender had nothing to say about that field, not that it should be cleared.\n  if (isEmpty(incoming)) return { write: false, status: 'preserved' };\n\n  if (isEmpty(existing)) return { write: true, status: 'filled-empty' };\n\n  if (sameValue(existing, incoming)) return { write: false, status: 'unchanged' };\n\n  const effective = strategy === 'INHERIT' ? policy : strategy;\n  return effective === 'NEWEST_WINS'\n    ? { write: true, status: 'set' }\n    : { write: false, status: 'preserved' };\n}\n\n/**\n * Apply the policy across a whole patch.\n *\n * Returns only the keys that should actually be sent, plus a per-field record of\n * what was decided so the caller can report it.\n */\nexport function mergePatch(\n  existing: Record<string, unknown>,\n  incoming: Record<string, unknown>,\n  policy: MergePolicy,\n  strategyFor: (field: string) => FieldMergeStrategy = () => 'INHERIT',\n): {\n  patch: Record<string, unknown>;\n  decisions: Record<string, MergeDecision>;\n} {\n  const patch: Record<string, unknown> = {};\n  const decisions: Record<string, MergeDecision> = {};\n\n  for (const [key, value] of Object.entries(incoming)) {\n    const decision = decideMerge(existing[key], value, policy, strategyFor(key));\n    decisions[key] = decision;\n    if (decision.write) patch[key] = value;\n  }\n\n  return { patch, decisions };\n}\n\nexport function parseFieldMergeStrategy(raw: string | null | undefined): FieldMergeStrategy {\n  switch ((raw ?? '').toUpperCase()) {\n    case 'PRESERVE': return 'PRESERVE';\n    case 'NEWEST_WINS': return 'NEWEST_WINS';\n    default: return 'INHERIT';\n  }\n}\n\n/**\n * Emptiness has to understand Twenty's composite shapes, not just `null`.\n *\n * A person whose name has never been set comes back as\n * `{ firstName: '', lastName: '' }`, and an unset phone as an object of empty\n * strings. Treating those as \"already has a value\" is what would make PRESERVE\n * refuse to ever populate a record, so the check recurses into objects and\n * calls one empty when every leaf inside it is.\n */\nexport function isEmpty(value: unknown): boolean {\n  if (value === null || value === undefined) return true;\n  if (typeof value === 'string') return value.trim() === '';\n  if (Array.isArray(value)) return value.length === 0;\n  if (typeof value === 'object') {\n    const entries = Object.entries(value as Record<string, unknown>);\n    if (entries.length === 0) return true;\n    // Ignore the metadata Twenty attaches to composites \u2014 a currency code alone\n    // does not mean an amount was entered.\n    const meaningful = entries.filter(([k]) => !IGNORED_COMPOSITE_KEYS.has(k));\n    if (meaningful.length === 0) return true;\n    return meaningful.every(([, v]) => isEmpty(v));\n  }\n  return false;\n}\n\nconst IGNORED_COMPOSITE_KEYS = new Set([\n  'currencyCode',\n  'primaryLinkLabel',\n  'primaryPhoneCountryCode',\n  'primaryPhoneCallingCode',\n  'additionalLinks',\n  'additionalEmails',\n  'additionalPhones',\n]);\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n  if (a === b) return true;\n  if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {\n    try {\n      return JSON.stringify(sortKeys(a)) === JSON.stringify(sortKeys(b));\n    } catch {\n      return false;\n    }\n  }\n  return String(a) === String(b);\n}\n\nfunction sortKeys(value: unknown): unknown {\n  if (value === null || typeof value !== 'object') return value;\n  if (Array.isArray(value)) return value.map(sortKeys);\n  const obj = value as Record<string, unknown>;\n  const out: Record<string, unknown> = {};\n  for (const key of Object.keys(obj).sort()) out[key] = sortKeys(obj[key]);\n  return out;\n}\n", "import { FIELD_MAP, normaliseKey, type FieldAction, type TwentyFieldType } from '../constants/field-map';\nimport { detectType } from './type-detector';\nimport { classify } from './classifier';\nimport { parseFieldMergeStrategy, type FieldMergeStrategy } from './merge';\n\n/**\n * Which record a field is written to.\n *\n * `auto` defers to the shape of the payload \u2014 the primary record, which is the\n * person when there is person data and the company otherwise. `opportunity` is\n * never chosen by inference: a field lands on the deal only when a rule says so\n * or the incoming key carries an explicit opportunity prefix, because guessing\n * that `budget` is a deal attribute rather than a contact attribute is exactly\n * the kind of surprise that makes an ingestion layer untrustworthy.\n */\nexport type TargetObject = 'auto' | 'person' | 'company' | 'opportunity';\n\nexport type NormalizedField = {\n  originalKey: string;\n  canonicalName: string;\n  twentyType: TwentyFieldType;\n  action: FieldAction;\n  value: unknown;\n  source: 'builtin' | 'rule' | 'passthrough';\n  targetObject: TargetObject;\n  /**\n   * Set by SyncSchemaStage once it knows what the workspace actually has. A\n   * field is writable when it is a standard field, an ext field that exists or\n   * was just created, or a rule-named field that already exists on the target.\n   * Anything else is demoted to the note rather than silently dropped.\n   */\n  writable?: boolean;\n  /** Per-field override of the workspace merge policy, from the matching rule. */\n  mergeStrategy?: FieldMergeStrategy;\n};\n\nexport type DatabaseRule = {\n  inputPattern: string;\n  canonicalName: string;\n  fieldType: string;\n  action?: FieldAction;\n  priority: number;\n  targetObject?: string | null;\n  mergeStrategy?: string | null;\n};\n\n/**\n * A rule states its intent through `fieldType`: the list offers NOTE and SKIP\n * alongside the real Twenty types. There is no separate action column to read,\n * and reading one that does not exist is how a SKIP rule ended up writing the\n * value it was configured to discard \u2014 `action` came back undefined, and\n * `partition` treats anything that is not 'skip' or 'note' as a field to write.\n */\nexport function actionForRule(rule: DatabaseRule): FieldAction {\n  if (rule.action) return rule.action;\n  switch ((rule.fieldType ?? '').toUpperCase()) {\n    case 'SKIP': return 'skip';\n    case 'NOTE': return 'note';\n    default: return 'field';\n  }\n}\n\n/** NOTE and SKIP are instructions, not column types \u2014 give them a real one. */\nexport function typeForRule(rule: DatabaseRule): TwentyFieldType {\n  switch ((rule.fieldType ?? '').toUpperCase()) {\n    case 'NOTE': return 'RICH_TEXT';\n    case 'SKIP': return 'TEXT';\n    case '': return 'TEXT';\n    default: return rule.fieldType as TwentyFieldType;\n  }\n}\n\n/**\n * Explicit per-key routing to the deal. `opportunity_budget`, `opp_budget` and\n * `deal_budget` all put `extBudget` on the Opportunity, so a caller can route a\n * field without configuring a rule first.\n */\nconst OPPORTUNITY_KEY_PREFIXES = ['opportunity_', 'opp_', 'deal_'];\n\nfunction parseTargetObject(raw: string | null | undefined): TargetObject {\n  switch ((raw ?? '').toUpperCase()) {\n    case 'PERSON': return 'person';\n    case 'COMPANY': return 'company';\n    case 'OPPORTUNITY': return 'opportunity';\n    default: return 'auto';\n  }\n}\n\n/**\n * The canonical names that make a contact findable and reachable.\n *\n * A rule outranks the built-in map everywhere except here. These five names are\n * the ones `assembleComposites` folds into `name`, `emails` and `phones`, and\n * `email` is additionally the key every person dedup runs on \u2014 so a pattern that\n * swept one of them into the note would create contacts with no address to write\n * to and a fresh duplicate on every submission, silently and forever.\n *\n * The protection is narrow: a rule may still restate one of these mappings, and\n * may still set its type, target and merge strategy. What it may not do is send\n * the value to a different field, to the note, or to the bin.\n *\n * A rule that wants a company address or a second phone should match a key the\n * built-in map does not already own (`company_email`, `alt_phone`); those are\n * unprotected and behave like any other key.\n */\nconst PROTECTED_CANONICAL_NAMES = new Set([\n  'email', 'phone', 'firstName', 'lastName', 'fullName',\n]);\n\ntype CompiledRule = DatabaseRule & { matcher: RegExp | null };\n\n/**\n * Compile once per ingestion rather than once per key. An unparseable pattern\n * keeps the rule alive as an exact-match-only rule instead of discarding it.\n */\nfunction compileRule(rule: DatabaseRule): CompiledRule {\n  try {\n    return { ...rule, matcher: new RegExp(rule.inputPattern, 'i') };\n  } catch {\n    return { ...rule, matcher: null };\n  }\n}\n\n/**\n * Whether a rule claims a key.\n *\n * Both spellings of the key are offered to the pattern. `flatten` camelCases\n * every incoming key before rules run, so a payload sending `lead_id` arrives\n * here as `leadId`; testing only that form made `^lead_id$` silently never\n * match while the exact string `lead_id` matched fine through the equality\n * branch \u2014 the same rule working or not depending on whether the author reached\n * for a regex. Matching against the original and the normalised form makes\n * `^lead_id$` and `^leadId$` equally valid ways to say the same thing.\n */\nfunction ruleMatches(rule: CompiledRule, key: string, lookupKey: string): boolean {\n  if (rule.inputPattern === key || rule.inputPattern === lookupKey) return true;\n  if (!rule.matcher) return false;\n  return rule.matcher.test(key) || rule.matcher.test(lookupKey);\n}\n\n/**\n * Normalize a flat record of incoming key\u2192value pairs into typed,\n * canonically-named fields ready for classification and schema sync.\n *\n * Strategy priority:\n *  1. Database rules      (highest \u2014 user-configured per source or global)\n *  2. Built-in field map  (covers known common variations)\n *  3. Passthrough         (unknown fields get an ext prefix + auto type detection)\n *\n * Rules come first because that is what \"override the built-in map\" has to mean\n * to be worth configuring: a source that reports `utm_source` as a real field,\n * or a `website` that belongs on the deal rather than the company, cannot be\n * expressed at all if a built-in entry silently wins first. The one exception is\n * PROTECTED_CANONICAL_NAMES.\n *\n * Anything the precedence declines is appended to `warnings`, so a rule that did\n * not do what its author expected says so on the log instead of being invisible.\n */\nexport function normalizeFields(\n  flat: Record<string, unknown>,\n  rules: DatabaseRule[] = [],\n  warnings: string[] = [],\n): NormalizedField[] {\n  const compiled = [...rules]\n    .sort((a, b) => b.priority - a.priority)\n    .map(compileRule);\n\n  return Object.entries(flat).map(([key, value]) =>\n    normalizeField(key, value, compiled, warnings),\n  );\n}\n\nfunction normalizeField(\n  key: string,\n  value: unknown,\n  rules: CompiledRule[],\n  warnings: string[],\n): NormalizedField {\n  const lookupKey = normaliseKey(key);\n  const builtIn = FIELD_MAP[lookupKey];\n\n  // 1. Database rules (exact match or regex), highest priority first\n  for (const rule of rules) {\n    if (!ruleMatches(rule, key, lookupKey)) continue;\n\n    // A rule that agrees with the built-in about where a protected value goes is\n    // free to restate it \u2014 that is what the seeded `emailAddress \u2192 email` rules\n    // do, and declining them would warn on a workspace that has done nothing\n    // wrong. Only a rule that would move the value somewhere else, or stop it\n    // being written at all, is turned away.\n    if (builtIn && PROTECTED_CANONICAL_NAMES.has(builtIn.canonicalName)) {\n      const displaces =\n        rule.canonicalName !== builtIn.canonicalName || actionForRule(rule) !== 'field';\n      if (displaces) {\n        warnings.push(\n          `Rule \"${rule.inputPattern}\" matched \"${key}\", which the built-in map holds as the ` +\n          `contact's ${builtIn.canonicalName}. The built-in mapping was kept: a contact with no ` +\n          `${builtIn.canonicalName} cannot be found again, so every submission would create a ` +\n          `duplicate. Point the rule at a key the built-in map does not already own.`,\n        );\n        continue;\n      }\n    }\n\n    return {\n      originalKey: key,\n      canonicalName: rule.canonicalName,\n      twentyType: typeForRule(rule),\n      action: actionForRule(rule),\n      value,\n      source: 'rule',\n      targetObject: parseTargetObject(rule.targetObject),\n      mergeStrategy: parseFieldMergeStrategy(rule.mergeStrategy),\n    };\n  }\n\n  // 2. Built-in map\n  if (builtIn) {\n    return {\n      originalKey: key,\n      canonicalName: builtIn.canonicalName,\n      twentyType: builtIn.twentyType,\n      action: builtIn.action,\n      value,\n      source: 'builtin',\n      targetObject: 'auto',\n    };\n  }\n\n  // 3. Passthrough \u2014 auto-detect type, classify, ext prefix\n  const detectedType = detectType(value);\n  const classification = classify(key, value);\n\n  // An explicit opportunity prefix routes the field to the deal and is stripped\n  // before the ext name is built, so `opportunity_budget` becomes `extBudget`\n  // rather than `extOpportunityBudget`.\n  const prefix = OPPORTUNITY_KEY_PREFIXES.find((p) => lookupKey.startsWith(p));\n  const targetObject: TargetObject = prefix ? 'opportunity' : 'auto';\n  const bareKey = prefix ? lookupKey.slice(prefix.length) : key;\n\n  const canonicalName =\n    classification === 'field'\n      ? toExtFieldName(bareKey)\n      : lookupKey;\n\n  return {\n    originalKey: key,\n    canonicalName,\n    twentyType: detectedType,\n    action: classification === 'field' ? 'field' : classification,\n    value,\n    source: 'passthrough',\n    targetObject,\n  };\n}\n\n/** Convert any key to a valid Twenty custom field name: extPascalCase */\nfunction toExtFieldName(key: string): string {\n  const cleaned = key\n    .replace(/[-_\\s]+([a-zA-Z0-9])/g, (_, c: string) => c.toUpperCase())\n    .replace(/[^a-zA-Z0-9]/g, '');\n  return 'ext' + cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n}\n\n/**\n * Split normalised fields into those that become CRM fields\n * and those that go into the overflow note.\n */\nexport function partition(fields: NormalizedField[]): {\n  crmFields: NormalizedField[];\n  noteFields: NormalizedField[];\n  skipped: NormalizedField[];\n} {\n  const crmFields: NormalizedField[] = [];\n  const noteFields: NormalizedField[] = [];\n  const skipped: NormalizedField[] = [];\n\n  for (const f of fields) {\n    if (f.action === 'skip') skipped.push(f);\n    else if (f.action === 'note') noteFields.push(f);\n    else crmFields.push(f);\n  }\n\n  return { crmFields, noteFields, skipped };\n}\n\n/**\n * Assemble individual canonical field names into Twenty's composite\n * field structures (FULL_NAME, PHONES, EMAILS, ADDRESS, LINKS).\n */\nexport function assembleComposites(crmFields: NormalizedField[]): {\n  assembled: Record<string, unknown>;\n  remainder: NormalizedField[];\n} {\n  const assembled: Record<string, unknown> = {};\n  const consumed = new Set<string>();\n\n  // \u2500\u2500 FULL_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\n  const firstName = crmFields.find((f) => f.canonicalName === 'firstName');\n  const lastName = crmFields.find((f) => f.canonicalName === 'lastName');\n  const fullName = crmFields.find((f) => f.canonicalName === 'fullName');\n\n  if (fullName && !firstName && !lastName) {\n    const parts = splitFullName(String(fullName.value ?? ''));\n    assembled['name'] = { firstName: parts[0], lastName: parts[1] };\n    consumed.add('fullName');\n  } else if (firstName || lastName) {\n    assembled['name'] = {\n      firstName: String(firstName?.value ?? ''),\n      lastName: String(lastName?.value ?? ''),\n    };\n    if (firstName) consumed.add('firstName');\n    if (lastName) consumed.add('lastName');\n  }\n\n  // \u2500\u2500 EMAILS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 email = crmFields.find((f) => f.canonicalName === 'email');\n  if (email) {\n    assembled['emails'] = { primaryEmail: String(email.value ?? '') };\n    consumed.add('email');\n  }\n\n  // \u2500\u2500 PHONES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 phone = crmFields.find((f) => f.canonicalName === 'phone');\n  if (phone) {\n    const raw = String(phone.value ?? '');\n    assembled['phones'] = {\n      primaryPhoneNumber: raw.replace(/\\D/g, '').slice(-10),\n      primaryPhoneCountryCode: 'US',\n      primaryPhoneCallingCode: '+1',\n    };\n    consumed.add('phone');\n  }\n\n  // \u2500\u2500 LINKS (domainName / website) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 domain = crmFields.find((f) => f.canonicalName === 'domainName');\n  if (domain) {\n    assembled['domainName'] = {\n      primaryLinkUrl: String(domain.value ?? ''),\n      primaryLinkLabel: '',\n    };\n    consumed.add('domainName');\n  }\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\n  const addressFields: Array<[string, string]> = [\n    ['addressStreet1', 'addressStreet1'],\n    ['addressStreet2', 'addressStreet2'],\n    ['addressCity', 'addressCity'],\n    ['addressState', 'addressState'],\n    ['addressPostcode', 'addressPostcode'],\n    ['addressCountry', 'addressCountry'],\n  ];\n  const addressParts: Record<string, unknown> = {};\n  for (const [canonical, outKey] of addressFields) {\n    const f = crmFields.find((x) => x.canonicalName === canonical);\n    if (f) {\n      addressParts[outKey] = f.value;\n      consumed.add(canonical);\n    }\n  }\n  if (Object.keys(addressParts).length > 0) {\n    assembled['address'] = addressParts;\n  }\n\n  const remainder = crmFields.filter((f) => !consumed.has(f.canonicalName));\n  return { assembled, remainder };\n}\n\nfunction splitFullName(fullName: string): [string, string] {\n  const parts = fullName.trim().split(/\\s+/);\n  if (parts.length === 0) return ['', ''];\n  if (parts.length === 1) return [parts[0] ?? '', ''];\n  const lastName = parts.pop() ?? '';\n  return [parts.join(' '), lastName];\n}\n", "import type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { Result } from '../result';\nimport { ok, err, ErrorCode } from '../result';\nimport { normalizeFields, partition, assembleComposites, type DatabaseRule } from '../normalizer';\nimport type { CoreApiClient } from 'twenty-client-sdk/core';\n\nexport class NormalizeStage implements PipelineStage {\n  readonly name = 'normalize';\n\n  constructor(private readonly client: CoreApiClient) {}\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    if (!ctx.flat) {\n      return err(ErrorCode.INTERNAL_ERROR, 'FlattenStage must run before NormalizeStage');\n    }\n\n    const rules = await this.loadRules(ctx.source?.id);\n    // Precedence decisions the normalizer declined are reported on the log, so a\n    // rule that did not do what its author expected is visible rather than silent.\n    const normalized = normalizeFields(ctx.flat, rules, ctx.warnings);\n    const { crmFields, noteFields } = partition(normalized);\n\n    return ok({\n      ...ctx,\n      normalizedFields: normalized,\n      crmFields,\n      noteFields,\n    });\n  }\n\n  private async loadRules(sourceId?: string): Promise<DatabaseRule[]> {\n    try {\n      const filter = sourceId\n        ? { or: [{ intakeSource: { id: { eq: sourceId } } }, { intakeSource: { id: { is: 'NULL' } } }] }\n        : { intakeSource: { id: { is: 'NULL' } } };\n\n      const result = await this.client.query({\n        intakeFieldRules: {\n          __args: {\n            filter: { and: [filter, { isActive: { eq: true } }] },\n            orderBy: { priority: 'DescNullsLast' },\n          },\n          edges: {\n            node: {\n              inputPattern: true,\n              canonicalName: true,\n              fieldType: true,\n              priority: true,\n              targetObject: true,\n              mergeStrategy: true,\n            },\n          },\n        },\n      });\n\n      return (result.intakeFieldRules?.edges ?? []).map((e: { node: DatabaseRule }) => e.node);\n    } catch {\n      // Selecting a field the workspace has not synced yet fails the whole\n      // query, which would silently drop every rule. Ask again for the columns\n      // that have existed since 0.1.0 so a workspace mid-upgrade keeps working.\n      return this.loadRulesLegacy(sourceId);\n    }\n  }\n\n  private async loadRulesLegacy(sourceId?: string): Promise<DatabaseRule[]> {\n    try {\n      const filter = sourceId\n        ? { or: [{ intakeSource: { id: { eq: sourceId } } }, { intakeSource: { id: { is: 'NULL' } } }] }\n        : { intakeSource: { id: { is: 'NULL' } } };\n\n      const result = await this.client.query({\n        intakeFieldRules: {\n          __args: {\n            filter: { and: [filter, { isActive: { eq: true } }] },\n            orderBy: { priority: 'DescNullsLast' },\n          },\n          edges: {\n            node: {\n              inputPattern: true,\n              canonicalName: true,\n              fieldType: true,\n              priority: true,\n            },\n          },\n        },\n      });\n\n      return (result.intakeFieldRules?.edges ?? []).map((e: { node: DatabaseRule }) => e.node);\n    } catch {\n      return [];\n    }\n  }\n}\n", "/**\n * Spam scoring for inbound payloads.\n *\n * Two mechanisms, deliberately separate.\n *\n * The honeypot is deterministic: a field no human can see was filled, so a\n * script filled it. It has no false positives and applies whenever a honeypot\n * key is configured, independent of the score filter.\n *\n * The heuristics are additive and each one is worth points rather than a\n * verdict, because every signal here is individually wrong sometimes. A real\n * lead does occasionally write from a throwaway address. Requiring the score to\n * clear a threshold means one odd-looking signal is never enough on its own.\n *\n * Signals that would score people rather than behaviour are deliberately\n * absent \u2014 non-Latin characters in a name, or the presence of words like \"SEO\"\n * in a message, would quarantine genuine customers.\n */\n\nexport type SpamSignal = { reason: string; points: number };\n\nexport type SpamVerdict = {\n  score: number;\n  signals: SpamSignal[];\n  /** Set when a honeypot field was filled \u2014 an immediate, non-negotiable quarantine. */\n  honeypot: boolean;\n};\n\n/** The score a honeypot hit reports, far above any reachable threshold. */\nexport const HONEYPOT_SCORE = 100;\n\n/**\n * Addresses from these domains cannot receive a reply, so a lead arriving from\n * one is unreachable even when it is sincere. Kept short and specific: every\n * entry is a service whose stated purpose is a discardable inbox.\n */\nconst DISPOSABLE_EMAIL_DOMAINS = new Set([\n  'mailinator.com', 'guerrillamail.com', 'guerrillamail.net', 'sharklasers.com',\n  '10minutemail.com', '10minutemail.net', 'tempmail.com', 'temp-mail.org',\n  'throwawaymail.com', 'yopmail.com', 'yopmail.net', 'trashmail.com',\n  'dispostable.com', 'getnada.com', 'nada.email', 'fakeinbox.com',\n  'maildrop.cc', 'mintemail.com', 'spamgourmet.com', 'mailnesia.com',\n  'tempinbox.com', 'emailondeck.com', 'moakt.com', 'mohmal.com',\n  'inboxkitten.com', 'mailcatch.com', 'mytemp.email', 'discard.email',\n]);\n\n/** Domains reserved by standards or convention \u2014 never a real correspondent. */\nconst PLACEHOLDER_EMAIL_DOMAINS = new Set([\n  'example.com', 'example.org', 'example.net', 'test.com', 'localhost',\n  'domain.com', 'email.com', 'yourdomain.com',\n]);\n\nconst URL_RE = /https?:\\/\\/|www\\./i;\nconst GLOBAL_URL_RE = /https?:\\/\\/[^\\s<>\"']+/gi;\nconst MARKUP_RE = /<\\s*a\\s|<\\s*script|\\[url[=\\]]|\\[link[=\\]]|\\]\\(https?:/i;\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\n\n/** Keys whose value is a human's name, where a URL has no business being. */\nconst NAME_KEYS = new Set([\n  'name', 'fullName', 'firstName', 'lastName', 'contact_name', 'your_name',\n]);\n\n/** Keys carrying free prose, where several links is the classic link-blast shape. */\nconst MESSAGE_KEYS = new Set([\n  'message', 'description', 'comments', 'notes', 'body', 'details',\n  'content', 'text', 'inquiry', 'summary', 'feedback',\n]);\n\n/**\n * Score a flattened payload.\n *\n * `honeypotKey` is matched case-insensitively against the payload's own keys so\n * a form field named `website_url_confirm` matches however the sender cased it.\n */\nexport function scoreSpam(\n  flat: Record<string, unknown>,\n  honeypotKey?: string | null,\n): SpamVerdict {\n  const signals: SpamSignal[] = [];\n\n  if (honeypotKey) {\n    const hit = Object.entries(flat).find(\n      ([k, v]) => k.toLowerCase() === honeypotKey.toLowerCase() && isFilled(v),\n    );\n    if (hit) {\n      return {\n        score: HONEYPOT_SCORE,\n        signals: [{ reason: `Honeypot field \"${hit[0]}\" was filled`, points: HONEYPOT_SCORE }],\n        honeypot: true,\n      };\n    }\n  }\n\n  const add = (reason: string, points: number) => signals.push({ reason, points });\n\n  for (const [key, value] of Object.entries(flat)) {\n    if (typeof value !== 'string' || value === '') continue;\n    const bare = key.replace(/^(opportunity_|opp_|deal_)/i, '');\n\n    if (NAME_KEYS.has(bare) || NAME_KEYS.has(key)) {\n      if (URL_RE.test(value)) add(`Name field \"${key}\" contains a URL`, 4);\n      if (MARKUP_RE.test(value)) add(`Name field \"${key}\" contains markup or a link tag`, 4);\n    }\n\n    if (MESSAGE_KEYS.has(bare) || MESSAGE_KEYS.has(key)) {\n      const urls = value.match(GLOBAL_URL_RE) ?? [];\n      const distinct = new Set(urls.map((u) => u.toLowerCase()));\n      if (distinct.size >= 5) add(`Message field \"${key}\" contains ${distinct.size} links`, 3);\n      if (MARKUP_RE.test(value)) add(`Message field \"${key}\" contains anchor or BBCode markup`, 2);\n    }\n  }\n\n  // Email quality. An address that cannot be replied to makes the lead\n  // worthless even when the rest of the payload looks ordinary.\n  const email = firstEmail(flat);\n  if (email !== null) {\n    if (!EMAIL_RE.test(email)) {\n      add(`Email \"${truncate(email)}\" is not a valid address`, 3);\n    } else {\n      const domain = email.slice(email.lastIndexOf('@') + 1).toLowerCase();\n      if (DISPOSABLE_EMAIL_DOMAINS.has(domain)) {\n        add(`Email domain \"${domain}\" is a disposable inbox`, 4);\n      } else if (PLACEHOLDER_EMAIL_DOMAINS.has(domain)) {\n        add(`Email domain \"${domain}\" is a placeholder domain`, 3);\n      }\n    }\n  }\n\n  // The same string pasted into every box is a form-filler that did not know\n  // what any of the boxes were for.\n  const repeated = mostRepeatedValue(flat);\n  if (repeated && repeated.count >= 4) {\n    add(`The same value appears in ${repeated.count} different fields`, 2);\n  }\n\n  const phone = firstPhone(flat);\n  if (phone && isImplausiblePhone(phone)) {\n    add(`Phone \"${truncate(phone)}\" is a placeholder number`, 2);\n  }\n\n  const score = signals.reduce((sum, s) => sum + s.points, 0);\n  return { score, signals, honeypot: false };\n}\n\n/** A one-line summary for the log's quarantine reason. */\nexport function describeVerdict(verdict: SpamVerdict): string {\n  return verdict.signals.map((s) => s.reason).join('; ').slice(0, 500);\n}\n\nfunction isFilled(value: unknown): boolean {\n  if (value === null || value === undefined) return false;\n  if (typeof value === 'string') return value.trim() !== '';\n  if (typeof value === 'boolean') return value;\n  return true;\n}\n\nfunction firstEmail(flat: Record<string, unknown>): string | null {\n  for (const [key, value] of Object.entries(flat)) {\n    if (typeof value !== 'string' || value === '') continue;\n    if (/mail/i.test(key)) return value.trim();\n  }\n  return null;\n}\n\nfunction firstPhone(flat: Record<string, unknown>): string | null {\n  for (const [key, value] of Object.entries(flat)) {\n    if (typeof value !== 'string' || value === '') continue;\n    if (/phone|mobile|tel|cell/i.test(key)) return value.trim();\n  }\n  return null;\n}\n\n/** Repeated digits (5555555555) or a straight run (1234567890). */\nfunction isImplausiblePhone(raw: string): boolean {\n  const digits = raw.replace(/\\D/g, '');\n  if (digits.length < 7) return false;\n  if (/^(\\d)\\1+$/.test(digits)) return true;\n  const ascending = digits.split('').every((d, i, arr) =>\n    i === 0 || Number(d) === (Number(arr[i - 1]) + 1) % 10);\n  return ascending;\n}\n\nfunction mostRepeatedValue(\n  flat: Record<string, unknown>,\n): { value: string; count: number } | null {\n  const counts = new Map<string, number>();\n  for (const value of Object.values(flat)) {\n    if (typeof value !== 'string') continue;\n    const v = value.trim();\n    // Short values legitimately repeat \u2014 a city typed into two address boxes\n    // is not evidence of anything.\n    if (v.length < 8) continue;\n    counts.set(v, (counts.get(v) ?? 0) + 1);\n  }\n  let best: { value: string; count: number } | null = null;\n  for (const [value, count] of counts) {\n    if (!best || count > best.count) best = { value, count };\n  }\n  return best;\n}\n\nfunction truncate(s: string): string {\n  return s.length > 60 ? `${s.slice(0, 60)}\u2026` : s;\n}\n", "import type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { Result } from '../result';\nimport { ok, err, ErrorCode } from '../result';\nimport { scoreSpam, describeVerdict } from '../spam';\nimport { honeypotField, spamFilterEnabled, spamScoreThreshold } from '../settings';\n\n/**\n * Holds a payload that looks automated, before anything is written.\n *\n * Placed after normalization so the scoring sees canonical names \u2014 `email`\n * rather than whichever of nine spellings the form used \u2014 and before schema sync\n * so a bot cannot extend the workspace's schema or mint a Person, Company,\n * Opportunity and Note on its way to being noticed. That ordering is the whole\n * point: quarantine that runs after the writes is just a slower cleanup job.\n *\n * The payload is not discarded. It is recorded with its score and its reasons,\n * and an operator can release it, which re-runs it with the filter overruled.\n */\nexport class QuarantineStage implements PipelineStage {\n  readonly name = 'quarantine';\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    const honeypot = ctx.source?.honeypotField || honeypotField();\n    const filterOn = spamFilterEnabled();\n\n    // Nothing configured and the filter off \u2014 no work, and no cost.\n    if (!honeypot && !filterOn) return ok(ctx);\n\n    const verdict = scoreSpam(ctx.flat ?? {}, honeypot);\n    const next = { ...ctx, spam: verdict };\n\n    if (ctx.skipSpamFilter) {\n      if (verdict.score > 0) {\n        next.warnings.push(`Spam filter overruled by release \u2014 score was ${verdict.score}: ${describeVerdict(verdict)}`);\n      }\n      return ok(next);\n    }\n\n    // The honeypot is decisive on its own. Scoring is not: it only acts when the\n    // workspace has switched the filter on, so upgrading to 0.4.0 cannot start\n    // silently withholding leads from anyone who did not ask for it.\n    const quarantine = verdict.honeypot || (filterOn && verdict.score >= spamScoreThreshold());\n\n    if (quarantine) {\n      return err(\n        ErrorCode.QUARANTINED,\n        verdict.honeypot\n          ? 'Honeypot field was filled \u2014 payload quarantined'\n          : `Spam score ${verdict.score} met the threshold of ${spamScoreThreshold()} \u2014 payload quarantined`,\n        { spamScore: verdict.score, reason: describeVerdict(verdict), signals: verdict.signals },\n      );\n    }\n\n    if (verdict.score > 0) {\n      next.warnings.push(`Spam score ${verdict.score} (below threshold): ${describeVerdict(verdict)}`);\n    }\n\n    return ok(next);\n  }\n}\n", "import type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { NormalizedField, TargetObject } from '../normalizer';\nimport type { Result } from '../result';\nimport { ok } from '../result';\nimport { metaApi } from '../rest-client';\nimport { FIELD_MAP } from '../../constants/field-map';\n\n/**\n * Names the app already knows how to place on a person or company.\n *\n * Two groups, and both matter. The literals are the Twenty field names written\n * directly. The FIELD_MAP values are the *pre-composite* canonical names \u2014\n * `email`, `phone`, `addressCity` \u2014 which never appear on the object because\n * assembleComposites folds them into `emails`, `phones` and `address` later.\n * Deriving them from the map rather than restating them is what stops a new\n * built-in mapping from being mistaken for an unknown field and diverted into\n * the note, which would quietly create people with no email address.\n */\nconst STANDARD_FIELDS = new Set<string>([\n  'name', 'emails', 'phones', 'domainName', 'address',\n  'linkedInLink', 'xLink', 'companyName', 'firstName', 'lastName', 'fullName',\n  'jobTitle', 'city', 'employees', 'annualRecurringRevenue', 'idealCustomerProfile',\n  ...Object.values(FIELD_MAP).map((e) => e.canonicalName),\n]);\n\n/** Standard Opportunity fields the upsert stage writes directly. */\nconst STANDARD_OPPORTUNITY_FIELDS = new Set(['name', 'stage', 'amount', 'closeDate']);\n\nconst EXT_RE = /^ext[A-Z]/;\n\nexport class SyncSchemaStage implements PipelineStage {\n  readonly name = 'sync-schema';\n\n  private objectCache = new Map<string, { id: string; fields: Set<string>; at: number }>();\n  private readonly TTL = 5 * 60 * 1000;\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    if (!ctx.crmFields || ctx.crmFields.length === 0) return ok(ctx);\n\n    const primary = this.primaryTarget(ctx);\n    const creationEnabled = process.env['INTAKE_FIELD_CREATION_ENABLED'] !== 'false';\n    if (!creationEnabled) {\n      ctx.warnings.push('Field creation disabled by INTAKE_FIELD_CREATION_ENABLED setting \u2014 ext fields skipped');\n    }\n\n    // Resolve every field's destination up front, so the rest of the pipeline\n    // never has to re-derive it. `auto` collapses onto the primary record.\n    const resolved: NormalizedField[] = ctx.crmFields.map((f) => ({\n      ...f,\n      targetObject: this.resolveTarget(f.targetObject, primary, ctx),\n    }));\n\n    let fieldsCreated = 0;\n    let fieldsMatched = 0;\n    const demoted: NormalizedField[] = [];\n\n    for (const objectName of this.targetObjects(resolved)) {\n      const fields = resolved.filter((f) => f.targetObject === objectName);\n      if (fields.length === 0) continue;\n\n      const standardForObject = objectName === 'opportunity'\n        ? STANDARD_OPPORTUNITY_FIELDS\n        : STANDARD_FIELDS;\n\n      const meta = await this.getObjectMeta(objectName);\n      if (!meta) {\n        for (const f of fields) {\n          if (standardForObject.has(f.canonicalName)) { f.writable = true; fieldsMatched++; continue; }\n          ctx.warnings.push(`Could not read schema for ${objectName} \u2014 \"${f.canonicalName}\" goes to note`);\n          demoted.push(f);\n        }\n        continue;\n      }\n\n      const maxExt = parseInt(process.env['INTAKE_MAX_EXT_FIELDS'] ?? '50', 10);\n      let extCount = [...meta.fields].filter((f) => EXT_RE.test(f)).length;\n\n      for (const field of fields) {\n        const name = field.canonicalName;\n\n        // Standard fields are written by the upsert stage directly.\n        if (standardForObject.has(name)) { field.writable = true; fieldsMatched++; continue; }\n\n        // Already on the object \u2014 whether an ext field from an earlier run or a\n        // field created by hand that a rule now points at.\n        if (meta.fields.has(name)) { field.writable = true; fieldsMatched++; continue; }\n\n        // Not present. Only ext-prefixed names are auto-created: minting a field\n        // for any name a rule happens to contain would turn a typo into a\n        // permanent schema change.\n        if (!EXT_RE.test(name)) {\n          ctx.warnings.push(\n            `Field \"${name}\" does not exist on ${objectName} and is not ext-prefixed \u2014 value routed to note. ` +\n            `Create the field in Twenty first, or rename the rule's canonical name to ext${name.charAt(0).toUpperCase()}${name.slice(1)}.`,\n          );\n          demoted.push(field);\n          continue;\n        }\n\n        if (!creationEnabled) { demoted.push(field); continue; }\n\n        if (extCount >= maxExt) {\n          ctx.warnings.push(`ext field limit (${maxExt}) reached for ${objectName} \u2014 \"${name}\" goes to note`);\n          demoted.push(field);\n          continue;\n        }\n\n        const created = await this.createField(meta.id, name, field.twentyType);\n        if (created) {\n          meta.fields.add(name);\n          this.objectCache.get(objectName)?.fields.add(name);\n          extCount++;\n          fieldsCreated++;\n          field.writable = true;\n        } else {\n          ctx.warnings.push(`Could not create field \"${name}\" on ${objectName} \u2014 will go to note`);\n          demoted.push(field);\n        }\n      }\n    }\n\n    // A field we cannot write is not \"matched\" \u2014 it is note overflow. Moving it\n    // here is what keeps the value from disappearing between the two stages.\n    const demotedSet = new Set(demoted);\n    return ok({\n      ...ctx,\n      crmFields: resolved.filter((f) => !demotedSet.has(f)),\n      noteFields: [...(ctx.noteFields ?? []), ...demoted],\n      fieldsCreated,\n      fieldsMatched,\n    });\n  }\n\n  /** Where an `auto` field lands: the person, or the company for company-only payloads. */\n  private primaryTarget(ctx: PipelineContext): 'person' | 'company' {\n    const t = ctx.source?.targetObject ?? 'AUTO';\n    if (t === 'PERSON') return 'person';\n    if (t === 'COMPANY') return 'company';\n\n    // For structured payloads (pipeline app), extras are company-level data\n    if (ctx.structuredCompany) return 'company';\n\n    const hasPersonData = ctx.crmFields?.some((f) =>\n      ['firstName', 'lastName', 'email', 'phone', 'fullName', 'emails', 'phones', 'name'].includes(f.canonicalName));\n    const hasCompanyData = ctx.crmFields?.some((f) =>\n      ['companyName', 'domainName'].includes(f.canonicalName));\n\n    if (hasPersonData) return 'person';\n    if (hasCompanyData) return 'company';\n    return 'person';\n  }\n\n  private resolveTarget(\n    target: TargetObject,\n    primary: 'person' | 'company',\n    ctx: PipelineContext,\n  ): TargetObject {\n    if (target === 'auto') return primary;\n    // A field routed to the deal has nowhere to go when the source does not open\n    // one, so it falls back to the primary record instead of being lost.\n    if (target === 'opportunity' && !ctx.source?.createOpportunity) {\n      ctx.warnings.push('Field routed to opportunity but this source does not create opportunities \u2014 written to the primary record instead');\n      return primary;\n    }\n    return target;\n  }\n\n  private targetObjects(fields: NormalizedField[]): string[] {\n    const seen = new Set<string>();\n    for (const f of fields) if (f.targetObject !== 'auto') seen.add(f.targetObject);\n    return [...seen];\n  }\n\n  private async getObjectMeta(objectName: string): Promise<{ id: string; fields: Set<string> } | null> {\n    const cached = this.objectCache.get(objectName);\n    if (cached && Date.now() - cached.at < this.TTL) return cached;\n\n    try {\n      // Metadata API returns all objects \u2014 filter client-side by nameSingular\n      const res = await metaApi.get<Array<{ id: string; nameSingular: string; fields: Array<{ name: string }> }>>('/objects');\n      const objects = Array.isArray(res) ? res : (res as { data: typeof res }).data ?? [];\n      const node = (objects as Array<{ id: string; nameSingular: string; fields: Array<{ name: string }> }>)\n        .find(o => o.nameSingular === objectName);\n      if (!node) return null;\n      const fields = new Set<string>((node.fields ?? []).map((f: { name: string }) => f.name));\n      const entry = { id: node.id, fields, at: Date.now() };\n      this.objectCache.set(objectName, entry);\n      return entry;\n    } catch {\n      return null;\n    }\n  }\n\n  private async createField(objectMetadataId: string, name: string, type: string): Promise<boolean> {\n    try {\n      await metaApi.post('/fields', {\n        objectMetadataId,\n        name,\n        label: toLabel(name),\n        type,\n        description: 'Auto-created by Intake',\n        icon: 'IconPlug',\n      });\n      return true;\n    } catch {\n      return false;\n    }\n  }\n}\n\nfunction toLabel(name: string): string {\n  return name.replace(/^ext_/, '').replace(/_/g, ' ')\n    .replace(/([A-Z])/g, ' $1').split(' ')\n    .map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n    .join(' ').trim();\n}\n", "type NoteField = { label: string; value: unknown };\n\n/**\n * Format the overflow data as a readable markdown note body.\n *\n * Output example:\n * **Source:** contact-form\n * **Message:** Need a new website, SEO help\n * **UTM Source:** google\n * **Marketing Score:** 72\n */\nexport function formatNote(\n  sourceName: string,\n  fields: Record<string, unknown>,\n): string {\n  const lines: string[] = [];\n\n  lines.push(`**Source:** ${sourceName}`);\n\n  const entries = buildEntries(fields);\n\n  for (const { label, value } of entries) {\n    const formatted = formatValue(value);\n    if (formatted !== null) {\n      lines.push(`**${label}:** ${formatted}`);\n    }\n  }\n\n  return lines.join('\\n');\n}\n\nfunction buildEntries(fields: Record<string, unknown>): NoteField[] {\n  return Object.entries(fields).map(([key, value]) => ({\n    label: toLabel(key),\n    value,\n  }));\n}\n\nfunction toLabel(key: string): string {\n  return key\n    .replace(/^ext_/, '')\n    .replace(/_/g, ' ')\n    .replace(/([A-Z])/g, ' $1')\n    .split(' ')\n    .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n    .join(' ')\n    .trim();\n}\n\nfunction formatValue(value: unknown): string | null {\n  if (value === null || value === undefined || value === '') return null;\n  if (Array.isArray(value)) return value.join(', ');\n  if (typeof value === 'object') {\n    try {\n      return JSON.stringify(value, null, 2);\n    } catch {\n      return String(value);\n    }\n  }\n  return String(value);\n}\n", "/**\n * Email domains that identify a person, not an employer.\n *\n * A consumer mailbox says nothing about who someone works for. Turning\n * `jane@gmail.com` into a company called \"gmail.com\" is the single fastest way\n * to make a CRM untrustworthy: most small-business enquiries arrive from a free\n * provider, so one webhook wired to a busy form quietly fills the Companies\n * table with mail hosts and attaches real people to them.\n *\n * Intake never derives a company from an email domain at all \u2014 see the note in\n * README under \"Companies from email domains\". This list exists so that the\n * places where a domain *does* drive a decision \u2014 company dedup, and the\n * domainName written onto a company record \u2014 refuse to act on a mailbox host\n * that arrived as a website or domain field.\n *\n * The list is deliberately the common providers rather than an exhaustive\n * disposable-address feed. It has to be cheap to read on every ingestion and\n * easy to audit; a missed niche provider costs one junk company that a person\n * can merge, whereas a wrong entry silently discards a real customer's domain.\n */\nconst FREE_EMAIL_DOMAINS = new Set<string>([\n  // Google\n  'gmail.com', 'googlemail.com',\n  // Microsoft\n  'outlook.com', 'outlook.co.uk', 'outlook.fr', 'outlook.de', 'outlook.es',\n  'hotmail.com', 'hotmail.co.uk', 'hotmail.fr', 'hotmail.de', 'hotmail.it', 'hotmail.es',\n  'live.com', 'live.co.uk', 'live.nl', 'msn.com', 'passport.com',\n  // Yahoo\n  'yahoo.com', 'yahoo.co.uk', 'yahoo.co.jp', 'yahoo.ca', 'yahoo.com.au',\n  'yahoo.de', 'yahoo.fr', 'yahoo.es', 'yahoo.it', 'yahoo.in', 'ymail.com', 'rocketmail.com',\n  // Apple\n  'icloud.com', 'me.com', 'mac.com',\n  // AOL / Verizon / legacy US ISPs \u2014 common on older consumer accounts\n  'aol.com', 'aim.com', 'verizon.net', 'att.net', 'sbcglobal.net', 'bellsouth.net',\n  'comcast.net', 'cox.net', 'charter.net', 'earthlink.net', 'juno.com', 'netzero.net',\n  'roadrunner.com', 'rr.com', 'optonline.net', 'frontier.com', 'windstream.net',\n  // Privacy-oriented providers\n  'proton.me', 'protonmail.com', 'protonmail.ch', 'pm.me',\n  'tutanota.com', 'tutanota.de', 'tuta.io', 'tuta.com',\n  'hushmail.com', 'fastmail.com', 'fastmail.fm', 'posteo.de', 'mailbox.org',\n  // Other large free providers\n  'gmx.com', 'gmx.net', 'gmx.de', 'gmx.at', 'gmx.ch',\n  'mail.com', 'email.com', 'usa.com', 'inbox.com',\n  'zoho.com', 'zohomail.com', 'yandex.com', 'yandex.ru', 'ya.ru',\n  'mail.ru', 'bk.ru', 'list.ru', 'inbox.ru', 'rambler.ru',\n  'qq.com', 'foxmail.com', '163.com', '126.com', 'sina.com', 'sina.cn', 'sohu.com',\n  'naver.com', 'hanmail.net', 'daum.net', 'nate.com',\n  'seznam.cz', 'wp.pl', 'o2.pl', 'onet.pl', 'interia.pl',\n  'libero.it', 'virgilio.it', 'alice.it', 'tiscali.it',\n  'orange.fr', 'wanadoo.fr', 'free.fr', 'laposte.net', 'sfr.fr', 'bbox.fr', 'neuf.fr',\n  'web.de', 't-online.de', 'freenet.de', 'arcor.de',\n  'btinternet.com', 'sky.com', 'talktalk.net', 'virginmedia.com', 'ntlworld.com',\n  'bigpond.com', 'bigpond.net.au', 'optusnet.com.au', 'iinet.net.au', 'xtra.co.nz',\n  'terra.com.br', 'uol.com.br', 'bol.com.br', 'ig.com.br',\n  'rediffmail.com', 'sify.com', 'indiatimes.com',\n  // Throwaway / disposable services \u2014 never a real employer\n  'mailinator.com', 'guerrillamail.com', 'guerrillamail.net', 'sharklasers.com',\n  '10minutemail.com', 'temp-mail.org', 'tempmail.com', 'throwawaymail.com',\n  'yopmail.com', 'trashmail.com', 'getnada.com', 'dispostable.com',\n  'maildrop.cc', 'mintemail.com', 'fakeinbox.com', 'spamgourmet.com',\n  'mailnesia.com', 'moakt.com', 'emailondeck.com', 'burnermail.io',\n  'anonaddy.com', 'simplelogin.io', 'duck.com', 'relay.firefox.com',\n  // Placeholder domains that show up in test and pasted payloads\n  'example.com', 'example.org', 'example.net', 'test.com', 'localhost',\n]);\n\n/**\n * Whether a host belongs to a mail provider rather than a business.\n *\n * Accepts a bare host, a host with `www.`, or anything `extractHost` produced.\n * Matching is case-insensitive and ignores a trailing dot.\n */\nexport function isFreeEmailDomain(host: string): boolean {\n  const normalised = host.trim().toLowerCase().replace(/^www\\./, '').replace(/\\.$/, '');\n  if (!normalised) return false;\n  return FREE_EMAIL_DOMAINS.has(normalised);\n}\n\nexport { FREE_EMAIL_DOMAINS };\n", "/**\n * Finding the record an incoming payload refers to.\n *\n * Both the live upsert and the dry-run diff need this, and they must agree: a\n * preview that says \"update\" while the real run creates a second contact is\n * worse than no preview. Sharing the query is what keeps them honest, so the\n * two callers ask the same question of the same endpoint with the same filter.\n *\n * The full record comes back, not just the id, because the merge policy has to\n * compare against what is already written before deciding whether to overwrite.\n */\n\nimport { coreApi } from './rest-client';\nimport { isFreeEmailDomain } from '../constants/free-email-domains';\n\nexport type ExistingRecord = Record<string, unknown> & { id: string };\n\n/** Twenty's REST list shape, whatever collection is being read. */\ntype ListResponse = {\n  data?: Record<string, { edges?: Array<{ node?: ExistingRecord }> }>;\n};\n\nasync function firstMatch(collection: string, query: string): Promise<ExistingRecord | null> {\n  try {\n    const res = await coreApi.get<ListResponse>(`/${collection}?${query}&first=1`);\n    return res.data?.[collection]?.edges?.[0]?.node ?? null;\n  } catch {\n    // A lookup failure must not stop an ingestion. Treating it as \"not found\"\n    // means the pipeline creates a record instead of dropping the lead; a\n    // duplicate contact is recoverable, a lost enquiry is not.\n    return null;\n  }\n}\n\nexport async function findPersonByEmail(email: string): Promise<ExistingRecord | null> {\n  if (!email) return null;\n  return firstMatch('people', `filter=emails.primaryEmail[eq]:${encodeURIComponent(email)}`);\n}\n\n/**\n * The company that owns a domain, if one is already on file.\n *\n * A mail provider is not a domain any company owns. The lookup is a substring\n * match, so asking it about `gmail.com` would hand back whichever company\n * happened to have a gmail address on file and attach an unrelated lead to it \u2014\n * and finding nothing is worse still, because the caller then creates a company\n * called \"gmail.com\". Refusing the question is the only safe answer.\n */\nexport async function findCompanyByDomain(domain: string): Promise<ExistingRecord | null> {\n  if (!domain) return null;\n  const host = extractHost(domain);\n  if (!host) return null;\n  if (isFreeEmailDomain(host)) return null;\n  return firstMatch(\n    'companies',\n    `filter=domainName.primaryLinkUrl[like]:%25${encodeURIComponent(host)}%25`,\n  );\n}\n\n/** Whether a domain is one a company can legitimately be identified by. */\nexport function isCompanyDomain(domain: string): boolean {\n  const host = extractHost(domain);\n  return host.length > 0 && !isFreeEmailDomain(host);\n}\n\nexport function extractHost(url: string): string {\n  try {\n    return new URL(url).hostname.replace(/^www\\./, '');\n  } catch {\n    return url;\n  }\n}\n\n/** The email an assembled payload will dedupe on, if any. */\nexport function primaryEmailOf(assembled: Record<string, unknown>): string {\n  const emails = assembled['emails'] as Record<string, unknown> | undefined;\n  const value = emails?.['primaryEmail'];\n  return typeof value === 'string' ? value : '';\n}\n\n/**\n * The domain an assembled payload will dedupe on, if any.\n *\n * The flat path assembles `domainName` into Twenty's LINKS composite, while a\n * structured payload may hand it over as a bare string. Both are read, so the\n * dry run and the live run see the same domain either way.\n */\nexport function primaryDomainOf(standard: Record<string, unknown>): string {\n  const domainName = standard['domainName'];\n  if (typeof domainName === 'string') return domainName;\n  const value = (domainName as Record<string, unknown> | undefined)?.['primaryLinkUrl'];\n  return typeof value === 'string' ? value : '';\n}\n", "import type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { NormalizedField } from '../normalizer';\nimport type { Result } from '../result';\nimport { ok, err, ErrorCode } from '../result';\nimport { assembleComposites } from '../normalizer';\nimport { formatNote } from '../note-formatter';\nimport { coreApi, gql } from '../rest-client';\nimport { mergePatch, type FieldMergeStrategy } from '../merge';\nimport { mergePolicy as workspaceMergePolicy, appLabel, defaultOpportunityStage } from '../settings';\nimport { findPersonByEmail, findCompanyByDomain, isCompanyDomain, extractHost, type ExistingRecord } from '../lookup';\n\n/**\n * Updates an existing record under the merge policy. Passed down rather than\n * reconstructed per method so one run resolves the policy exactly once.\n */\ntype PatchExisting = (\n  collection: string,\n  object: string,\n  existing: ExistingRecord,\n  incoming: Record<string, unknown>,\n) => Promise<void>;\n\n/** Fields each object accepts directly, so they are never PATCHed as custom fields. */\nconst STANDARD_BY_OBJECT: Record<'person' | 'company' | 'opportunity', 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 class UpsertRecordStage implements PipelineStage {\n  readonly name = 'upsert-record';\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    if (!ctx.crmFields) return err(ErrorCode.INTERNAL_ERROR, 'No CRM fields to upsert');\n\n    // Strip internal __company_ routing keys from crmFields before assembly\n    const cleanFields = ctx.crmFields.filter(f => !f.originalKey.startsWith('__'));\n    const { assembled, remainder } = assembleComposites(cleanFields);\n    for (const f of remainder) assembled[f.canonicalName] = f.value;\n\n    // For structured payloads, company/person objects are already in Twenty format\n    const structuredCompany = ctx.structuredCompany;\n    const structuredPerson = ctx.structuredPerson;\n\n    const rawCompanyName = (structuredCompany?.['name'] as string | undefined)\n      ?? (assembled['companyName'] as string | undefined);\n\n    // Custom-field values, grouped by the record SyncSchemaStage resolved them\n    // onto. Only fields it marked writable appear here \u2014 anything it could not\n    // place was already moved into the note, so nothing is silently dropped.\n    const customFor = (object: 'person' | 'company' | 'opportunity'): Record<string, unknown> => {\n      const out: Record<string, unknown> = {};\n      for (const f of (ctx.crmFields ?? [])) {\n        if (f.targetObject !== object || f.writable === false || f.writable === undefined) continue;\n        if (STANDARD_BY_OBJECT[object].has(f.canonicalName)) continue;\n        out[f.canonicalName] = formatExtValue(f.value, f.twentyType);\n      }\n      return out;\n    };\n\n    // A source may override the workspace default; a rule may override both for\n    // one field. Resolved once here so every write in this run agrees.\n    const policy = ctx.source?.mergePolicy ?? workspaceMergePolicy();\n    const strategyByField = new Map<string, FieldMergeStrategy>();\n    for (const f of (ctx.crmFields ?? [])) {\n      if (f.mergeStrategy && f.mergeStrategy !== 'INHERIT') {\n        strategyByField.set(f.canonicalName, f.mergeStrategy);\n      }\n    }\n    const strategyFor = (field: string): FieldMergeStrategy =>\n      strategyByField.get(field) ?? 'INHERIT';\n\n    /**\n     * Update an existing record under the merge policy.\n     *\n     * Everything the payload wants to write is compared against what is already\n     * there, and only the keys the policy actually approves are sent. Sending a\n     * PATCH with nothing in it would be a wasted call, so an update where the\n     * policy preserved every field makes no request at all.\n     */\n    const patchExisting = async (\n      collection: string,\n      object: string,\n      existing: ExistingRecord,\n      incoming: Record<string, unknown>,\n    ): Promise<void> => {\n      const { patch, decisions } = mergePatch(existing, incoming, policy, strategyFor);\n      ctx.mergeDecisions = { ...(ctx.mergeDecisions ?? {}), [object]: decisions };\n\n      const preserved = Object.entries(decisions)\n        .filter(([, d]) => d.status === 'preserved' && d.write === false)\n        .map(([field]) => field);\n      if (preserved.length > 0 && policy === 'PRESERVE') {\n        ctx.warnings.push(\n          `Kept the existing ${object} values for ${preserved.join(', ')} \u2014 INTAKE_MERGE_POLICY is PRESERVE`,\n        );\n      }\n\n      if (Object.keys(patch).length === 0) return;\n      await coreApi.patch(`/${collection}/${existing.id}`, patch);\n    };\n\n    try {\n      let companyId: string | undefined;\n      let personId: string | undefined;\n\n      // \u2500\u2500 1. Upsert 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\n      const needsCompany = rawCompanyName || ctx.source?.targetObject === 'COMPANY';\n      if (needsCompany) {\n        companyId = await this.upsertCompany(assembled, structuredCompany ?? {}, rawCompanyName, customFor('company'), ctx, patchExisting);\n      }\n\n      // \u2500\u2500 2. Upsert 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\n      const personData = structuredPerson ?? assembled;\n      const hasPersonData = personData['emails'] || personData['name'] || assembled['emails'] || assembled['name'];\n      if (hasPersonData || ctx.source?.targetObject === 'PERSON') {\n        personId = await this.upsertPerson(assembled, structuredPerson, companyId, customFor('person'), ctx, patchExisting);\n      }\n\n      // \u2500\u2500 3. Create 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\n      let opportunityId: string | undefined;\n      if (ctx.source?.createOpportunity && (personId || companyId)) {\n        opportunityId = await this.createOpportunity(assembled, personId, companyId, ctx.crmFields ?? [], customFor('opportunity'), ctx);\n      }\n\n      // \u2500\u2500 4. Create Note + link targets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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      let noteId: string | undefined;\n      const noteFields = ctx.noteFields ?? [];\n      if (noteFields.length > 0) {\n        const overflow: Record<string, unknown> = {};\n        for (const f of noteFields) overflow[f.canonicalName] = f.value;\n        noteId = await this.createNote(overflow, personId, companyId, ctx);\n      }\n\n      return ok({\n        ...ctx,\n        personId,\n        companyId,\n        opportunityId,\n        noteId,\n        recordType: personId ? 'PERSON' : 'COMPANY',\n      });\n    } catch (e) {\n      return err(ErrorCode.RECORD_UPSERT_FAILED, `Failed to upsert record: ${String(e)}`);\n    }\n  }\n\n  private async upsertCompany(\n    assembled: Record<string, unknown>,\n    structuredCompany: Record<string, unknown>,\n    companyName: string | undefined,\n    customData: Record<string, unknown>,\n    ctx: PipelineContext,\n    patchExisting: PatchExisting,\n  ): Promise<string | undefined> {\n    // Standard fields only for the initial create/update\n    const standard: Record<string, unknown> = { ...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    // A mail provider is not a company domain. Someone filling in a form with\n    // their own address where a website was asked for must not turn Gmail into\n    // an organisation, nor match this lead onto whichever company already had a\n    // Gmail address on file.\n    // A structured payload may send `domainName` as a bare string rather than\n    // the composite shape the flat path assembles, so read both.\n    const proposedDomain = typeof standard['domainName'] === 'string'\n      ? standard['domainName']\n      : (standard['domainName'] as Record<string, unknown> | undefined)?.['primaryLinkUrl'];\n    if (typeof proposedDomain === 'string' && proposedDomain && !isCompanyDomain(proposedDomain)) {\n      delete standard['domainName'];\n      ctx.warnings.push(\n        `\"${extractHost(proposedDomain)}\" is an email provider, not a company domain \u2014 ` +\n        `it was left off the company record.`,\n      );\n    }\n\n    // Nothing left to identify a company by. Creating a nameless, domainless\n    // record here would be inventing an organisation out of a blank form.\n    if (!standard['name'] && !standard['domainName']) {\n      ctx.warnings.push('No company name or domain in this payload \u2014 no company was created.');\n      return undefined;\n    }\n\n    // Dedup by domain\n    const domain = (standard['domainName'] as Record<string, unknown> | undefined)?.['primaryLinkUrl'] as string | undefined;\n    let companyId: string | undefined;\n    if (domain) {\n      const existing = await findCompanyByDomain(domain);\n      if (existing) {\n        // Standard and custom fields go through the policy together: an update\n        // is one decision about one record, and splitting it would let a custom\n        // field overwrite while the name beside it was preserved.\n        await patchExisting('companies', 'company', existing, { ...standard, ...customData });\n        return existing.id;\n      }\n    }\n\n    if (!companyId) {\n      try {\n        const created = await coreApi.post<{ data: { createCompany: { id: string } } }>('/companies', standard);\n        companyId = created.data.createCompany.id;\n      } catch (e) {\n        // Duplicate \u2014 find by name via GraphQL\n        if (String(e).toLowerCase().includes('duplicate') && standard['name']) {\n          try {\n            const found = await gql<{ data: { companies: { edges: Array<{ node: { id: string } }> } } }>(\n              `query FindCompany($name: String!) { companies(filter: { name: { eq: $name } }) { edges { node { id } } } }`,\n              { name: String(standard['name']) },\n            );\n            companyId = found.data?.companies?.edges?.[0]?.node?.id;\n          } catch { /* ignore */ }\n        }\n        if (!companyId) throw e;\n      }\n    }\n\n    // Step 2: PATCH custom fields separately (non-fatal)\n    if (Object.keys(customData).length > 0) {\n      await coreApi.patch(`/companies/${companyId}`, customData).catch((e: Error) => {\n        ctx.warnings.push(`Could not set custom fields on company: ${e.message.slice(0, 120)}`);\n      });\n    }\n\n    return companyId;\n  }\n\n  private async upsertPerson(\n    assembled: Record<string, unknown>,\n    structuredPerson: Record<string, unknown> | undefined,\n    companyId: string | undefined,\n    customData: Record<string, unknown>,\n    ctx: PipelineContext,\n    patchExisting: PatchExisting,\n  ): Promise<string> {\n    // Standard fields only for initial create/update\n    const standard: Record<string, unknown> = structuredPerson ? { ...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    if (companyId) standard['companyId'] = companyId;\n\n    // Dedup by email\n    const emails = (standard['emails'] ?? assembled['emails']) as Record<string, string> | undefined;\n    const email = emails?.['primaryEmail'];\n    let personId: string | undefined;\n    if (email) {\n      const existing = await findPersonByEmail(email);\n      if (existing) {\n        await patchExisting('people', 'person', existing, { ...standard, ...customData });\n        return existing.id;\n      }\n    }\n\n    if (!personId) {\n      const created = await coreApi.post<{ data: { createPerson: { id: string } } }>('/people', standard);\n      personId = created.data.createPerson.id;\n    }\n\n    // Step 2: PATCH custom fields separately (non-fatal)\n    if (Object.keys(customData).length > 0) {\n      await coreApi.patch(`/people/${personId}`, customData).catch((e: Error) => {\n        ctx.warnings.push(`Could not set custom fields on person: ${e.message.slice(0, 120)}`);\n      });\n    }\n\n    return personId;\n  }\n\n  private async createOpportunity(\n    assembled: Record<string, unknown>,\n    personId: string | undefined,\n    companyId: string | undefined,\n    crmFields: NormalizedField[],\n    customData: Record<string, unknown>,\n    ctx: PipelineContext,\n  ): Promise<string | undefined> {\n    let opportunityId: string | undefined;\n    try {\n      const name = buildOpportunityName(ctx, assembled);\n      const stage = defaultOpportunityStage();\n      const data: Record<string, unknown> = { name, stage };\n      if (personId) data['pointOfContactId'] = personId;\n      if (companyId) data['companyId'] = companyId;\n\n      // Standard deal fields a rule can target by name \u2014 `amount` is what makes\n      // a stated budget show up in pipeline totals rather than sitting in text.\n      for (const f of crmFields) {\n        if (f.targetObject !== 'opportunity' || !f.writable) continue;\n        if (f.canonicalName === 'amount' || f.canonicalName === 'closeDate') {\n          data[f.canonicalName] = formatExtValue(f.value, f.twentyType);\n        }\n      }\n\n      const created = await coreApi.post<{ data: { createOpportunity: { id: string } } }>('/opportunities', data);\n      opportunityId = created.data.createOpportunity.id;\n    } catch {\n      ctx.warnings.push('Opportunity creation failed \u2014 record was still created');\n      return undefined;\n    }\n\n    // Custom fields go on in a second call, the same way person and company do:\n    // a rejected custom field must not cost us the opportunity itself.\n    if (Object.keys(customData).length > 0) {\n      await coreApi.patch(`/opportunities/${opportunityId}`, customData).catch((e: Error) => {\n        ctx.warnings.push(`Could not set custom fields on opportunity: ${e.message.slice(0, 120)}`);\n      });\n    }\n\n    return opportunityId;\n  }\n\n  private async createNote(\n    overflow: Record<string, unknown>,\n    personId: string | undefined,\n    companyId: string | undefined,\n    ctx: PipelineContext,\n  ): Promise<string | undefined> {\n    try {\n      const markdown = formatNote(ctx.source?.name ?? ctx.sourceSlug, overflow);\n      const label = appLabel();\n      const created = await coreApi.post<{ data: { createNote: { id: string } } }>('/notes', {\n        title: `${ctx.source?.name ?? label} \u2014 Lead`,\n        bodyV2: { markdown },\n      });\n      const noteId = created.data.createNote.id;\n\n      // NoteTarget's columns are `targetPersonId` / `targetCompanyId`, not\n      // `personId` / `companyId`. Posting the short names created the note and\n      // silently attached it to nothing, so the lead's own message was invisible\n      // from their record \u2014 the one place anyone would look for it.\n      //\n      // The failure is reported rather than swallowed for the same reason it went\n      // unnoticed: a note nobody can find looks identical to no note at all.\n      if (personId) {\n        await coreApi.post('/noteTargets', { noteId, targetPersonId: personId }).catch((e: Error) => {\n          ctx.warnings.push(`Could not attach the note to the person: ${e.message.slice(0, 120)}`);\n        });\n      }\n      if (companyId) {\n        await coreApi.post('/noteTargets', { noteId, targetCompanyId: companyId }).catch((e: Error) => {\n          ctx.warnings.push(`Could not attach the note to the company: ${e.message.slice(0, 120)}`);\n        });\n      }\n      return noteId;\n    } catch {\n      ctx.warnings.push('Note creation failed \u2014 record was still created');\n      return undefined;\n    }\n  }\n}\n\nfunction buildOpportunityName(ctx: PipelineContext, assembled: Record<string, unknown>): string {\n  const label = appLabel();\n  const defaultTemplate = `{{source}} \u2014 {{firstName}} {{lastName}}`;\n  const template = ctx.source?.opportunityNameTemplate ?? defaultTemplate;\n  const name = assembled['name'] as Record<string, string> | undefined;\n  return template\n    .replace('{{source}}', ctx.source?.name ?? label)\n    .replace('{{firstName}}', name?.['firstName'] ?? '')\n    .replace('{{lastName}}', name?.['lastName'] ?? '')\n    .replace('{{email}}', (assembled['emails'] as Record<string, string> | undefined)?.['primaryEmail'] ?? '')\n    .replace('{{company}}', assembled['companyName'] as string ?? '')\n    .trim().replace(/\\s+/g, ' ').replace(/\\s*\u2014\\s*$/, '');\n}\n\n/** Format a custom field value to match Twenty's expected composite types. */\nfunction formatExtValue(value: unknown, type?: string): unknown {\n  if (type === 'LINKS' && typeof value === 'string') {\n    return { primaryLinkUrl: value, primaryLinkLabel: '' };\n  }\n  if (type === 'CURRENCY') {\n    // Twenty stores currency in micros. Accept a bare number or a written\n    // amount (\"$5,000/mo\") and take the first figure in it.\n    const amount = typeof value === 'number'\n      ? value\n      : Number(String(value ?? '').replace(/[^\\d.]/g, ''));\n    if (!Number.isFinite(amount) || amount <= 0) return undefined;\n    return { amountMicros: Math.round(amount * 1_000_000), currencyCode: 'USD' };\n  }\n  return value;\n}\n", "/**\n * What gets kept of an inbound payload, and for how much of it.\n *\n * The stored copy is what makes replay possible: a field mapping added a week\n * late can be applied to everything already received instead of being lost. That\n * value only exists if the copy is faithful, so retention is FULL by default and\n * the payload is stored as sent.\n *\n * Two things are nonetheless never stored. Credentials are stripped\n * unconditionally \u2014 an audit log is the last place a password or API token\n * should come to rest, and no replay needs one. And the copy is capped, because\n * an IntakeLog row is not the right home for a multi-megabyte upload.\n *\n * What remains is still personal data: names, emails, phone numbers and whatever\n * else a sender chose to put in the body. It lives in the IntakeLog object under\n * the workspace's own access control, and it is visible to anyone who can read\n * that object. Workspaces that would rather not hold a second copy can set\n * INTAKE_RAW_PAYLOAD_RETENTION=NONE, which switches replay and retry off.\n */\n\nimport { rawPayloadMaxBytes, rawPayloadRetention } from './settings';\n\n/**\n * Keys whose values are secrets. Matched on the normalised key, as a substring,\n * so `stripe_secret_key` and `apiToken` are both caught. Deliberately broad: a\n * false positive costs one redacted field in an audit record, a false negative\n * writes a live credential into a database.\n */\nconst CREDENTIAL_KEY_PATTERNS = [\n  'password', 'passwd', 'secret', 'token', 'apikey', 'api_key',\n  'authorization', 'auth', 'credential', 'privatekey', 'private_key',\n  'session', 'cookie', 'ssn', 'socialsecurity', 'creditcard', 'credit_card',\n  'cardnumber', 'card_number', 'cvv', 'cvc', 'pin', 'accesskey', 'access_key',\n];\n\nexport const REDACTED = '[redacted]';\n\nfunction isCredentialKey(key: string): boolean {\n  const norm = key.toLowerCase().replace(/[-\\s]/g, '_');\n  return CREDENTIAL_KEY_PATTERNS.some((p) => norm.includes(p));\n}\n\n/**\n * Replace credential values with a marker, recursively, leaving structure and\n * every other value intact so the result still replays.\n */\nexport function redactCredentials(value: unknown, depth = 0): unknown {\n  if (depth > 12 || value === null || typeof value !== 'object') return value;\n  if (Array.isArray(value)) return value.map((v) => redactCredentials(v, depth + 1));\n\n  const out: Record<string, unknown> = {};\n  for (const [key, v] of Object.entries(value as Record<string, unknown>)) {\n    out[key] = isCredentialKey(key) ? REDACTED : redactCredentials(v, depth + 1);\n  }\n  return out;\n}\n\nexport type RetainedPayload = {\n  /** JSON to store, or null when retention is off or the payload is too large. */\n  stored: string | null;\n  /** A warning for the log when something was not kept, otherwise null. */\n  warning: string | null;\n};\n\n/**\n * Prepare a payload for storage on an IntakeLog.\n *\n * An oversized payload is dropped rather than truncated. A half-stored JSON\n * document cannot be parsed and so cannot be replayed; keeping it would only\n * offer a replay that fails later, which is worse than saying so now.\n */\nexport function prepareForStorage(payload: Record<string, unknown>): RetainedPayload {\n  if (rawPayloadRetention() === 'NONE') {\n    return { stored: null, warning: null };\n  }\n\n  let json: string;\n  try {\n    json = JSON.stringify(redactCredentials(payload));\n  } catch {\n    return { stored: null, warning: 'Payload could not be serialised \u2014 not stored, so replay is unavailable for this log' };\n  }\n\n  const max = rawPayloadMaxBytes();\n  if (Buffer.byteLength(json, 'utf8') > max) {\n    return {\n      stored: null,\n      warning: `Payload exceeds INTAKE_RAW_PAYLOAD_MAX_BYTES (${max}) \u2014 not stored, so replay is unavailable for this log`,\n    };\n  }\n\n  return { stored: json, warning: null };\n}\n", "/**\n * Writing the IntakeLog.\n *\n * This used to live inside WriteLogStage, which only ever ran when the pipeline\n * reached the end of its stages. Anything that failed earlier \u2014 a bad signature,\n * a rejected record, and now a quarantine \u2014 returned an error to the caller and\n * left no trace in the workspace at all. The object is described as a full audit\n * trail of every payload received, and a trail that records only the successes\n * is the exact shape of the problem this release is trying to fix: you cannot\n * notice a failure you were never told about.\n *\n * Pulling the write out here lets the terminal error paths record too, using the\n * same fields and the same retention rules.\n */\n\nimport { coreApi } from './rest-client';\nimport type { PipelineContext } from './pipeline';\nimport { prepareForStorage } from './retention';\n\nexport type LogStatus =\n  | 'SUCCESS'\n  | 'PARTIAL'\n  | 'FAILED'\n  | 'DUPLICATE'\n  | 'QUARANTINED'\n  | 'DISCARDED';\n\nexport type LogWriteInput = {\n  ctx: PipelineContext;\n  status: LogStatus;\n  error?: string | null;\n  spamScore?: number | null;\n  quarantineReason?: string | null;\n};\n\n/**\n * Persist one IntakeLog. Never throws: a failure to write the audit record must\n * not turn a successful ingestion into a reported failure.\n */\nexport async function writeIntakeLog(input: LogWriteInput): Promise<string | null> {\n  const { ctx, status } = input;\n\n  const retained = prepareForStorage(ctx.rawPayload);\n  if (retained.warning) ctx.warnings.push(retained.warning);\n\n  try {\n    const created = await coreApi.post<{ data: { createIntakeLog: { id: string } } }>('/intakeLogs', {\n      intakeSourceId: ctx.source?.id ?? null,\n      status,\n      payloadHash: ctx.payloadHash ?? '',\n      idempotencyKey: ctx.idempotencyKey ?? null,\n      rawPayload: retained.stored,\n      recordId: ctx.personId ?? ctx.companyId ?? null,\n      recordType: ctx.recordType ?? null,\n      fieldsCreated: ctx.fieldsCreated,\n      fieldsMatched: ctx.fieldsMatched,\n      overflowCount: ctx.noteFields?.length ?? 0,\n      processingMs: Date.now() - ctx.startedAt,\n      processedAt: new Date().toISOString(),\n      error: input.error ?? null,\n      spamScore: input.spamScore ?? ctx.spam?.score ?? null,\n      quarantineReason: input.quarantineReason ?? null,\n      replayOfLogId: ctx.replayOfLogId ?? null,\n    });\n    return created.data?.createIntakeLog?.id ?? null;\n  } catch {\n    ctx.warnings.push('Intake log write failed');\n    return null;\n  }\n}\n\n/**\n * Move a source's counters. Read-then-write, so it races under concurrent\n * ingestion \u2014 the counters are a dashboard convenience, and `lastIngestedAt`,\n * which silence detection actually depends on, is a plain timestamp that a lost\n * race still sets correctly.\n */\nexport async function bumpSourceCounters(\n  sourceId: string,\n  outcome: 'ingested' | 'failed',\n): Promise<void> {\n  try {\n    const current = await coreApi.get<{\n      data: { intakeSource: { totalIngested: number | null; totalFailed: number | null } };\n    }>(`/intakeSources/${sourceId}`);\n\n    const node = current.data?.intakeSource;\n    const patch: Record<string, unknown> = {};\n\n    if (outcome === 'ingested') {\n      patch['totalIngested'] = (node?.totalIngested ?? 0) + 1;\n      patch['lastIngestedAt'] = new Date().toISOString();\n      // Anything arriving is proof the source is not silent.\n      patch['healthStatus'] = 'HEALTHY';\n      patch['silentSince'] = null;\n    } else {\n      patch['totalFailed'] = (node?.totalFailed ?? 0) + 1;\n    }\n\n    await coreApi.patch(`/intakeSources/${sourceId}`, patch);\n  } catch {\n    /* non-fatal */\n  }\n}\n", "import type { PipelineStage, PipelineContext } from '../pipeline';\nimport type { Result } from '../result';\nimport { ok } from '../result';\nimport { writeIntakeLog, bumpSourceCounters } from '../intake-log-writer';\n\nexport class WriteLogStage implements PipelineStage {\n  readonly name = 'write-log';\n\n  async execute(ctx: PipelineContext): Promise<Result<PipelineContext>> {\n    const status = ctx.personId || ctx.companyId ? 'SUCCESS' : 'PARTIAL';\n\n    await writeIntakeLog({ ctx, status });\n\n    if (ctx.source) await bumpSourceCounters(ctx.source.id, 'ingested');\n\n    return ok(ctx);\n  }\n}\n", "import type { CoreApiClient } from 'twenty-client-sdk/core';\nimport type { MetadataApiClient } from 'twenty-client-sdk/metadata';\nimport { Pipeline } from './pipeline';\nimport { LoadSourceStage } from './stages/load-source.stage';\nimport { VerifySignatureStage } from './stages/verify-signature.stage';\nimport { IdempotencyStage } from './stages/idempotency.stage';\nimport { FlattenStage } from './stages/flatten.stage';\nimport { NormalizeStage } from './stages/normalize.stage';\nimport { QuarantineStage } from './stages/quarantine.stage';\nimport { SyncSchemaStage } from './stages/sync-schema.stage';\nimport { UpsertRecordStage } from './stages/upsert-record.stage';\nimport { WriteLogStage } from './stages/write-log.stage';\n\n/**\n * Assemble the 9-stage ingestion pipeline.\n *\n * Stage order is intentional:\n *  1. LoadSource        \u2014 resolve source config (needed by all later stages)\n *  2. VerifySignature   \u2014 auth check (needs source's webhookSecret)\n *  3. Idempotency       \u2014 dedup before any expensive work\n *  4. Flatten           \u2014 normalise nested structure to flat record\n *  5. Normalize         \u2014 map field names, detect types\n *  6. Quarantine        \u2014 hold suspected spam BEFORE anything is created\n *  7. SyncSchema        \u2014 create missing fields in Twenty via Metadata API\n *  8. UpsertRecord      \u2014 create/update Person, Company, Opportunity, Note\n *  9. WriteLog          \u2014 persist IntakeLog (always runs, even on partial)\n *\n * Quarantine sits where it does for a reason. It runs after Normalize so it can\n * read canonical names rather than guess at whichever spelling the form used,\n * and before SyncSchema so a bot cannot extend the workspace schema or leave a\n * Person, Company, Opportunity and Note behind on its way to being caught.\n */\nexport function createPipeline(\n  coreClient: CoreApiClient,\n  metadataClient: MetadataApiClient,\n): Pipeline {\n  return new Pipeline()\n    .use(new LoadSourceStage(coreClient))\n    .use(new VerifySignatureStage())\n    .use(new IdempotencyStage(coreClient))\n    .use(new FlattenStage())\n    .use(new NormalizeStage(coreClient))\n    .use(new QuarantineStage())\n    .use(new SyncSchemaStage(metadataClient))\n    .use(new UpsertRecordStage(coreClient))\n    .use(new WriteLogStage(coreClient));\n}\n", "/**\n * Re-running a payload that was already received.\n *\n * The problem this solves is a mapping added too late. A field arrives for weeks\n * with no rule to place it, goes into the overflow note, and the day someone\n * writes the rule it only applies to what comes next \u2014 everything already in the\n * CRM has to be repaired by hand from note text, if it can be repaired at all.\n * Replay runs the stored payloads back through the rules as they exist now, so\n * the fix reaches the records that needed it.\n *\n * Distinct from retry, which exists for failures and refuses a log that already\n * succeeded. Replay is for logs that succeeded: the run worked, the mapping was\n * simply not there yet.\n */\n\nimport { coreApi } from './rest-client';\nimport { runIngestion } from './ingest';\nimport { replayMaxBatch } from './settings';\n\nexport type StoredLog = {\n  id: string;\n  status: string;\n  rawPayload: string | null;\n  intakeSourceId: string | null;\n  processedAt: string | null;\n};\n\nexport type ReplayResult = {\n  logId: string;\n  ok: boolean;\n  reason?: string;\n  recordId?: string | undefined;\n  personId?: string | undefined;\n  companyId?: string | undefined;\n  opportunityId?: string | undefined;\n  fieldsCreated?: number;\n  warnings?: string[];\n};\n\nexport async function loadLog(logId: string): Promise<StoredLog | null> {\n  try {\n    const res = await coreApi.get<{ data: { intakeLog: StoredLog } }>(`/intakeLogs/${logId}`);\n    return res.data?.intakeLog ?? null;\n  } catch {\n    return null;\n  }\n}\n\nexport async function slugForSource(sourceId: string | null): Promise<string | null> {\n  if (!sourceId) return null;\n  try {\n    const res = await coreApi.get<{ data: { intakeSource: { slug: string } } }>(`/intakeSources/${sourceId}`);\n    return res.data?.intakeSource?.slug ?? null;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Replay one stored log.\n *\n * Deduplication is bypassed, because the payload is by definition one we have\n * seen \u2014 that is the whole point. Spam scoring is not bypassed: a payload that\n * would be quarantined today should still be quarantined when it is replayed.\n */\nexport async function replayLog(log: StoredLog): Promise<ReplayResult> {\n  if (!log.rawPayload) {\n    return {\n      logId: log.id,\n      ok: false,\n      reason: 'No stored payload \u2014 retention was off, or the payload exceeded the size cap when it arrived',\n    };\n  }\n\n  let payload: Record<string, unknown>;\n  try {\n    const parsed: unknown = JSON.parse(log.rawPayload);\n    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n      return { logId: log.id, ok: false, reason: 'Stored payload is not a JSON object' };\n    }\n    payload = parsed as Record<string, unknown>;\n  } catch {\n    return { logId: log.id, ok: false, reason: 'Stored payload is not valid JSON' };\n  }\n\n  const slug = await slugForSource(log.intakeSourceId);\n  if (!slug) {\n    return { logId: log.id, ok: false, reason: 'The source for this log no longer exists' };\n  }\n\n  const outcome = await runIngestion({\n    sourceSlug: slug,\n    rawPayload: payload,\n    // The signature was checked when the payload first arrived; there is no body\n    // to re-verify against and the request is now coming from an authenticated\n    // operator rather than the network.\n    skipDedup: true,\n    replayOfLogId: log.id,\n  });\n\n  if (!outcome.ok) {\n    return { logId: log.id, ok: false, reason: `${outcome.error.code}: ${outcome.error.message}` };\n  }\n\n  return {\n    logId: log.id,\n    ok: true,\n    recordId: outcome.ctx.personId ?? outcome.ctx.companyId,\n    personId: outcome.ctx.personId,\n    companyId: outcome.ctx.companyId,\n    opportunityId: outcome.ctx.opportunityId,\n    fieldsCreated: outcome.ctx.fieldsCreated,\n    warnings: outcome.ctx.warnings,\n  };\n}\n\nexport type BulkQuery = {\n  sourceSlug?: string | undefined;\n  status?: string | undefined;\n  since?: string | undefined;\n  until?: string | undefined;\n  limit?: number | undefined;\n};\n\n/**\n * Select logs for a bulk replay.\n *\n * Capped by INTAKE_REPLAY_MAX_BATCH, and the cap is not negotiable from the\n * request: a replay writes to the CRM for every log it touches, and an\n * unbounded one aimed at a year of history would rewrite a workspace in a single\n * call. Callers wanting more run several batches and can see what each one did.\n */\nexport async function selectLogs(query: BulkQuery): Promise<StoredLog[]> {\n  const max = replayMaxBatch();\n  const limit = Math.min(query.limit ?? max, max);\n\n  const filters: string[] = [];\n  if (query.status) filters.push(`status[eq]:${encodeURIComponent(query.status)}`);\n  if (query.since) filters.push(`processedAt[gte]:${encodeURIComponent(query.since)}`);\n  if (query.until) filters.push(`processedAt[lte]:${encodeURIComponent(query.until)}`);\n\n  if (query.sourceSlug) {\n    const sourceId = await idForSlug(query.sourceSlug);\n    if (!sourceId) return [];\n    filters.push(`intakeSourceId[eq]:${sourceId}`);\n  }\n\n  const qs = [\n    filters.length > 0 ? `filter=${filters.join(',')}` : '',\n    `orderBy=processedAt[AscNullsLast]`,\n    `limit=${limit}`,\n  ].filter(Boolean).join('&');\n\n  try {\n    const res = await coreApi.get<{ data: { intakeLogs: { edges: Array<{ node: StoredLog }> } } }>(\n      `/intakeLogs?${qs}`,\n    );\n    return (res.data?.intakeLogs?.edges ?? []).map((e) => e.node);\n  } catch {\n    return [];\n  }\n}\n\nasync function idForSlug(slug: string): Promise<string | null> {\n  try {\n    const res = await coreApi.get<{ data: { intakeSources: { edges: Array<{ node: { id: string } }> } } }>(\n      `/intakeSources?filter=slug[eq]:${encodeURIComponent(slug)}&first=1`,\n    );\n    return res.data?.intakeSources?.edges?.[0]?.node?.id ?? null;\n  } catch {\n    return null;\n  }\n}\n", "import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';\nimport { IDS } from '../constants/universal-identifiers';\nimport { coreApi } from '../lib/rest-client';\nimport { loadLog, slugForSource } from '../lib/replay';\nimport { runIngestion } from '../lib/ingest';\n\n/**\n * Let a held payload through.\n *\n * The scoring still runs and is still recorded \u2014 the release is a human\n * overruling a judgement, and the log should show both. The original log is\n * marked FAILED rather than deleted, because the record of what was held and\n * who let it through is the part that makes the filter safe to trust.\n */\nconst handler = async (params: RoutePayload) => {\n  const logId = params.pathParameters?.['logId'];\n  if (!logId) return { statusCode: 400, body: { error: 'Missing logId in URL' } };\n\n  const log = await loadLog(logId);\n  if (!log) return { statusCode: 404, body: { error: `IntakeLog ${logId} not found` } };\n\n  if (log.status !== 'QUARANTINED') {\n    return {\n      statusCode: 400,\n      body: { error: `Log ${logId} is ${log.status}, not QUARANTINED \u2014 nothing to release`, status: log.status },\n    };\n  }\n\n  if (!log.rawPayload) {\n    return {\n      statusCode: 400,\n      body: {\n        error: 'No stored payload \u2014 this log cannot be released. Set INTAKE_RAW_PAYLOAD_RETENTION=FULL to keep payloads for review.',\n        code: 'NO_STORED_PAYLOAD',\n      },\n    };\n  }\n\n  let payload: Record<string, unknown>;\n  try {\n    payload = JSON.parse(log.rawPayload) as Record<string, unknown>;\n  } catch {\n    return { statusCode: 400, body: { error: 'Stored payload is not valid JSON' } };\n  }\n\n  const slug = await slugForSource(log.intakeSourceId);\n  if (!slug) return { statusCode: 400, body: { error: 'The source for this log no longer exists' } };\n\n  const outcome = await runIngestion({\n    sourceSlug: slug,\n    rawPayload: payload,\n    skipDedup: true,\n    skipSpamFilter: true,\n    replayOfLogId: log.id,\n  });\n\n  if (!outcome.ok) {\n    return {\n      statusCode: 500,\n      body: { error: `Release failed: ${outcome.error.message}`, code: outcome.error.code, logId },\n    };\n  }\n\n  await coreApi.patch(`/intakeLogs/${logId}`, {\n    status: 'FAILED',\n    error: 'Released from quarantine \u2014 see the log created by the release for the result',\n  }).catch(() => null);\n\n  return {\n    statusCode: 200,\n    body: {\n      success: true,\n      releasedLogId: logId,\n      recordId: outcome.ctx.personId ?? outcome.ctx.companyId,\n      personId: outcome.ctx.personId,\n      companyId: outcome.ctx.companyId,\n      opportunityId: outcome.ctx.opportunityId,\n      warnings: outcome.ctx.warnings,\n    },\n  };\n};\n\nexport default defineLogicFunction({\n  universalIdentifier: IDS.QUARANTINE_RELEASE_LOGIC_FUNCTION,\n  name: 'intake-quarantine-release',\n  description: 'Release a quarantined payload and ingest it, overruling the spam filter.',\n  timeoutSeconds: 30,\n  handler,\n  httpRouteTriggerSettings: {\n    path: '/intake/quarantine/:logId/release',\n    httpMethod: 'POST',\n    isAuthRequired: true,\n  },\n});\n"],
  "mappings": "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",
  "names": ["prior", "URL_RE", "EMAIL_RE", "toLabel"]
}
