{"version":3,"file":"impl-D0-ioxpZ.mjs","names":[],"sources":["../src/commands/consent/pull-consent-preferences/impl.ts"],"sourcesContent":["import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport {\n  buildTranscendGraphQLClient,\n  createSombraGotInstance,\n  fetchAllIdentifiers,\n  fetchAllPurposesAndPreferences,\n  fetchConsentPreferences,\n  fetchConsentPreferencesChunked,\n  transformPreferenceRecordToCsv,\n  type PreferenceIdentifier,\n} from '@transcend-io/sdk';\nimport colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { initCsvFile, appendCsvRowsOrdered } from '../../../lib/helpers/index.js';\nimport { logger } from '../../../logger.js';\n\n// Known “core” columns your transformer usually produces up front.\n// Leave this list conservative; we’ll still union with transformer keys.\nconst CORE_COLS = [\n  'userId',\n  'timestamp',\n  'partition',\n  'decryptionStatus',\n  'updatedAt',\n  'usp',\n  'gpp',\n  'tcf',\n  'airgapVersion',\n  'metadata',\n  'metadataTimestamp',\n];\n\nexport interface PullConsentPreferencesCommandFlags {\n  auth: string;\n  partition: string;\n  sombraAuth?: string;\n  file: string;\n  transcendUrl: string;\n  timestampBefore?: Date;\n  exportIdentifiersWithDelimiter: string;\n  timestampAfter?: Date;\n  updatedBefore?: Date;\n  updatedAfter?: Date;\n  identifiers?: string[];\n  concurrency: number;\n  shouldChunk: boolean;\n  windowConcurrency: number;\n  maxChunks: number;\n  maxLookbackDays: number;\n}\n\nexport async function pullConsentPreferences(\n  this: LocalContext,\n  {\n    auth,\n    partition,\n    sombraAuth,\n    file,\n    transcendUrl,\n    timestampBefore,\n    timestampAfter,\n    updatedBefore,\n    updatedAfter,\n    identifiers = [],\n    concurrency,\n    shouldChunk,\n    windowConcurrency,\n    maxChunks,\n    exportIdentifiersWithDelimiter,\n    maxLookbackDays,\n  }: PullConsentPreferencesCommandFlags,\n): Promise<void> {\n  doneInputValidation(this.process.exit);\n\n  // Create sombra instance to communicate with\n  const sombra = await createSombraGotInstance(transcendUrl, auth, {\n    logger,\n    sombraApiKey: sombraAuth,\n    sombraUrl: process.env.SOMBRA_URL,\n  });\n  const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n  // Identifiers are key:value, parse to PreferenceIdentifier[]\n  const parsedIdentifiers = identifiers.map((identifier): PreferenceIdentifier => {\n    if (!identifier.includes(':')) {\n      return {\n        name: 'email',\n        value: identifier,\n      };\n    }\n    const [name, value] = identifier.split(':');\n    return { name, value };\n  });\n\n  // Build filter\n  const filterBy = {\n    ...(timestampBefore ? { timestampBefore: timestampBefore.toISOString() } : {}),\n    ...(timestampAfter ? { timestampAfter: timestampAfter.toISOString() } : {}),\n    ...(updatedAfter || updatedBefore\n      ? {\n          system: {\n            ...(updatedBefore ? { updatedBefore: updatedBefore.toISOString() } : {}),\n            ...(updatedAfter ? { updatedAfter: updatedAfter.toISOString() } : {}),\n          },\n        }\n      : {}),\n    ...(parsedIdentifiers.length > 0 ? { identifiers: parsedIdentifiers } : {}),\n  };\n\n  logger.info(\n    `Fetching consent preferences from partition ${partition}, using mode=${\n      shouldChunk ? 'chunked-stream' : 'paged-stream'\n    }...`,\n  );\n\n  logger.info(colors.magenta(`Preparing CSV at: ${file}`));\n\n  // Fetch full sets (purposes+topics, identifiers) to ensure header completeness\n  const [purposesWithTopics, allIdentifiers] = await Promise.all([\n    fetchAllPurposesAndPreferences(client, { logger }),\n    fetchAllIdentifiers(client, { logger }),\n  ]);\n\n  // Identifier columns: exactly the identifier names\n  const identifierCols = allIdentifiers.map((i) => i.name);\n\n  // Preference topic columns: topic names (de-duped)\n  const topicCols = Array.from(\n    new Set(\n      purposesWithTopics.flatMap((p) => p.topics?.map((t) => `${p.trackingType}_${t.slug}`) ?? []),\n    ),\n  ).sort((a, b) => a.localeCompare(b));\n\n  // Some setups also want a per-purpose boolean column (e.g., “Email”, “Sms”).\n  // If your transformer includes those, list them here, derived from purposes:\n  const purposeCols = Array.from(new Set(purposesWithTopics.map((p) => p.trackingType))).sort(\n    (a, b) => a.localeCompare(b),\n  );\n\n  // Build the complete header skeleton.\n  // We’ll still union with the first transformed row’s keys to be safe.\n  const completeHeadersList = [...CORE_COLS, ...identifierCols, ...purposeCols, ...topicCols];\n\n  // Lazily initialize CSV header order from the first transformed row.\n  let headerOrder: string[] | null = null;\n  let wroteHeader = false;\n  const writeRows = (items: PreferenceQueryResponseItem[]): void => {\n    if (!items || items.length === 0) return;\n    const rows = items.map((row) =>\n      transformPreferenceRecordToCsv(row, exportIdentifiersWithDelimiter),\n    );\n    if (!wroteHeader) {\n      const firstKeys = Object.keys(rows[0] ?? {});\n      const seen = new Set<string>();\n      headerOrder = [...completeHeadersList, ...firstKeys].filter((k) => {\n        if (k === undefined) return false;\n        if (seen.has(k)) return false;\n        seen.add(k);\n        return true;\n      });\n      initCsvFile(file, headerOrder);\n      wroteHeader = true;\n    }\n    appendCsvRowsOrdered(file, rows, headerOrder!);\n  };\n\n  if (shouldChunk) {\n    // Stream via chunked fetcher with page callback\n    await fetchConsentPreferencesChunked(sombra, {\n      partition,\n      filterBy,\n      limit: concurrency,\n      windowConcurrency,\n      maxChunks,\n      maxLookbackDays,\n      logger,\n      onItems: (items) => writeRows(items),\n    });\n\n    logger.info(colors.green(`Finished writing CSV to ${file}`));\n    return;\n  }\n\n  // Non-chunked path: still stream page-by-page via onItems (no in-memory accumulation)\n  await fetchConsentPreferences(sombra, {\n    partition,\n    filterBy,\n    limit: concurrency, // page size (API max 50 enforced internally)\n    logger,\n    onItems: (items) => writeRows(items),\n  });\n\n  logger.info(colors.green(`Finished writing CSV to ${file}`));\n}\n"],"mappings":"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"}