{
  "version": 3,
  "sources": ["twenty-sdk-define-stub:__twenty-sdk-define-stub__", "../../../../src/logic-functions/test-ingest.ts", "../../../../src/lib/flattener.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/idempotency.ts", "../../../../src/lib/note-formatter.ts", "../../../../src/lib/rest-client.ts", "../../../../src/constants/free-email-domains.ts", "../../../../src/lib/lookup.ts", "../../../../src/lib/diff.ts", "../../../../src/lib/spam.ts", "../../../../src/lib/settings.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", "import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';\nimport { CoreApiClient } from 'twenty-client-sdk/core';\nimport { detectStructure, flatten } from '../lib/flattener';\nimport { normalizeFields, partition, type DatabaseRule } from '../lib/normalizer';\nimport { computePayloadHash } from '../lib/idempotency';\nimport { formatNote } from '../lib/note-formatter';\nimport { coreApi } from '../lib/rest-client';\nimport { buildDiff } from '../lib/diff';\nimport { scoreSpam, describeVerdict } from '../lib/spam';\nimport { honeypotField, mergePolicy, spamFilterEnabled, spamScoreThreshold } from '../lib/settings';\n\n/**\n * Dry run: what would happen, without it happening.\n *\n * Two things changed in 0.4.0 and both were correctness, not presentation. The\n * preview now loads the same field rules the live pipeline loads \u2014 it previously\n * ran with none, so any payload relying on a rule was previewed wrongly. And the\n * result is a structured diff rather than a list of names, so it can be asserted\n * against in a test or read by a deployment check instead of eyeballed.\n */\nconst handler = async (params: RoutePayload) => {\n  const sourceSlug = params.pathParameters?.['slug'];\n  if (!sourceSlug) return { statusCode: 400, body: { error: 'Missing source slug' } };\n\n  let rawPayload: Record<string, unknown> = {};\n  if (params.body && typeof params.body === 'object' && !Array.isArray(params.body)) {\n    rawPayload = params.body as Record<string, unknown>;\n  }\n\n  const source = await loadSource(sourceSlug);\n  const rules = await loadRules(source?.id);\n\n  const structure = detectStructure(rawPayload);\n  const flat = structure.type === 'flat'\n    ? structure.data\n    : flatten(structure.extra ?? {});\n\n  // Declared before normalization so a precedence decision the normalizer\n  // declined shows up in the preview \u2014 the dry run is where someone checks a\n  // rule before trusting it with live traffic, so it has to say what happened.\n  const warnings: string[] = [];\n\n  const normalized = normalizeFields(flat, rules, warnings);\n  const { crmFields, noteFields, skipped } = partition(normalized);\n\n  const honeypot = source?.honeypotField || honeypotField();\n  const verdict = scoreSpam(flat, honeypot);\n  const wouldQuarantine = verdict.honeypot\n    || (spamFilterEnabled() && verdict.score >= spamScoreThreshold());\n\n  if (!source) warnings.push(`No source registered with slug \"${sourceSlug}\" \u2014 previewing with default settings. A live request would return 404.`);\n  if (source?.status === 'PAUSED') warnings.push('This source is PAUSED \u2014 a live request would return 423.');\n\n  const noteOverflow: Record<string, unknown> = {};\n  for (const f of noteFields) noteOverflow[f.canonicalName] = f.value;\n\n  // A payload that would be held never reaches the record stages, so describing\n  // what it would write would be describing something that will not happen.\n  const diff = wouldQuarantine ? null : await buildDiff({\n    crmFields,\n    noteFields,\n    skipped,\n    structuredCompany: structure.type === 'structured' ? (structure.company ?? undefined) : undefined,\n    structuredPerson: structure.type === 'structured' ? (structure.person ?? undefined) : undefined,\n    createOpportunity: source?.createOpportunity ?? true,\n    targetObject: (source?.targetObject as 'PERSON' | 'COMPANY' | 'AUTO') ?? 'AUTO',\n    policy: parsePolicy(source?.mergePolicy) ?? mergePolicy(),\n    warnings,\n  });\n\n  return {\n    statusCode: 200,\n    body: {\n      dryRun: true,\n      source: source ? { id: source.id, name: source.name, slug: source.slug, status: source.status } : null,\n      payloadHash: computePayloadHash(rawPayload),\n      payloadStructure: structure.type,\n\n      spam: {\n        score: verdict.score,\n        threshold: spamScoreThreshold(),\n        filterEnabled: spamFilterEnabled(),\n        honeypotField: honeypot || null,\n        wouldQuarantine,\n        signals: verdict.signals,\n        reason: verdict.signals.length > 0 ? describeVerdict(verdict) : null,\n      },\n\n      mergePolicy: parsePolicy(source?.mergePolicy) ?? mergePolicy(),\n\n      /**\n       * Per object: whether it would be created or updated, which record it\n       * matched, what each field would do to the value already there, and which\n       * fields do not exist yet and would be added to the schema.\n       */\n      diff,\n\n      note: noteFields.length > 0\n        ? { fields: noteFields.map((f) => f.canonicalName), preview: formatNote(source?.name ?? sourceSlug, noteOverflow) }\n        : null,\n\n      rulesApplied: rules.length,\n      warnings,\n      rawFlat: flat,\n    },\n  };\n};\n\ntype SourceNode = {\n  id: string;\n  name: string;\n  slug: string;\n  status: string;\n  targetObject: string;\n  createOpportunity: boolean | null;\n  honeypotField?: string | null;\n  mergePolicy?: string | null;\n};\n\nasync function loadSource(slug: string): Promise<SourceNode | null> {\n  try {\n    const res = await coreApi.get<{ data: { intakeSources: { edges: Array<{ node: SourceNode }> } } }>(\n      `/intakeSources?filter=slug[eq]:${encodeURIComponent(slug)}&first=1`,\n    );\n    return res.data?.intakeSources?.edges?.[0]?.node ?? null;\n  } catch {\n    return null;\n  }\n}\n\n/** The same query NormalizeStage runs, so the preview sees the same rules. */\nasync function loadRules(sourceId: string | undefined): Promise<DatabaseRule[]> {\n  try {\n    const client = new CoreApiClient();\n    const filter = sourceId\n      ? { or: [{ intakeSource: { id: { eq: sourceId } } }, { intakeSource: { id: { is: 'NULL' } } }] }\n      : { intakeSource: { id: { is: 'NULL' } } };\n\n    const result = await 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    return [];\n  }\n}\n\nfunction parsePolicy(raw: string | null | undefined): 'PRESERVE' | 'NEWEST_WINS' | null {\n  switch ((raw ?? '').toUpperCase()) {\n    case 'PRESERVE': return 'PRESERVE';\n    case 'NEWEST_WINS': return 'NEWEST_WINS';\n    default: return null;\n  }\n}\n\nexport default defineLogicFunction({\n  universalIdentifier: '3f7a9c21-8d4b-4e6f-a1c2-5b8d0f2e7a4c',\n  name: 'intake-test',\n  description: 'Dry-run: returns a structured diff of what would be created or updated, writing nothing.',\n  timeoutSeconds: 20,\n  handler,\n  httpRouteTriggerSettings: {\n    path: '/intake/:slug/test',\n    httpMethod: 'POST',\n    isAuthRequired: false,\n  },\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", "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 { 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", "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 * 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 * 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", "/**\n * What an ingestion would do, worked out without doing it.\n *\n * The old dry-run answered in prose: a list of field names and a rendered note.\n * It read well and could not be acted on \u2014 nothing in it distinguished a field\n * that would be created from one that already existed, or a new contact from an\n * update to somebody's existing record, which are the two questions worth asking\n * before pointing live traffic at a source.\n *\n * This returns the same decisions the pipeline makes, as data. The lookups are\n * the ones the upsert stage uses, so a preview that says \"update\" is describing\n * the record the real run would actually touch, and the merge policy is applied\n * to the values it finds, so a preview can show that an incoming name is going\n * to be refused because a better one is already there.\n *\n * Read-only throughout: no field is created, no record is written.\n */\n\nimport type { NormalizedField } from './normalizer';\nimport { assembleComposites } from './normalizer';\nimport { mergePatch, type FieldMergeStrategy } from './merge';\nimport type { MergePolicy } from './settings';\nimport { findPersonByEmail, findCompanyByDomain, primaryEmailOf, primaryDomainOf, isCompanyDomain, extractHost } from './lookup';\nimport { metaApi } from './rest-client';\n\nexport type DiffFieldStatus =\n  | 'create'      // written to a brand new record\n  | 'fill-empty'  // the record exists and this field is blank\n  | 'overwrite'   // the record exists, holds a different value, and policy allows replacing it\n  | 'preserved'   // the record exists, holds a value, and policy keeps it\n  | 'unchanged';  // already exactly this value\n\nexport type DiffField = {\n  name: string;\n  from: 'builtin' | 'rule' | 'passthrough' | 'derived';\n  incoming: unknown;\n  existing?: unknown;\n  status: DiffFieldStatus;\n  /** True when the field itself does not exist on the object yet. */\n  fieldWouldBeCreated: boolean;\n  twentyType?: string;\n};\n\nexport type DiffObject = {\n  object: 'person' | 'company' | 'opportunity' | 'note';\n  operation: 'create' | 'update' | 'skip';\n  recordId: string | null;\n  matchedBy: { field: string; value: string } | null;\n  fields: DiffField[];\n  fieldsToCreate: Array<{ name: string; type: string }>;\n};\n\nexport type IntakeDiff = {\n  objects: DiffObject[];\n  noteFields: string[];\n  skipped: string[];\n  warnings: string[];\n};\n\nexport type DiffInput = {\n  crmFields: NormalizedField[];\n  noteFields: NormalizedField[];\n  skipped: NormalizedField[];\n  structuredCompany?: Record<string, unknown> | undefined;\n  structuredPerson?: Record<string, unknown> | undefined;\n  createOpportunity: boolean;\n  targetObject: 'PERSON' | 'COMPANY' | 'AUTO';\n  policy: MergePolicy;\n  warnings: string[];\n};\n\nconst STANDARD_BY_OBJECT: Record<string, Set<string>> = {\n  person: new Set(['name', 'emails', 'phones', 'firstName', 'lastName', 'fullName', 'email', 'phone',\n    'linkedInLink', 'xLink', 'jobTitle', 'city', 'companyName', 'domainName', 'address']),\n  company: new Set(['name', 'domainName', 'address', 'companyName', 'linkedInLink', 'xLink',\n    'employees', 'annualRecurringRevenue', 'idealCustomerProfile', 'city']),\n  opportunity: new Set(['name', 'stage', 'amount', 'closeDate']),\n};\n\nexport async function buildDiff(input: DiffInput): Promise<IntakeDiff> {\n  const clean = input.crmFields.filter((f) => !f.originalKey.startsWith('__'));\n  const { assembled, remainder } = assembleComposites(clean);\n  for (const f of remainder) assembled[f.canonicalName] = f.value;\n\n  const objects: DiffObject[] = [];\n  const existingFields = await objectFieldNames();\n\n  const strategyFor = (field: string): FieldMergeStrategy =>\n    clean.find((f) => f.canonicalName === field)?.mergeStrategy ?? 'INHERIT';\n\n  // \u2500\u2500 Company \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  const companyName = (input.structuredCompany?.['name'] as string | undefined)\n    ?? (assembled['companyName'] as string | undefined);\n\n  if (companyName || input.targetObject === 'COMPANY') {\n    const standard: Record<string, unknown> = { ...(input.structuredCompany ?? {}) };\n    if (companyName && !standard['name']) standard['name'] = companyName;\n    if (!standard['domainName'] && assembled['domainName']) standard['domainName'] = assembled['domainName'];\n    if (!standard['address'] && assembled['address']) standard['address'] = assembled['address'];\n\n    // The same refusal UpsertRecordStage makes, made here too. A preview that\n    // showed a company the live run declines to create is a preview nobody can\n    // gate a deployment on.\n    const proposedDomain = primaryDomainOf(standard);\n    if (proposedDomain && !isCompanyDomain(proposedDomain)) {\n      delete standard['domainName'];\n      input.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    if (!standard['name'] && !standard['domainName']) {\n      input.warnings.push('No company name or domain in this payload \u2014 no company was created.');\n    } else {\n      const domain = primaryDomainOf(standard);\n      const existing = domain ? await findCompanyByDomain(domain) : null;\n\n      objects.push(describe(\n        'company',\n        existing,\n        domain ? { field: 'domainName', value: domain } : null,\n        { ...standard, ...customFor(clean, 'company') },\n        clean,\n        existingFields['company'] ?? new Set(),\n        input.policy,\n        strategyFor,\n      ));\n    }\n  }\n\n  // \u2500\u2500 Person \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  const hasPersonData = assembled['emails'] || assembled['name'] || input.structuredPerson;\n  if (hasPersonData || input.targetObject === 'PERSON') {\n    const standard: Record<string, unknown> = { ...(input.structuredPerson ?? {}) };\n    if (!standard['name'] && assembled['name']) standard['name'] = assembled['name'];\n    if (!standard['emails'] && assembled['emails']) standard['emails'] = assembled['emails'];\n    if (!standard['phones'] && assembled['phones']) standard['phones'] = assembled['phones'];\n\n    const email = primaryEmailOf(standard) || primaryEmailOf(assembled);\n    const existing = email ? await findPersonByEmail(email) : null;\n\n    objects.push(describe(\n      'person',\n      existing,\n      email ? { field: 'emails.primaryEmail', value: email } : null,\n      { ...standard, ...customFor(clean, 'person') },\n      clean,\n      existingFields['person'] ?? new Set(),\n      input.policy,\n      strategyFor,\n    ));\n  }\n\n  // \u2500\u2500 Opportunity \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Always created when the source is configured to, never matched against an\n  // existing deal \u2014 the pipeline does not dedupe opportunities, and a preview\n  // that implied otherwise would be lying about what happens next.\n  if (input.createOpportunity && objects.some((o) => o.operation !== 'skip')) {\n    const oppCustom = customFor(clean, 'opportunity');\n    const oppStandard: Record<string, unknown> = {};\n    for (const f of clean) {\n      if (f.targetObject === 'opportunity' && (f.canonicalName === 'amount' || f.canonicalName === 'closeDate')) {\n        oppStandard[f.canonicalName] = f.value;\n      }\n    }\n    objects.push(describe(\n      'opportunity',\n      null,\n      null,\n      { ...oppStandard, ...oppCustom },\n      clean,\n      existingFields['opportunity'] ?? new Set(),\n      input.policy,\n      strategyFor,\n    ));\n  }\n\n  return {\n    objects,\n    noteFields: input.noteFields.map((f) => f.canonicalName),\n    skipped: input.skipped.map((f) => f.originalKey),\n    warnings: input.warnings,\n  };\n}\n\nfunction customFor(fields: NormalizedField[], object: string): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  for (const f of fields) {\n    const target = f.targetObject === 'auto' ? impliedPrimary(fields) : f.targetObject;\n    if (target !== object) continue;\n    if (STANDARD_BY_OBJECT[object]?.has(f.canonicalName)) continue;\n    out[f.canonicalName] = f.value;\n  }\n  return out;\n}\n\n/** Mirrors SyncSchemaStage's primaryTarget for fields still marked `auto`. */\nfunction impliedPrimary(fields: NormalizedField[]): 'person' | 'company' {\n  const hasPerson = fields.some((f) =>\n    ['firstName', 'lastName', 'email', 'phone', 'fullName', 'emails', 'phones', 'name'].includes(f.canonicalName));\n  return hasPerson ? 'person' : 'company';\n}\n\nfunction describe(\n  object: DiffObject['object'],\n  existing: Record<string, unknown> | null,\n  matchedBy: { field: string; value: string } | null,\n  incoming: Record<string, unknown>,\n  normalized: NormalizedField[],\n  objectFields: Set<string>,\n  policy: MergePolicy,\n  strategyFor: (field: string) => FieldMergeStrategy,\n): DiffObject {\n  const { decisions } = existing\n    ? mergePatch(existing, incoming, policy, strategyFor)\n    : { decisions: {} as Record<string, { write: boolean; status: string }> };\n\n  const fields: DiffField[] = [];\n  const fieldsToCreate: Array<{ name: string; type: string }> = [];\n\n  for (const [name, value] of Object.entries(incoming)) {\n    const source = normalized.find((f) => f.canonicalName === name);\n    const isStandard = STANDARD_BY_OBJECT[object]?.has(name) ?? false;\n    const wouldCreate = !isStandard && !objectFields.has(name);\n\n    if (wouldCreate) fieldsToCreate.push({ name, type: source?.twentyType ?? 'TEXT' });\n\n    let status: DiffFieldStatus = 'create';\n    if (existing) {\n      const decided = decisions[name]?.status;\n      status = decided === 'filled-empty' ? 'fill-empty'\n        : decided === 'unchanged' ? 'unchanged'\n        : decided === 'set' ? 'overwrite'\n        : 'preserved';\n    }\n\n    fields.push({\n      name,\n      from: source?.source ?? 'derived',\n      incoming: value,\n      ...(existing ? { existing: existing[name] } : {}),\n      status,\n      fieldWouldBeCreated: wouldCreate,\n      twentyType: source?.twentyType,\n    });\n  }\n\n  return {\n    object,\n    operation: existing ? 'update' : 'create',\n    recordId: (existing?.['id'] as string | undefined) ?? null,\n    matchedBy,\n    fields,\n    fieldsToCreate,\n  };\n}\n\n/**\n * Which fields each object already has. One metadata read serves every object in\n * the diff; a failure degrades to \"assume nothing exists\", which overstates the\n * fields that would be created rather than hiding any.\n */\nexport async function objectFieldNames(): Promise<Record<string, Set<string>>> {\n  try {\n    const res = await metaApi.get<Array<{ nameSingular: string; fields: Array<{ name: string }> }>>('/objects');\n    const list = Array.isArray(res) ? res : ((res as { data?: typeof res }).data ?? []);\n    const out: Record<string, Set<string>> = {};\n    for (const node of list) {\n      out[node.nameSingular] = new Set((node.fields ?? []).map((f) => f.name));\n    }\n    return out;\n  } catch {\n    return {};\n  }\n}\n", "/**\n * 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", "/**\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"],
  "mappings": "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",
  "names": ["URL_RE", "EMAIL_RE"]
}
