@transcend-io/sdk 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PURPOSES","PAGE_SIZE","PURPOSES","PAGE_SIZE","MAX_BATCH_SIZE","pmap","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","MAX_PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","MAX_PAGE_SIZE","MAX_PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","CHUNK_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","CHUNK_SIZE"],"sources":["../src/gqls/shared.ts","../src/api/buildTranscendGraphQLClient.ts","../src/api/makeGraphQLRequest.ts","../src/api/gqls/organization.ts","../src/api/createSombraGotInstance.ts","../src/api/withTransientRetry.ts","../src/data-inventory/gqls/businessEntity.ts","../src/data-inventory/fetchAllBusinessEntities.ts","../src/data-inventory/gqls/dataCategory.ts","../src/data-inventory/fetchAllDataCategories.ts","../src/data-inventory/gqls/dataPoint.ts","../src/data-inventory/fetchAllDataPoints.ts","../src/data-inventory/gqls/dataSilo.ts","../src/data-inventory/fetchAllDataSilos.ts","../src/data-inventory/gqls/identifier.ts","../src/data-inventory/fetchAllIdentifiers.ts","../src/data-inventory/gqls/processingActivity.ts","../src/data-inventory/fetchAllProcessingActivities.ts","../src/data-inventory/gqls/vendor.ts","../src/data-inventory/fetchAllVendors.ts","../src/data-inventory/fetchEnrichedDataSilos.ts","../src/data-inventory/syncBusinessEntities.ts","../src/data-inventory/syncDataCategories.ts","../src/data-inventory/syncDataSiloDependencies.ts","../src/data-inventory/syncProcessingActivities.ts","../src/data-inventory/syncVendors.ts","../src/preference-management/gqls/purpose.ts","../src/preference-management/fetchAllPurposes.ts","../src/preference-management/gqls/preferenceTopic.ts","../src/preference-management/fetchAllPreferenceTopics.ts","../src/preference-management/fetchAllPurposesAndPreferences.ts","../src/preference-management/gqls/preferenceAccessTokens.ts","../src/preference-management/createPreferenceAccessTokens.ts","../src/preference-management/types.ts","../src/preference-management/codecs.ts","../src/preference-management/getPreferenceMetadataFromRow.ts","../src/preference-management/getPreferenceIdentifiersFromRow.ts","../src/preference-management/getUniquePreferenceIdentifierNamesFromRow.ts","../src/preference-management/getPreferenceUpdatesFromRow.ts","../src/preference-management/checkIfPendingPreferenceUpdatesAreNoOp.ts","../src/preference-management/checkIfPendingPreferenceUpdatesCauseConflict.ts","../src/preference-management/transformPreferenceRecordToCsv.ts","../src/preference-management/buildConsentChunks.ts","../src/preference-management/pickConsentChunkMode.ts","../src/preference-management/getComparisonTimeForRecord.ts","../src/preference-management/iterateConsentPages.ts","../src/preference-management/consentWindowHasAny.ts","../src/preference-management/discoverConsentWindow.ts","../src/preference-management/fetchConsentPreferences.ts","../src/preference-management/getPreferencesForIdentifiers.ts","../src/preference-management/fetchConsentPreferencesChunked.ts","../src/preference-management/loadReferenceData.ts","../src/preference-management/gqls/preferenceOptionValues.ts","../src/preference-management/fetchAllPreferenceOptionValues.ts","../src/preference-management/syncPreferenceOptionValues.ts","../src/preference-management/syncPurposes.ts","../src/preference-management/syncPreferenceTopics.ts","../src/administration/gqls/attribute.ts","../src/administration/fetchAllAttributes.ts","../src/administration/gqls/message.ts","../src/administration/fetchAllMessages.ts","../src/administration/gqls/team.ts","../src/administration/fetchAllTeams.ts","../src/administration/fetchParentOrganizationTeams.ts","../src/administration/gqls/user.ts","../src/administration/fetchAllUsers.ts","../src/administration/gqls/apiKey.ts","../src/administration/fetchApiKeys.ts","../src/administration/formatRegions.ts","../src/administration/gqls/auth.ts","../src/administration/loginUser.ts","../src/administration/manageApiKeys.ts","../src/administration/setResourceAttributes.ts","../src/administration/syncAttribute.ts","../src/administration/syncIntlMessages.ts","../src/administration/syncTeams.ts","../src/administration/gqls/attributeKey.ts","../src/consent/createTranscendConsentGotInstance.ts","../src/consent/gqls/consentManager.ts","../src/consent/deployConsentManager.ts","../src/consent/fetchConsentManagerId.ts","../src/consent/gqls/shared.ts","../src/consent/gqls/cookies.ts","../src/consent/fetchAllCookies.ts","../src/consent/gqls/dataFlows.ts","../src/consent/fetchAllDataFlows.ts","../src/consent/gqls/privacyCenter.ts","../src/consent/fetchPrivacyCenterId.ts","../src/consent/gqls/policy.ts","../src/consent/fetchAllPolicies.ts","../src/consent/fetchAllPrivacyCenters.ts","../src/consent/gqls/processingPurpose.ts","../src/consent/fetchAllProcessingPurposes.ts","../src/consent/fetchConsentManager.ts","../src/consent/gqls/consentManagerMetrics.ts","../src/consent/fetchConsentManagerAnalyticsData.ts","../src/consent/gqls/experiences.ts","../src/consent/fetchConsentManagerExperiences.ts","../src/consent/fetchConsentManagerTheme.ts","../src/consent/fetchConsentThemes.ts","../src/consent/fetchConsentVariants.ts","../src/consent/gqls/airgapBundleAnalytics.ts","../src/consent/fetchAirgapBundleAnalytics.ts","../src/consent/gqls/purposes.ts","../src/consent/gqls/partitions.ts","../src/consent/gqls/stats.ts","../src/consent/gqls/consentWorkflowTrigger.ts","../src/consent/resolveDisplayedChildOrganizationIds.ts","../src/consent/syncConsentUi.ts","../src/consent/syncPartitions.ts","../src/consent/syncConsentManager.ts","../src/consent/syncCookies.ts","../src/consent/syncDataFlows.ts","../src/consent/syncPolicies.ts","../src/consent/syncPrivacyCenterFooterLinks.ts","../src/consent/syncPrivacyCenter.ts","../src/consent/fetchAllConsentWorkflowTriggers.ts","../src/dsr-automation/gqls/action.ts","../src/dsr-automation/fetchAllActions.ts","../src/dsr-automation/gqls/workflowConfig.ts","../src/dsr-automation/fetchAllWorkflowConfigs.ts","../src/dsr-automation/gqls/dataSubject.ts","../src/dsr-automation/fetchDataSubjects.ts","../src/consent/syncConsentWorkflowTriggers.ts","../src/consent/syncProcessingPurposes.ts","../src/dsr-automation/gqls/requestEnricher.ts","../src/dsr-automation/fetchAllRequestEnrichers.ts","../src/dsr-automation/gqls/sombraVersion.ts","../src/dsr-automation/fetchAllRequestIdentifiers.ts","../src/dsr-automation/gqls/requestIdentifier.ts","../src/dsr-automation/fetchAllRequestIdentifierMetadata.ts","../src/dsr-automation/gqls/dsrIdentifier.ts","../src/dsr-automation/fetchIdentifiers.ts","../src/dsr-automation/retryRequestEnricher.ts","../src/dsr-automation/syncDataSubject.ts","../src/dsr-automation/gqls/enricher.ts","../src/dsr-automation/syncEnrichers.ts","../src/dsr-automation/syncIdentifier.ts","../src/dsr-automation/gqls/siloDiscovery.ts","../src/dsr-automation/fetchActiveSiloDiscoPlugin.ts","../src/dsr-automation/fetchAllAttributeKeys.ts","../src/dsr-automation/gqls/siloDiscoveryResult.ts","../src/dsr-automation/fetchAllSiloDiscoveryResults.ts","../src/dsr-automation/gqls/catalog.ts","../src/dsr-automation/fetchCatalogs.ts","../src/dsr-automation/gqls/requestDataSilo.ts","../src/dsr-automation/fetchRequestDataSilo.ts","../src/dsr-automation/gqls/requestFile.ts","../src/dsr-automation/fetchRequestFilesForRequest.ts","../src/dsr-automation/formatAttributeValues.ts","../src/dsr-automation/syncAction.ts","../src/dsr-automation/gqls/template.ts","../src/dsr-automation/syncTemplates.ts","../src/dsr-automation/resolveWorkflowConfigMatch.ts","../src/dsr-automation/syncWorkflowConfigs.ts","../src/dsr-automation/uploadSiloDiscoveryResults.ts","../src/ai/gqls/promptRun.ts","../src/ai/addMessagesToPromptRun.ts","../src/ai/gqls/agentFile.ts","../src/ai/fetchAllAgentFiles.ts","../src/ai/gqls/agentFunction.ts","../src/ai/fetchAllAgentFunctions.ts","../src/ai/gqls/agent.ts","../src/ai/fetchAllAgents.ts","../src/ai/gqls/largeLanguageModel.ts","../src/ai/fetchLargeLanguageModels.ts","../src/ai/gqls/promptThread.ts","../src/ai/fetchPromptThreads.ts","../src/ai/syncAgentFiles.ts","../src/ai/syncAgentFunctions.ts","../src/ai/syncAgents.ts","../src/assessments/gqls/actionItemCollection.ts","../src/assessments/fetchAllActionItemCollections.ts","../src/assessments/gqls/actionItem.ts","../src/assessments/fetchAllActionItems.ts","../src/assessments/gqls/assessment.ts","../src/assessments/fetchAllAssessments.ts","../src/assessments/parseAssessmentDisplayLogic.ts","../src/assessments/parseAssessmentRiskLogic.ts","../src/assessments/syncActionItemCollections.ts","../src/assessments/syncActionItems.ts","../src/code-intelligence/gqls/codePackage.ts","../src/code-intelligence/fetchAllCodePackages.ts","../src/code-intelligence/gqls/repository.ts","../src/code-intelligence/fetchAllRepositories.ts","../src/code-intelligence/gqls/softwareDevelopmentKit.ts","../src/code-intelligence/fetchAllSoftwareDevelopmentKits.ts","../src/code-intelligence/syncRepositories.ts","../src/code-intelligence/syncSoftwareDevelopmentKits.ts","../src/custom-functions/buildCustomFunctionSignPayload.ts","../src/custom-functions/codeSigning.ts","../src/custom-functions/gqls/customFunction.ts","../src/custom-functions/customFunctionDataSilo.ts","../src/custom-functions/fetchAllCustomFunctions.ts","../src/custom-functions/injectDataSiloIntoDsrTestPayload.ts","../src/custom-functions/resolveEffectiveSombraId.ts","../src/custom-functions/resolveExistingCustomFunction.ts","../src/custom-functions/runCustomFunctionTest.ts","../src/custom-functions/signCustomFunctionCode.ts","../src/custom-functions/syncCustomFunction.ts","../src/index.ts"],"sourcesContent":["/**\n * Shared GQL field selection constants and types used across\n * multiple SDK domains (consent, data-inventory, etc.).\n */\n\n/** Owner fields shared across queries */\nexport const OWNER_FIELDS = `\n id\n name\n email\n`;\n\n/** Team fields shared across queries */\nexport const TEAM_FIELDS = `\n id\n name\n`;\n\n/** Attribute value fields (with nested key) shared across queries */\nexport const ATTRIBUTE_VALUE_FIELDS = `\n id\n name\n attributeKey {\n id\n name\n }\n`;\n\n/** Owner assigned to an entity */\nexport interface TranscendOwnerGql {\n /** Owner ID */\n id: string;\n /** Owner display name */\n name: string;\n /** Owner email */\n email: string;\n}\n\n/** Team assigned to an entity */\nexport interface TranscendTeamGql {\n /** Team ID */\n id: string;\n /** Team name */\n name: string;\n}\n\n/** Attribute value on an entity */\nexport interface TranscendAttributeValueGql {\n /** Attribute value ID */\n id: string;\n /** Attribute value name */\n name: string;\n /** Attribute key */\n attributeKey: {\n /** Attribute key ID */\n id: string;\n /** Attribute key name */\n name: string;\n };\n}\n","import { GraphQLClient } from 'graphql-request';\n\n/**\n * Create a GraphQL client\n *\n * @param transcendUrl - Transcend API URL\n * @param headers - Request headers to include in each request\n * @param version - Optional version string to include in request headers\n * @returns GraphQL client\n */\nexport function buildTranscendGraphQLClientGeneric(\n transcendUrl: string,\n headers: Record<string, string>,\n version?: string,\n): GraphQLClient {\n return new GraphQLClient(`${transcendUrl}/graphql`, {\n headers: {\n ...headers,\n ...(version ? { version } : {}),\n },\n });\n}\n\n/**\n * Create a GraphQL client capable of submitting requests with an API key\n *\n * @param transcendUrl - Transcend API URL\n * @param auth - API key to authenticate to API\n * @param version - Optional version string to include in request headers\n * @returns GraphQL client\n */\nexport function buildTranscendGraphQLClient(\n transcendUrl: string,\n auth: string,\n version?: string,\n): GraphQLClient {\n return buildTranscendGraphQLClientGeneric(\n transcendUrl,\n { Authorization: `Bearer ${auth}` },\n version,\n );\n}\n","import { sleepPromise, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient, RequestDocument, Variables } from 'graphql-request';\n\n/** Logger that silently discards all messages */\nexport const NOOP_LOGGER: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n};\n\nconst DEFAULT_MAX_RETRIES = 4;\n\nconst KNOWN_ERRORS = [\n 'syntax error',\n 'got invalid value',\n 'Client error',\n 'cannot affect row a second time',\n 'GRAPHQL_VALIDATION_FAILED',\n // Backend io-ts payload validation failures are deterministic — retrying\n // just repeats a (potentially enormous) codec error message\n 'Failed to decode codec',\n];\n\n/**\n * Make a GraphQL request with retries\n *\n * @param client - GraphQL client\n * @param document - GraphQL document\n * @param options - Options including logger, variables, headers, and retry config\n * @returns Response\n */\nexport async function makeGraphQLRequest<T, V extends Variables = Variables>(\n client: GraphQLClient,\n document: RequestDocument,\n options: {\n /** GraphQL variables */\n variables?: V;\n /** Logger for retry/error messages */\n logger?: Logger;\n /** Additional request headers */\n requestHeaders?: Record<string, string> | string[][] | Headers;\n /** Max number of retry attempts (default 4) */\n maxRetries?: number;\n },\n): Promise<T> {\n const {\n variables,\n logger = NOOP_LOGGER,\n requestHeaders,\n maxRetries = DEFAULT_MAX_RETRIES,\n } = options;\n\n let retryCount = 0;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n const result = await client.request(document, variables, requestHeaders);\n return result as T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (err: any) {\n if (err.message?.includes('API key is invalid')) {\n throw new Error(\n 'API key is invalid. ' +\n 'Please ensure that the key provided has the proper scope and is not expired, ' +\n 'and that the transcendUrl corresponds to the correct backend for your organization.',\n );\n }\n\n if (KNOWN_ERRORS.some((msg) => err.message?.includes(msg))) {\n throw err;\n }\n\n if (err.message?.startsWith('Client error: Too many requests')) {\n const rateLimitResetAt = err.response?.headers?.get('x-ratelimit-reset');\n const sleepTime = rateLimitResetAt\n ? new Date(rateLimitResetAt).getTime() - new Date().getTime() + 100\n : 1000 * 10;\n logger.warn(`DETECTED RATE LIMIT: ${err.message}. Sleeping for ${sleepTime}ms`);\n await sleepPromise(sleepTime);\n }\n\n if (retryCount >= maxRetries) {\n throw err;\n }\n retryCount += 1;\n logger.warn(`Retrying failed request (${retryCount} / ${maxRetries}): ${err.message}`);\n }\n }\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const ORGANIZATION: DocumentNode = parse(gql`\n query TranscendCliOrganization {\n organization {\n sombra {\n id\n customerUrl\n }\n sombras {\n id\n customerUrl\n }\n }\n }\n`);\n","import type { Logger } from '@transcend-io/utils';\nimport got, { type Got } from 'got';\n\nimport { buildTranscendGraphQLClient } from './buildTranscendGraphQLClient.js';\nimport { ORGANIZATION } from './gqls/organization.js';\nimport { makeGraphQLRequest, NOOP_LOGGER } from './makeGraphQLRequest.js';\n\n/** Preview of a Sombra gateway in the organization */\ninterface SombraPreview {\n /** Sombra gateway ID */\n id: string;\n /** Customer-ingress URL */\n customerUrl: string | null;\n}\n\nconst UNCONFIGURED_CUSTOMER_URLS = [\n 'https://sombra-reverse-tunnel.transcend.io',\n 'https://sombra-reverse-tunnel.us.transcend.io',\n];\n\n/**\n * Instantiate an instance of got that is capable of making requests\n * to a sombra gateway.\n *\n * @param transcendUrl - URL of Transcend API\n * @param transcendApiKey - Transcend API key\n * @param options - Additional options\n * @returns The instance of got that is capable of making requests to the customer ingress\n */\nexport async function createSombraGotInstance(\n transcendUrl: string,\n transcendApiKey: string,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Sombra API key */\n sombraApiKey?: string;\n /** Override Sombra URL (replaces process.env.SOMBRA_URL lookup) */\n sombraUrl?: string;\n /**\n * Connect to a specific Sombra gateway by ID instead of the\n * organization's primary gateway\n */\n sombraId?: string;\n } = {},\n): Promise<Got> {\n const { logger = NOOP_LOGGER, sombraApiKey, sombraUrl, sombraId } = options;\n\n const client = buildTranscendGraphQLClient(transcendUrl, transcendApiKey);\n const { organization } = await makeGraphQLRequest<{\n /** Organization */\n organization: {\n /** Primary Sombra */\n sombra: SombraPreview;\n /** All Sombra gateways in the organization */\n sombras: SombraPreview[];\n };\n }>(client, ORGANIZATION, { logger });\n\n let customerUrl: string | null;\n if (sombraId) {\n const target = organization.sombras.find(({ id }) => id === sombraId);\n if (!target) {\n throw new Error(`Could not find a Sombra gateway with ID: \"${sombraId}\"`);\n }\n customerUrl = target.customerUrl;\n } else {\n customerUrl = organization.sombra.customerUrl;\n }\n\n const sombraToUse = sombraUrl || customerUrl;\n\n if (!sombraUrl && (!customerUrl || UNCONFIGURED_CUSTOMER_URLS.includes(customerUrl))) {\n throw new Error(\n `It looks like the customer ingress URL of your Sombra gateway${\n sombraId ? ` \"${sombraId}\"` : ''\n } has not been set up. ` +\n 'Please follow the instructions here to configure networking for Sombra: ' +\n 'https://docs.transcend.io/docs/articles/sombra/deploying/customizing-sombra/networking',\n );\n }\n logger.info(`Using sombra: ${sombraToUse}`);\n\n return (got as unknown as Got).extend({\n prefixUrl: sombraToUse as string,\n headers: {\n Authorization: `Bearer ${transcendApiKey}`,\n ...(sombraApiKey ? { 'X-Sombra-Authorization': `Bearer ${sombraApiKey}` } : {}),\n },\n });\n}\n","import { extractErrorMessage, sleepPromise, type Logger } from '@transcend-io/utils';\n\nimport { NOOP_LOGGER } from './makeGraphQLRequest.js';\n\n/**\n * Transient network / platform errors that merit a retry.\n *\n * We keep this list short and specific to avoid masking real failures (e.g.\n * bad payloads, auth errors, or validation errors). Matches the behaviour that\n * was previously scoped to preference-management operations.\n */\nexport const RETRY_TRANSIENT_MSGS: string[] = [\n 'ENOTFOUND',\n 'ECONNRESET',\n 'ETIMEDOUT',\n 'EAI_AGAIN',\n 'EPIPE',\n // Gateway / upstream 5xx surfaced in messages; we match the bare reason\n // phrase rather than `502 Bad Gateway` because `got`'s default message\n // shape is `Response code 502 (Bad Gateway)` (parenthesised) so a strict\n // substring against the numeric form would miss real failures.\n 'bad gateway',\n 'service unavailable',\n 'gateway time-out',\n 'gateway timeout',\n '429',\n 'rate limit exceeded',\n 'too many requests',\n 'task timed out after',\n 'unknown request error',\n 'socket hang up',\n].map((s) => s.toLowerCase());\n\n/**\n * Transient HTTP status codes that merit a retry. Covers rate limiting and\n * the typical gateway / upstream classes of 5xx errors surfaced by our\n * reverse tunnel and load balancers.\n */\nconst RETRY_TRANSIENT_STATUS_CODES: readonly number[] = [408, 425, 429, 500, 502, 503, 504];\n\n/**\n * Options for running an operation with transient-error retries.\n */\nexport type RetryOptions = {\n /** Logger used to emit retry breadcrumbs */\n logger?: Logger;\n /** Max attempts including the first try (default 12) */\n maxAttempts?: number;\n /** Initial backoff in ms; doubled on each attempt with added jitter (default 250) */\n baseDelayMs?: number;\n /** Optional custom predicate to decide if an error is retryable */\n isRetryable?: (err: unknown, message: string) => boolean;\n /** Optional hook called once per retry attempt (before the sleep) */\n onRetry?: (attempt: number, err: unknown, message: string) => void;\n};\n\n/**\n * Default retry predicate. Matches either a known transient error message\n * substring or a known transient HTTP status code on `got`-style errors.\n *\n * @param err - Thrown error\n * @param message - Extracted human-readable message\n * @returns True if the error should be retried\n */\nexport function isTransientError(err: unknown, message: string): boolean {\n const lower = message.toLowerCase();\n if (RETRY_TRANSIENT_MSGS.some((m) => lower.includes(m))) {\n return true;\n }\n\n // `got` HTTPError puts the status code on `err.response.statusCode`; other\n // clients use `err.status` or `err.statusCode`.\n const maybe = err as {\n /** `got` HTTPError shape */\n response?: {\n /** HTTP status code returned by the server */\n statusCode?: number;\n };\n /** Axios / fetch-style shape */\n status?: number;\n /** Node http error shape */\n statusCode?: number;\n };\n const statusCode = maybe?.response?.statusCode ?? maybe?.status ?? maybe?.statusCode;\n return typeof statusCode === 'number' && RETRY_TRANSIENT_STATUS_CODES.includes(statusCode);\n}\n\n/**\n * Run an async function with standardized retry behaviour for transient\n * network / gateway errors (502/503/504/429, ECONNRESET, ETIMEDOUT, etc.).\n *\n * Applies exponential backoff with jitter, and only retries on known-transient\n * errors to avoid masking real client-side failures.\n *\n * @param name - Short name of the operation, used in logs and the final error message\n * @param fn - Function to run; called once per attempt\n * @param options - Retry options\n * @returns Result of the function on the first successful attempt\n */\nexport async function withTransientRetry<T>(\n name: string,\n fn: () => Promise<T>,\n {\n logger = NOOP_LOGGER,\n maxAttempts = 12,\n baseDelayMs = 250,\n isRetryable = isTransientError,\n onRetry,\n }: RetryOptions = {},\n): Promise<T> {\n let attempt = 0;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n attempt += 1;\n try {\n return await fn();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (err: any) {\n const msg: string = extractErrorMessage(err);\n const willRetry = attempt < maxAttempts && isRetryable(err, msg);\n if (!willRetry) {\n throw new Error(`${name} failed after ${attempt} attempt(s): ${msg}`);\n }\n onRetry?.(attempt, err, msg);\n\n const backoff = baseDelayMs * 2 ** (attempt - 1);\n const jitter = Math.floor(Math.random() * baseDelayMs);\n const delay = backoff + jitter;\n logger.warn(`[retry] attempt ${attempt}/${maxAttempts - 1}; backing off ${delay}ms: ${msg}`);\n await sleepPromise(delay);\n }\n }\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const BUSINESS_ENTITIES = gql`\n query TranscendCliBusinessEntities($first: Int!, $offset: Int!) {\n businessEntities(\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n useMaster: false\n ) {\n nodes {\n id\n title\n description\n dataProtectionOfficerName\n dataProtectionOfficerEmail\n address\n headquarterCountry\n headquarterSubDivision\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n }\n }\n }\n`;\n\nexport const CREATE_BUSINESS_ENTITY = gql`\n mutation TranscendCliCreateBusinessEntity($input: CreateBusinessEntityInput!) {\n createBusinessEntity(input: $input) {\n businessEntity {\n id\n title\n description\n dataProtectionOfficerName\n dataProtectionOfficerEmail\n address\n headquarterCountry\n headquarterSubDivision\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n }\n }\n }\n`;\n\nexport const UPDATE_BUSINESS_ENTITIES = gql`\n mutation TranscendCliUpdateBusinessEntities($input: [UpdateBusinessEntityInput!]!) {\n updateBusinessEntities(input: { businessEntities: $input }) {\n clientMutationId\n }\n }\n`;\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { BUSINESS_ENTITIES } from './gqls/businessEntity.js';\n\nexport interface BusinessEntity {\n /** ID of business entity */\n id: string;\n /** Title of business entity */\n title: string;\n /** Description of business entity */\n description?: string;\n /** Data protection officer name */\n dataProtectionOfficerName?: string;\n /** Data protection officer email */\n dataProtectionOfficerEmail?: string;\n /** Address of business entity */\n address?: string;\n /** Headquarters of business entity */\n headquarterCountry?: IsoCountryCode;\n /** Subdivision of business entity */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Attributes */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key */\n attributeKey: {\n /** Name of attribute key */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all businessEntities in the organization\n *\n * @param client - GraphQL client\n * @returns All businessEntities in the organization\n */\nexport async function fetchAllBusinessEntities(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<BusinessEntity[]> {\n const { logger } = options;\n const businessEntities: BusinessEntity[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n businessEntities: { nodes },\n } = await makeGraphQLRequest<{\n /** Business entities */\n businessEntities: {\n /** List */\n nodes: BusinessEntity[];\n };\n }>(client, BUSINESS_ENTITIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n businessEntities.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return businessEntities.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: name, direction: ASC }\n// ]\nexport const DATA_SUB_CATEGORIES = gql`\n query TranscendCliDataSubCategories($first: Int!, $offset: Int!) {\n dataSubCategories(first: $first, offset: $offset, isExportCsv: true, useMaster: false) {\n nodes {\n id\n name\n category\n description\n regex\n teams {\n name\n }\n owners {\n email\n }\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_DATA_SUB_CATEGORY = gql`\n mutation TranscendCliCreateDataSubCategory($input: CreateDataInventorySubCategoryInput!) {\n createDataSubCategory(input: $input) {\n dataSubCategory {\n id\n name\n category\n }\n }\n }\n`;\n\nexport const UPDATE_DATA_SUB_CATEGORIES = gql`\n mutation TranscendCliUpdateDataSubCategories($input: UpdateDataSubCategoriesInput!) {\n updateDataSubCategories(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { DataCategoryType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DATA_SUB_CATEGORIES } from './gqls/dataCategory.js';\n\nexport interface DataSubCategory {\n /** ID of data category */\n id: string;\n /** Name of data category */\n name: string;\n /** Type of data category */\n category: DataCategoryType;\n /** Description of data category */\n description?: string;\n /** Regex for data category */\n regex?: string;\n /** Assigned teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Assigned owners */\n owners: {\n /** Email */\n email: string;\n }[];\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all dataSubCategories in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All dataSubCategories in the organization\n */\nexport async function fetchAllDataCategories(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<DataSubCategory[]> {\n const { logger } = options;\n const dataSubCategories: DataSubCategory[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n dataSubCategories: { nodes },\n } = await makeGraphQLRequest<{\n /** DataCategories */\n dataSubCategories: {\n /** List */\n nodes: DataSubCategory[];\n };\n }>(client, DATA_SUB_CATEGORIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n dataSubCategories.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return dataSubCategories.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_POINTS = gql`\n query TranscendCliDataPoints($filterBy: DataPointFiltersInput, $first: Int!, $offset: Int!) {\n dataPoints(\n filterBy: $filterBy\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n title {\n defaultMessage\n }\n description {\n defaultMessage\n }\n owners {\n email\n }\n teams {\n name\n }\n name\n path\n actionSettings {\n type\n active\n }\n dataCollection {\n title {\n defaultMessage\n }\n }\n dbIntegrationQueries {\n query\n suggestedQuery\n requestType\n }\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_POINT_COUNT = gql`\n query TranscendCliDataPointCount($filterBy: DataPointFiltersInput) {\n dataPoints(filterBy: $filterBy, useMaster: false) {\n totalCount\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - add orderBy\n// isExportCsv: true\nexport const SUB_DATA_POINTS = gql`\n query TranscendCliSubDataPoints(\n $filterBy: SubDataPointFiltersInput\n $first: Int!\n $offset: Int!\n ) {\n subDataPoints(filterBy: $filterBy, first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n name\n description\n purposes {\n name\n purpose\n }\n categories {\n name\n category\n }\n accessRequestVisibilityEnabled\n erasureRequestRedactionEnabled\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const SUB_DATA_POINTS_COUNT = gql`\n query TranscendCliSubDataPointsCount($filterBy: SubDataPointFiltersInput) {\n subDataPoints(filterBy: $filterBy, useMaster: false) {\n totalCount\n }\n }\n`;\n\nexport const SUB_DATA_POINTS_WITH_GUESSES = gql`\n query TranscendCliSubDataPointGuesses(\n $filterBy: SubDataPointFiltersInput\n $first: Int!\n $offset: Int!\n ) {\n subDataPoints(filterBy: $filterBy, first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n name\n description\n purposes {\n name\n purpose\n }\n categories {\n name\n category\n }\n pendingCategoryGuesses {\n category {\n name\n category\n }\n status\n confidence\n classifierVersion\n }\n accessRequestVisibilityEnabled\n erasureRequestRedactionEnabled\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const UPDATE_OR_CREATE_DATA_POINT = gql`\n mutation TranscendCliUpdateOrCreateDataPoint(\n $dataSiloId: ID!\n $name: String!\n $path: [String!]\n $title: String\n $description: String\n $ownerIds: [ID!]\n $ownerEmails: [String!]\n $teamNames: [String!]\n $teamIds: [ID!]\n $dataCollectionTag: String\n $querySuggestions: [DbIntegrationQuerySuggestionInput!]\n $enabledActions: [RequestActionObjectResolver!]\n $subDataPoints: [DataPointSubDataPointInput!]\n ) {\n updateOrCreateDataPoint(\n input: {\n dataSiloId: $dataSiloId\n name: $name\n path: $path\n title: $title\n teamNames: $teamNames\n ownerEmails: $ownerEmails\n dataCollectionTag: $dataCollectionTag\n description: $description\n ownerIds: $ownerIds\n teamIds: $teamIds\n querySuggestions: $querySuggestions\n enabledActions: $enabledActions\n subDataPoints: $subDataPoints\n }\n ) {\n dataPoint {\n id\n name\n }\n }\n }\n`;\n\nexport const DATAPOINT_EXPORT = gql`\n query TranscendCliDataPointCsvExport($filterBy: DataPointFiltersInput, $first: Int!) {\n dataPoints(filterBy: $filterBy, first: $first, useMaster: false) {\n nodes {\n id\n title {\n defaultMessage\n }\n description {\n defaultMessage\n }\n owners {\n email\n }\n teams {\n name\n }\n name\n path\n }\n }\n }\n`;\n","import {\n ConfidenceLabel,\n DataCategoryType,\n ProcessingPurpose,\n RequestActionObjectResolver,\n SubDataPointDataSubCategoryGuessStatus,\n} from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { sortBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSiloAttributeValue } from './fetchAllDataSilos.js';\nimport { DATA_POINTS, SUB_DATA_POINTS, SUB_DATA_POINTS_WITH_GUESSES } from './gqls/dataPoint.js';\n\nexport interface SubDataPointCategory {\n /** Sub-category name */\n name: string;\n /** Top-level category */\n category: DataCategoryType;\n}\n\nexport interface SubDataPointPurpose {\n /** Sub-purpose name */\n name: string;\n /** Top-level purpose */\n purpose: ProcessingPurpose;\n}\n\nexport interface SubDataPoint {\n /** Name (or key) of the subdatapoint */\n name: string;\n /** The description */\n description?: string;\n /** Personal data category */\n categories: SubDataPointCategory[];\n /** The processing purpose for this sub datapoint */\n purposes: SubDataPointPurpose[];\n /**\n * When true, this subdatapoint should be revealed in a data access request.\n * When false, this field should be redacted\n */\n accessRequestVisibilityEnabled: boolean;\n /**\n * When true, this subdatapoint should be redacted during an erasure request.\n * There normally is a choice of enabling hard deletion or redaction at the\n * datapoint level, but if redaction is enabled, this column can be used\n * to define which fields should be redacted.\n */\n erasureRequestRedactionEnabled: boolean;\n /** Attribute attached to subdatapoint */\n attributeValues: DataSiloAttributeValue[];\n /** Data category guesses that are output by the classifier */\n pendingCategoryGuesses?: {\n /** Data category being guessed */\n category: SubDataPointCategory;\n /** Status of guess */\n status: SubDataPointDataSubCategoryGuessStatus;\n /** Confidence level of guess */\n confidence: number;\n /** Confidence label */\n confidenceLabel: ConfidenceLabel;\n /** classifier version that produced the guess */\n classifierVersion: number;\n }[];\n}\n\nexport interface DataPoint {\n /** ID of dataPoint */\n id: string;\n /** Title of dataPoint */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** The path to this data point */\n path: string[];\n /** Description */\n description: {\n /** Default message */\n defaultMessage: string;\n };\n /** Name */\n name: string;\n /** Global actions */\n actionSettings: {\n /** Action type */\n type: RequestActionObjectResolver;\n /** Is enabled */\n active: boolean;\n }[];\n /** Data collection tag for privacy request download zip labeling */\n dataCollection?: {\n /** Title of data collection */\n title: {\n /** Default message (since message can be translated) */\n defaultMessage: string;\n };\n };\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n /** Owners of the datapoint */\n owners: {\n /** Email address of the owner */\n email: string;\n }[];\n /** Teams that own the datapoint */\n teams: {\n /** Name of the team */\n name: string;\n }[];\n /** Database integration queries */\n dbIntegrationQueries: {\n /** Approved query */\n query: string | null;\n /** Suggested query */\n suggestedQuery: string | null;\n /** Request action */\n requestType: RequestActionObjectResolver;\n }[];\n}\n\nexport interface DataPointWithSubDataPoint extends DataPoint {\n /** The associated subdatapoints */\n subDataPoints: SubDataPoint[];\n}\n\n/**\n * Helper to fetch all subdatapoints for a given datapoint\n *\n * @param client - The GraphQL client\n * @param dataPointId - The datapoint ID\n * @param options - Options\n * @returns The list of subdatapoints\n */\nexport async function fetchAllSubDataPoints(\n client: GraphQLClient,\n options: {\n /** Filter criteria */\n filterBy: {\n /** Datapoint ID to fetch sub-datapoints for */\n dataPointId: string;\n };\n /** Enable debug logging */\n debug?: boolean;\n /** Page size for paginated fetching */\n pageSize: number;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<SubDataPoint[]> {\n const {\n filterBy: { dataPointId },\n debug,\n includeGuessedCategories,\n pageSize,\n logger = NOOP_LOGGER,\n } = options;\n const subDataPoints: SubDataPoint[] = [];\n\n let offset = 0;\n\n let shouldContinue = false;\n do {\n try {\n if (debug) {\n logger.info(`Pulling in subdatapoints for offset ${offset}`);\n }\n const {\n subDataPoints: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n subDataPoints: {\n /** List of matches */\n nodes: SubDataPoint[];\n };\n }>(client, includeGuessedCategories ? SUB_DATA_POINTS_WITH_GUESSES : SUB_DATA_POINTS, {\n variables: {\n first: pageSize,\n filterBy: {\n dataPoints: [dataPointId],\n },\n offset,\n },\n logger,\n });\n\n subDataPoints.push(...nodes);\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n\n if (debug) {\n logger.info(`Pulled in subdatapoints for offset ${offset} for dataPointId=${dataPointId}`);\n }\n } catch (err) {\n logger.error(\n `An error fetching subdatapoints for offset ${offset} for dataPointId=${dataPointId}`,\n );\n throw err;\n }\n } while (shouldContinue);\n return sortBy(subDataPoints, 'name');\n}\n\n/**\n * Fetch all datapoints for a data silo\n *\n * @param client - GraphQL client\n * @param dataSiloId - Data silo ID\n * @param options - Options\n * @returns List of datapoints\n */\nexport async function fetchAllDataPoints(\n client: GraphQLClient,\n options: {\n /** Filter criteria */\n filterBy: {\n /** Data silo ID to fetch datapoints for */\n dataSiloId: string;\n };\n /** Enable debug logging */\n debug?: boolean;\n /** Page size for paginated fetching */\n pageSize: number;\n /** Skip fetching of subdatapoints */\n skipSubDatapoints?: boolean;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<DataPointWithSubDataPoint[]> {\n const {\n filterBy: { dataSiloId },\n debug,\n pageSize,\n skipSubDatapoints,\n includeGuessedCategories,\n logger = NOOP_LOGGER,\n } = options;\n const dataPoints: DataPointWithSubDataPoint[] = [];\n\n // TODO: https://transcend.height.app/T-40481 - add cursor pagination\n let offset = 0;\n\n let shouldContinue = false;\n do {\n if (debug) {\n logger.info(`Fetching datapoints with offset: ${offset}`);\n }\n\n const {\n dataPoints: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n dataPoints: {\n /** List of matches */\n nodes: DataPoint[];\n };\n }>(client, DATA_POINTS, {\n variables: {\n first: pageSize,\n filterBy: {\n dataSilos: [dataSiloId],\n },\n offset,\n },\n logger,\n });\n\n if (debug) {\n logger.info(`Fetched ${nodes.length} datapoints at offset: ${offset}`);\n }\n\n if (!skipSubDatapoints) {\n await map(\n nodes,\n async (node) => {\n try {\n if (debug) {\n logger.info(`Fetching subdatapoints for ${node.name} for datapoint offset ${offset}`);\n }\n\n const subDataPoints = await fetchAllSubDataPoints(client, {\n filterBy: { dataPointId: node.id },\n pageSize: 1000,\n debug,\n includeGuessedCategories,\n logger,\n });\n dataPoints.push({\n ...node,\n subDataPoints: subDataPoints.sort((a, b) => a.name.localeCompare(b.name)),\n });\n\n if (debug) {\n logger.info(`Successfully fetched subdatapoints for ${node.name}`);\n }\n } catch (err) {\n logger.error(\n `An error fetching subdatapoints for ${node.name} datapoint offset ${offset}`,\n );\n throw err;\n }\n },\n {\n concurrency: 5,\n },\n );\n\n if (debug) {\n logger.info(`Fetched all subdatapoints for page of datapoints at offset: ${offset}`);\n }\n }\n\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n } while (shouldContinue);\n return dataPoints.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_SILOS = gql`\n query TranscendCliDataSilos($filterBy: DataSiloFiltersInput!, $first: Int!, $offset: Int!) {\n dataSilos(\n filterBy: $filterBy\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n useMaster: false\n ) {\n nodes {\n id\n title\n link\n type\n catalog {\n hasAvcFunctionality\n }\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_SILO_EXPORT = gql`\n query TranscendCliDataSiloExport($filterBy: DataSiloFiltersInput!, $first: Int!) {\n dataSilos(filterBy: $filterBy, first: $first, useMaster: false) {\n nodes {\n id\n title\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_SILOS_ENRICHED = gql`\n query TranscendCliDataSilosEnriched(\n $filterBy: DataSiloFiltersInput!\n $first: Int!\n $offset: Int!\n ) {\n dataSilos(\n filterBy: $filterBy\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n useMaster: false\n ) {\n nodes {\n id\n title\n description\n type\n outerType\n link\n country\n countrySubDivision\n url\n notifyEmailAddress\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n apiKeys {\n title\n }\n subjectBlocklist {\n type\n }\n identifiers {\n name\n isConnected\n }\n dependentDataSilos {\n title\n }\n owners {\n email\n }\n teams {\n id\n name\n }\n catalog {\n hasAvcFunctionality\n }\n isLive\n promptAVendorEmailSendFrequency\n promptAVendorEmailSendType\n promptAVendorEmailIncludeIdentifiersAttachment\n promptAVendorEmailCompletionLinkType\n manualWorkRetryFrequency\n discoveredBy {\n title\n }\n businessEntities {\n title\n }\n }\n }\n }\n`;\n\n/**\n * Singular data silo fetch — `sombra` is available on `DataSilo` but not on\n * the `DataSiloBulkPreview` nodes returned by the paginated `dataSilos` query.\n */\nexport const DATA_SILO_SOMBRA = gql`\n query TranscendCliDataSiloSombra($id: String!) {\n dataSilo(id: $id) {\n id\n sombra {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_DATA_SILOS = gql`\n mutation TranscendCliUpdateDataSilo($input: UpdateDataSilosInput!) {\n updateDataSilos(input: $input) {\n clientMutationId\n dataSilos {\n id\n title\n }\n }\n }\n`;\n\nexport const CREATE_DATA_SILOS = gql`\n mutation TranscendCliCreateDataSilo($input: [CreateDataSilosInput!]!) {\n createDataSilos(input: $input) {\n dataSilos {\n id\n title\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSiloAttributeValue } from '../dsr-automation/formatAttributeValues.js';\nimport { DATA_SILOS } from './gqls/dataSilo.js';\n\nexport type { DataSiloAttributeValue };\n\nexport interface DataSilo {\n /** ID of dataSilo */\n id: string;\n /** Title of dataSilo */\n title: string;\n /** Type of silo */\n type: string;\n /** The link to the data silo */\n link: string;\n /** Attribute labels */\n attributeValues: DataSiloAttributeValue[];\n /** description */\n description: string;\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n}\n\n/**\n * Fetch all dataSilos in the organization\n *\n * @param client - GraphQL client\n * @param options - Filter and pagination options\n * @returns All dataSilos in the organization\n */\nexport async function fetchAllDataSilos<TDataSilo extends DataSilo>(\n client: GraphQLClient,\n options: {\n /** Page size to fetch data silos in */\n pageSize: number;\n /** Filter by title */\n titles?: string[];\n /** Filter by IDs */\n ids?: string[];\n /** Set of integration names to fetch */\n integrationNames?: string[];\n /** GQL query override for data silos */\n gql?: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<TDataSilo[]> {\n const {\n titles,\n pageSize,\n ids = [],\n gql = DATA_SILOS,\n integrationNames = [],\n logger = NOOP_LOGGER,\n } = options;\n\n logger.info(`Fetching ${ids.length === 0 ? 'all' : ids.length} Data Silos...`);\n\n const dataSilos: TDataSilo[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n dataSilos: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n dataSilos: {\n /** List of matches */\n nodes: TDataSilo[];\n };\n }>(client, gql, {\n variables: {\n filterBy: {\n ids: ids.length > 0 ? ids : undefined,\n type: integrationNames.length > 0 ? integrationNames : undefined,\n titles,\n },\n first: pageSize,\n offset,\n },\n logger,\n });\n dataSilos.push(...nodes);\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n } while (shouldContinue);\n\n logger.info(\n `Found a total of ${dataSilos.length} data silo${\n ids.length > 0 ? ` matching IDs ${ids.join(',')}` : ''\n }s${\n integrationNames.length > 0 ? ` matching integrationNames ${integrationNames.join(',')}` : ''\n }`,\n );\n\n return dataSilos.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const IDENTIFIERS: DocumentNode = parse(gql`\n query TranscendCliIdentifiers($first: Int!, $offset: Int!) {\n identifiers(\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n type\n regex\n selectOptions\n privacyCenterVisibility\n dataSubjects {\n type\n }\n isRequiredInForm\n placeholder\n displayTitle {\n defaultMessage\n }\n displayDescription {\n defaultMessage\n }\n displayOrder\n isUniqueOnPreferenceStore\n }\n }\n }\n`);\n","import { IdentifierType, RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { IDENTIFIERS } from './gqls/identifier.js';\n\nexport interface Identifier {\n /** ID of identifier */\n id: string;\n /** Name of identifier */\n name: string;\n /** The type of identifier */\n type: IdentifierType;\n /** Regular expression */\n regex: string;\n /** The set of options that the identifier supports */\n selectOptions: string[];\n /** Whether identifier is enabled on privacy center */\n privacyCenterVisibility: RequestAction[];\n /** Enabled data subjects that are exposed this identifier on the privacy center */\n dataSubjects: {\n /** type of data subjects */\n type: string;\n }[];\n /** Whether identifier is a required field in privacy center form */\n isRequiredInForm: boolean;\n /** Identifier placeholder text */\n placeholder: string;\n /** Display title for identifier */\n displayTitle: {\n /** Default message */\n defaultMessage: string;\n };\n /** Display description for identifier */\n displayDescription: {\n /** Default */\n defaultMessage: string;\n };\n /** Display order */\n displayOrder: number;\n /** Does this identifier uniquely identify a consent record */\n isUniqueOnPreferenceStore: boolean;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all identifiers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All identifiers in the organization\n */\nexport async function fetchAllIdentifiers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Identifier[]> {\n const { logger } = options;\n const identifiers: Identifier[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n identifiers: { nodes },\n } = await makeGraphQLRequest<{\n /** Identifiers */\n identifiers: {\n /** List */\n nodes: Identifier[];\n };\n }>(client, IDENTIFIERS, {\n logger,\n variables: { first: PAGE_SIZE, offset },\n });\n identifiers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return identifiers.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const PROCESSING_ACTIVITIES = gql`\n query TranscendCliProcessingActivities($first: Int!, $offset: Int!) {\n processingActivities(first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n title\n description\n securityMeasureDetails\n controllerships\n storageRegions {\n countrySubDivision\n country\n }\n transferRegions {\n countrySubDivision\n country\n }\n retentionType\n retentionPeriod\n dataProtectionImpactAssessmentLink\n dataProtectionImpactAssessmentStatus\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n dataSilos {\n title\n }\n dataSubjects {\n type\n }\n teams {\n name\n }\n owners {\n email\n }\n processingPurposeSubCategories {\n name\n purpose\n }\n dataSubCategories {\n name\n category\n }\n saaSCategories {\n title\n }\n }\n }\n }\n`;\n\nexport const CREATE_PROCESSING_ACTIVITY = gql`\n mutation TranscendCliCreateProcessingActivity($input: CreateProcessingActivityInput!) {\n createProcessingActivity(input: $input) {\n processingActivity {\n id\n title\n }\n }\n }\n`;\n\nexport const UPDATE_PROCESSING_ACTIVITIES = gql`\n mutation TranscendCliUpdateProcessingActivities($input: UpdateProcessingActivitiesInput!) {\n updateProcessingActivities(input: $input) {\n clientMutationId\n }\n }\n`;\n","import type {\n ProcessingPurpose,\n DataCategoryType,\n DataProtectionImpactAssessmentStatus,\n Controllership,\n RetentionType,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport type { Region } from '../administration/formatRegions.js';\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PROCESSING_ACTIVITIES } from './gqls/processingActivity.js';\n\nexport interface ProcessingActivity {\n /** ID of processing activity */\n id: string;\n /** Title of processing activity */\n title: string;\n /** Description of processing activity */\n description: string;\n /** Security measure details */\n securityMeasureDetails?: string;\n /** Controllerships */\n controllerships: Controllership[];\n /** Storage regions */\n storageRegions: Region[];\n /** Transfer regions */\n transferRegions: Region[];\n /** Retention type */\n retentionType: RetentionType;\n /** Retention period in days */\n retentionPeriod?: number;\n /** Data protection impact assessment link */\n dataProtectionImpactAssessmentLink?: string;\n /** Data protection impact assessment status */\n dataProtectionImpactAssessmentStatus: DataProtectionImpactAssessmentStatus;\n /** Attribute values */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key */\n attributeKey: {\n /** Name of attribute key */\n name: string;\n };\n }[];\n /** Data silos */\n dataSilos: {\n /** Data silo title */\n title: string;\n }[];\n /** Data subjects */\n dataSubjects: {\n /** Data subject type */\n type: string;\n }[];\n /** Teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Owners */\n owners: {\n /** Owner email */\n email: string;\n }[];\n /** Processing purpose sub categories */\n processingPurposeSubCategories: {\n /** Processing purpose sub category name */\n name: string;\n /** Processing purpose */\n purpose: ProcessingPurpose;\n }[];\n /** Data sub categories */\n dataSubCategories: {\n /** Data sub category name */\n name: string;\n /** Data category */\n category: DataCategoryType;\n }[];\n /** SaaS categories */\n saaSCategories: {\n /** Title */\n title: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all processingActivities in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All processingActivities in the organization\n */\nexport async function fetchAllProcessingActivities(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ProcessingActivity[]> {\n const { logger } = options;\n const processingActivities: ProcessingActivity[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n processingActivities: { nodes },\n } = await makeGraphQLRequest<{\n /** Processing activities */\n processingActivities: {\n /** List */\n nodes: ProcessingActivity[];\n };\n }>(client, PROCESSING_ACTIVITIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n processingActivities.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return processingActivities.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import { gql } from 'graphql-request';\n\nexport const VENDORS = gql`\n query TranscendCliVendors($first: Int!, $offset: Int!) {\n vendors(\n first: $first\n offset: $offset\n useMaster: false\n isExportCsv: true\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n ) {\n nodes {\n id\n title\n description\n dataProcessingAgreementLink\n contactName\n contactEmail\n contactPhone\n address\n headquarterCountry\n headquarterSubDivision\n websiteUrl\n businessEntity {\n title\n }\n teams {\n name\n }\n owners {\n email\n }\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_VENDOR = gql`\n mutation TranscendCliCreateVendor($input: CreateVendorInput!) {\n createVendor(input: $input) {\n vendor {\n id\n title\n }\n }\n }\n`;\n\nexport const UPDATE_VENDORS = gql`\n mutation TranscendCliUpdateVendor($input: UpdateVendorsInput!) {\n updateVendors(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { VENDORS } from './gqls/vendor.js';\n\nexport interface Vendor {\n /** ID of vendor */\n id: string;\n /** Title of vendor */\n title: string;\n /** Description of vendor */\n description: string;\n /** DPA link */\n dataProcessingAgreementLink?: string;\n /** Contract email */\n contactName?: string;\n /** Contract phone */\n contactPhone?: string;\n /** Address */\n address?: string;\n /** Headquarters country */\n headquarterCountry?: IsoCountryCode;\n /** Headquarters subdivision */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Website URL */\n websiteUrl?: string;\n /** Business entity */\n businessEntity?: {\n /** Business entity title */\n title: string;\n };\n /** Assigned teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Assigned owners */\n owners: {\n /** Email */\n email: string;\n }[];\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all vendors in the organization\n *\n * @param client - GraphQL client\n * @returns All vendors in the organization\n */\nexport async function fetchAllVendors(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Vendor[]> {\n const { logger } = options;\n const vendors: Vendor[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n vendors: { nodes },\n } = await makeGraphQLRequest<{\n /** Vendors */\n vendors: {\n /** List */\n nodes: Vendor[];\n };\n }>(client, VENDORS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n vendors.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return vendors.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import {\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n PromptAVendorEmailCompletionLinkType,\n PromptAVendorEmailSendType,\n} from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataPoints, type DataPointWithSubDataPoint } from './fetchAllDataPoints.js';\nimport { fetchAllDataSilos, type DataSiloAttributeValue } from './fetchAllDataSilos.js';\nimport { DATA_SILO_SOMBRA, DATA_SILOS_ENRICHED } from './gqls/dataSilo.js';\n\nexport interface DataSiloEnriched {\n /** ID of dataSilo */\n id: string;\n /** Title of dataSilo */\n title: string;\n /** Type of silo */\n type: string;\n /** Link to silo */\n link: string;\n /** Outer type of silo */\n outerType: string;\n /** Description of data silo */\n description: string;\n /** Webhook URL */\n url?: string;\n /** Email address of user to notify for prompt a person use case */\n notifyEmailAddress?: string;\n /** Associated API keys */\n apiKeys: {\n /** Title */\n title: string;\n }[];\n /** Data subject block list */\n subjectBlocklist: {\n /** Type of data subject */\n type: string;\n }[];\n /** Identifiers */\n identifiers: {\n /** Name of identifier */\n name: string;\n /** True if identifier is wired */\n isConnected: boolean;\n }[];\n /** Dependent data silos */\n dependentDataSilos: {\n /** Title of silo */\n title: string;\n }[];\n /** Silo owners */\n owners: {\n /** Email owners */\n email: string;\n }[];\n /** The teams assigned to this data silo */\n teams: {\n /** Name of the team assigned to this data silo */\n name: string;\n }[];\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n /** Silo is live */\n isLive: boolean;\n /** Hosting country of data silo */\n country?: IsoCountryCode;\n /** Hosting subdivision data silo */\n countrySubDivision?: IsoCountrySubdivisionCode;\n /**\n * The frequency with which we should be sending emails for this data silo, in milliseconds.\n */\n promptAVendorEmailSendFrequency: number;\n /**\n * The type of emails to send for this data silo, i.e. send an email for each DSR, across all open DSRs,\n * or per profile in a DSR.\n */\n promptAVendorEmailSendType: PromptAVendorEmailSendType;\n /**\n * Indicates whether prompt-a-vendor emails should include a list of identifiers\n * in addition to a link to the bulk processing UI.\n */\n promptAVendorEmailIncludeIdentifiersAttachment: boolean;\n /**\n * Indicates what kind of link to generate as part of the emails sent out for this Prompt-a-Vendor silo.\n */\n promptAVendorEmailCompletionLinkType: PromptAVendorEmailCompletionLinkType;\n /**\n * The frequency with which we should retry sending emails for this data silo, in milliseconds.\n * Needs to be a string because the number can be larger than the MAX_INT\n */\n manualWorkRetryFrequency: string;\n /** Attribute values tagged to data silo */\n attributeValues: DataSiloAttributeValue[];\n /**\n * The data silos that discovered this particular data silo\n */\n discoveredBy: {\n /** Title of data silo */\n title: string;\n }[];\n /**\n * The business entities assigned directly to this data silo\n */\n businessEntities: {\n /** Title of business entity */\n title: string;\n }[];\n /**\n * Configured Sombra instance that processes this data silo\n */\n sombra?: {\n /** Sombra instance ID */\n id: string;\n } | null;\n}\n\n/**\n * Fetch all dataSilos with additional metadata\n *\n * @param client - GraphQL client\n * @param options - Filter options\n * @returns All dataSilos in the organization\n */\nexport async function fetchEnrichedDataSilos(\n client: GraphQLClient,\n options: {\n /** Page size */\n pageSize: number;\n /** Filter by IDs */\n ids?: string[];\n /** Enable debug logs */\n debug?: boolean;\n /** Filter by title */\n titles?: string[];\n /** Integration names */\n integrationNames?: string[];\n /** Skip fetching of datapoints */\n skipDatapoints?: boolean;\n /** Skip fetching of subdatapoints */\n skipSubDatapoints?: boolean;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<[DataSiloEnriched, DataPointWithSubDataPoint[]][]> {\n const {\n ids,\n pageSize,\n titles,\n debug,\n skipDatapoints,\n skipSubDatapoints,\n includeGuessedCategories,\n integrationNames,\n logger = NOOP_LOGGER,\n } = options;\n\n const dataSilos: [DataSiloEnriched, DataPointWithSubDataPoint[]][] = [];\n\n const silos = await fetchAllDataSilos<DataSiloEnriched>(client, {\n titles,\n ids,\n integrationNames,\n pageSize,\n gql: DATA_SILOS_ENRICHED,\n logger,\n });\n\n // `sombra` is only on singular `DataSilo`, not `DataSiloBulkPreview`.\n await mapSeries(silos, async (silo) => {\n const {\n dataSilo: { sombra },\n } = await makeGraphQLRequest<{\n /** Singular data silo */\n dataSilo: {\n /** Data silo ID */\n id: string;\n /** Configured Sombra instance */\n sombra?: {\n /** Sombra instance ID */\n id: string;\n } | null;\n };\n }>(client, DATA_SILO_SOMBRA, {\n variables: { id: silo.id },\n logger,\n });\n silo.sombra = sombra;\n });\n\n if (!skipDatapoints) {\n await mapSeries(silos, async (silo, index) => {\n logger.info(`[${index + 1}/${silos.length}] Fetching data silo - ${silo.title}`);\n\n const dataPoints = await fetchAllDataPoints(client, {\n filterBy: { dataSiloId: silo.id },\n debug,\n pageSize,\n skipSubDatapoints,\n includeGuessedCategories,\n logger,\n });\n\n if (debug) {\n logger.info(\n `[${index + 1}/${silos.length}] Successfully fetched datapoint for - ${silo.title}`,\n );\n }\n\n dataSilos.push([silo, dataPoints]);\n });\n } else {\n silos.forEach((silo) => {\n dataSilos.push([silo, []]);\n });\n }\n\n logger.info(`Successfully fetched all ${silos.length} data silo configurations`);\n\n return dataSilos;\n}\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllBusinessEntities, type BusinessEntity } from './fetchAllBusinessEntities.js';\nimport { UPDATE_BUSINESS_ENTITIES, CREATE_BUSINESS_ENTITY } from './gqls/businessEntity.js';\n\nexport interface BusinessEntityInput {\n /** Display title of the business entity */\n title: string;\n /** Description of the business entity */\n description?: string;\n /** Physical address */\n address?: string;\n /** Headquarters country */\n headquarterCountry?: IsoCountryCode;\n /** Headquarters country subdivision */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Name of the data protection officer */\n dataProtectionOfficerName?: string;\n /** Email of the data protection officer */\n dataProtectionOfficerEmail?: string;\n /** Custom attribute values to assign */\n attributes?: {\n /** Attribute key name */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n /** Team names to assign */\n teams?: string[];\n /** Owner email addresses to assign */\n owners?: string[];\n}\n\n/**\n * Input to create a new business entity\n *\n * @param client - GraphQL client\n * @param businessEntity - Input\n * @returns Created business entity\n */\nexport async function createBusinessEntity(\n client: GraphQLClient,\n options: {\n /** Business entity to create */\n input: BusinessEntityInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<BusinessEntity> {\n const { input: businessEntity, logger = NOOP_LOGGER } = options;\n const input = {\n title: businessEntity.title,\n description: businessEntity.description,\n address: businessEntity.address,\n headquarterCountry: businessEntity.headquarterCountry,\n headquarterSubDivision: businessEntity.headquarterSubDivision,\n dataProtectionOfficerName: businessEntity.dataProtectionOfficerName,\n dataProtectionOfficerEmail: businessEntity.dataProtectionOfficerEmail,\n attributes: businessEntity.attributes,\n teamNames: businessEntity.teams,\n ownerEmails: businessEntity.owners,\n };\n\n const { createBusinessEntity } = await makeGraphQLRequest<{\n /** Create business entity mutation */\n createBusinessEntity: {\n /** Created business entity */\n businessEntity: BusinessEntity;\n };\n }>(client, CREATE_BUSINESS_ENTITY, {\n variables: { input },\n logger,\n });\n return createBusinessEntity.businessEntity;\n}\n\n/**\n * Input to update business entities\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateBusinessEntities(\n client: GraphQLClient,\n options: {\n /** [BusinessEntityInput, businessEntityId] list */\n input: [BusinessEntityInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: businessEntities, logger = NOOP_LOGGER } = options;\n const chunkedUpdates = chunk(businessEntities, 100);\n await mapSeries(chunkedUpdates, async (chunked) => {\n await makeGraphQLRequest(client, UPDATE_BUSINESS_ENTITIES, {\n variables: {\n input: chunked.map(([businessEntity, id]) => ({\n id,\n title: businessEntity.title,\n description: businessEntity.description,\n address: businessEntity.address,\n headquarterCountry: businessEntity.headquarterCountry,\n headquarterSubDivision: businessEntity.headquarterSubDivision,\n dataProtectionOfficerName: businessEntity.dataProtectionOfficerName,\n dataProtectionOfficerEmail: businessEntity.dataProtectionOfficerEmail,\n attributes: businessEntity.attributes,\n teamNames: businessEntity.teams,\n ownerEmails: businessEntity.owners,\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the data inventory business entities\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncBusinessEntities(\n client: GraphQLClient,\n inputs: BusinessEntityInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" business entities...`);\n\n let encounteredError = false;\n\n // Fetch existing\n const existingBusinessEntities = await fetchAllBusinessEntities(client, { logger });\n\n // Look up by title\n const businessEntityByTitle = keyBy(existingBusinessEntities, 'title');\n\n // Create new business entities\n const newBusinessEntities = inputs.filter((input) => !businessEntityByTitle[input.title]);\n\n // Create new business entities\n await mapSeries(newBusinessEntities, async (businessEntity) => {\n try {\n const newBusinessEntity = await createBusinessEntity(client, {\n input: businessEntity,\n logger,\n });\n businessEntityByTitle[newBusinessEntity.title] = newBusinessEntity;\n logger.info(`Successfully synced business entity \"${businessEntity.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync business entity \"${businessEntity.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n // Update all business entities\n try {\n logger.info(`Updating \"${inputs.length}\" business entities!`);\n await updateBusinessEntities(client, {\n input: inputs.map((input) => [input, businessEntityByTitle[input.title]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" business entities!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync \"${inputs.length}\" business entities ! - ${(err as Error).message}`,\n );\n }\n\n return !encounteredError;\n}\n","import { DataCategoryType } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataCategories, type DataSubCategory } from './fetchAllDataCategories.js';\nimport { UPDATE_DATA_SUB_CATEGORIES, CREATE_DATA_SUB_CATEGORY } from './gqls/dataCategory.js';\n\nexport interface DataCategoryInput {\n /** Name of data category */\n name: string;\n /** Type of data category */\n category: DataCategoryType;\n /** Description of data category */\n description?: string;\n /** Regex for data category */\n regex?: string;\n /** Owner email addresses to assign */\n owners?: string[];\n /** Team names to assign */\n teams?: string[];\n /** Attribute value and its corresponding attribute key */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\n/**\n * Create a new data category\n *\n * @param client - GraphQL client\n * @param dataCategory - Input\n * @param options - Options\n * @returns Created data category\n */\nexport async function createDataCategory(\n client: GraphQLClient,\n options: {\n /** Data category to create */\n input: DataCategoryInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<DataSubCategory, 'id' | 'name' | 'category'>> {\n const { input: dataCategory, logger = NOOP_LOGGER } = options;\n const input = {\n name: dataCategory.name,\n category: dataCategory.category,\n description: dataCategory.description,\n // TODO: https://transcend.height.app/T-31994 - add attributes, teams, owners\n };\n\n const { createDataCategory } = await makeGraphQLRequest<{\n /** Create data category mutation */\n createDataCategory: {\n /** Created data category */\n dataCategory: DataSubCategory;\n };\n }>(client, CREATE_DATA_SUB_CATEGORY, {\n variables: { input },\n logger,\n });\n return createDataCategory.dataCategory;\n}\n\n/**\n * Update data categories\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateDataCategories(\n client: GraphQLClient,\n options: {\n /** [DataCategoryInput, dataCategoryId] list */\n input: [DataCategoryInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataCategories, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_DATA_SUB_CATEGORIES, {\n variables: {\n input: {\n dataSubCategories: dataCategories.map(([dataCategory, id]) => ({\n id,\n description: dataCategory.description,\n // TODO: https://transcend.height.app/T-31994 - add teams, owners\n attributes: dataCategory.attributes,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory data categories\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncDataCategories(\n client: GraphQLClient,\n inputs: DataCategoryInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" data categories...`);\n\n let encounteredError = false;\n\n // Fetch existing\n const existingDataCategories = await fetchAllDataCategories(client, {\n logger,\n });\n\n // Look up by name\n const dataCategoryByName: {\n [k in string]: Pick<DataSubCategory, 'id' | 'name' | 'category'>;\n } = keyBy(existingDataCategories, ({ name, category }) => `${name}:${category}`);\n\n // Create new data categories\n const newDataCategories = inputs.filter(\n (input) => !dataCategoryByName[`${input.name}:${input.category}`],\n );\n\n await mapSeries(newDataCategories, async (dataCategory) => {\n try {\n const newDataCategory = await createDataCategory(client, { input: dataCategory, logger });\n dataCategoryByName[`${newDataCategory.name}:${newDataCategory.category}`] = newDataCategory;\n logger.info(`Successfully synced data category \"${dataCategory.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync data category \"${dataCategory.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n // Update all data categories\n try {\n logger.info(`Updating \"${inputs.length}\" data categories!`);\n await updateDataCategories(client, {\n input: inputs.map((input) => [\n input,\n dataCategoryByName[`${input.name}:${input.category}`]!.id,\n ]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" data categories!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" data categories ! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSilo } from './fetchAllDataSilos.js';\nimport { UPDATE_DATA_SILOS } from './gqls/dataSilo.js';\n\nconst BATCH_SILOS_LIMIT = 20;\n\n/**\n * A set of data silos that another data silo depends on during an erasure request,\n * either globally or scoped to a single erasure workflow.\n */\nexport interface DependedOnDataSiloInput {\n /** IDs of the data silos depended on */\n ids?: string[];\n /** Titles of the data silos depended on */\n titles?: string[];\n /** ID of the workflow config this override applies to. Omit for the global configuration */\n workflowConfigId?: string;\n /** Internal name of the workflow config this override applies to. Omit for the global configuration */\n workflowConfigInternalName?: string;\n /** Clear the workflow-scoped override so the data silo falls back to the global configuration */\n resetToGlobal?: boolean;\n}\n\n/**\n * Sync data silo dependencies\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns True upon success\n */\nexport async function syncDataSiloDependencies(\n client: GraphQLClient,\n options: {\n /** Pairs of [data silo ID, dependency entries] */\n input: [string, DependedOnDataSiloInput[]][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<boolean> {\n const { input: dependencyUpdates, logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${dependencyUpdates.length}\" data silo dependencies...`);\n\n const chunkedUpdates = chunk(dependencyUpdates, BATCH_SILOS_LIMIT);\n await mapSeries(chunkedUpdates, async (dependencyUpdateChunk, ind) => {\n logger.info(\n `[Batch ${ind}/${dependencyUpdateChunk.length}] Updating \"${dependencyUpdateChunk.length}\" data silos...`,\n );\n try {\n await makeGraphQLRequest<{\n /** Mutation result */\n updateDataSilos: {\n /** New data silos */\n dataSilos: Pick<DataSilo, 'id' | 'title'>[];\n };\n }>(client, UPDATE_DATA_SILOS, {\n variables: {\n input: {\n dataSilos: dependencyUpdateChunk.map(([id, dependedOnDataSilos]) => ({\n id,\n dependedOnDataSilos,\n })),\n },\n },\n logger,\n });\n logger.info(\n `[Batch ${ind + 1}/${dependencyUpdateChunk.length}] ` +\n `Synced \"${dependencyUpdateChunk.length}\" data silos!`,\n );\n } catch (err) {\n encounteredError = true;\n logger.error(\n `[Batch ${ind + 1}/${dependencyUpdateChunk.length}] ` +\n `Failed to update \"${dependencyUpdateChunk.length}\" silos! - ${(err as Error).message}`,\n );\n }\n });\n return !encounteredError;\n}\n","import type {\n ProcessingPurpose,\n DataCategoryType,\n DataProtectionImpactAssessmentStatus,\n Controllership,\n RetentionType,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n} from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n fetchAllProcessingActivities,\n type ProcessingActivity,\n} from './fetchAllProcessingActivities.js';\nimport {\n UPDATE_PROCESSING_ACTIVITIES,\n CREATE_PROCESSING_ACTIVITY,\n} from './gqls/processingActivity.js';\n\nexport interface ProcessingActivityInput {\n /** The title of the processing activity */\n title: string;\n /** Description of the processing activity */\n description?: string;\n /** Security measure details */\n securityMeasureDetails?: string;\n /** Controllerships */\n controllerships?: Controllership[];\n /** Storage regions */\n storageRegions?: {\n /** The country */\n country?: IsoCountryCode;\n /** The country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n }[];\n /** Transfer regions */\n transferRegions?: {\n /** The country */\n country?: IsoCountryCode;\n /** The country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n }[];\n /** Retention type */\n retentionType?: RetentionType;\n /** Retention period in days */\n retentionPeriod?: number;\n /** Data protection impact assessment link */\n dataProtectionImpactAssessmentLink?: string;\n /** Data protection impact assessment status */\n dataProtectionImpactAssessmentStatus?: DataProtectionImpactAssessmentStatus;\n /** Attribute value and its corresponding attribute key */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n /** Data silo titles */\n dataSiloTitles?: string[];\n /** Data subject types */\n dataSubjectTypes?: string[];\n /** Team names */\n teamNames?: string[];\n /** Owner emails */\n ownerEmails?: string[];\n /** Processing sub purposes */\n processingSubPurposes?: {\n /** The parent purpose */\n purpose: ProcessingPurpose;\n /** User-defined name for this processing purpose sub category */\n name?: string;\n }[];\n /** Data sub categories */\n dataSubCategories?: {\n /** The parent category */\n category: DataCategoryType;\n /** User-defined name for this sub category */\n name?: string;\n }[];\n /** SaaS category titles */\n saaSCategories?: string[];\n}\n\n/**\n * Create a new processing activity, setting only title and description\n *\n * @param client - GraphQL client\n * @param processingActivity - Input\n * @param options - Options\n * @returns Created processingActivity\n */\nasync function createProcessingActivity(\n client: GraphQLClient,\n processingActivity: ProcessingActivityInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Pick<ProcessingActivity, 'id' | 'title'>> {\n const { logger = NOOP_LOGGER } = options;\n const input = {\n title: processingActivity.title,\n description: processingActivity.description,\n };\n\n const { createProcessingActivity } = await makeGraphQLRequest<{\n /** Create processingActivity mutation */\n createProcessingActivity: {\n /** Created processingActivity */\n processingActivity: ProcessingActivity;\n };\n }>(client, CREATE_PROCESSING_ACTIVITY, {\n variables: { input },\n logger,\n });\n return createProcessingActivity.processingActivity;\n}\n\n/**\n * Update a list of processing activities.\n *\n * @param client - GraphQL client\n * @param processingActivityIdPairs - [ProcessingActivityInput, processingActivityId] list\n * @param options - Options\n */\nasync function updateProcessingActivities(\n client: GraphQLClient,\n processingActivityIdPairs: [ProcessingActivityInput, string][],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n const invalidProcessingActivityTitles = processingActivityIdPairs\n .filter(([, id]) => id === undefined)\n .map(([{ title }]) => title);\n if (invalidProcessingActivityTitles.length > 0) {\n throw new Error(\n `The following ${\n invalidProcessingActivityTitles.length\n } processing activities do not exist and thus can't be updated: \"${invalidProcessingActivityTitles.join(\n '\", \"',\n )}\"`,\n );\n }\n await makeGraphQLRequest(client, UPDATE_PROCESSING_ACTIVITIES, {\n variables: {\n input: {\n processingActivities: processingActivityIdPairs.map(\n ([\n { processingSubPurposes, dataSubCategories, saaSCategories, ...processingActivity },\n id,\n ]) => ({\n dataSubCategoryInputs: dataSubCategories?.map(({ category, name }) => ({\n category,\n name: name ?? '',\n })),\n processingPurposeSubCategoryInputs: processingSubPurposes?.map(({ purpose, name }) => ({\n purpose,\n name: name ?? 'Other',\n })),\n saaSCategoryTitles: saaSCategories,\n ...processingActivity,\n id,\n }),\n ),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory processing activities\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncProcessingActivities(\n client: GraphQLClient,\n inputs: ProcessingActivityInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" processing activities...`);\n const existingProcessingActivities = await fetchAllProcessingActivities(client, { logger });\n\n // Look up by title\n const processingActivityByTitle: Record<string, Pick<ProcessingActivity, 'id' | 'title'>> = keyBy(\n existingProcessingActivities,\n 'title',\n );\n\n // Create new processingActivities\n const newProcessingActivities = inputs.filter((input) => !processingActivityByTitle[input.title]);\n if (newProcessingActivities.length > 0) {\n logger.info(`Creating \"${newProcessingActivities.length}\" new processing activities...`);\n }\n await mapSeries(newProcessingActivities, async (processingActivity) => {\n try {\n const newProcessingActivity = await createProcessingActivity(client, processingActivity, {\n logger,\n });\n processingActivityByTitle[newProcessingActivity.title] = newProcessingActivity;\n logger.info(`Successfully created processing activity \"${processingActivity.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to create processing activity \"${processingActivity.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n // Update all processing activities\n try {\n logger.info(`Updating \"${inputs.length}\" processing activities!`);\n await updateProcessingActivities(\n client,\n inputs\n .map((input) => [input, processingActivityByTitle[input.title]?.id] as const)\n .filter((x): x is [ProcessingActivityInput, string] => !!x[1]),\n { logger },\n );\n logger.info(`Successfully synced \"${inputs.length}\" processingActivities!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync \"${inputs.length}\" processingActivities! - ${(err as Error).message}`,\n );\n }\n\n return !encounteredError;\n}\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllVendors, type Vendor } from './fetchAllVendors.js';\nimport { UPDATE_VENDORS, CREATE_VENDOR } from './gqls/vendor.js';\n\nexport interface VendorInput {\n /** Display title of the vendor */\n title: string;\n /** Description of the vendor */\n description?: string;\n /** URL to the data processing agreement */\n dataProcessingAgreementLink?: string;\n /** Name of the primary contact */\n contactName?: string;\n /** Phone number of the primary contact */\n contactPhone?: string;\n /** Physical address */\n address?: string;\n /** Headquarters country */\n headquarterCountry?: IsoCountryCode;\n /** Headquarters country subdivision */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Vendor website URL */\n websiteUrl?: string;\n /** Title of the associated business entity */\n businessEntity?: string;\n /** Owner email addresses to assign */\n owners?: string[];\n /** Team names to assign */\n teams?: string[];\n /** Custom attribute values to assign */\n attributes?: {\n /** Attribute key name */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\n/**\n * Input to create a new vendor\n *\n * @param client - GraphQL client\n * @param vendor - Input\n * @returns Created vendor\n */\nexport async function createVendor(\n client: GraphQLClient,\n options: {\n /** Vendor to create */\n input: VendorInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Vendor, 'id' | 'title'>> {\n const { input: vendor, logger = NOOP_LOGGER } = options;\n const input = {\n title: vendor.title,\n description: vendor.description,\n address: vendor.address,\n headquarterCountry: vendor.headquarterCountry,\n headquarterSubDivision: vendor.headquarterSubDivision,\n dataProcessingAgreementLink: vendor.dataProcessingAgreementLink,\n contactName: vendor.contactName,\n contactPhone: vendor.contactPhone,\n websiteUrl: vendor.websiteUrl,\n // TODO: https://transcend.height.app/T-31994 - add attributes, teams, owners\n };\n\n const { createVendor } = await makeGraphQLRequest<{\n /** Create vendor mutation */\n createVendor: {\n /** Created vendor */\n vendor: Vendor;\n };\n }>(client, CREATE_VENDOR, {\n variables: { input },\n logger,\n });\n return createVendor.vendor;\n}\n\n/**\n * Input to update vendors\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateVendors(\n client: GraphQLClient,\n options: {\n /** [VendorInput, vendorId] list */\n input: [VendorInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: vendors, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_VENDORS, {\n variables: {\n input: {\n vendors: vendors.map(([vendor, id]) => ({\n id,\n title: vendor.title,\n description: vendor.description,\n address: vendor.address,\n headquarterCountry: vendor.headquarterCountry,\n headquarterSubDivision: vendor.headquarterSubDivision,\n dataProcessingAgreementLink: vendor.dataProcessingAgreementLink,\n contactName: vendor.contactName,\n contactPhone: vendor.contactPhone,\n websiteUrl: vendor.websiteUrl,\n // TODO: https://transcend.height.app/T-31994 - add teams, owners\n attributes: vendor.attributes,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory vendors\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncVendors(\n client: GraphQLClient,\n inputs: VendorInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" vendors...`);\n\n let encounteredError = false;\n\n // Fetch existing\n const existingVendors = await fetchAllVendors(client, { logger });\n\n // Look up by title\n const vendorByTitle: { [k in string]: Pick<Vendor, 'id' | 'title'> } = keyBy(\n existingVendors,\n 'title',\n );\n\n // Create new vendors\n const newVendors = inputs.filter((input) => !vendorByTitle[input.title]);\n\n // Create new vendors\n await mapSeries(newVendors, async (vendor) => {\n try {\n const newVendor = await createVendor(client, { input: vendor, logger });\n vendorByTitle[newVendor.title] = newVendor;\n logger.info(`Successfully synced vendor \"${vendor.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync vendor \"${vendor.title}\"! - ${(err as Error).message}`);\n }\n });\n\n // Update all vendors\n try {\n logger.info(`Updating \"${inputs.length}\" vendors!`);\n await updateVendors(client, {\n input: inputs.map((input) => [input, vendorByTitle[input.title]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" vendors!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" vendors ! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const PURPOSES: DocumentNode = parse(gql`\n query TranscendCliPurposes(\n $first: Int!\n $offset: Int!\n $filterBy: TrackingPurposeFiltersInput\n $input: TrackingPurposeInput!\n ) {\n purposes(first: $first, offset: $offset, filterBy: $filterBy, input: $input) {\n nodes {\n id\n name\n description\n defaultConsent\n trackingType\n configurable\n essential\n showInConsentManager\n isActive\n displayOrder\n optOutSignals\n deletedAt\n authLevel\n showInPrivacyCenter\n title\n }\n }\n }\n`);\n\nexport const CREATE_PURPOSE: DocumentNode = parse(gql`\n mutation TranscendCliCreatePurpose($input: CreatePurposeInput!) {\n createPurpose(input: $input) {\n purpose {\n id\n trackingType\n }\n }\n }\n`);\n\nexport const UPDATE_PURPOSE: DocumentNode = parse(gql`\n mutation TranscendCliUpdatePurpose($input: UpdatePurposeInput!) {\n updatePurpose(input: $input) {\n purpose {\n id\n trackingType\n }\n }\n }\n`);\n","import { UserPrivacySignalEnum } from '@transcend-io/airgap.js-types';\nimport { DefaultConsentOption, PreferenceStoreAuthLevel } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PURPOSES } from './gqls/purpose.js';\n\nexport interface Purpose {\n /** ID of purpose */\n id: string;\n /** Name of purpose */\n name: string;\n /** Description of purpose */\n description: string;\n /** Default consent status */\n defaultConsent: DefaultConsentOption;\n /** Slug of purpose */\n trackingType: string;\n /** Whether the purpose is configurable */\n configurable: boolean;\n /** Whether the purpose is essential */\n essential: boolean;\n /** Whether to show the purpose in the consent manager */\n showInConsentManager: boolean;\n /** Whether the purpose is active */\n isActive: boolean;\n /** Display order of the purpose */\n displayOrder: number;\n /** Opt-out signals for the purpose */\n optOutSignals: UserPrivacySignalEnum[];\n /** Whether the purpose is deleted */\n deletedAt?: string;\n /** Authorization level required for the purpose */\n authLevel: PreferenceStoreAuthLevel;\n /** Whether to show the purpose in the privacy center */\n showInPrivacyCenter: boolean;\n /** Title of the purpose */\n title: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all purposes in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All purposes in the organization\n */\nexport async function fetchAllPurposes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Whether to include deleted purposes */\n includeDeleted?: boolean;\n };\n } = {},\n): Promise<Purpose[]> {\n const { logger, filterBy: { includeDeleted = false } = {} } = options;\n const purposes: Purpose[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n purposes: { nodes },\n } = await makeGraphQLRequest<{\n /** Purposes */\n purposes: {\n /** List */\n nodes: Purpose[];\n };\n }>(client, PURPOSES, {\n logger,\n variables: { first: PAGE_SIZE, offset, input: { includeDeleted } },\n });\n purposes.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return purposes.sort((a, b) => a.trackingType.localeCompare(b.trackingType));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const PREFERENCE_TOPICS: DocumentNode = parse(gql`\n query TranscendCliPreferenceTopics(\n $first: Int!\n $offset: Int!\n $filterBy: PreferenceTopicFilterInput\n ) {\n preferenceTopics(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n slug\n type\n color\n title {\n id\n defaultMessage\n }\n showInPrivacyCenter\n displayDescription {\n id\n defaultMessage\n }\n defaultConfiguration\n preferenceOptionValues {\n slug\n title {\n id\n defaultMessage\n }\n }\n purpose {\n trackingType\n }\n }\n }\n }\n`);\n\nexport const CREATE_OR_UPDATE_PREFERENCE_TOPIC: DocumentNode = parse(gql`\n mutation TranscendCliCreateOrUpdatePreferenceTopic($input: CreateOrUpdatePreferenceTopicInput!) {\n createOrUpdatePreferenceTopic(input: $input) {\n preferenceTopic {\n id\n slug\n }\n }\n }\n`);\n","import { PreferenceTopicType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PREFERENCE_TOPICS } from './gqls/preferenceTopic.js';\n\nexport interface PreferenceTopic {\n /** ID of preference topic */\n id: string;\n /** Slug of preference topic */\n slug: string;\n /** Title of topic */\n title: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n /** Whether to show in privacy center */\n showInPrivacyCenter: boolean;\n /** Description to display in privacy center */\n displayDescription: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n /** Type of preference topic */\n type: PreferenceTopicType;\n /** Default configuration */\n defaultConfiguration: string;\n /** Display color for the preference topic */\n color?: string;\n /** Option values */\n preferenceOptionValues: {\n /** Slug of value */\n slug: string;\n /** Title of value */\n title: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n }[];\n /** Related purpose */\n purpose: {\n /** Slug */\n trackingType: string;\n };\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all preference topics in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All preference topics in the organization\n */\nexport async function fetchAllPreferenceTopics(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PreferenceTopic[]> {\n const { logger } = options;\n const preferenceTopics: PreferenceTopic[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n preferenceTopics: { nodes },\n } = await makeGraphQLRequest<{\n /** Preference topics */\n preferenceTopics: {\n /** List */\n nodes: PreferenceTopic[];\n };\n }>(client, PREFERENCE_TOPICS, {\n logger,\n variables: { first: PAGE_SIZE, offset },\n });\n preferenceTopics.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return preferenceTopics.sort((a, b) =>\n `${a.slug}:${a.purpose.trackingType}`.localeCompare(`${b.slug}:${b.purpose.trackingType}`),\n );\n}\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { type PreferenceTopic, fetchAllPreferenceTopics } from './fetchAllPreferenceTopics.js';\nimport { type Purpose, fetchAllPurposes } from './fetchAllPurposes.js';\n\nexport interface PurposeWithPreferences extends Purpose {\n /** Topics */\n topics: PreferenceTopic[];\n}\n\n/**\n * Fetch all purposes and preferences\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of purposes with their preference topics\n */\nexport async function fetchAllPurposesAndPreferences(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PurposeWithPreferences[]> {\n const [purposes, topics] = await Promise.all([\n fetchAllPurposes(client, options),\n fetchAllPreferenceTopics(client, options),\n ]);\n\n return purposes.map((purpose) => ({\n ...purpose,\n topics: topics.filter((topic) => topic.purpose.trackingType === purpose.trackingType),\n }));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const CREATE_PREFERENCE_ACCESS_TOKENS: DocumentNode = parse(gql`\n mutation TranscendCliCreatePreferenceAccessTokens($input: CreatePrivacyCenterAccessTokensInput!) {\n createPrivacyCenterAccessTokens(input: $input) {\n nodes {\n token\n }\n }\n }\n`);\n","import type { SombraStandardScope } from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CREATE_PREFERENCE_ACCESS_TOKENS } from './gqls/preferenceAccessTokens.js';\n\nexport interface PreferenceAccessTokenInput {\n /** Slug of data subject to authenticate as */\n subjectType: string;\n /** Scopes to grant */\n scopes: SombraStandardScope[];\n /** Expiration time in seconds */\n expiresIn?: number;\n /** Email address of user */\n email: string;\n /** Core identifier for the user */\n coreIdentifier?: string;\n}\n\nexport interface PreferenceAccessTokenInputWithIndex extends PreferenceAccessTokenInput {\n /** Index of the input record */\n index?: number;\n}\n\nconst MAX_BATCH_SIZE = 50;\n\n/**\n * Create preference access tokens for a single page of identifiers.\n *\n * @param client - GraphQL client\n * @param records - Inputs to sign\n * @param logger - Logger\n * @returns list of access tokens\n */\nasync function createPreferenceAccessTokensPage(\n client: GraphQLClient,\n records: PreferenceAccessTokenInput[],\n logger?: Logger,\n): Promise<string[]> {\n const {\n createPrivacyCenterAccessTokens: { nodes },\n } = await makeGraphQLRequest<{\n /** createPrivacyCenterAccessTokens mutation */\n createPrivacyCenterAccessTokens: {\n /** Nodes */\n nodes: {\n /** Token */\n token: string;\n }[];\n };\n }>(client, CREATE_PREFERENCE_ACCESS_TOKENS, {\n logger,\n variables: { input: { records } },\n });\n return nodes.map((node) => node.token);\n}\n\n/**\n * Create preference access tokens for the given identifiers.\n *\n * @see https://docs.transcend.io/docs/articles/preference-management/access-links\n * @param client - GraphQL client\n * @param options - Options\n * @returns list of access tokens/input identifiers\n */\nexport async function createPreferenceAccessTokens(\n client: GraphQLClient,\n options: {\n /** Records to create tokens for */\n records: PreferenceAccessTokenInputWithIndex[];\n /** Logger instance */\n logger?: Logger;\n /** Optional progress emitter */\n emitProgress?: (progress: number) => void;\n /** Number of concurrent requests to make (default: 10) */\n concurrency?: number;\n },\n): Promise<\n {\n /** Identifier for the record */\n input: PreferenceAccessTokenInputWithIndex;\n /** Access token */\n accessToken: string;\n }[]\n> {\n const { records, logger, emitProgress, concurrency = 10 } = options;\n\n let completed = 0;\n emitProgress?.(0);\n\n const results: {\n /** Identifier for the record */\n input: PreferenceAccessTokenInput;\n /** Access token */\n accessToken: string;\n }[] = [];\n\n await map(\n chunk(records, MAX_BATCH_SIZE),\n async (chunkedRecords) => {\n const tokens = await createPreferenceAccessTokensPage(\n client,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n chunkedRecords.map(({ index, ...rest }) => rest),\n logger,\n );\n const mappedResults = tokens.map((token, idx) => ({\n input: chunkedRecords[idx]!,\n accessToken: token,\n }));\n results.push(...mappedResults);\n completed += chunkedRecords.length;\n emitProgress?.(completed);\n },\n { concurrency },\n );\n\n return results;\n}\n","import { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport * as t from 'io-ts';\n\n/**\n * New response codec for the query endpoint\n */\nexport const ConsentPreferenceResponse = t.intersection([\n t.type({\n nodes: t.array(PreferenceQueryResponseItem),\n }),\n t.partial({\n /** Cursor for next page (opaque) */\n cursor: t.string,\n }),\n]);\n\n/**\n * Type override\n */\nexport type ConsentPreferenceResponse = t.TypeOf<typeof ConsentPreferenceResponse>;\n\n/** Identifier filter (new shape) */\nexport type PreferenceIdentifier = {\n /** e.g., \"email\", \"phone\" */\n name: string;\n /** identifier value */\n value: string;\n};\n\n/** Filter shape for the new query endpoint */\nexport type PreferencesQueryFilter = {\n /** Identifiers to filter by */\n identifiers?: PreferenceIdentifier[];\n /** Consent collection time */\n timestampBefore?: string;\n /** Consent collection time */\n timestampAfter?: string;\n /** System updatedAt time */\n system?: {\n /** Updated before this time */\n updatedBefore?: string;\n /** Updated after this time */\n updatedAfter?: string;\n };\n};\n\n/** Which dimension we chunk on */\nexport type ChunkMode = 'timestamp' | 'updated';\n","import { UserPrivacySignalEnum } from '@transcend-io/airgap.js-types';\nimport {\n PreferenceQueryResponseItem,\n PreferenceStoreAuthLevel,\n PreferenceStoreIdentifier,\n PreferenceTopicType,\n PreferenceUpdateItem,\n} from '@transcend-io/privacy-types';\nimport { valuesOf } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\n\nexport const PurposeRowMapping = t.type({\n /**\n * The slug or trackingType of the purpose to map to\n *\n * e.g. `Marketing`\n */\n purpose: t.string,\n /**\n * If the column maps to a preference instead of a purpose\n * this is the slug of the purpose.\n *\n * null value indicates that this column maps to the true/false\n * value of the purpose\n */\n preference: t.union([t.string, t.null]),\n /**\n * The mapping between each row value and purpose/preference value.\n *\n * e.g. for a boolean preference or purpose\n * {\n * 'true': true,\n * 'false': false,\n * '': true,\n * }\n *\n * or for a single or multi select preference\n * {\n * '': true,\n * 'value1': 'Value1',\n * 'value2': 'Value2',\n * }\n */\n valueMapping: t.record(t.string, t.union([t.string, t.boolean, t.null, t.undefined])),\n});\n\n/** Override type */\nexport type PurposeRowMapping = t.TypeOf<typeof PurposeRowMapping>;\n\n/**\n * Mapping of column name to purpose row mapping.\n * This is used to map each column in the CSV to the relevant purpose and preference definitions in\n * transcend.\n */\nexport const ColumnPurposeMap = t.record(t.string, PurposeRowMapping);\n\n/** Override type */\nexport type ColumnPurposeMap = t.TypeOf<typeof ColumnPurposeMap>;\n\nexport const IdentifierMetadataForPreference = t.type({\n /** The identifier name */\n name: t.string,\n /** Is unique on preference store */\n isUniqueOnPreferenceStore: t.boolean,\n});\n\n/** Override type */\nexport type IdentifierMetadataForPreference = t.TypeOf<typeof IdentifierMetadataForPreference>;\n\n/**\n * Mapping of identifier name to the column name in the CSV file.\n * This is used to map each identifier name to the column in the CSV file.\n */\nexport const ColumnIdentifierMap = t.record(t.string, IdentifierMetadataForPreference);\n\n/** Override type */\nexport type ColumnIdentifierMap = t.TypeOf<typeof ColumnIdentifierMap>;\n\n/** Mapping of a CSV column to a metadata key in the preference store. */\nexport const MetadataMapping = t.type({\n /** The metadata key name in the preference store */\n key: t.string,\n});\n\n/** Override type */\nexport type MetadataMapping = t.TypeOf<typeof MetadataMapping>;\n\n/** Record mapping CSV column names to metadata keys. */\nexport const ColumnMetadataMap = t.record(t.string, MetadataMapping);\n\n/** Override type */\nexport type ColumnMetadataMap = t.TypeOf<typeof ColumnMetadataMap>;\n\nexport const FileMetadataState = t.intersection([\n t.type({\n /**\n * Definition of how to map each column in the CSV to\n * the relevant purpose and preference definitions in transcend\n */\n columnToPurposeName: t.record(t.string, PurposeRowMapping),\n /** Last time the file was last parsed at */\n lastFetchedAt: t.string,\n /**\n * Mapping of userId to the rows in the file that need to be uploaded\n * These uploads are overwriting non-existent preferences and are safe\n */\n pendingSafeUpdates: t.record(t.string, t.record(t.string, t.string)),\n /**\n * Mapping of userId to the rows in the file that need to be uploaded\n * these records have conflicts with existing consent preferences\n */\n pendingConflictUpdates: t.record(\n t.string,\n t.type({\n record: PreferenceQueryResponseItem,\n row: t.record(t.string, t.string),\n }),\n ),\n /**\n * Mapping of userId to the rows in the file that can be skipped because\n * their preferences are already in the store\n */\n skippedUpdates: t.record(t.string, t.record(t.string, t.string)),\n }),\n t.partial({\n /** Determine which column name in file maps to consent record identifier to upload on */\n identifierColumn: t.string,\n /** Determine which column name in file maps to the timestamp */\n timestampColum: t.string,\n }),\n]);\n\n/** Override type */\nexport type FileMetadataState = t.TypeOf<typeof FileMetadataState>;\n\n/**\n * Schema-only state for a preference CSV file format.\n *\n * Unlike FileMetadataState this does NOT embed upload receipts — it only\n * describes how columns map to identifiers, purposes, timestamps, and metadata.\n */\nexport const FileFormatState = t.intersection([\n t.type({\n /** Maps each CSV column to its purpose/preference definition in Transcend */\n columnToPurposeName: ColumnPurposeMap,\n /** ISO 8601 timestamp of when this config was last generated or refreshed */\n lastFetchedAt: t.string,\n /** Maps each CSV column to the identifier it represents (e.g. email, userId) */\n columnToIdentifier: ColumnIdentifierMap,\n }),\n t.partial({\n /** CSV column whose values contain the consent timestamp */\n timestampColumn: t.string,\n /** Maps CSV columns to metadata keys stored alongside the preference record */\n columnToMetadata: ColumnMetadataMap,\n /** CSV columns that should be skipped during upload */\n columnsToIgnore: t.array(t.string),\n }),\n]);\n\n/** Override type */\nexport type FileFormatState = t.TypeOf<typeof FileFormatState>;\n\n/**\n * This is the type of the receipts that are stored in the file\n * that is used to track the state of the upload process.\n * It is used to resume the upload process from where it left off.\n * It is used to persist the state of the upload process across multiple runs.\n */\nexport const PreferenceUpdateMap = t.record(\n t.string,\n // This can either be true to indicate the record is pending\n // or it can be an object showing the object\n // We only return a fixed number of results to avoid\n // making the JSON file too large\n t.union([t.boolean, PreferenceUpdateItem]),\n);\n\n/** Override type */\nexport type PreferenceUpdateMap = t.TypeOf<typeof PreferenceUpdateMap>;\n\n/**\n * This is the type of the pending updates that are safe to run without\n * conflicts with existing consent preferences.\n *\n * Key is primaryKey of the record in the file.\n * The value is the row in the file that is safe to upload.\n */\nexport const PendingSafePreferenceUpdates = t.record(\n t.string,\n // This can either be true to indicate the record is safe\n // or it can be an object showing the object\n // We only return a fixed number of results to avoid\n // making the JSON file too large\n t.union([t.boolean, t.record(t.string, t.string)]),\n);\n\n/** Override type */\nexport type PendingSafePreferenceUpdates = t.TypeOf<typeof PendingSafePreferenceUpdates>;\n\n/**\n * These are the updates that failed to be uploaded to the API.\n */\nexport const FailingPreferenceUpdates = t.record(\n t.string,\n t.type({\n /** Time upload ran at */\n uploadedAt: t.string,\n /** Attempts to upload that resulted in an error */\n error: t.string,\n /** The update body */\n update: PreferenceUpdateItem,\n }),\n);\n\n/** Override type */\nexport type FailingPreferenceUpdates = t.TypeOf<typeof FailingPreferenceUpdates>;\n\n/**\n * This is the type of the pending updates that are in conflict with existing consent preferences.\n *\n * Key is primaryKey of the record in the file.\n * The value is the row in the file that is pending upload.\n */\nexport const PendingWithConflictPreferenceUpdates = t.record(\n t.string,\n // We always return the conflicts for investigation\n t.type({\n /** Record to be inserted to transcend v1/preferences API */\n record: PreferenceQueryResponseItem,\n /** The row in the file that is pending upload */\n row: t.record(t.string, t.string),\n }),\n);\n\n/** Override type */\nexport type PendingWithConflictPreferenceUpdates = t.TypeOf<\n typeof PendingWithConflictPreferenceUpdates\n>;\n\n/**\n * The set of preference updates that are skipped\n * Key is primaryKey and value is the row in the CSV\n * that is skipped.\n *\n * This is usually because the preferences are already in the store\n * or there are duplicate rows in the CSV file that are identical.\n */\nexport const SkippedPreferenceUpdates = t.record(t.string, t.record(t.string, t.string));\n\n/** Override type */\nexport type SkippedPreferenceUpdates = t.TypeOf<typeof SkippedPreferenceUpdates>;\n\n/** Persist this data between runs of the script */\nexport const PreferenceState = t.type({\n /**\n * Store a cache of previous files read in\n */\n fileMetadata: t.record(t.string, FileMetadataState),\n /**\n * The set of successful uploads to Transcend\n * Mapping from userId to the upload metadata\n */\n failingUpdates: t.record(\n t.string,\n t.type({\n /** Time upload ran at */\n uploadedAt: t.string,\n /** Attempts to upload that resulted in an error */\n error: t.string,\n /** The update body */\n update: PreferenceUpdateItem,\n }),\n ),\n /**\n * The set of pending uploads to Transcend\n * Mapping from userId to the upload metadata\n */\n pendingUpdates: t.record(t.string, PreferenceUpdateItem),\n});\n\n/** Override type */\nexport type PreferenceState = t.TypeOf<typeof PreferenceState>;\n\nexport const RequestUploadReceipts = t.type({\n /** ISO 8601 timestamp of when the receipt file was last written */\n lastFetchedAt: t.string,\n /** Updates that can be applied without conflicting with existing preferences */\n pendingSafeUpdates: PendingSafePreferenceUpdates,\n /** Updates that conflict with existing preference values and need review */\n pendingConflictUpdates: PendingWithConflictPreferenceUpdates,\n /** Rows skipped because their preferences already match the store */\n skippedUpdates: SkippedPreferenceUpdates,\n /** Updates that were attempted but failed with an API error */\n failingUpdates: FailingPreferenceUpdates,\n /** Updates still queued to be sent to the API */\n pendingUpdates: PreferenceUpdateMap,\n /** Updates that have been successfully written to the preference store */\n successfulUpdates: PreferenceUpdateMap,\n});\n\n/** Override type */\nexport type RequestUploadReceipts = t.TypeOf<typeof RequestUploadReceipts>;\n\nexport const DeletePreferenceRecordsInput = t.type({\n /** Array of consent preference records to delete */\n records: t.array(\n t.type({\n /** The anchor identifier to locate the consent record */\n anchorIdentifier: PreferenceStoreIdentifier,\n /** The ISO 8601 timestamp of when the deletion is requested */\n timestamp: t.string,\n }),\n ),\n});\n\n/** Override type */\nexport type DeletePreferenceRecordsInput = t.TypeOf<typeof DeletePreferenceRecordsInput>;\n\nexport const DeletePreferenceRecordsResponse = t.intersection([\n t.type({\n /** Array of results for each preference record deletion */\n records: t.array(\n t.intersection([\n t.type({\n /** Whether the deletion was successful */\n success: t.boolean,\n }),\n t.partial({\n /** An error message if the deletion failed */\n errorMessage: t.string,\n }),\n ]),\n ),\n /** The list of failed deletions with their respective errors */\n failures: t.array(\n t.type({\n /** The index of the failed update in the original request */\n index: t.number,\n /** The error message associated with the failure */\n error: t.string,\n }),\n ),\n }),\n t.partial({\n /** Any general errors that occurred during the operation */\n errors: t.array(t.string),\n }),\n]);\n\n/** Override type */\nexport type DeletePreferenceRecordsResponse = t.TypeOf<typeof DeletePreferenceRecordsResponse>;\n\n/** CLI CSV Row for deleting preference records */\nexport const DeletePreferenceRecordCliCsvRow = t.type({\n /** The name of the identifier type (e.g., email, userId) */\n name: t.string,\n /** The value of the identifier */\n value: t.string,\n});\n\n/** Override type */\nexport type DeletePreferenceRecordCliCsvRow = t.TypeOf<typeof DeletePreferenceRecordCliCsvRow>;\n\n/** Preference option value input for sync */\nexport const PreferenceOptionValueInput = t.type({\n /** Title of option value */\n title: t.string,\n /** API slug */\n slug: t.string,\n});\n\n/** Override type */\nexport type PreferenceOptionValueInput = t.TypeOf<typeof PreferenceOptionValueInput>;\n\n/** Preference topic input for sync */\nexport const PreferenceTopicSyncInput = t.intersection([\n t.type({\n /** Purpose slug (trackingType) this topic belongs to */\n 'tracking-type': t.string,\n /** The type of the preference topic */\n type: valuesOf(PreferenceTopicType),\n /** The title of the preference topic */\n title: t.string,\n /** The description of the preference topic */\n description: t.string,\n /** API slug for the preference topic */\n slug: t.string,\n }),\n t.partial({\n /** Default configuration value */\n 'default-configuration': t.string,\n /** Whether the preference topic is shown in the privacy center */\n 'show-in-privacy-center': t.boolean,\n /** The option values when the type is single or multi select */\n options: t.array(PreferenceOptionValueInput),\n }),\n]);\n\n/** Override type */\nexport type PreferenceTopicSyncInput = t.TypeOf<typeof PreferenceTopicSyncInput>;\n\n/** Consent purpose input for sync */\nexport const PurposeInput = t.intersection([\n t.type({\n /** Purpose slug (immutable key used to match existing purposes) */\n trackingType: t.string,\n /** Display name of the purpose */\n name: t.string,\n }),\n t.partial({\n /** Title shown in Consent Management and Privacy Center UIs */\n title: t.string,\n /** Description of the purpose */\n description: t.string,\n /** Whether the purpose is active */\n 'is-active': t.boolean,\n /** Whether the purpose is configurable */\n configurable: t.boolean,\n /** Display order of the purpose in the privacy center */\n 'display-order': t.number,\n /** Whether the purpose is shown in the privacy center */\n 'show-in-privacy-center': t.boolean,\n /** Whether the purpose is shown in the consent manager */\n 'show-in-consent-manager': t.boolean,\n /** Authentication level required for the purpose */\n 'auth-level': valuesOf(PreferenceStoreAuthLevel),\n /** Opt-out signals that instantly opt out of this purpose */\n 'opt-out-signals': t.array(valuesOf(UserPrivacySignalEnum)),\n }),\n]);\n\n/** Override type */\nexport type PurposeInput = t.TypeOf<typeof PurposeInput>;\n","import type { ColumnMetadataMap } from './codecs.js';\n\n/**\n * Extract metadata values from a CSV row based on the column-to-metadata mapping.\n *\n * @param options - Options for extracting metadata\n * @returns Array of metadata key-value pairs for the preference store API\n */\nexport function getPreferenceMetadataFromRow({\n row,\n columnToMetadata,\n}: {\n /** The CSV row as a record of column name to value */\n row: Record<string, string>;\n /** Mapping from CSV column name to metadata key */\n columnToMetadata: ColumnMetadataMap;\n}): Array<{\n /** Metadata key name */ key: string;\n /** Metadata value from the CSV row */\n value: string;\n}> {\n return Object.entries(columnToMetadata)\n .map(([columnName, { key }]) => {\n const value = row[columnName];\n // Skip if no value in the row or empty string\n if (value === undefined || value === '') {\n return null;\n }\n return { key, value };\n })\n .filter(\n (\n x,\n ): x is {\n /** Metadata key name */ key: string;\n /** Metadata value from the CSV row */\n value: string;\n } => x !== null,\n );\n}\n","import type { PreferenceStoreIdentifier } from '@transcend-io/privacy-types';\n\nimport type { FileFormatState } from './codecs.js';\n\n/**\n * Extract preference store identifiers from a CSV row based on the column-to-identifier mapping.\n *\n * @param options - Options\n * @returns Array of identifiers for the preference store API\n */\nexport function getPreferenceIdentifiersFromRow({\n row,\n columnToIdentifier,\n}: {\n /** The current row from CSV file */\n row: Record<string, string>;\n /** The current file metadata state */\n columnToIdentifier: FileFormatState['columnToIdentifier'];\n}): PreferenceStoreIdentifier[] {\n const identifiers = Object.entries(columnToIdentifier)\n .filter(([col]) => !!row[col])\n .map(([col, identifierMapping]) => ({\n name: identifierMapping.name,\n value: row[col]!,\n }));\n return identifiers.sort(\n (a, b) =>\n (a.name === 'email' ? -1 : 0) - (b.name === 'email' ? -1 : 0) ||\n a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }),\n );\n}\n","import type { FileFormatState, IdentifierMetadataForPreference } from './codecs.js';\n\n/**\n * Helper function to get unique identifier name present in a row\n *\n * @param options - Options\n * @param options.row - The current row from CSV file\n * @param options.columnToIdentifier - The column to identifier mapping metadata\n * @returns The unique identifier names present in the row\n */\nexport function getUniquePreferenceIdentifierNamesFromRow({\n row,\n columnToIdentifier,\n}: {\n /** The current row from CSV file */\n row: Record<string, string>;\n /** The current file metadata state */\n columnToIdentifier: FileFormatState['columnToIdentifier'];\n}): (IdentifierMetadataForPreference & {\n /** Column name */\n columnName: string;\n /** Value of the identifier in the row */\n value: string;\n})[] {\n return Object.entries(columnToIdentifier)\n .sort(\n ([, a], [, b]) =>\n (a.name === 'email' ? -1 : 0) - (b.name === 'email' ? -1 : 0) ||\n a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }),\n )\n .filter(([col]) => row[col] && columnToIdentifier[col]!.isUniqueOnPreferenceStore)\n .map(([col, identifier]) => ({\n ...identifier,\n columnName: col,\n value: row[col]!,\n }));\n}\n","import { PreferenceStorePurposeResponse, PreferenceTopicType } from '@transcend-io/privacy-types';\nimport { apply } from '@transcend-io/type-utils';\nimport { splitCsvToList } from '@transcend-io/utils';\n\nimport { PurposeRowMapping } from './codecs.js';\nimport type { PreferenceTopic } from './fetchAllPreferenceTopics.js';\n\n/**\n * Parse an arbitrary object to the Transcend PUT /v1/preference update shape\n * by using a mapping of column names to purpose/preference slugs.\n *\n * `columnToPurposeName` looks like:\n * {\n * 'my_purpose': { purpose: 'Marketing', preference: null, valueMapping: { 'true': true, 'false': false } },\n * 'has_topic_1': { purpose: 'Marketing', preference: 'BooleanPreference1', valueMapping: { 'true': true, 'false': false } },\n * 'has_topic_2': { purpose: 'Marketing', preference: 'SingleSelectPreference', valueMapping: { 'Option 1': 'Value1', 'Option 2': 'Value2' } }\n * }\n *\n * `row` looks like:\n * {\n * 'my_purpose': 'true',\n * 'has_topic_1': 'true',\n * 'has_topic_2': 'Option 1'\n * }\n *\n * OMISSION RULE:\n * - If `valueMapping[row[columnName]]`\n * returns `undefined` or `null`, we **omit** that column entirely (do not set purpose enabled, do not push a preference).\n * - For MultiSelect, **each token** is treated independently: tokens that map to `undefined|null` are skipped;\n * if all tokens are skipped, nothing is pushed.\n * - We still validate **types** for mapped values (e.g., boolean must map to boolean, select must map to string, etc.).\n *\n * NOTE:\n * - Final shape must have `enabled` for every purpose touched (enforced by `apply` below). If you omit all top-level purpose mappings,\n * but emit preferences, this will throw at the end. This preserves the existing “enabled required” contract.\n *\n * @param options - Options\n * @returns The parsed row\n */\nexport function getPreferenceUpdatesFromRow({\n row,\n columnToPurposeName,\n purposeSlugs,\n preferenceTopics,\n}: {\n /** Row to parse */\n row: Record<string, string>;\n /** Mapping from column name to parser config */\n columnToPurposeName: Record<string, PurposeRowMapping>;\n /** The set of allowed purpose slugs */\n purposeSlugs: string[];\n /** The preference topics */\n preferenceTopics: PreferenceTopic[];\n}): {\n [k in string]: Omit<PreferenceStorePurposeResponse, 'purpose'>;\n} {\n // Create a result object to store the parsed preferences\n const result: {\n [k in string]: Partial<PreferenceStorePurposeResponse>;\n } = {};\n\n // Iterate over each column and map to the purpose or preference\n Object.entries(columnToPurposeName).forEach(\n ([columnName, { purpose, preference, valueMapping }]) => {\n // Ensure the purpose is valid\n if (!purposeSlugs.includes(purpose)) {\n throw new Error(`Invalid purpose slug: ${purpose}, expected: ${purposeSlugs.join(', ')}`);\n }\n\n // The raw value from the CSV row for this column\n const rawValue = row[columnName] ?? '';\n\n // Check if parsing a preference or just the top level purpose\n if (preference) {\n const preferenceTopic = preferenceTopics.find(\n (x) => x.slug === preference && x.purpose.trackingType === purpose,\n );\n if (!preferenceTopic) {\n const allowedTopics = preferenceTopics\n .filter((x) => x.purpose.trackingType === purpose)\n .map((x) => x.slug);\n throw new Error(\n `Invalid preference slug: ${preference} for purpose: ${purpose}. ` +\n `Allowed preference slugs for purpose are: ${allowedTopics.join(',')}`,\n );\n }\n\n // Ensure destination array\n if (!result[purpose]) {\n result[purpose] = {\n preferences: [],\n };\n }\n if (!result[purpose].preferences) {\n result[purpose].preferences = [];\n }\n\n // handle each type of preference\n switch (preferenceTopic.type) {\n case PreferenceTopicType.Boolean: {\n const mappedValue = valueMapping[rawValue];\n // Throw error on missing mapping\n if (mappedValue === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for value \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=${preference})`,\n );\n }\n\n // Purposefully missing mapping\n if (mappedValue === null || mappedValue === undefined) {\n return;\n }\n\n // Ensure boolean\n if (typeof mappedValue !== 'boolean') {\n throw new Error(\n `Invalid value for boolean preference: ${preference}, expected boolean, got: ${rawValue}`,\n );\n }\n result[purpose].preferences!.push({\n topic: preference,\n choice: { booleanValue: mappedValue },\n });\n break;\n }\n\n case PreferenceTopicType.Select: {\n const mappedValue = valueMapping[rawValue];\n // Throw error on missing mapping\n if (mappedValue === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for value \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=${preference})`,\n );\n }\n\n // Omit if null\n if (mappedValue === null || mappedValue === undefined) {\n return;\n }\n\n // Ensure string\n if (typeof mappedValue !== 'string') {\n throw new Error(\n `Invalid value for select preference: ${preference}, expected string, got: ${rawValue}`,\n );\n }\n const trimmed = mappedValue.trim() || null;\n\n if (\n trimmed &&\n !preferenceTopic.preferenceOptionValues.map(({ slug }) => slug).includes(trimmed)\n ) {\n throw new Error(\n `Invalid value for select preference: ${preference}, expected one of: ` +\n `${preferenceTopic.preferenceOptionValues\n .map(({ slug }) => slug)\n .join(', ')}, got: ${rawValue}`,\n );\n }\n\n result[purpose].preferences!.push({\n topic: preference,\n choice: { selectValue: trimmed },\n });\n break;\n }\n\n case PreferenceTopicType.MultiSelect: {\n if (typeof rawValue !== 'string') {\n throw new Error(\n `Invalid value for multi select preference: ${preference}, expected string, got: ${rawValue}`,\n );\n }\n\n // IMPORTANT: Do NOT rely on valueMapping[rawValue] for CSV.\n // Split and map per token with the new rule.\n const selectValues = splitCsvToList(rawValue)\n .map((token) => {\n const tokenMapped = valueMapping[token];\n // Throw error on missing mapping\n if (tokenMapped === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for multi select token \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=${preference})`,\n );\n }\n\n // Omit if null\n if (tokenMapped === null || tokenMapped === undefined) {\n return null;\n }\n\n // Ensure string\n if (typeof tokenMapped !== 'string') {\n throw new Error(\n `Invalid value for multi select preference: ${preference}, ` +\n `expected one of: ${preferenceTopic.preferenceOptionValues\n .map(({ slug }) => slug)\n .join(', ')}, got: ${token}`,\n );\n }\n return tokenMapped;\n })\n .filter((x): x is string => x !== null)\n .sort((a, b) => a.localeCompare(b));\n\n // Only push if at least one mapped token survived\n if (selectValues.length > 0) {\n result[purpose].preferences!.push({\n topic: preference,\n choice: { selectValues },\n });\n }\n break;\n }\n\n default:\n throw new Error(`Unknown preference type: ${preferenceTopic.type}`);\n }\n } else {\n // Top-level purpose (no preference)\n const mappedValue = valueMapping[rawValue];\n if (mappedValue === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for value \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=∅) ${JSON.stringify(row)}`,\n );\n }\n if (mappedValue === null) {\n return; // Omit if null\n }\n\n if (!result[purpose]) {\n // Top-level purpose: set enabled strictly from mapped boolean\n result[purpose] = { enabled: mappedValue === true };\n } else {\n // Preserve preferences; update enabled\n result[purpose].enabled = mappedValue === true;\n }\n }\n },\n );\n\n // Ensure that enabled is provided for any purpose that appears.\n // (This preserves the prior contract and existing tests.)\n return apply(result, (x, purposeName) => {\n if (typeof x.enabled !== 'boolean') {\n throw new Error(`No mapping provided for purpose.enabled=true/false value: ${purposeName}`);\n }\n return {\n ...x,\n enabled: x.enabled!,\n };\n });\n}\n","import {\n PreferenceQueryResponseItem,\n PreferenceStorePurposeResponse,\n PreferenceTopicType,\n} from '@transcend-io/privacy-types';\n\nimport type { PreferenceTopic } from './fetchAllPreferenceTopics.js';\n\n/**\n * Check if the pending set of updates are exactly the same as the current consent record.\n *\n * @param options - Options\n * @returns Whether the pending updates already exist in the preference store\n */\nexport function checkIfPendingPreferenceUpdatesAreNoOp({\n currentConsentRecord,\n pendingUpdates,\n preferenceTopics,\n}: {\n /** The current consent record */\n currentConsentRecord: PreferenceQueryResponseItem;\n /** The pending updates */\n pendingUpdates: {\n [purposeName in string]: Omit<PreferenceStorePurposeResponse, 'purpose'>;\n };\n /** The preference topic configurations */\n preferenceTopics: PreferenceTopic[];\n}): boolean {\n // Check each update\n return Object.entries(pendingUpdates).every(([purposeName, { preferences = [], enabled }]) => {\n // Ensure the purpose exists\n const currentPurpose = currentConsentRecord.purposes.find(\n (existingPurpose) => existingPurpose.purpose === purposeName,\n );\n\n // Ensure purpose.enabled is in sync\n // Also false if the purpose does not exist\n const enabledIsInSync = !!currentPurpose && currentPurpose.enabled === enabled;\n if (!enabledIsInSync) {\n return false;\n }\n\n // Compare the preferences are in sync\n return preferences.every(\n ({ topic, choice }) =>\n // ensure preferences exist on record\n currentPurpose.preferences &&\n currentPurpose.preferences.find((existingPreference) => {\n // find matching topic\n if (existingPreference.topic !== topic) {\n return false;\n }\n\n // Determine type of preference topic\n const preferenceTopic = preferenceTopics.find(\n (x) => x.slug === topic && x.purpose.trackingType === purposeName,\n );\n if (!preferenceTopic) {\n throw new Error(`Could not find preference topic for ${topic}`);\n }\n\n // Handle comparison based on type\n switch (preferenceTopic.type) {\n case PreferenceTopicType.Boolean:\n return existingPreference.choice.booleanValue === choice.booleanValue;\n case PreferenceTopicType.Select:\n return existingPreference.choice.selectValue === choice.selectValue;\n case PreferenceTopicType.MultiSelect:\n // eslint-disable-next-line no-case-declarations\n const sortedCurrentValues = (existingPreference.choice.selectValues || []).sort();\n // eslint-disable-next-line no-case-declarations\n const sortedNewValues = (choice.selectValues || []).sort();\n return (\n sortedCurrentValues.length === sortedNewValues.length &&\n sortedCurrentValues.every((x, i) => x === sortedNewValues[i])\n );\n default:\n throw new Error(`Unknown preference topic type: ${preferenceTopic.type}`);\n }\n }),\n );\n });\n}\n","import {\n PreferenceQueryResponseItem,\n PreferenceStorePurposeResponse,\n PreferenceTopicType,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\n\nimport type { PreferenceTopic } from './fetchAllPreferenceTopics.js';\n\n/**\n * Check if the pending set of updates will result in a change of\n * value to an existing purpose or preference in the preference store.\n *\n * @param options - Options\n * @returns True if conflict, false if no conflict and just adding new data for first time\n */\nexport function checkIfPendingPreferenceUpdatesCauseConflict({\n currentConsentRecord,\n pendingUpdates,\n preferenceTopics,\n logger,\n}: {\n /** The current consent record */\n currentConsentRecord: PreferenceQueryResponseItem;\n /** The pending updates */\n pendingUpdates: {\n [purposeName in string]: Omit<PreferenceStorePurposeResponse, 'purpose'>;\n };\n /** The preference topic configurations */\n preferenceTopics: PreferenceTopic[];\n /** Optional logger — when provided, conflicts are logged */\n logger?: Logger;\n}): boolean {\n // Check if any update has conflict\n return !!Object.entries(pendingUpdates).find(([purposeName, { preferences = [], enabled }]) => {\n // Ensure the purpose exists\n const currentPurpose = currentConsentRecord.purposes.find(\n (existingPurpose) => existingPurpose.purpose === purposeName,\n );\n\n // If no purpose exists, then it is not a conflict\n if (!currentPurpose) {\n logger?.warn(\n `No existing purpose found for ${purposeName} in consent record for ${currentConsentRecord.userId}.`,\n );\n return false;\n }\n\n // If purpose.enabled value is off, this is a conflict\n if (currentPurpose.enabled !== enabled) {\n logger?.warn(\n `Purpose ${purposeName} enabled value conflict for user ${currentConsentRecord.userId}. ` +\n `Pending Value: ${enabled}, Current Value: ${currentPurpose.enabled}`,\n );\n return true;\n }\n\n // Check if any preferences are out of sync\n return !!preferences.find(({ topic, choice }) => {\n // find matching topic\n const currentPreference = (currentPurpose.preferences || []).find(\n (existingPreference) => existingPreference.topic === topic,\n );\n\n // if no topic exists, no conflict\n if (!currentPreference) {\n logger?.warn(\n `No existing preference found for topic ${topic} in purpose ` +\n `${purposeName} for user ${currentConsentRecord.userId}.`,\n );\n return false;\n }\n\n // Determine type of preference topic\n const preferenceTopic = preferenceTopics.find(\n (x) => x.slug === topic && x.purpose.trackingType === purposeName,\n );\n if (!preferenceTopic) {\n throw new Error(`Could not find preference topic for ${topic}`);\n }\n\n // Handle comparison based on type\n let boolMatch: boolean;\n let selectMatch: boolean;\n switch (preferenceTopic.type) {\n case PreferenceTopicType.Boolean:\n boolMatch = currentPreference.choice.booleanValue !== choice.booleanValue;\n logger?.warn(\n `Preference topic ${topic} boolean value conflict for user ` +\n `${currentConsentRecord.userId}. Expected: ${choice.booleanValue}, ` +\n `Found: ${currentPreference.choice.booleanValue}`,\n );\n return boolMatch;\n case PreferenceTopicType.Select:\n selectMatch = currentPreference.choice.selectValue !== choice.selectValue;\n logger?.warn(\n `Preference topic ${topic} select value conflict for user ` +\n `${currentConsentRecord.userId}. Expected: ${choice.selectValue}, ` +\n `Found: ${currentPreference.choice.selectValue}`,\n );\n return selectMatch;\n case PreferenceTopicType.MultiSelect:\n // eslint-disable-next-line no-case-declarations\n const sortedCurrentValues = (currentPreference.choice.selectValues || []).sort();\n // eslint-disable-next-line no-case-declarations\n const sortedNewValues = (choice.selectValues || []).sort();\n selectMatch =\n sortedCurrentValues.length !== sortedNewValues.length ||\n !sortedCurrentValues.every((x, i) => x === sortedNewValues[i]);\n logger?.warn(\n `Preference topic ${topic} multi-select value conflict for user ` +\n `${currentConsentRecord.userId}. Expected: ${sortedNewValues.join(\n ', ',\n )}, Found: ${sortedCurrentValues.join(', ')}`,\n );\n return selectMatch;\n default:\n throw new Error(`Unknown preference topic type: ${preferenceTopic.type}`);\n }\n });\n });\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\n\n/**\n * Transforms the output of the consent preferences query into a CSV-friendly format.\n *\n * @param input - The input object containing consent preferences data.\n * @param exportIdentifiersWithDelimiter - Delimiter to use when combining multiple identifier values.\n * @returns A record representing the transformed CSV output.\n */\nexport function transformPreferenceRecordToCsv(\n {\n identifiers = [],\n purposes = [],\n metadata = [],\n consentManagement = {},\n system = {\n decryptionStatus: 'DECRYPTED',\n },\n // keep other top-level fields as-is (e.g., partition, timestamp, metadataTimestamp)\n ...topLevel\n }: PreferenceQueryResponseItem,\n exportIdentifiersWithDelimiter: string,\n): Record<string, unknown> {\n // Start with: all other top-level fields + spread system and consentManagement\n const out: Record<string, unknown> = {\n ...topLevel,\n ...system,\n ...consentManagement,\n };\n\n // ── identifiers: each identifier.name -> CSV of values\n if (Array.isArray(identifiers)) {\n const byName = new Map<string, Set<string>>();\n for (const { name, value } of identifiers) {\n if (!byName.has(name)) byName.set(name, new Set());\n if (value) byName.get(name)!.add(value);\n }\n for (const [name, set] of byName.entries()) {\n out[name] = Array.from(set).join(exportIdentifiersWithDelimiter);\n }\n }\n\n // ── metadata: serialize as JSON\n if (Array.isArray(metadata)) {\n out.metadata = JSON.stringify(\n metadata.reduce(\n (acc, { key, value }) => {\n acc[key] = value;\n return acc;\n },\n {} as Record<string, string>,\n ),\n );\n }\n\n // ── purposes:\n // - purpose.slug column => true/false (enabled)\n // - for each preference: purpose.slug_preference.slug => bool | single | CSV (multi)\n if (Array.isArray(purposes)) {\n for (const { purpose, preferences, enabled } of purposes) {\n out[purpose] = Boolean(enabled);\n\n // nested preferences\n if (Array.isArray(preferences)) {\n for (const { topic, choice } of preferences) {\n const col = `${purpose}_${topic}`;\n\n let val: unknown = null;\n\n if (typeof choice.booleanValue === 'boolean') {\n val = choice.booleanValue;\n } else if (choice.selectValue) {\n val = choice.selectValue;\n } else if (Array.isArray(choice.selectValues)) {\n const vs = choice.selectValues.filter((v) => v.length > 0);\n val = vs.join(',');\n } else {\n // no pref value present -> null\n val = null;\n }\n\n out[col] = val;\n }\n }\n }\n }\n\n return out;\n}\n","import { FIVE_MIN_MS } from '@transcend-io/utils';\n\nimport type { ChunkMode, PreferencesQueryFilter } from './types.js';\n\n/**\n * Build chunk windows by splitting [lower, upperExclusive) into up to `maxChunks`\n * equal-sized ranges, with a minimum chunk span of 5 minutes. Boundaries are snapped\n * once at the start to the nearest 5-minute boundary for stability.\n *\n * Each returned window is already \"half-open\" for an *inclusive* backend:\n * we subtract 1ms from the exclusive end so adjacent chunks do not overlap.\n *\n * Example (timestamp mode): [10:00, 12:00) → { after=10:00:00.000Z, before=11:59:59.999Z }\n *\n * @param mode - 'timestamp' or 'updated'\n * @param lower - Lower bound (inclusive)\n * @param upperExclusive - Upper bound (exclusive)\n * @param maxChunks - Maximum number of chunks to create\n * @returns Array of chunked preference query filters\n */\nexport function buildConsentChunks(\n mode: ChunkMode,\n lower: Date,\n upperExclusive: Date,\n maxChunks = 5000,\n): Array<PreferencesQueryFilter> {\n const totalMs = Math.max(0, upperExclusive.getTime() - lower.getTime());\n if (totalMs === 0) return [];\n\n // Snap only the starting boundary to the nearest 5-minute boundary.\n // We avoid re-snapping every step to prevent cumulative drift.\n const seriesStart = new Date(Math.floor(lower.getTime() / FIVE_MIN_MS) * FIVE_MIN_MS);\n\n // Compute base chunk size (ceil to ensure ≤ maxChunks), enforced ≥ 5m.\n const rawChunkMs = Math.ceil(totalMs / Math.max(1, maxChunks));\n const chunkMs = Math.max(FIVE_MIN_MS, rawChunkMs);\n\n // Number of chunks needed to cover [seriesStart, upperExclusive)\n const count = Math.ceil((upperExclusive.getTime() - seriesStart.getTime()) / chunkMs);\n\n const chunks: PreferencesQueryFilter[] = [];\n\n for (let i = 0; i < count; i += 1) {\n const startMs = seriesStart.getTime() + i * chunkMs;\n const endExclusiveMs = Math.min(upperExclusive.getTime(), startMs + chunkMs);\n\n // Convert exclusive end to inclusive end for an inclusive backend: -1ms.\n const endInclusiveMs = endExclusiveMs - 1;\n\n // Guard: in degenerate cases (shouldn’t happen with the math above), clamp.\n const safeEndMs = Math.max(startMs, endInclusiveMs);\n\n const afterIso = new Date(startMs).toISOString();\n const beforeIso = new Date(safeEndMs).toISOString();\n\n if (mode === 'timestamp') {\n chunks.push({\n timestampAfter: afterIso,\n timestampBefore: beforeIso,\n });\n } else {\n chunks.push({\n system: {\n updatedAfter: afterIso,\n updatedBefore: beforeIso,\n },\n });\n }\n }\n\n return chunks;\n}\n","import type { ChunkMode, PreferencesQueryFilter } from './types.js';\n\n/**\n * Decide which dimension to chunk on: 'timestamp' if timestamps provided, otherwise 'updated'\n *\n * @param filterBy - Filter to examine\n * @returns Chosen chunk mode\n */\nexport function pickConsentChunkMode(filterBy: PreferencesQueryFilter): ChunkMode {\n const hasTimestamp = !!filterBy.timestampAfter || !!filterBy.timestampBefore;\n return hasTimestamp ? 'timestamp' : 'updated';\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\n\nimport type { ChunkMode } from './types.js';\n\n/**\n * Get the comparison instant for sorting based on the chosen dimension.\n *\n * @param mode - Chunking mode\n * @param item - Preference item\n * @returns date\n */\nexport function getComparisonTimeForRecord(\n mode: ChunkMode,\n item: PreferenceQueryResponseItem,\n): Date {\n if (mode === 'timestamp') {\n return new Date(item.timestamp);\n }\n // mode === 'updated'\n return item.system?.updatedAt ? new Date(item.system.updatedAt) : new Date();\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse, type PreferencesQueryFilter } from './types.js';\n\n/**\n * Async generator over pages for a given filter\n *\n * @param sombra - Sombra Got instance\n * @param partition - Partition key\n * @param filter - Query filter\n * @param pageSize - Number of items per page\n * @param logger - Logger for retries\n * @yields Pages of PreferenceQueryResponseItem\n */\nexport async function* iterateConsentPages(\n sombra: Got,\n partition: string,\n filter: PreferencesQueryFilter,\n pageSize: number,\n logger: Logger,\n): AsyncGenerator<PreferenceQueryResponseItem[], void, void> {\n let cursor: string | undefined;\n\n while (true) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: any = { limit: pageSize };\n if (filter && Object.keys(filter).length) body.filter = filter;\n if (cursor) body.cursor = cursor;\n\n const resp = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partition}/query`, {\n json: body,\n })\n .json(),\n {\n logger,\n onRetry: (attempt, _error, message) => {\n logger.warn(`Retry attempt ${attempt} for iterateConsentPages due to error: ${message}`);\n },\n },\n );\n\n const { nodes, cursor: nextCursor } = decodeCodec(ConsentPreferenceResponse, resp);\n if (!nodes?.length) break;\n\n yield nodes;\n\n if (!nextCursor) break;\n cursor = nextCursor;\n }\n}\n","import { decodeCodec } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse, type PreferencesQueryFilter, type ChunkMode } from './types.js';\n\n/**\n * Probe window: does it contain any records? Uses the given mode.\n *\n * @param sombra - Sombra\n * @param options - Options\n * @returns True if any records exist in the given window\n */\nexport async function consentWindowHasAny(\n sombra: Got,\n {\n partition,\n mode,\n baseFilter,\n afterISO,\n beforeISO,\n logger = NOOP_LOGGER,\n }: {\n /** Partition */\n partition: string;\n /** Chunking mode */\n mode: ChunkMode;\n /** Base filter */\n baseFilter: PreferencesQueryFilter;\n /** After ISO date */\n afterISO: string;\n /** Before ISO date */\n beforeISO: string;\n logger?: Logger;\n },\n): Promise<boolean> {\n const filter: PreferencesQueryFilter =\n mode === 'timestamp'\n ? {\n ...baseFilter,\n timestampAfter: afterISO,\n timestampBefore: beforeISO,\n system: baseFilter.system,\n }\n : {\n ...baseFilter,\n timestampAfter: undefined,\n timestampBefore: undefined,\n system: {\n ...baseFilter.system,\n updatedAfter: afterISO,\n updatedBefore: beforeISO,\n },\n };\n const resp = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partition}/query`, {\n json: { limit: 1, filter },\n })\n .json(),\n {\n logger,\n onRetry: (attempt, error, message) => {\n logger.warn(`Retry attempt ${attempt} for consentWindowHasAny due to error: ${message}`);\n },\n },\n );\n\n const { nodes } = decodeCodec(ConsentPreferenceResponse, resp);\n return Array.isArray(nodes) && nodes.length > 0;\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { startOfUtcDay, DAY_MS, type Logger } from '@transcend-io/utils';\n/* eslint-disable max-lines */\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { getComparisonTimeForRecord } from './getComparisonTimeForRecord.js';\nimport { iterateConsentPages } from './iterateConsentPages.js';\nimport { pickConsentChunkMode } from './pickConsentChunkMode.js';\nimport { type ChunkMode, type PreferencesQueryFilter } from './types.js';\n\n/**\n * Get after/before bounds from filter for the given mode\n *\n * @param mode - Chunking mode\n * @param filterBy - Filter to examine\n * @returns after/before dates\n */\nexport function getBoundsFromConsentFilter(\n mode: ChunkMode,\n filterBy: PreferencesQueryFilter,\n): {\n /** After date */\n after?: Date;\n /** Before date */\n before?: Date;\n} {\n if (mode === 'timestamp') {\n return {\n after: filterBy.timestampAfter ? new Date(filterBy.timestampAfter) : undefined,\n before: filterBy.timestampBefore ? new Date(filterBy.timestampBefore) : undefined,\n };\n }\n const u = filterBy.system ?? {};\n return {\n after: u.updatedAfter ? new Date(u.updatedAfter) : undefined,\n before: u.updatedBefore ? new Date(u.updatedBefore) : undefined,\n };\n}\n\n/**\n * Merge base filter with a \"before\" bound (without mixing dimensions).\n *\n * @param mode - Chunking mode\n * @param base - Base filter to augment\n * @param beforeISO - ISO timestamp to apply as the exclusive *Before bound for the chosen dimension\n * @returns New filter with the appropriate *Before constraint applied\n */\nfunction withBeforeBound(\n mode: ChunkMode,\n base: PreferencesQueryFilter,\n beforeISO?: string,\n): PreferencesQueryFilter {\n if (mode === 'timestamp') {\n return {\n ...base,\n timestampBefore: beforeISO ?? base.timestampBefore,\n };\n }\n return {\n ...base,\n system: {\n ...base.system,\n ...(beforeISO ? { updatedBefore: beforeISO } : {}),\n },\n // ensure we don't mix dimensions\n timestampAfter: undefined,\n timestampBefore: undefined,\n };\n}\n\n/**\n * Fetch a single record (or null) with the given filter.\n *\n * @param sombra - Got instance configured for Sombra API\n * @param partition - Preference Store partition id\n * @param filter - Query filter to use (page size internally forced to 1)\n * @param logger - Logger\n * @returns The first record or null if none\n */\nasync function fetchOne(\n sombra: Got,\n partition: string,\n filter: PreferencesQueryFilter,\n logger: Logger,\n): Promise<PreferenceQueryResponseItem | null> {\n logger.info(`Single-record probe with filter: ${JSON.stringify(filter)}`);\n const it = iterateConsentPages(sombra, partition, filter, /* pageSize */ 1, logger);\n const res = await it.next();\n if (res.done || !res.value || res.value.length === 0) {\n logger.info('Probe result: no record');\n return null;\n }\n const item = res.value[0]!;\n logger.info(\n `Probe result: found record at ${getComparisonTimeForRecord(\n pickConsentChunkMode(filter),\n item,\n ).toISOString()}`,\n );\n return item;\n}\n\n/**\n * Robust earliest-day search (UTC):\n * 1) Anchor at the newest record (single-record probe).\n * 2) Exponential “jump back” using seeds (1d, 7d, 30d) then doubling (60d, 120d, 240d, …)\n * to cross into an empty region and establish a lower empty bound.\n * 3) **Exponential forward-from-empty**: gallop forward from the empty bound toward the last-found\n * to land close to the frontier quickly.\n * 4) Tighten with a short binary search on time using single-record probes.\n *\n * (Implementation note: preserves the public signature and docs while improving efficiency.)\n *\n * @param sombra - Sombra\n * @param opts - Options\n * @returns Earliest day with data (UTC start-of-day)\n */\nexport async function findEarliestDayWithData(\n sombra: Got,\n opts: {\n /** Partition */\n partition: string;\n /** Chunking mode */\n mode: ChunkMode;\n /** Base filter */\n baseFilter: PreferencesQueryFilter;\n /** Optional safety cap in days to avoid unbounded lookback (default ~10 years) */\n maxLookbackDays?: number;\n /** Logger */\n logger?: Logger;\n },\n): Promise<Date> {\n const { partition, mode, baseFilter, maxLookbackDays = 3650, logger = NOOP_LOGGER } = opts;\n\n // 1) Find newest record (anchors our backtracking).\n const newest = await fetchOne(sombra, partition, withBeforeBound(mode, baseFilter), logger);\n if (!newest) {\n logger.info('No records found; defaulting earliest day to today.');\n return startOfUtcDay(new Date());\n }\n const newestInstant = getComparisonTimeForRecord(mode, newest);\n logger.info(`Newest instant: ${newestInstant.toISOString()}`);\n\n // 2) Exponential jump back to find an empty region.\n const seedSteps = [1, 7, 30] as const; // days\n let stepDaysIdx = 0;\n let stepMs = seedSteps[0]! * DAY_MS;\n\n let lastFoundInstant = newestInstant; // last instant we *could* find a record before\n let emptyBeforeInstant: Date | null = null; // first bound that yielded no results\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const probeBound =\n stepDaysIdx < seedSteps.length\n ? new Date(newestInstant.getTime() - seedSteps[stepDaysIdx]! * DAY_MS)\n : new Date(newestInstant.getTime() - stepMs);\n\n // stop if we exceeded lookback cap\n const daysSince =\n (startOfUtcDay(new Date()).getTime() - startOfUtcDay(probeBound).getTime()) / DAY_MS;\n if (daysSince > maxLookbackDays) {\n logger.warn(\n `Exponential jump exceeded maxLookbackDays=${maxLookbackDays}. Using current bounds.`,\n );\n emptyBeforeInstant = probeBound;\n break;\n }\n\n logger.info(\n `Probing before=${probeBound.toISOString()} (jump step ${\n stepDaysIdx < seedSteps.length\n ? `${seedSteps[stepDaysIdx]!}d`\n : `${Math.round(stepMs / DAY_MS)}d`\n })…`,\n );\n\n const hit = await fetchOne(\n sombra,\n partition,\n withBeforeBound(mode, baseFilter, probeBound.toISOString()),\n logger,\n );\n\n if (hit) {\n lastFoundInstant = getComparisonTimeForRecord(mode, hit);\n logger.info(\n `Found older record at ${lastFoundInstant.toISOString()} — continue jumping back.`,\n );\n // advance step\n if (stepDaysIdx < seedSteps.length - 1) {\n stepDaysIdx += 1;\n stepMs = seedSteps[stepDaysIdx]! * DAY_MS;\n } else if (stepDaysIdx === seedSteps.length - 1) {\n stepDaysIdx += 1; // switch to doubling mode\n stepMs = seedSteps[seedSteps.length - 1]! * 2 * DAY_MS; // start at 60d\n } else {\n stepMs *= 2;\n }\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // crossed into an empty zone — remember this bound\n emptyBeforeInstant = probeBound;\n logger.info(`No record before ${probeBound.toISOString()} — established empty lower bound.`);\n break;\n }\n\n // Guard: if for some reason empty bound wasn't set, synthesize one “just before” lastFound.\n if (!emptyBeforeInstant) {\n emptyBeforeInstant = new Date(lastFoundInstant.getTime() - DAY_MS);\n }\n\n // 3) Exponential forward-from-empty toward the found frontier.\n // This “gallop” reduces the span dramatically before binary search.\n // We keep moving the empty bound forward with exponentially growing steps\n // until we get a hit; then we shrink onto that hit instant.\n let lo = emptyBeforeInstant; // known EMPTY (no data before this bound)\n let hi = lastFoundInstant; // known FOUND (there is data before this instant)\n let fwdStep = Math.max(DAY_MS, Math.floor((hi.getTime() - lo.getTime()) / 64)); // start small-ish\n logger.info(\n `Exponential forward-from-empty start: empty=${lo.toISOString()} found=${hi.toISOString()} step=${Math.round(\n fwdStep / DAY_MS,\n )}d`,\n );\n\n // Do a few gallop iterations (bounded so we don't loop forever if distribution is dense)\n for (let i = 0; i < 8; i += 1) {\n const probe = new Date(lo.getTime() + fwdStep);\n if (probe.getTime() >= hi.getTime()) break;\n\n logger.info(`Forward gallop probe before=${probe.toISOString()}…`);\n const hit = await fetchOne(\n sombra,\n partition,\n withBeforeBound(mode, baseFilter, probe.toISOString()),\n logger,\n );\n\n if (hit) {\n // We crossed into data — tighten hi to the actual hit instant.\n hi = getComparisonTimeForRecord(mode, hit);\n logger.info(`Gallop hit at ${hi.toISOString()} — tightening found bound. Next step halves.`);\n fwdStep = Math.max(DAY_MS, Math.floor(fwdStep / 2));\n } else {\n // Still empty up to probe — advance lo and double the step.\n lo.setTime(probe.getTime());\n logger.info(`Gallop miss — advancing empty bound to ${lo.toISOString()}. Next step doubles.`);\n fwdStep = Math.min(hi.getTime() - lo.getTime(), fwdStep * 2);\n if (fwdStep < DAY_MS) fwdStep = DAY_MS;\n }\n\n if (hi.getTime() - lo.getTime() <= DAY_MS) break;\n }\n\n // 4) Finish with a short binary search between [lo (empty), hi (found)].\n while (hi.getTime() - lo.getTime() > DAY_MS) {\n const mid = new Date(lo.getTime() + Math.floor((hi.getTime() - lo.getTime()) / 2));\n logger.info(`Binary probe before=${mid.toISOString()}…`);\n\n const hit = await fetchOne(\n sombra,\n partition,\n withBeforeBound(mode, baseFilter, mid.toISOString()),\n logger,\n );\n\n if (hit) {\n const when = getComparisonTimeForRecord(mode, hit);\n logger.info(`Binary probe found record at ${when.toISOString()}.`);\n hi = when; // there is data before mid -> earliest could be even earlier\n } else {\n logger.info('Binary probe found no record.');\n lo = mid; // still empty -> move low up\n }\n }\n\n const earliestDay = startOfUtcDay(hi);\n logger.info(\n `Earliest day (UTC) resolved to ${earliestDay.toISOString()} (instant ≈ ${hi.toISOString()}).`,\n );\n return earliestDay;\n}\n\n/**\n * Find latest day with data using exponential growth forward from earliest (UTC day math).\n *\n * (Implementation note: per your request, we now fetch a single newest record to infer the latest day.)\n *\n * @param sombra - Sombra\n * @param opts - Options\n * @returns Latest day with data\n */\nexport async function findLatestDayWithData(\n sombra: Got,\n opts: {\n /** Partition */\n partition: string;\n /** Chunking mode */\n mode: ChunkMode;\n /** Base filter */\n baseFilter: PreferencesQueryFilter;\n /** Earliest date */\n earliest: Date; // inclusive day start\n /** Logger */\n logger?: Logger;\n },\n): Promise<Date> {\n const { partition, mode, baseFilter, logger = NOOP_LOGGER } = opts;\n\n logger.info('Latest-day discovery: probing newest record…');\n const latest = await fetchOne(sombra, partition, withBeforeBound(mode, baseFilter), logger);\n if (!latest) {\n logger.info('No records found at all; defaulting latest day to today.');\n return startOfUtcDay(new Date());\n }\n\n const when = getComparisonTimeForRecord(mode, latest);\n logger.info(`Newest record instant is ${when.toISOString()}.`);\n\n const latestDay = startOfUtcDay(when);\n logger.info(\n `Latest day (UTC) resolved to ${latestDay.toISOString()} from instant ${when.toISOString()}.`,\n );\n\n return latestDay;\n}\n/* eslint-enable max-lines */\n","import { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse, type PreferencesQueryFilter } from './types.js';\n\n/**\n * Fetch consent preferences for the managed consent database (new query endpoint)\n *\n * Uses POST /v1/preferences/{partition}/query with cursor pagination.\n *\n * If `onItems` is provided, this streams pages to the callback and does not\n * accumulate results in memory. If omitted, the function returns all items.\n *\n * @param sombra - Sombra instance (must include auth headers)\n * @param options - Query options\n * @returns All nodes (only when onItems is not provided)\n */\nexport async function fetchConsentPreferences(\n sombra: Got,\n {\n partition,\n filterBy = {},\n limit = 50,\n onItems,\n logger = NOOP_LOGGER,\n }: {\n /** Partition key to fetch (moved to URL path on new endpoint) */\n partition: string;\n /** Query filter (wrapped under \"filter\" in request body) */\n filterBy?: PreferencesQueryFilter;\n /** Number of users per page (1–50 per API spec) */\n limit?: number;\n /** Optional streaming sink; if provided, pages are not accumulated */\n onItems?: (items: PreferenceQueryResponseItem[]) => Promise<void> | void;\n logger?: Logger;\n },\n): Promise<PreferenceQueryResponseItem[]> {\n const collected: PreferenceQueryResponseItem[] = [];\n\n // Cursor-based pagination per new endpoint\n let cursor: string | undefined;\n\n // Build the filter payload, omitting empty filter\n const hasFilter =\n filterBy &&\n (Object.keys(filterBy).length > 0 ||\n (filterBy.system && Object.keys(filterBy.system).length > 0));\n\n // Enforce API max (defensive; backend also validates)\n const pageSize = Math.max(1, Math.min(50, limit ?? 50));\n\n // Keep fetching until no cursor is returned\n // (The API returns an opaque cursor string for the next page)\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const body: {\n /** Filter by user identifiers (new shape) */\n filter?: PreferencesQueryFilter;\n /** Cursor for pagination */\n cursor?: string;\n /** Number of records per page */\n limit: number;\n } = { limit: pageSize };\n\n if (hasFilter) {\n body.filter = filterBy;\n }\n if (cursor) {\n body.cursor = cursor;\n }\n\n const response = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partition}/query`, {\n json: body,\n })\n .json(),\n {\n logger,\n onRetry: (attempt, _error, message) => {\n logger.warn(\n `Retry attempt ${attempt} for fetchConsentPreferences due to error: ${message}`,\n );\n },\n },\n );\n\n const { nodes, cursor: nextCursor } = decodeCodec(ConsentPreferenceResponse, response);\n\n if (!nodes || nodes.length === 0) {\n break;\n }\n\n if (onItems) {\n await onItems(nodes);\n } else {\n collected.push(...nodes);\n }\n\n if (!nextCursor) {\n break;\n }\n cursor = nextCursor;\n }\n\n return onItems ? [] : collected;\n}\n","import { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { map, type Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\nimport { chunk } from 'lodash-es';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse } from './types.js';\n\n/**\n * Grab the current consent preference values for a list of identifiers\n *\n * @param sombra - Backend to make API call to\n * @param options - Options\n * @returns Plaintext context information\n */\nexport async function getPreferencesForIdentifiers(\n sombra: Got,\n {\n identifiers,\n partitionKey,\n skipLogging = false,\n concurrency = 40,\n logger = NOOP_LOGGER,\n onProgress,\n }: {\n /** The list of identifiers to look up */\n identifiers: {\n /** The value of the identifier */\n value: string;\n }[];\n /** The partition key to look up */\n partitionKey: string;\n /** Whether to skip logging */\n skipLogging?: boolean;\n /** Concurrency for requests (default 40) */\n concurrency?: number;\n /** Logger */\n logger?: Logger;\n /** Optional progress callback (completed count, total identifiers) */\n onProgress?: (completed: number, total: number) => void;\n },\n): Promise<PreferenceQueryResponseItem[]> {\n const results: PreferenceQueryResponseItem[] = [];\n const groupedIdentifiers = chunk(identifiers, 100);\n\n const t0 = new Date().getTime();\n\n let total = 0;\n await map(\n groupedIdentifiers,\n async (group) => {\n const rawResult = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partitionKey}/query`, {\n json: {\n filter: { identifiers: group },\n limit: group.length,\n },\n })\n .json(),\n {\n logger,\n onRetry: (attempt, _err, msg) => {\n logger.warn(\n `[RETRY] group size=${group.length} partition=${partitionKey} attempt=${attempt}: ${msg}`,\n );\n },\n },\n );\n\n const result = decodeCodec(ConsentPreferenceResponse, rawResult);\n results.push(...result.nodes);\n total += group.length;\n onProgress?.(total, identifiers.length);\n },\n {\n concurrency,\n },\n );\n\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n if (!skipLogging) {\n logger.info(`Completed download in \"${totalTime / 1000}\" seconds.`);\n }\n\n return results;\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { addDaysUtc, clampPageSize, map as pmap, type Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { buildConsentChunks } from './buildConsentChunks.js';\nimport {\n findEarliestDayWithData,\n findLatestDayWithData,\n getBoundsFromConsentFilter,\n} from './discoverConsentWindow.js';\nimport { iterateConsentPages } from './iterateConsentPages.js';\nimport { pickConsentChunkMode } from './pickConsentChunkMode.js';\nimport { type PreferencesQueryFilter, type ChunkMode } from './types.js';\n\n/**\n * Merge baseFilter with a window filter, taking care not to mix timestamp/updated fields improperly.\n *\n * @param mode - The chunking mode\n * @param base - The base filter\n * @param window - The per-chunk window filter\n * @returns merged filter\n */\nfunction mergeFilter(\n mode: ChunkMode,\n base: PreferencesQueryFilter,\n window: PreferencesQueryFilter,\n): PreferencesQueryFilter {\n if (mode === 'timestamp') {\n return {\n ...base,\n timestampAfter: window.timestampAfter ?? base.timestampAfter,\n timestampBefore: window.timestampBefore ?? base.timestampBefore,\n // ensure we don't pass `system.*` when chunking by timestamp\n system: undefined,\n };\n }\n // mode === 'updated'\n return {\n ...base,\n system: {\n ...base.system,\n ...(window.system?.updatedAfter ? { updatedAfter: window.system.updatedAfter } : {}),\n ...(window.system?.updatedBefore ? { updatedBefore: window.system.updatedBefore } : {}),\n },\n // Ensure we don't mix dimensions\n timestampAfter: undefined,\n timestampBefore: undefined,\n };\n}\n\n/**\n * High-level chunked fetch with optional progress callback.\n *\n * If an `onItems` callback is provided, pages are streamed to the callback\n * as they are fetched (no accumulation in memory). If no callback is provided,\n * the function returns all items (legacy behavior).\n *\n * @param sombra - Got instance\n * @param options - Options\n * @returns preference items (only if onItems is not provided)\n */\nexport async function fetchConsentPreferencesChunked(\n sombra: Got,\n {\n partition,\n filterBy = {},\n limit = 50,\n windowConcurrency = 25,\n maxChunks = 5000,\n maxLookbackDays = 3650,\n onItems,\n logger = NOOP_LOGGER,\n onProgress,\n }: {\n /** Partition */\n partition: string;\n /** Filter by preferences */\n filterBy?: PreferencesQueryFilter;\n /** Limit number of results (page size) */\n limit?: number;\n /** Window concurrency */\n windowConcurrency?: number;\n /** Max chunks */\n maxChunks?: number; // up to N chunks; min 1 hour per chunk\n /** Max lookback days for discovering bounds */\n maxLookbackDays?: number;\n /** Optional streaming sink; if provided, items are not accumulated */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onItems?: (items: PreferenceQueryResponseItem[]) => Promise<any> | any;\n /** Logger */\n logger?: Logger;\n /** Optional progress: completed chunks, total chunks, records fetched so far */\n onProgress?: (completed: number, total: number, fetched: number) => void;\n },\n): Promise<PreferenceQueryResponseItem[]> {\n const mode: ChunkMode = pickConsentChunkMode(filterBy);\n logger.info(\n `Fetching consent preferences in chunks by ${\n mode === 'timestamp' ? 'timestamp' : 'system.updatedAt'\n }...`,\n );\n\n // Resolve / discover bounds (UTC)\n let { after, before } = getBoundsFromConsentFilter(mode, filterBy);\n logger.info(\n `Initial bounds: after=${after?.toISOString() ?? 'undefined'} before=${\n before?.toISOString() ?? 'undefined'\n }`,\n );\n\n if (!after || !before) {\n if (!after) {\n logger.info(`Discovering earliest day with data for partition ${partition}...`);\n after = await findEarliestDayWithData(sombra, {\n partition,\n mode,\n baseFilter: filterBy,\n maxLookbackDays,\n logger,\n });\n logger.info(`Discovered earliest day with data: ${after.toISOString()}`);\n }\n if (!before) {\n logger.info(`Discovering latest day with data for partition ${partition}...`);\n const latestDay = await findLatestDayWithData(sombra, {\n partition,\n mode,\n baseFilter: filterBy,\n earliest: after,\n logger,\n });\n // Exclusive upper bound = latest day start + 1 day (UTC)\n before = addDaysUtc(latestDay, 1);\n logger.info(`Discovered latest day with data: ${latestDay.toISOString()}`);\n }\n }\n\n logger.info(`Final bounds (UTC): after=${after.toISOString()} before=${before.toISOString()}`);\n\n // Build up to `maxChunks` chunks, min 1 hour each\n const chunks = buildConsentChunks(mode, after, before, maxChunks);\n\n logger.info(\n `Fetching consent preferences from partition ${partition} in ${chunks.length} chunks...`,\n );\n\n let completed = 0; // finished chunks (out-of-order)\n let fetched = 0; // raw records counter\n\n const t0 = Date.now();\n const pageSize = clampPageSize(limit);\n\n // If we are streaming, do not accumulate everything in memory.\n const out: PreferenceQueryResponseItem[] = [];\n\n await pmap(\n chunks.map((windowFilter, idx) => ({ windowFilter, idx })),\n async ({ windowFilter }) => {\n const filter = mergeFilter(mode, filterBy, windowFilter);\n\n // Stream this chunk page-by-page\n for await (const page of iterateConsentPages(sombra, partition, filter, pageSize, logger)) {\n fetched += page.length;\n onProgress?.(completed, chunks.length, fetched);\n\n if (onItems) {\n await onItems(page);\n } else {\n out.push(...page);\n }\n }\n\n completed += 1;\n onProgress?.(completed, chunks.length, fetched);\n },\n { concurrency: Math.max(1, windowConcurrency) },\n );\n\n onProgress?.(completed, chunks.length, fetched);\n\n logger.info(\n `Fetched ${fetched} consent preference records from partition ${partition} in ${\n (Date.now() - t0) / 1000\n }s.`,\n );\n\n return onItems ? [] : out;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { fetchAllIdentifiers, type Identifier } from '../data-inventory/fetchAllIdentifiers.js';\nimport { fetchAllPreferenceTopics, type PreferenceTopic } from './fetchAllPreferenceTopics.js';\nimport { fetchAllPurposes, type Purpose } from './fetchAllPurposes.js';\n\nexport interface PreferenceUploadReferenceData {\n /** List of purposes in the organization */\n purposes: Purpose[];\n /** List of preference topics in the organization */\n preferenceTopics: PreferenceTopic[];\n /** List of identifiers in the organization */\n identifiers: Identifier[];\n}\n\n/**\n * Load all required reference data for an upload run.\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Reference data arrays\n */\nexport async function loadReferenceData(\n client: GraphQLClient,\n { logger }: { logger: Logger },\n): Promise<PreferenceUploadReferenceData> {\n const [purposes, preferenceTopics, identifiers] = await Promise.all([\n fetchAllPurposes(client, { logger }),\n fetchAllPreferenceTopics(client, { logger }),\n fetchAllIdentifiers(client, { logger }),\n ]);\n return { purposes, preferenceTopics, identifiers };\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const PREFERENCE_OPTION_VALUES: DocumentNode = parse(gql`\n query TranscendCliPreferenceOptionValues($first: Int!, $offset: Int!) {\n preferenceOptionValues(first: $first, offset: $offset) {\n nodes {\n id\n title {\n id\n defaultMessage\n }\n slug\n }\n }\n }\n`);\n\nexport const CREATE_OR_UPDATE_PREFERENCE_OPTION_VALUES: DocumentNode = parse(gql`\n mutation TranscendCliCreateOrUpdatePreferenceOptionValues(\n $input: CreateOrUpdatePreferenceOptionValuesInput!\n ) {\n createOrUpdatePreferenceOptionValues(input: $input) {\n preferenceOptionValues {\n id\n slug\n }\n }\n }\n`);\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PREFERENCE_OPTION_VALUES } from './gqls/preferenceOptionValues.js';\n\nexport interface PreferenceOptionValue {\n /** ID of preference option value */\n id: string;\n /** Slug of preference option value */\n slug: string;\n /** Title of preference option value */\n title: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all preference option values in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All preference option values in the organization\n */\nexport async function fetchAllPreferenceOptionValues(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PreferenceOptionValue[]> {\n const { logger } = options;\n const preferenceOptionValues: PreferenceOptionValue[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n preferenceOptionValues: { nodes },\n } = await makeGraphQLRequest<{\n /** Preference option values */\n preferenceOptionValues: {\n /** List */\n nodes: PreferenceOptionValue[];\n };\n }>(client, PREFERENCE_OPTION_VALUES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n preferenceOptionValues.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return preferenceOptionValues.sort((a, b) => a.slug.localeCompare(b.slug));\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { type PreferenceOptionValueInput } from './codecs.js';\nimport {\n fetchAllPreferenceOptionValues,\n type PreferenceOptionValue,\n} from './fetchAllPreferenceOptionValues.js';\nimport { CREATE_OR_UPDATE_PREFERENCE_OPTION_VALUES } from './gqls/preferenceOptionValues.js';\n\nconst MAX_BATCH_SIZE = 50;\n\n/**\n * Create or update preference option values\n *\n * @param client - GraphQL client\n * @param optionValues - Preference option values paired with existing IDs\n * @param options - Options\n * @returns Created/updated preference option values\n */\nexport async function createOrUpdatePreferenceOptionValues(\n client: GraphQLClient,\n optionValues: [PreferenceOptionValueInput, string | undefined][],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PreferenceOptionValue[]> {\n const { logger } = options;\n const results: PreferenceOptionValue[] = [];\n\n await mapSeries(chunk(optionValues, MAX_BATCH_SIZE), async (batch) => {\n const result = await makeGraphQLRequest<{\n /** createOrUpdatePreferenceOptionValues mutation */\n createOrUpdatePreferenceOptionValues: {\n /** Preference option values */\n preferenceOptionValues: PreferenceOptionValue[];\n };\n }>(client, CREATE_OR_UPDATE_PREFERENCE_OPTION_VALUES, {\n variables: {\n input: {\n preferenceOptionValues: batch.map(([optionValue, id]) =>\n id\n ? { id, title: optionValue.title }\n : { slug: optionValue.slug, title: optionValue.title },\n ),\n },\n },\n logger,\n });\n results.push(...result.createOrUpdatePreferenceOptionValues.preferenceOptionValues);\n });\n\n return results;\n}\n\n/**\n * Sync the preference option values\n *\n * @param client - GraphQL client\n * @param optionValues - Preference option values\n * @param options - Options\n * @returns True if run without error\n */\nexport async function syncPreferenceOptionValues(\n client: GraphQLClient,\n optionValues: PreferenceOptionValueInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger } = options;\n logger?.info(`Syncing \"${optionValues.length}\" preference option values...`);\n\n const existing = await fetchAllPreferenceOptionValues(client, { logger });\n const optionValueBySlug = keyBy(existing, 'slug');\n\n const changedOptionValues = optionValues.filter((optionValueInput) => {\n const found = optionValueBySlug[optionValueInput.slug];\n return !found || found.title.defaultMessage !== optionValueInput.title;\n });\n\n if (changedOptionValues.length === 0) {\n logger?.info(`Skipping \"${optionValues.length}\" unchanged preference option values`);\n return true;\n }\n\n try {\n await createOrUpdatePreferenceOptionValues(\n client,\n changedOptionValues.map((optionValueInput) => [\n optionValueInput,\n optionValueBySlug[optionValueInput.slug]?.id,\n ]),\n { logger },\n );\n logger?.info(\n `Successfully synced \"${changedOptionValues.length}\" preference option values (${optionValues.length - changedOptionValues.length} unchanged skipped)!`,\n );\n return true;\n } catch (err) {\n logger?.error(`Failed to sync preference option values! - ${(err as Error).message}`);\n return false;\n }\n}\n","import { KnownDefaultPurpose, type UserPrivacySignalEnum } from '@transcend-io/airgap.js-types';\nimport { type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { type PurposeInput } from './codecs.js';\nimport { fetchAllPurposes, type Purpose } from './fetchAllPurposes.js';\nimport { CREATE_PURPOSE, UPDATE_PURPOSE } from './gqls/purpose.js';\n\nexport type { PurposeInput } from './codecs.js';\n\nexport interface SyncPurposesResult {\n /** Whether every purpose synced without error */\n success: boolean;\n /** Map from purpose trackingType to its ID (used to sync nested preference topics) */\n purposeIdByTrackingType: Record<string, string>;\n}\n\nconst DEFAULT_PURPOSE_TRACKING_TYPES: ReadonlySet<string> = new Set(\n Object.values(KnownDefaultPurpose),\n);\n\n/**\n * Whether a purpose tracking type is a built-in default purpose.\n *\n * @param trackingType - Purpose tracking type slug\n * @returns True when the purpose is a built-in default\n */\nfunction isDefaultPurposeTrackingType(trackingType: string): boolean {\n return DEFAULT_PURPOSE_TRACKING_TYPES.has(trackingType);\n}\n\n/**\n * Compare opt-out signal lists regardless of order.\n *\n * @param left - First signal list\n * @param right - Second signal list\n * @returns True when both lists contain the same signals\n */\nfunction optOutSignalsMatch(\n left: UserPrivacySignalEnum[] | undefined,\n right: UserPrivacySignalEnum[],\n): boolean {\n const normalizedLeft = [...(left ?? [])].sort();\n const normalizedRight = [...right].sort();\n return (\n normalizedLeft.length === normalizedRight.length &&\n normalizedLeft.every((signal, index) => signal === normalizedRight[index])\n );\n}\n\n/**\n * Build updatePurpose input with only changed fields.\n * Built-in default purposes cannot sync `configurable` or `showInConsentManager`.\n *\n * @param found - Existing purpose from the API\n * @param input - Purpose input from YAML\n * @param options - Options\n * @returns Mutation input and any warnings for omitted default-purpose fields\n */\nfunction buildUpdatePurposeInput(\n found: Purpose,\n input: PurposeInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): { input: Record<string, unknown>; isEmpty: boolean } {\n const { logger } = options;\n const isDefault = isDefaultPurposeTrackingType(input.trackingType);\n const updateInput: Record<string, unknown> = { id: found.id };\n\n if (input.name !== found.name) {\n updateInput.name = input.name;\n }\n if (input.title !== found.title) {\n updateInput.title = input.title;\n }\n if (input.description !== found.description) {\n updateInput.description = input.description;\n }\n if (input['is-active'] !== found.isActive) {\n updateInput.isActive = input['is-active'];\n }\n if (input['display-order'] !== found.displayOrder) {\n updateInput.displayOrder = input['display-order'];\n }\n if (input['show-in-privacy-center'] !== found.showInPrivacyCenter) {\n updateInput.showInPrivacyCenter = input['show-in-privacy-center'];\n }\n if (input['auth-level'] !== found.authLevel) {\n updateInput.authLevel = input['auth-level'];\n }\n if (!optOutSignalsMatch(input['opt-out-signals'], found.optOutSignals)) {\n updateInput.optOutSignals = input['opt-out-signals'];\n }\n\n if (isDefault) {\n if (input.configurable !== found.configurable) {\n logger?.warn(\n `Cannot sync \"configurable\" for built-in purpose \"${input.trackingType}\" via API — ` +\n 'update this field in the Admin Dashboard instead.',\n );\n }\n if (input['show-in-consent-manager'] !== found.showInConsentManager) {\n logger?.warn(\n `Cannot sync \"show-in-consent-manager\" for built-in purpose \"${input.trackingType}\" via API — ` +\n 'update this field in the Admin Dashboard instead.',\n );\n }\n } else {\n if (input.configurable !== found.configurable) {\n updateInput.configurable = input.configurable;\n }\n if (input['show-in-consent-manager'] !== found.showInConsentManager) {\n updateInput.showInConsentManager = input['show-in-consent-manager'];\n }\n }\n\n const isEmpty = Object.keys(updateInput).length === 1;\n return { input: updateInput, isEmpty };\n}\n\n/**\n * Sync tracking purposes to Transcend, matching existing purposes by trackingType.\n * Creates purposes that do not yet exist and updates the rest.\n *\n * @param client - GraphQL client\n * @param purposes - Purpose inputs from YAML\n * @param options - Options\n * @returns Whether the sync succeeded and a trackingType -> purpose ID map\n */\nexport async function syncPurposes(\n client: GraphQLClient,\n purposes: PurposeInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SyncPurposesResult> {\n const { logger } = options;\n logger?.info(`Syncing \"${purposes.length}\" purposes...`);\n\n const existing = await fetchAllPurposes(client, { logger });\n const existingByTrackingType = keyBy(existing, 'trackingType');\n const purposeIdByTrackingType: Record<string, string> = Object.fromEntries(\n existing.map(({ trackingType, id }) => [trackingType, id]),\n );\n\n let success = true;\n for (const purpose of purposes) {\n const found = existingByTrackingType[purpose.trackingType];\n try {\n if (found) {\n const { input: updateInput, isEmpty } = buildUpdatePurposeInput(found, purpose, {\n logger,\n });\n if (isEmpty) {\n logger?.info(\n `Skipping purpose \"${purpose.trackingType}\" — only blocked fields differ for built-in purpose`,\n );\n purposeIdByTrackingType[purpose.trackingType] = found.id;\n continue;\n }\n\n await makeGraphQLRequest(client, UPDATE_PURPOSE, {\n variables: { input: updateInput },\n logger,\n });\n purposeIdByTrackingType[purpose.trackingType] = found.id;\n } else {\n const {\n createPurpose: { purpose: created },\n } = await makeGraphQLRequest<{\n /** createPurpose mutation */\n createPurpose: {\n /** Created purpose */\n purpose: {\n /** ID */\n id: string;\n /** Slug */\n trackingType: string;\n };\n };\n }>(client, CREATE_PURPOSE, {\n variables: {\n input: {\n name: purpose.name,\n description: purpose.description ?? '',\n trackingType: purpose.trackingType,\n isActive: purpose['is-active'],\n configurable: purpose.configurable,\n displayOrder: purpose['display-order'],\n showInPrivacyCenter: purpose['show-in-privacy-center'],\n showInConsentManager: purpose['show-in-consent-manager'],\n authLevel: purpose['auth-level'],\n optOutSignals: purpose['opt-out-signals'],\n },\n },\n logger,\n });\n purposeIdByTrackingType[purpose.trackingType] = created.id;\n\n // `title` can only be set via updatePurpose (not createPurpose)\n if (purpose.title !== undefined) {\n await makeGraphQLRequest(client, UPDATE_PURPOSE, {\n variables: { input: { id: created.id, title: purpose.title } },\n logger,\n });\n }\n }\n } catch (err) {\n success = false;\n logger?.error(\n `Failed to sync purpose \"${purpose.trackingType}\"! - ${(err as Error).message}`,\n );\n }\n }\n\n if (success) {\n logger?.info(`Successfully synced \"${purposes.length}\" purposes!`);\n }\n return { success, purposeIdByTrackingType };\n}\n","import { PreferenceTopicType } from '@transcend-io/privacy-types';\nimport { type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy, uniqBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { type PreferenceTopicSyncInput } from './codecs.js';\nimport { fetchAllPreferenceOptionValues } from './fetchAllPreferenceOptionValues.js';\nimport { fetchAllPreferenceTopics } from './fetchAllPreferenceTopics.js';\nimport { fetchAllPurposes } from './fetchAllPurposes.js';\nimport { CREATE_OR_UPDATE_PREFERENCE_TOPIC } from './gqls/preferenceTopic.js';\nimport { createOrUpdatePreferenceOptionValues } from './syncPreferenceOptionValues.js';\n\nexport type { PreferenceTopicSyncInput } from './codecs.js';\n\n/**\n * Build GraphQL input for creating a preference topic.\n *\n * @param topic - Topic input from YAML\n * @param purposeId - Resolved purpose ID\n * @param preferenceOptionValueIds - Linked option value IDs (omit for BOOLEAN)\n * @returns Mutation input with only the fields allowed on create\n */\nfunction buildCreatePreferenceTopicInput(\n topic: PreferenceTopicSyncInput,\n purposeId: string,\n preferenceOptionValueIds: string[],\n): Record<string, unknown> {\n const input: Record<string, unknown> = {\n title: topic.title,\n slug: topic.slug,\n type: topic.type,\n displayDescription: topic.description,\n purposeId,\n };\n if (topic['show-in-privacy-center'] !== undefined) {\n input.showInPrivacyCenter = topic['show-in-privacy-center'];\n }\n if (topic['default-configuration'] !== undefined) {\n input.defaultConfiguration = topic['default-configuration'];\n }\n if (topic.type !== PreferenceTopicType.Boolean && preferenceOptionValueIds.length > 0) {\n input.preferenceOptionValueIds = preferenceOptionValueIds;\n }\n return input;\n}\n\n/**\n * Build GraphQL input for updating an existing preference topic.\n * Slug, type, and purposeId must be omitted — the backend rejects them on update.\n *\n * @param existingId - ID of the existing topic\n * @param topic - Topic input from YAML\n * @param preferenceOptionValueIds - Linked option value IDs (omit for BOOLEAN)\n * @returns Mutation input with only the fields allowed on update\n */\nfunction buildUpdatePreferenceTopicInput(\n existingId: string,\n topic: PreferenceTopicSyncInput,\n preferenceOptionValueIds: string[],\n): Record<string, unknown> {\n const input: Record<string, unknown> = {\n id: existingId,\n title: topic.title,\n displayDescription: topic.description,\n };\n if (topic['show-in-privacy-center'] !== undefined) {\n input.showInPrivacyCenter = topic['show-in-privacy-center'];\n }\n if (topic['default-configuration'] !== undefined) {\n input.defaultConfiguration = topic['default-configuration'];\n }\n if (topic.type !== PreferenceTopicType.Boolean && preferenceOptionValueIds.length > 0) {\n input.preferenceOptionValueIds = preferenceOptionValueIds;\n }\n return input;\n}\n\n/**\n * Sync preference topics to Transcend, matching existing topics by (trackingType, slug).\n * Inline option values are upserted (by slug) so their IDs can be linked to the topic.\n *\n * @param client - GraphQL client\n * @param topics - Preference topic inputs, each tagged with its purpose trackingType\n * @param options - Options\n * @returns True if every topic synced without error\n */\nexport async function syncPreferenceTopics(\n client: GraphQLClient,\n topics: PreferenceTopicSyncInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Pre-resolved trackingType -> purpose ID map (avoids re-fetching purposes) */\n purposeIdByTrackingType?: Record<string, string>;\n } = {},\n): Promise<boolean> {\n const { logger } = options;\n logger?.info(`Syncing \"${topics.length}\" preference topics...`);\n\n // Resolve purpose IDs (reuse the map from syncPurposes when provided)\n let { purposeIdByTrackingType } = options;\n if (!purposeIdByTrackingType) {\n const purposes = await fetchAllPurposes(client, { logger });\n purposeIdByTrackingType = Object.fromEntries(\n purposes.map(({ trackingType, id }) => [trackingType, id]),\n );\n }\n\n // Upsert inline option values so their IDs can be linked to topics\n const existingOptionValues = await fetchAllPreferenceOptionValues(client, { logger });\n const optionIdBySlug: Record<string, string> = Object.fromEntries(\n existingOptionValues.map(({ slug, id }) => [slug, id]),\n );\n const inlineOptions = uniqBy(\n topics\n .filter((topic) => topic.type !== PreferenceTopicType.Boolean)\n .flatMap((topic) => topic.options ?? []),\n 'slug',\n ).filter((optionValue) => {\n const found = existingOptionValues.find(({ slug }) => slug === optionValue.slug);\n return !found || found.title.defaultMessage !== optionValue.title;\n });\n if (inlineOptions.length > 0) {\n try {\n const upserted = await createOrUpdatePreferenceOptionValues(\n client,\n inlineOptions.map((optionValue) => [optionValue, optionIdBySlug[optionValue.slug]]),\n { logger },\n );\n upserted.forEach(({ slug, id }) => {\n optionIdBySlug[slug] = id;\n });\n } catch (err) {\n logger?.error(`Failed to sync inline preference option values! - ${(err as Error).message}`);\n return false;\n }\n }\n\n // Existing topics for matching updates\n const existingTopics = await fetchAllPreferenceTopics(client, { logger });\n const topicBySlugKey = keyBy(\n existingTopics,\n ({ purpose, slug }) => `${purpose.trackingType}:${slug}`,\n );\n\n let success = true;\n for (const topic of topics) {\n const trackingType = topic['tracking-type'];\n const purposeId = purposeIdByTrackingType[trackingType];\n if (!purposeId) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - unknown purpose \"${trackingType}\"`,\n );\n continue;\n }\n\n if (topic.type === PreferenceTopicType.Boolean && (topic.options ?? []).length > 0) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - BOOLEAN preference topics cannot have options in YAML (the backend auto-creates True/False options)`,\n );\n continue;\n }\n\n const existing = topicBySlugKey[`${trackingType}:${topic.slug}`];\n\n if (existing && existing.type !== topic.type) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - Cannot change preference topic type from \"${existing.type}\" to \"${topic.type}\" for an existing topic`,\n );\n continue;\n }\n\n const preferenceOptionValueIds =\n topic.type === PreferenceTopicType.Boolean\n ? []\n : (topic.options ?? [])\n .map((optionValue) => optionIdBySlug[optionValue.slug])\n .filter((id): id is string => Boolean(id));\n\n try {\n const input = existing\n ? buildUpdatePreferenceTopicInput(existing.id, topic, preferenceOptionValueIds)\n : buildCreatePreferenceTopicInput(topic, purposeId, preferenceOptionValueIds);\n\n await makeGraphQLRequest(client, CREATE_OR_UPDATE_PREFERENCE_TOPIC, {\n variables: { input },\n logger,\n });\n } catch (err) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - ${(err as Error).message}`,\n );\n }\n }\n\n if (success) {\n logger?.info(`Successfully synced \"${topics.length}\" preference topics!`);\n }\n return success;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const ATTRIBUTES = gql`\n query TranscendCliAttributes($first: Int!, $offset: Int!) {\n attributeKeys(first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n isCustom\n description\n enabledOn\n name\n type\n }\n }\n }\n`;\n\nexport const CREATE_ATTRIBUTE_VALUES = gql`\n mutation TranscendCliCreateAttributeValues($input: [CreateAttributeValuesInput!]!) {\n createAttributeValues(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_ATTRIBUTE_VALUES = gql`\n mutation TranscendCliUpdateAttributeValues($input: [UpdateAttributeValueInput!]!) {\n updateAttributeValues(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const DELETE_ATTRIBUTE_VALUE = gql`\n mutation TranscendCliDeleteAttributeValue($id: ID!) {\n deleteAttributeValue(id: $id) {\n clientMutationId\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const ATTRIBUTE_VALUES = gql`\n query TranscendCliAttributeValues($first: Int!, $offset: Int!, $attributeKeyId: ID!) {\n attributeValues(\n first: $first\n offset: $offset\n useMaster: false\n filterBy: { attributeKeys: [$attributeKeyId] }\n ) {\n nodes {\n id\n name\n description\n color\n }\n }\n }\n`;\n\nexport const CREATE_ATTRIBUTE = gql`\n mutation TranscendCliCreateAttribute(\n $name: String!\n $type: AttributeKeyType!\n $description: String\n $enabledOn: [AttributeSupportedResourceType!]\n ) {\n createAttributeKey(\n input: { name: $name, type: $type, description: $description, enabledOn: $enabledOn }\n ) {\n clientMutationId\n attributeKey {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_ATTRIBUTE = gql`\n mutation TranscendCliCreateAttribute(\n $attributeKeyId: ID!\n $description: String\n $enabledOn: [AttributeSupportedResourceType!]\n ) {\n updateAttributeKey(\n input: { id: $attributeKeyId, description: $description, enabledOn: $enabledOn }\n ) {\n clientMutationId\n attributeKey {\n id\n }\n }\n }\n`;\n\nexport const SET_RESOURCE_ATTRIBUTES = gql`\n mutation TranscendCliSetResourceAttributes($input: SetResourceAttributesInput!) {\n setResourceAttributes(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { AttributeKeyType, AttributeSupportedResourceType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ATTRIBUTES, ATTRIBUTE_VALUES } from './gqls/attribute.js';\n\nexport interface AttributeValue {\n /** Attribute ID */\n id: string;\n /** Attribute name */\n name: string;\n /** Attribute description */\n description: string;\n /** Color of attribute value */\n color: string;\n}\n\nexport interface Attribute {\n /** ID of attribute */\n id: string;\n /** Name of attribute */\n name: string;\n /** if custom attribute */\n isCustom: boolean;\n /** Description */\n description: string;\n /** Type of attribute */\n type: AttributeKeyType;\n /** Values */\n values: AttributeValue[];\n /** The different fields that the attribute is enabled on */\n enabledOn: AttributeSupportedResourceType[];\n}\n\nconst PAGE_SIZE = 100;\n\n/**\n * Fetch all attribute values for an attribute key\n *\n * @param client - GraphQL client\n * @param attributeKeyId - Attribute keyID\n * @returns A map from apiKey title to Identifier\n */\nexport async function fetchAllAttributeValues(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** Attribute key ID */\n attributeKeyId: string;\n };\n },\n): Promise<AttributeValue[]> {\n const {\n logger,\n filterBy: { attributeKeyId },\n } = options;\n const attributeValues: AttributeValue[] = [];\n let offset = 0;\n\n // Paginate\n let shouldContinue = false;\n do {\n const {\n attributeValues: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n attributeValues: {\n /** List of matches */\n nodes: AttributeValue[];\n };\n }>(client, ATTRIBUTE_VALUES, {\n variables: { first: PAGE_SIZE, offset, attributeKeyId },\n logger,\n });\n attributeValues.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return attributeValues.sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport const SYNC_ATTRIBUTE_TYPES = [AttributeKeyType.MultiSelect, AttributeKeyType.SingleSelect];\n\n/**\n * Fetch all attributes in an organization\n *\n * @param client - GraphQL client\n * @returns A map from apiKey title to Identifier\n */\nexport async function fetchAllAttributes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Attribute[]> {\n const { logger } = options;\n const attributes: Attribute[] = [];\n let offset = 0;\n\n // Paginate\n let shouldContinue = false;\n do {\n const {\n attributeKeys: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n attributeKeys: {\n /** List of matches */\n nodes: Attribute[];\n };\n }>(client, ATTRIBUTES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n attributes.push(\n ...(await Promise.all(\n nodes.map(async (node) => ({\n ...node,\n values: SYNC_ATTRIBUTE_TYPES.includes(node.type)\n ? await fetchAllAttributeValues(client, {\n logger,\n filterBy: { attributeKeyId: node.id },\n })\n : [],\n })),\n )),\n );\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return attributes.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const MESSAGES = gql`\n query TranscendCliFetchMessage {\n translatedMessages {\n id\n defaultMessage\n description\n targetReactIntlId\n translations {\n locale\n value\n }\n }\n }\n`;\n\nexport const UPDATE_INTL_MESSAGES = gql`\n mutation TranscendCliUpdateIntlMessages($messages: [MessageInput!]!) {\n updateIntlMessages(input: { messages: $messages, skipPublish: true }) {\n clientMutationId\n }\n }\n`;\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { MESSAGES } from './gqls/message.js';\n\nexport interface Message {\n /** ID of message */\n id: string;\n /** Default message */\n defaultMessage: string;\n /** Description */\n description: string;\n /** React Intl ID */\n targetReactIntlId: string | null;\n /** Disabled locales */\n translations: {\n /** Locale */\n locale: LocaleValue;\n /** Value */\n value: string;\n }[];\n}\n\n/**\n * Fetch all messages in the organization\n *\n * @param client - GraphQL client\n * @returns All messages in the organization\n */\nexport async function fetchAllMessages(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Message[]> {\n const { logger } = options;\n const { translatedMessages } = await makeGraphQLRequest<{\n /** Messages */\n translatedMessages: Message[];\n }>(client, MESSAGES, { logger });\n return translatedMessages;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const TEAMS = gql`\n query TranscendCliTeams($first: Int!, $offset: Int!, $input: TeamFiltersInput) {\n teams(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n ssoDepartment\n ssoGroup\n ssoTitle\n users {\n id\n email\n name\n }\n scopes {\n id\n name\n title\n }\n parentTeam {\n id\n name\n }\n }\n }\n }\n`;\n\nexport const PARENT_ORGANIZATION_TEAMS = gql`\n query TranscendCliParentOrganizationTeams(\n $first: Int!\n $offset: Int!\n $filterBy: ParentTeamFiltersInput\n ) {\n parentOrganizationTeams(\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n }\n totalCount\n }\n }\n`;\n\nexport const CREATE_TEAM = gql`\n mutation TranscendCliCreateTeam($input: TeamInput!) {\n createTeam(input: $input) {\n team {\n id\n name\n }\n }\n }\n`;\n\nexport const UPDATE_TEAM = gql`\n mutation TranscendCliUpdateTeam($input: UpdateTeamInput!) {\n updateTeam(input: $input) {\n team {\n id\n name\n }\n }\n }\n`;\n","import { ScopeName } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { TEAMS } from './gqls/team.js';\n\nexport interface Team {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n /** Description of team */\n description: string;\n /** SSO department for automated provisioning */\n ssoDepartment?: string;\n /** SSO group name for automated provisioning */\n ssoGroup?: string;\n /** SSO title mapping for automated provisioning */\n ssoTitle?: string;\n /** List of users on the team */\n users: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n /** Name of user */\n name: string;\n }[];\n /** List of scopes on the team */\n scopes: {\n /** ID of scope */\n id: string;\n /** Name of scope */\n name: ScopeName;\n /** Title of scope */\n title: string;\n }[];\n /** Linked parent organization team */\n parentTeam?: {\n /** ID of parent organization team */\n id: string;\n /** Name of parent organization team */\n name: string;\n };\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all teams in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All teams in the organization\n */\nexport async function fetchAllTeams(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Team[]> {\n const { logger } = options;\n const teams: Team[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n teams: { nodes },\n } = await makeGraphQLRequest<{\n /** Teams */\n teams: {\n /** List */\n nodes: Team[];\n };\n }>(client, TEAMS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n teams.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return teams.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PARENT_ORGANIZATION_TEAMS } from './gqls/team.js';\n\nexport interface ParentOrganizationTeam {\n /** ID of parent organization team */\n id: string;\n /** Display name of parent organization team */\n name: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch teams from the parent organization that can be linked to child-org teams\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Parent organization teams\n */\nexport async function fetchParentOrganizationTeams(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Partial match on team name or description */\n text?: string;\n /** Filter by team IDs */\n ids?: string[];\n };\n } = {},\n): Promise<ParentOrganizationTeam[]> {\n const { logger, filterBy } = options;\n const teams: ParentOrganizationTeam[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n parentOrganizationTeams: { nodes },\n } = await makeGraphQLRequest<{\n /** Parent organization teams */\n parentOrganizationTeams: {\n /** List */\n nodes: ParentOrganizationTeam[];\n };\n }>(client, PARENT_ORGANIZATION_TEAMS, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n teams.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return teams.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Resolve parent organization team names to IDs\n *\n * @param client - GraphQL client\n * @param parentTeamNames - Parent organization team names to resolve\n * @param options - Options\n * @returns Map from parent team name to ID\n */\nexport async function resolveParentTeamIdsByName(\n client: GraphQLClient,\n parentTeamNames: string[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<{ [name in string]: string }> {\n const { logger } = options;\n const uniqueNames = [...new Set(parentTeamNames)];\n const parentTeamIdsByName: { [name in string]: string } = {};\n\n for (const name of uniqueNames) {\n const teams = await fetchParentOrganizationTeams(client, {\n logger,\n filterBy: { text: name },\n });\n const match = teams.find((team) => team.name === name);\n if (!match) {\n throw new Error(`Failed to find parent organization team with name: ${name}`);\n }\n parentTeamIdsByName[name] = match.id;\n }\n\n return parentTeamIdsByName;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const USERS = gql`\n query TranscendCliUsers($first: Int!, $offset: Int!, $input: UserFiltersInput) {\n users(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n email\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { USERS } from './gqls/user.js';\n\nexport interface User {\n /** ID of user */\n id: string;\n /** Name of user */\n name: string;\n /** Email of user */\n email: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all users in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All users in the organization\n */\nexport async function fetchAllUsers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<User[]> {\n const { logger } = options;\n const users: User[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n users: { nodes },\n } = await makeGraphQLRequest<{\n /** Users */\n users: {\n /** List */\n nodes: User[];\n };\n }>(client, USERS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n users.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return users.sort((a, b) => a.email.localeCompare(b.email));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const API_KEYS = gql`\n query TranscendCliApiKeys($first: Int!, $offset: Int!, $titles: [String!]) {\n apiKeys(\n first: $first\n offset: $offset\n filterBy: { titles: $titles }\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n ) {\n nodes {\n id\n title\n }\n }\n }\n`;\n\nexport const CREATE_API_KEY = gql`\n mutation TranscendCliCreateApiKey($input: ApiKeyInput!) {\n createApiKey(input: $input) {\n apiKey {\n id\n apiKey\n title\n }\n }\n }\n`;\n\nexport const DELETE_API_KEY = gql`\n mutation TranscendCliDeleteApiKey($id: ID!) {\n deleteApiKey(id: $id) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, uniq, difference } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { API_KEYS } from './gqls/apiKey.js';\n\nexport interface ApiKey {\n /** ID of API key */\n id: string;\n /** Title of API key */\n title: string;\n}\n\nexport interface FetchApiKeysInput {\n /** API key definitions */\n 'api-keys'?: {\n /** Title of API key */\n title: string;\n }[];\n /** Data silo definitions that may reference API keys */\n 'data-silos'?: {\n /** Title of the API key to use */\n 'api-key-title'?: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\nconst ADMIN_LINK = 'https://app.transcend.io/infrastructure/api-keys';\n\n/**\n * Fetch all API keys in an organization\n *\n * @param client - Client\n * @param options - Options\n * @returns API keys\n */\nexport async function fetchAllApiKeys(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter on titles */\n titles?: string[];\n };\n } = {},\n): Promise<ApiKey[]> {\n const { logger = NOOP_LOGGER, filterBy: { titles } = {} } = options;\n const apiKeys: ApiKey[] = [];\n let offset = 0;\n\n // Paginate\n let shouldContinue = false;\n do {\n const {\n apiKeys: { nodes },\n } = await makeGraphQLRequest<{\n /** API keys */\n apiKeys: {\n /** List */\n nodes: ApiKey[];\n };\n }>(client, API_KEYS, {\n variables: { first: PAGE_SIZE, offset, titles },\n logger,\n });\n apiKeys.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n return apiKeys.sort((a, b) => a.title.localeCompare(b.title));\n}\n\n/**\n * Fetch all apiKeys and if any are found in the config that are\n * missing, create those apiKeys.\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns A map from apiKey title to Identifier\n */\nexport async function fetchApiKeys(\n client: GraphQLClient,\n options: {\n /** API key input configuration */\n apiKeyInputs?: FetchApiKeysInput;\n /** When true, fetch all API keys */\n fetchAll?: boolean;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<{ [k in string]: ApiKey }> {\n const {\n apiKeyInputs: { 'api-keys': apiKeyDefs = [], 'data-silos': dataSilos = [] } = {},\n fetchAll = false,\n logger = NOOP_LOGGER,\n } = options;\n logger.info(`Fetching ${fetchAll ? 'all' : apiKeyDefs.length} API keys...`);\n const titles = apiKeyDefs.map(({ title }) => title);\n const expectedApiKeyTitles = uniq(\n dataSilos.map((silo) => silo['api-key-title']).filter((x): x is string => !!x),\n );\n const allTitlesExpected = [...expectedApiKeyTitles, ...titles];\n const apiKeys = await fetchAllApiKeys(client, {\n logger,\n filterBy: {\n titles: fetchAll ? undefined : [...expectedApiKeyTitles, ...titles],\n },\n });\n\n // Create a map\n const apiKeysByTitle = keyBy(apiKeys, 'title');\n\n // Determine expected set of apiKeys expected\n const missingApiKeys = difference(\n allTitlesExpected,\n apiKeys.map(({ title }) => title),\n );\n\n // If there are missing apiKeys, throw an error\n if (missingApiKeys.length > 0) {\n logger.error(\n `Failed to find API keys \"${missingApiKeys.join(\n '\", \"',\n )}\"! Make sure these API keys are created at: ${ADMIN_LINK}`,\n );\n process.exit(1);\n }\n return apiKeysByTitle;\n}\n","import { IsoCountrySubdivisionCode, IsoCountryCode } from '@transcend-io/privacy-types';\n\nexport interface RegionInput {\n /** The country */\n country?: IsoCountryCode;\n /** The country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n}\n\n// Country subdivision is nullable in DB\nexport interface Region {\n /** Country */\n country: IsoCountryCode;\n /** Sub division (may be null in DB) */\n countrySubDivision?: IsoCountrySubdivisionCode | null;\n}\n\n/**\n * Format regions list to remove null country subdivisions\n *\n * @param vals - Regions\n * @returns formatted regions\n */\nexport function formatRegions(vals: Region[]): RegionInput[] {\n return vals.map(({ country, countrySubDivision }) => ({\n country,\n ...(countrySubDivision ? { countrySubDivision } : {}),\n }));\n}\n","import { gql } from 'graphql-request';\n\nexport const DETERMINE_LOGIN_METHOD = gql`\n mutation TranscendCliDetermineLoginMethod($email: String!) {\n determineLoginMethod(input: { email: $email }) {\n loginMethod {\n email\n sombraPublicKey\n }\n }\n }\n`;\n\nexport const LOGIN = gql`\n mutation TranscendCliLogin($email: String!, $password: String!, $publicKey: String!) {\n login(input: { email: $email, password: $password }, publicKey: $publicKey) {\n user {\n roles {\n id\n organization {\n name\n id\n uri\n parentOrganizationId\n }\n }\n }\n }\n }\n`;\n\nexport const ASSUME_ROLE = gql`\n mutation TranscendCliAssumeRole($id: ID!, $publicKey: String!) {\n assumeRole(id: $id, publicKey: $publicKey) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DETERMINE_LOGIN_METHOD, ASSUME_ROLE, LOGIN } from './gqls/auth.js';\n\nexport interface OrganizationPreview {\n /** Name of organization */\n name: string;\n /** Id of organization */\n id: string;\n /** uri of organization */\n uri: string;\n /** ID of parent organization */\n parentOrganizationId?: string;\n}\n\nexport interface UserRole {\n /** ID of role */\n id: string;\n /** Related organization */\n organization: OrganizationPreview;\n}\n\n/**\n * Log in as a user\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Cookie and roles\n */\nexport async function loginUser(\n client: GraphQLClient,\n options: {\n /** Email of user */\n email: string;\n /** Password of user */\n password: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<{\n /** Cookie to be used to make subsequent requests */\n loginCookie: string;\n /** Roles of the user */\n roles: UserRole[];\n}> {\n const { email, password, logger } = options;\n\n const {\n determineLoginMethod: { loginMethod },\n } = await makeGraphQLRequest<{\n /** Determine login method */\n determineLoginMethod: {\n /** Login method info */\n loginMethod: {\n /** Email being logged in */\n email: string;\n /** Sombra public key */\n sombraPublicKey: string;\n };\n };\n }>(client, DETERMINE_LOGIN_METHOD, {\n variables: { email },\n logger,\n });\n\n const res = await client.rawRequest<{\n /** Login */\n login: {\n /** User */\n user: {\n /** Roles of user */\n roles: UserRole[];\n };\n };\n }>(LOGIN, {\n email,\n password,\n publicKey: loginMethod.sombraPublicKey,\n });\n const {\n login: { user },\n } = res.data;\n\n // Get login cookie from response\n const loginCookie = res.headers.get('set-cookie');\n if (!loginCookie || !loginCookie.includes('laravel')) {\n throw new Error('Failed to get login cookie in response');\n }\n\n return {\n roles: user.roles,\n loginCookie,\n };\n}\n\n/**\n * Assume role for user into another organization\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function assumeRole(\n client: GraphQLClient,\n options: {\n /** Email of user */\n email: string;\n /** Role of user assuming into */\n roleId: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { email, roleId, logger } = options;\n\n const {\n determineLoginMethod: { loginMethod },\n } = await makeGraphQLRequest<{\n /** Determine login method */\n determineLoginMethod: {\n /** Login method info */\n loginMethod: {\n /** Email being logged in */\n email: string;\n /** Sombra public key */\n sombraPublicKey: string;\n };\n };\n }>(client, DETERMINE_LOGIN_METHOD, {\n variables: { email, userId: roleId },\n logger,\n });\n\n await client.rawRequest<{\n /** Assume role */\n assumeRole: {\n /** Mutation ID */\n clientMutationId: string;\n };\n }>(ASSUME_ROLE, {\n id: roleId,\n publicKey: loginMethod.sombraPublicKey,\n });\n}\n","import { ScopeName } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CREATE_API_KEY, DELETE_API_KEY } from './gqls/apiKey.js';\n\nexport interface CreatedApiKey {\n /** ID of API key */\n id: string;\n /** Actual API key */\n apiKey: string;\n /** Title of the API key */\n title: string;\n}\n\n/**\n * Create an API key\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns The API key\n */\nexport async function createApiKey(\n client: GraphQLClient,\n options: {\n /** Input for creating an API key */\n input: {\n /** Title of API key */\n title: string;\n /** Scopes for API key */\n scopes: ScopeName[];\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CreatedApiKey> {\n const {\n input: { title, scopes },\n logger,\n } = options;\n const {\n createApiKey: { apiKey },\n } = await makeGraphQLRequest<{\n /** Create API key */\n createApiKey: {\n /** API key */\n apiKey: CreatedApiKey;\n };\n }>(client, CREATE_API_KEY, { variables: { input: { title, scopes } }, logger });\n\n return apiKey;\n}\n\n/**\n * Delete an API key\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function deleteApiKey(\n client: GraphQLClient,\n options: {\n /** API key ID to delete */\n id: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { id, logger } = options;\n await makeGraphQLRequest(client, DELETE_API_KEY, { variables: { id }, logger });\n}\n","import { AttributeSupportedResourceType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { SET_RESOURCE_ATTRIBUTES } from './gqls/attribute.js';\n\ninterface SetResourceAttributesInput {\n /** ID of resource */\n resourceId: string;\n /** Type of resource */\n resourceType: AttributeSupportedResourceType;\n /** Attribute key ID */\n attributeKeyId: string;\n /** Attribute values by ID */\n attributeValueIds?: string[];\n /** Attribute values by name */\n attributeValueNames?: string[];\n}\n\n/**\n * Set attribute values on a particular resource\n *\n * @param client - GraphQL client\n * @param input - Input\n */\nexport async function setResourceAttributes(\n client: GraphQLClient,\n input: SetResourceAttributesInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger } = options;\n await makeGraphQLRequest(client, SET_RESOURCE_ATTRIBUTES, {\n variables: { input },\n logger,\n });\n}\n","import { AttributeKeyType, AttributeSupportedResourceType } from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, difference, groupBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { type Attribute } from './fetchAllAttributes.js';\nimport {\n CREATE_ATTRIBUTE,\n CREATE_ATTRIBUTE_VALUES,\n DELETE_ATTRIBUTE_VALUE,\n UPDATE_ATTRIBUTE,\n UPDATE_ATTRIBUTE_VALUES,\n} from './gqls/attribute.js';\n\nexport interface AttributeValueInput {\n /** Name of attribute value */\n name: string;\n /** Description */\n description?: string;\n /** Color */\n color?: string;\n}\n\nexport interface AttributeInput {\n /** Name of attribute */\n name: string;\n /** Type of attribute */\n type: AttributeKeyType;\n /** Description of attribute */\n description?: string;\n /** Resource types that the attribute is enabled on */\n resources?: AttributeSupportedResourceType[];\n /** Values of attribute */\n values?: AttributeValueInput[];\n}\n\n/**\n * Sync attribute\n *\n * @param client - GraphQL client\n * @param attribute - The attribute input\n * @param options - Options\n */\nexport async function syncAttribute(\n client: GraphQLClient,\n attribute: AttributeInput,\n {\n existingAttribute,\n deleteExtraAttributeValues,\n logger = NOOP_LOGGER,\n }: {\n /** The existing attribute configuration if it exists */\n existingAttribute?: Attribute;\n /** When true, delete extra attributes not specified in the list of values */\n deleteExtraAttributeValues?: boolean;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n // attribute key input\n const input = {\n name: attribute.name,\n enabledOn: attribute.resources,\n };\n\n // create or update attribute key\n let attributeKeyId: string;\n if (!existingAttribute) {\n const {\n createAttributeKey: { attributeKey },\n } = await makeGraphQLRequest<{\n /** Create attribute key response */\n createAttributeKey: {\n /** Attribute key */\n attributeKey: {\n /** ID */\n id: string;\n };\n };\n }>(client, CREATE_ATTRIBUTE, {\n variables: {\n type: attribute.type,\n description: attribute.description,\n ...input,\n },\n logger,\n });\n attributeKeyId = attributeKey.id;\n } else {\n await makeGraphQLRequest(client, UPDATE_ATTRIBUTE, {\n variables: {\n attributeKeyId: existingAttribute.id,\n description: existingAttribute.isCustom ? attribute.description : undefined,\n ...input,\n },\n logger,\n });\n attributeKeyId = existingAttribute.id;\n }\n\n // upsert attribute values\n const existingAttributeMap = keyBy(existingAttribute?.values || [], 'name');\n const { existingValues = [], newValues = [] } = groupBy(attribute.values || [], (field) =>\n existingAttributeMap[field.name] ? 'existingValues' : 'newValues',\n );\n const removedValues = difference(\n (existingAttribute?.values || []).map(({ name }) => name),\n (attribute.values || []).map(({ name }) => name),\n );\n\n // Create new attribute values\n if (newValues.length > 0) {\n await makeGraphQLRequest(client, CREATE_ATTRIBUTE_VALUES, {\n variables: {\n input: newValues.map(({ name, ...rest }) => ({\n name,\n attributeKeyId,\n ...rest,\n })),\n },\n logger,\n });\n logger.info(`Created ${newValues.length} attribute values`);\n }\n\n // Update existing attribute values\n if (existingValues.length > 0) {\n await makeGraphQLRequest(client, UPDATE_ATTRIBUTE_VALUES, {\n variables: {\n input: existingValues.map(({ name, ...rest }) => ({\n id: existingAttributeMap[name]!.id,\n name,\n description: existingAttributeMap[name]!.description,\n color: existingAttributeMap[name]!.color,\n ...rest,\n attributeKeyId,\n })),\n },\n logger,\n });\n logger.info(`Updated ${existingValues.length} attribute values`);\n }\n\n // Delete removed attribute values\n if (removedValues.length > 0 && deleteExtraAttributeValues) {\n await map(\n removedValues,\n async (value) => {\n await makeGraphQLRequest(client, DELETE_ATTRIBUTE_VALUE, {\n variables: { id: existingAttributeMap[value]!.id },\n logger,\n });\n },\n {\n concurrency: 10,\n },\n );\n logger.info(`Deleted ${removedValues.length} attribute values`);\n }\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { UPDATE_INTL_MESSAGES } from './gqls/message.js';\n\nexport interface IntlMessageInput {\n /** React Intl message ID */\n id: string;\n /** Target React Intl ID to map this message to */\n targetReactIntlId?: string;\n /** Description of the message for translators */\n description?: string;\n /** Default English message content */\n defaultMessage?: string;\n /** Map from locale code to translated string */\n translations?: Record<string, string>;\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Update or create intl messages\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateIntlMessages(\n client: GraphQLClient,\n options: {\n /** List of message inputs */\n input: IntlMessageInput[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: messages, logger = NOOP_LOGGER } = options;\n // Batch update messages\n await mapSeries(chunk(messages, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_INTL_MESSAGES, {\n variables: {\n messages: page.map((message) => ({\n ...(message.id.includes('.') ? {} : { id: message.id }),\n defaultMessage: message.defaultMessage,\n targetReactIntlId: message.targetReactIntlId,\n translations: !message.translations\n ? undefined\n : Object.entries(message.translations).map(([locale, value]) => ({\n locale,\n value,\n })),\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the set of messages from the YML interface into the product\n *\n * @param client - GraphQL client\n * @param messages - messages to sync\n * @returns True upon success, false upon failure\n */\nexport async function syncIntlMessages(\n client: GraphQLClient,\n messages: IntlMessageInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${messages.length}\" messages...`);\n\n // Ensure no duplicates are being uploaded\n const notUnique = messages.filter(\n (message) => messages.filter((pol) => message.id === pol.id).length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload messages as there were non-unique entries found: ${notUnique\n .map(({ id }) => id)\n .join(',')}`,\n );\n }\n\n try {\n logger.info(`Upserting \"${messages.length}\" new messages...`);\n await updateIntlMessages(client, { input: messages, logger });\n logger.info(`Successfully synced ${messages.length} messages!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create messages! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { ScopeName } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllTeams, type Team } from './fetchAllTeams.js';\nimport { resolveParentTeamIdsByName } from './fetchParentOrganizationTeams.js';\nimport { UPDATE_TEAM, CREATE_TEAM } from './gqls/team.js';\n\nexport interface TeamInput {\n /** The display name of the team */\n name: string;\n /** Team description */\n description: string;\n /** SSO department for automated provisioning */\n 'sso-department'?: string;\n /** SSO group name for automated provisioning */\n 'sso-group'?: string;\n /** SSO title mapping for automated provisioning */\n 'sso-title'?: string;\n /** Parent organization team name to link this team to */\n 'parent-team-name'?: string;\n /** List of user emails on the team */\n users?: string[];\n /** List of scopes that the team should have */\n scopes?: ScopeName[];\n}\n\n/**\n * Map team input to GraphQL mutation input\n *\n * @param team - Team input\n * @param parentTeamIdsByName - Resolved parent organization team IDs\n * @returns GraphQL team mutation input\n */\nfunction toTeamMutationInput(\n team: TeamInput,\n parentTeamIdsByName: { [name in string]: string },\n): {\n /** The display name of the team */\n name: string;\n /** Team description */\n description: string;\n /** SSO title mapping for automated provisioning */\n ssoTitle?: string;\n /** SSO department for automated provisioning */\n ssoDepartment?: string;\n /** SSO group name for automated provisioning */\n ssoGroup?: string;\n /** List of scopes that the team should have */\n scopes?: ScopeName[];\n /** List of user emails on the team */\n userEmails?: string[];\n /** Parent organization team ID to link this team to */\n parentTeam?: string;\n} {\n const parentTeamName = team['parent-team-name'];\n const parentTeam = parentTeamName ? parentTeamIdsByName[parentTeamName] : undefined;\n\n return {\n name: team.name,\n description: team.description,\n ssoTitle: team['sso-title'],\n ssoDepartment: team['sso-department'],\n ssoGroup: team['sso-group'],\n scopes: team.scopes,\n userEmails: team.users,\n ...(parentTeam ? { parentTeam } : {}),\n };\n}\n\n/**\n * Input to create a new team\n *\n * @param client - GraphQL client\n * @param team - Input\n * @param options - Options\n * @returns Created team\n */\nexport async function createTeam(\n client: GraphQLClient,\n options: {\n /** Team to create */\n input: TeamInput;\n /** Resolved parent organization team IDs */\n parentTeamIdsByName?: { [name in string]: string };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Team, 'id' | 'name'>> {\n const { input: team, parentTeamIdsByName = {}, logger = NOOP_LOGGER } = options;\n const input = toTeamMutationInput(team, parentTeamIdsByName);\n\n const { createTeam } = await makeGraphQLRequest<{\n /** Create team mutation */\n createTeam: {\n /** Created team */\n team: Team;\n };\n }>(client, CREATE_TEAM, {\n variables: { input },\n logger,\n });\n return createTeam.team;\n}\n\n/**\n * Input to update teams\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Updated team\n */\nexport async function updateTeam(\n client: GraphQLClient,\n options: {\n /** Team input to update */\n input: TeamInput & {\n /** ID of team to update */\n id: string;\n };\n /** Resolved parent organization team IDs */\n parentTeamIdsByName?: { [name in string]: string };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Team, 'id' | 'name'>> {\n const { input: team, parentTeamIdsByName = {}, logger = NOOP_LOGGER } = options;\n const teamId = team.id;\n const { updateTeam } = await makeGraphQLRequest<{\n /** Update team mutation */\n updateTeam: {\n /** Updated team */\n team: Team;\n };\n }>(client, UPDATE_TEAM, {\n variables: {\n input: {\n id: teamId,\n ...toTeamMutationInput(team, parentTeamIdsByName),\n },\n },\n logger,\n });\n return updateTeam.team;\n}\n\n/**\n * Sync the teams\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncTeams(\n client: GraphQLClient,\n inputs: TeamInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" teams...`);\n\n let encounteredError = false;\n\n const parentTeamNames = inputs\n .map((input) => input['parent-team-name'])\n .filter((name): name is string => !!name);\n let parentTeamIdsByName: { [name in string]: string } = {};\n if (parentTeamNames.length > 0) {\n try {\n parentTeamIdsByName = await resolveParentTeamIdsByName(client, parentTeamNames, { logger });\n } catch (err) {\n logger.error(`Failed to resolve parent organization teams - ${(err as Error).message}`);\n return false;\n }\n }\n\n // Fetch existing\n const existingTeams = await fetchAllTeams(client, { logger });\n\n // Look up by name\n const teamsByName: { [k in string]: Pick<Team, 'id' | 'name'> } = keyBy(existingTeams, 'name');\n\n // Create new teams\n const newTeams = inputs.filter((input) => !teamsByName[input.name]);\n const updatedTeams = inputs.filter((input) => !!teamsByName[input.name]);\n\n // Create new teams\n await mapSeries(newTeams, async (team) => {\n try {\n const newTeam = await createTeam(client, { input: team, parentTeamIdsByName, logger });\n teamsByName[newTeam.name] = newTeam;\n logger.info(`Successfully created team \"${team.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync team \"${team.name}\"! - ${(err as Error).message}`);\n }\n });\n\n // Update all teams\n await mapSeries(updatedTeams, async (input) => {\n try {\n const newTeam = await updateTeam(client, {\n input: { ...input, id: teamsByName[input.name]!.id },\n parentTeamIdsByName,\n logger,\n });\n teamsByName[newTeam.name] = newTeam;\n logger.info(`Successfully updated team \"${input.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync team \"${input.name}\"! - ${(err as Error).message}`);\n }\n });\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\nexport const ATTRIBUTE_KEYS_REQUESTS = gql`\n query TranscendCliAttributeKeys($first: Int!, $offset: Int!) {\n attributeKeys(\n filterBy: { enabledOn: [request] }\n first: $first\n useMaster: false\n offset: $offset\n ) {\n nodes {\n id\n name\n type\n }\n }\n }\n`;\n","import got, { type Got } from 'got';\n\n/**\n * Instantiate an instance of got that is capable of making requests\n * to a sombra gateway.\n *\n * @param transcendUrl - URL of Transcend API\n * @returns The instance of got that is capable of making requests to the customer ingress\n */\nexport function createTranscendConsentGotInstance(transcendUrl: string): Got {\n return (got as unknown as Got).extend({\n prefixUrl: transcendUrl,\n });\n}\n","import { gql } from 'graphql-request';\n\n// ---- FETCH_CONSENT_MANAGER_ID query ----\n\n/** Full response from FETCH_CONSENT_MANAGER_ID */\nexport interface TranscendCliFetchConsentManagerIdResponse {\n /** Consent manager wrapper */\n consentManager: {\n /** Consent manager object */\n consentManager: {\n /** Consent manager / airgap bundle ID */\n id: string;\n };\n };\n}\n\nexport const FETCH_CONSENT_MANAGER_ID = gql`\n query TranscendCliFetchConsentManagerId {\n consentManager {\n consentManager {\n id\n }\n }\n }\n`;\n\n// ---- FETCH_CONSENT_MANAGER query ----\n\n/** Consent manager configuration */\nexport interface TranscendConsentManagerConfigGql {\n /** Configured domains */\n domains: string[];\n /** Consent precedence setting */\n consentPrecedence: string;\n /** Unknown request policy */\n unknownRequestPolicy: string;\n /** Unknown cookie policy */\n unknownCookiePolicy: string;\n /** Sync endpoint URL */\n syncEndpoint: string;\n /** Telemetry partitioning strategy */\n telemetryPartitioning: string;\n /** Signed IAB agreement status */\n signedIabAgreement: string;\n /** Sync groups setting */\n syncGroups: string;\n /** Partition parameter */\n partition: string;\n}\n\n/** Consent manager node */\nexport interface TranscendConsentManagerGql {\n /** Consent manager / airgap bundle ID */\n id: string;\n /** Production bundle URL */\n bundleURL: string;\n /** Test bundle URL */\n testBundleURL: string;\n /** Configuration settings */\n configuration: TranscendConsentManagerConfigGql;\n /** Partition info */\n partition?: {\n /** Partition value */\n partition: string;\n };\n}\n\n/** Full response from FETCH_CONSENT_MANAGER */\nexport interface TranscendCliFetchConsentManagerResponse {\n /** Consent manager wrapper */\n consentManager: {\n /** Consent manager object */\n consentManager: TranscendConsentManagerGql;\n };\n}\n\nexport const FETCH_CONSENT_MANAGER = gql`\n query TranscendCliFetchConsentManager {\n consentManager {\n consentManager {\n id\n bundleURL\n testBundleURL\n configuration {\n domains\n consentPrecedence\n unknownRequestPolicy\n unknownCookiePolicy\n syncEndpoint\n telemetryPartitioning\n signedIabAgreement\n syncGroups\n partition\n }\n partition {\n partition\n }\n }\n }\n }\n`;\n\n// ---- FETCH_CONSENT_MANAGER_THEME query ----\n\n/** Consent manager theme fields */\nexport interface TranscendConsentManagerThemeGql {\n /** Primary color */\n primaryColor: string;\n /** Font color */\n fontColor: string;\n /** Privacy policy URL */\n privacyPolicy?: string;\n /** Auto-prompt setting */\n prompt: number;\n}\n\n/** Full response from FETCH_CONSENT_MANAGER_THEME */\nexport interface TranscendCliFetchConsentManagerThemeResponse {\n /** Theme wrapper */\n consentManagerTheme: {\n /** Theme object */\n theme: TranscendConsentManagerThemeGql;\n };\n}\n\nexport const FETCH_CONSENT_MANAGER_THEME = gql`\n query TranscendCliFetchConsentManagerTheme($airgapBundleId: ID!) {\n consentManagerTheme(input: { airgapBundleId: $airgapBundleId }) {\n theme {\n primaryColor\n fontColor\n privacyPolicy\n prompt\n }\n }\n }\n`;\n\n// ---- Consent Manager mutations ----\n\n/** Shared response type for mutations that return only clientMutationId */\nexport interface TranscendClientMutationIdResponse {\n /** Mutation result keyed by mutation name */\n [key: string]: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const CREATE_CONSENT_MANAGER = gql`\n mutation TranscendCliCreateConsentManager($privacyCenterId: ID!) {\n createConsentManager(input: { privacyCenterId: $privacyCenterId }) {\n consentManager {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_VERSION = gql`\n mutation TranscendCliUpdateConsentManager($airgapBundleId: ID!, $version: String!) {\n updateConsentManager(id: $airgapBundleId, input: { version: $version }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_TO_LATEST = gql`\n mutation TranscendCliUpdateConsentManagerToLatest(\n $airgapBundleId: ID!\n $bundleType: ConsentBundleType!\n ) {\n updateConsentManagerToLatestVersion(id: $airgapBundleId, input: { bundleType: $bundleType }) {\n clientMutationId\n }\n }\n`;\n\nexport const DEPLOY_CONSENT_MANAGER = gql`\n mutation TranscendCliDeployConsentManager($airgapBundleId: ID!, $bundleType: ConsentBundleType!) {\n deployConsentManagerBundle(id: $airgapBundleId, input: { bundleType: $bundleType }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_DOMAINS = gql`\n mutation TranscendCliUpdateConsentManagerDomains($airgapBundleId: ID!, $domains: [String!]!) {\n updateConsentManagerDomains(input: { id: $airgapBundleId, domains: $domains }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_PARTITION = gql`\n mutation TranscendCliUpdateConsentManagerPartition($airgapBundleId: ID!, $partitionId: ID!) {\n updateConsentManagerPartition(input: { id: $airgapBundleId, partitionId: $partitionId }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_LOAD_OPTIONS = gql`\n mutation TranscendCliUpdateLoadOptions($input: UpdateLoadOptionsInput!) {\n updateLoadOptions(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_UNKNOWN_REQUEST_POLICY = gql`\n mutation TranscendCliToggleUnknownRequestPolicy($input: ToggleUnknownRequestPolicyInput!) {\n toggleUnknownRequestPolicy(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_UNKNOWN_COOKIE_POLICY = gql`\n mutation TranscendCliToggleUnknownCookiePolicy($input: ToggleUnknownCookiePolicyInput!) {\n toggleUnknownCookiePolicy(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_TELEMETRY_PARTITION_STRATEGY = gql`\n mutation TranscendCliToggleTelemetryPartitionStrategy(\n $input: ToggleTelemetryPartitionStrategyInput!\n ) {\n toggleTelemetryPartitioning(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_CONSENT_PRECEDENCE = gql`\n mutation TranscendCliToggleConsentPrecedence($input: ToggleConsentPrecedenceInput!) {\n toggleConsentPrecedence(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_THEME = gql`\n mutation TranscendCliUpdateConsentManagerTheme($input: UpdateConsentManagerThemeInput!) {\n updateConsentManagerTheme(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const FETCH_CONSENT_UI_VARIANTS = gql`\n query TranscendCliConsentUiVariants($airgapBundleId: ID!, $first: Int!, $offset: Int!) {\n consentUiVariants(input: { airgapBundleId: $airgapBundleId }, first: $first, offset: $offset) {\n totalCount\n nodes {\n id\n name\n slug\n description\n status\n locales\n configuration {\n firstLayerConfig {\n showCloseButton\n alphaMask\n footer {\n showTranscendBadge\n showGpcDetected\n showLocalePicker\n }\n links\n buttons {\n type\n action\n }\n showHeader\n }\n secondLayerConfig {\n showCloseButton\n alphaMask\n footer {\n showTranscendBadge\n showGpcDetected\n showLocalePicker\n }\n links\n buttons {\n type\n action\n }\n showBulkActions\n closeAfterBulkAction\n showPurposeDescriptions\n showToggleText\n showToggleIcon\n }\n defaultLocale\n }\n theme {\n id\n slug\n }\n userFlow\n }\n }\n }\n`;\n\nexport const FETCH_CONSENT_UI_THEMES = gql`\n query TranscendCliConsentUiThemes($airgapBundleId: ID!, $first: Int!, $offset: Int!) {\n consentUiThemes(input: { airgapBundleId: $airgapBundleId }, first: $first, offset: $offset) {\n totalCount\n nodes {\n id\n name\n slug\n configuration {\n buttonThemes {\n backgroundColor\n backgroundHoverColor\n textColor\n textHoverColor\n borderRadius\n borderColor\n borderHoverColor\n linkColor\n linkHoverColor\n linkUnderline\n }\n firstLayer {\n alwaysShowScrollbar\n background {\n backgroundColor\n }\n buttons\n closeButton {\n backgroundColor\n backgroundHoverColor\n borderRadius\n borderColor\n borderHoverColor\n iconColor\n iconHoverColor\n }\n container {\n backgroundColor\n borderRadius\n borderColor\n }\n contentFlow\n contentLayout {\n breakpoint {\n value\n unit\n }\n maxWidths {\n flow\n width\n }\n fullWidth\n }\n cookieAndPrivacyPolicy {\n linkColor\n linkUnderline\n }\n poweredByTranscend {\n linkColor\n }\n description {\n textColor\n linkColor\n linkUnderline\n }\n footer {\n backgroundColor\n localePickerColor\n }\n header {\n textColor\n logoPosition\n logoImageUrl\n }\n horizontalAlign\n verticalAlign\n }\n secondLayer {\n alwaysOnText {\n textColor\n }\n alwaysShowScrollbar\n background {\n backgroundColor\n }\n bulkActionButtons\n buttons\n cardTitle {\n textColor\n }\n caretIcon {\n iconColor\n }\n closeButton {\n backgroundColor\n backgroundHoverColor\n borderColor\n borderHoverColor\n borderRadius\n iconColor\n iconHoverColor\n }\n container {\n backgroundColor\n borderRadius\n borderColor\n }\n cookieAndPrivacyPolicy {\n linkColor\n linkUnderline\n }\n poweredByTranscend {\n linkColor\n }\n description {\n textColor\n linkColor\n linkUnderline\n }\n footer {\n backgroundColor\n localePickerColor\n }\n header {\n logoImageUrl\n logoPosition\n textColor\n }\n horizontalAlign\n lockToEdges\n maxWidth\n modalSubtitle {\n textColor\n }\n modalTitle {\n textColor\n }\n purposeCard {\n backgroundColor\n borderRadius\n borderColor\n }\n purposeDescription {\n textColor\n linkColor\n linkUnderline\n }\n purposeListTitle {\n textColor\n }\n shrinkToFullWidth\n toggle {\n textColor\n iconColor\n enabledColor\n disabledColor\n knobColor\n }\n }\n }\n }\n }\n }\n`;\n\nexport const CREATE_CONSENT_UI_VARIANT = gql`\n mutation TranscendCliCreateConsentUiVariant($input: CreateConsentUiVariantInput!) {\n createConsentUiVariant(input: $input) {\n consentUiVariant {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_CONSENT_UI_VARIANT = gql`\n mutation TranscendCliUpdateConsentUiVariant($input: UpdateConsentUiVariantInput!) {\n updateConsentUiVariant(input: $input) {\n consentUiVariant {\n id\n }\n }\n }\n`;\n\nexport const CREATE_CONSENT_UI_THEME = gql`\n mutation TranscendCliCreateConsentUiTheme($input: CreateConsentUiThemeInput!) {\n createConsentUiTheme(input: $input) {\n consentUiTheme {\n id\n name\n }\n }\n }\n`;\n\nexport const UPDATE_CONSENT_UI_THEME = gql`\n mutation TranscendCliUpdateConsentUiTheme($input: UpdateConsentUiThemeInput!) {\n updateConsentUiTheme(input: $input) {\n consentUiTheme {\n id\n }\n }\n }\n`;\n","import { ConsentBundleType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DEPLOY_CONSENT_MANAGER, UPDATE_CONSENT_MANAGER_TO_LATEST } from './gqls/consentManager.js';\n\n/**\n * Deploy the Consent Manager\n *\n * @param client - GraphQL client\n * @param input - Deploy input\n * @param options - Options\n */\nexport async function deployConsentManager(\n client: GraphQLClient,\n input: {\n /** ID of Consent Manager */\n id: string;\n /** Type of bundle */\n bundleType: ConsentBundleType;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n await makeGraphQLRequest(client, DEPLOY_CONSENT_MANAGER, {\n variables: { airgapBundleId: input.id, bundleType: input.bundleType },\n logger: options.logger,\n });\n}\n\n/**\n * Update the Consent Manager to the latest airgap.js version\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateConsentManagerToLatest(\n client: GraphQLClient,\n options: {\n /** Consent manager to update */\n input: {\n /** ID of Consent Manager */\n id: string;\n /** Type of bundle */\n bundleType: ConsentBundleType;\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: consentManager, logger } = options;\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_TO_LATEST, {\n variables: { airgapBundleId: consentManager.id, bundleType: consentManager.bundleType },\n logger,\n });\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { FETCH_CONSENT_MANAGER_ID } from './gqls/consentManager.js';\n\n/**\n * Fetch consent manager ID\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager ID in organization\n */\nexport async function fetchConsentManagerId(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Max number of requests to send */\n maxRequests?: number;\n } = {},\n): Promise<string> {\n const {\n consentManager: { consentManager },\n } = await makeGraphQLRequest<{\n /** Consent manager query */\n consentManager: {\n /** Consent manager object */\n consentManager: {\n /** ID of bundle */\n id: string;\n };\n };\n }>(client, FETCH_CONSENT_MANAGER_ID, {\n logger: options.logger,\n maxRetries: options.maxRequests,\n });\n return consentManager.id;\n}\n","/** Service associated with a cookie or data flow */\nexport interface TranscendCookieServiceGql {\n /** Service ID */\n id: string;\n /** Human-readable service title */\n title: string;\n /** Integration slug */\n integrationName: string;\n}\n\n/** Service fields shared by cookies and data flows */\nexport const SERVICE_FIELDS = `\n id\n title\n integrationName\n`;\n\n/** Tracking purpose assigned to a cookie or data flow */\nexport interface TranscendTrackingPurposeGql {\n /** Purpose ID */\n id: string;\n /** Purpose display name */\n name: string;\n /** Purpose slug (e.g. \"Advertising\", \"Analytics\") */\n trackingType: string;\n}\n\n/** Tracking purpose fields */\nexport const TRACKING_PURPOSE_FIELDS = `\n id\n name\n trackingType\n`;\n","import type { ConsentTrackerSource, ConsentTrackerStatus } from '@transcend-io/privacy-types';\nimport { gql } from 'graphql-request';\n\nimport {\n ATTRIBUTE_VALUE_FIELDS,\n OWNER_FIELDS,\n TEAM_FIELDS,\n type TranscendAttributeValueGql,\n type TranscendOwnerGql,\n type TranscendTeamGql,\n} from '../../gqls/shared.js';\nimport {\n SERVICE_FIELDS,\n TRACKING_PURPOSE_FIELDS,\n type TranscendCookieServiceGql,\n type TranscendTrackingPurposeGql,\n} from './shared.js';\n\n// ---- Shared cookie types ----\n\n/** Cookie domain with occurrence count */\nexport interface TranscendCookieDomainGql {\n /** Domain ID */\n id: string;\n /** Domain name */\n domain: string;\n /** Number of occurrences on this domain */\n occurrences: number;\n}\n\n/** Cookie node returned by COOKIES query */\nexport interface TranscendCookieGql {\n /** Cookie ID */\n id: string;\n /** Cookie name */\n name: string;\n /** Whether this is a regex pattern */\n isRegex: boolean;\n /** Description */\n description?: string;\n /** Tracking purpose slugs (string array) */\n trackingPurposes: string[];\n /** Resolved tracking purposes */\n purposes: TranscendTrackingPurposeGql[];\n /** Frequency of occurrence (0-1) */\n frequency: number;\n /** Associated service */\n service?: TranscendCookieServiceGql;\n /** Whether marked as junk */\n isJunk: boolean;\n /** How the tracker was discovered */\n source: ConsentTrackerSource;\n /** Triage status */\n status: ConsentTrackerStatus;\n /** Assigned owners */\n owners: TranscendOwnerGql[];\n /** Assigned teams */\n teams: TranscendTeamGql[];\n /** Custom attribute values */\n attributeValues: TranscendAttributeValueGql[];\n /** Creation timestamp */\n createdAt: string;\n /** Last updated timestamp */\n updatedAt: string;\n /** Last seen by telemetry */\n lastDiscoveredAt?: string;\n /** Domains this cookie appears on */\n domains: TranscendCookieDomainGql[];\n /** Total telemetry occurrences */\n occurrences: number;\n /** Number of sites seen on (all time) */\n consentSiteCountAllTime: number;\n /** Number of sites seen on (last week) */\n consentSiteCountLastWeek: number;\n}\n\n/** Input for updating or creating a cookie */\nexport interface TranscendUpdateCookieInputGql {\n /** Cookie name (used as identifier for upsert) */\n name: string;\n /** Tracking purpose slugs */\n trackingPurposes?: string[];\n /** Purpose IDs to assign */\n purposeIds?: string[];\n /** Description */\n description?: string;\n /** Service integration name */\n service?: string;\n /** Mark as junk */\n isJunk?: boolean;\n /** Triage status */\n status?: ConsentTrackerStatus;\n /** Whether this is a regex pattern */\n isRegex?: boolean;\n /** How the tracker was discovered */\n source?: ConsentTrackerSource;\n}\n\n// ---- COOKIES query ----\n\n/** Full response from the COOKIES query */\nexport interface TranscendCliCookiesResponse {\n /** Cookies result */\n cookies: {\n /** List of cookie nodes */\n nodes: TranscendCookieGql[];\n /** Total count of matching cookies */\n totalCount: number;\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const COOKIES = gql`\n query TranscendCliCookies(\n $input: AirgapBundleInput!\n $first: Int!\n $offset: Int!\n $filterBy: CookiesFiltersInput\n $orderBy: [CookieOrder!]\n ) {\n cookies(\n input: $input\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: $orderBy\n useMaster: false\n ) {\n totalCount\n nodes {\n id\n name\n isRegex\n description\n trackingPurposes\n purposes { ${TRACKING_PURPOSE_FIELDS} }\n frequency\n service { ${SERVICE_FIELDS} }\n isJunk\n source\n status\n owners { ${OWNER_FIELDS} }\n teams { ${TEAM_FIELDS} }\n attributeValues { ${ATTRIBUTE_VALUE_FIELDS} }\n createdAt\n updatedAt\n lastDiscoveredAt\n domains {\n id\n domain\n occurrences\n }\n occurrences\n consentSiteCountAllTime\n consentSiteCountLastWeek\n }\n }\n }\n`;\n\n// ---- UPDATE_OR_CREATE_COOKIES mutation ----\n\n/** Full response from the UPDATE_OR_CREATE_COOKIES mutation */\nexport interface TranscendCliUpdateOrCreateCookiesResponse {\n /** Mutation result */\n updateOrCreateCookies: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const UPDATE_OR_CREATE_COOKIES = gql`\n mutation TranscendCliUpdateOrCreateCookies(\n $cookies: [UpdateOrCreateCookieInput!]!\n $airgapBundleId: ID!\n ) {\n updateOrCreateCookies(input: { airgapBundleId: $airgapBundleId, cookies: $cookies }) {\n clientMutationId\n }\n }\n`;\n\n// ---- DELETE_COOKIES mutation ----\n\n/** Full response from the DELETE_COOKIES mutation */\nexport interface TranscendCliDeleteCookiesResponse {\n /** Mutation result */\n deleteCookies: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const DELETE_COOKIES = gql`\n mutation TranscendCliDeleteCookies($input: DeleteCookiesInput!) {\n deleteCookies(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ConsentTrackerStatus, OrderDirection } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport {\n COOKIES,\n type TranscendCookieGql,\n type TranscendCliCookiesResponse,\n} from './gqls/cookies.js';\n\n/** Sort order for cookie queries */\nexport interface CookieOrder {\n /** Field to sort by */\n field: string;\n /** Sort direction */\n direction: OrderDirection;\n}\n\nconst PAGE_SIZE = 20;\n\nconst DEFAULT_COOKIE_ORDER: CookieOrder[] = [\n { field: 'createdAt', direction: OrderDirection.Asc },\n { field: 'name', direction: OrderDirection.Asc },\n];\n\n/**\n * Fetch all Cookies in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All Cookies in the organization\n */\nexport async function fetchAllCookies(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** The status to fetch */\n status?: ConsentTrackerStatus;\n };\n /** Sort ordering (defaults to createdAt ASC, name ASC) */\n orderBy?: CookieOrder[];\n } = {},\n): Promise<TranscendCookieGql[]> {\n const {\n logger,\n filterBy: { status = ConsentTrackerStatus.Live } = {},\n orderBy = DEFAULT_COOKIE_ORDER,\n } = options;\n const cookies: TranscendCookieGql[] = [];\n let offset = 0;\n\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n let shouldContinue = false;\n do {\n const {\n cookies: { nodes },\n } = await makeGraphQLRequest<TranscendCliCookiesResponse>(client, COOKIES, {\n variables: {\n input: { airgapBundleId },\n first: PAGE_SIZE,\n offset,\n filterBy: { status },\n orderBy,\n },\n logger,\n });\n cookies.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return cookies.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import type {\n ConsentTrackerSource,\n ConsentTrackerStatus,\n DataFlowScope,\n} from '@transcend-io/privacy-types';\nimport { gql } from 'graphql-request';\n\nimport {\n ATTRIBUTE_VALUE_FIELDS,\n OWNER_FIELDS,\n TEAM_FIELDS,\n type TranscendAttributeValueGql,\n type TranscendOwnerGql,\n type TranscendTeamGql,\n} from '../../gqls/shared.js';\nimport {\n SERVICE_FIELDS,\n TRACKING_PURPOSE_FIELDS,\n type TranscendCookieServiceGql,\n type TranscendTrackingPurposeGql,\n} from './shared.js';\n\n// ---- Shared data flow types ----\n\n/** Data flow node returned by DATA_FLOWS query and UPDATE_DATA_FLOWS mutation */\nexport interface TranscendDataFlowGql {\n /** Data flow ID */\n id: string;\n /** URL/host value */\n value: string;\n /** Scope type (HOST, PATH, REGEX, etc.) */\n type: DataFlowScope;\n /** Description */\n description?: string;\n /** Tracking purpose slugs */\n trackingType: string[];\n /** Resolved tracking purposes */\n purposes: TranscendTrackingPurposeGql[];\n /** Frequency of occurrence (0-1) */\n frequency: number;\n /** Associated service */\n service?: TranscendCookieServiceGql;\n /** Whether marked as junk */\n isJunk: boolean;\n /** How the tracker was discovered */\n source: ConsentTrackerSource;\n /** Triage status */\n status: ConsentTrackerStatus;\n /** Assigned owners */\n owners: TranscendOwnerGql[];\n /** Assigned teams */\n teams: TranscendTeamGql[];\n /** Custom attribute values */\n attributeValues: TranscendAttributeValueGql[];\n /** Creation timestamp */\n createdAt: string;\n /** Last updated timestamp */\n updatedAt: string;\n /** Last seen by telemetry */\n lastDiscoveredAt?: string;\n /** Total telemetry occurrences */\n occurrences: number;\n /** Number of sites seen on (all time) */\n consentSiteCountAllTime: number;\n /** Number of sites seen on (last week) */\n consentSiteCountLastWeek: number;\n}\n\n/** Input for updating a data flow */\nexport interface TranscendUpdateDataFlowInputGql {\n /** Data flow ID */\n id: string;\n /** URL/host value */\n value?: string;\n /** Scope type */\n type?: DataFlowScope;\n /** Tracking purpose slugs */\n trackingType?: string[];\n /** Purpose IDs to assign */\n purposeIds?: string[];\n /** Description */\n description?: string;\n /** Service integration name */\n service?: string;\n /** Mark as junk */\n isJunk?: boolean;\n /** Triage status */\n status?: ConsentTrackerStatus;\n}\n\n// ---- Shared field selection for mutation responses ----\n\nconst DATA_FLOW_RESPONSE_FIELDS = `\n id\n value\n type\n description\n trackingType\n purposes { ${TRACKING_PURPOSE_FIELDS} }\n frequency\n service { ${SERVICE_FIELDS} }\n isJunk\n source\n status\n createdAt\n updatedAt\n lastDiscoveredAt\n occurrences\n consentSiteCountAllTime\n consentSiteCountLastWeek\n`;\n\n// ---- CREATE_DATA_FLOWS mutation ----\n\n/** Full response from the CREATE_DATA_FLOWS mutation */\nexport interface TranscendCliCreateDataFlowsResponse {\n /** Mutation result */\n createDataFlows: {\n /** Created data flows (IDs only) */\n dataFlows: {\n /** Data flow ID */\n id: string;\n }[];\n };\n}\n\nexport const CREATE_DATA_FLOWS = gql`\n mutation TranscendCliCreateDataFlows(\n $dataFlows: [DataFlowInput!]!\n $airgapBundleId: ID!\n $classifyService: Boolean\n ) {\n createDataFlows(\n input: {\n airgapBundleId: $airgapBundleId\n dataFlows: $dataFlows\n classifyService: $classifyService\n }\n ) {\n dataFlows {\n id\n }\n }\n }\n`;\n\n// ---- UPDATE_DATA_FLOWS mutation ----\n\n/** Full response from the UPDATE_DATA_FLOWS mutation */\nexport interface TranscendCliUpdateDataFlowsResponse {\n /** Mutation result */\n updateDataFlows: {\n /** Updated data flows with full fields */\n dataFlows: TranscendDataFlowGql[];\n };\n}\n\nexport const UPDATE_DATA_FLOWS = gql`\n mutation TranscendCliUpdateDataFlows(\n $airgapBundleId: ID!\n $dataFlows: [UpdateDataFlowInput!]!\n $classifyService: Boolean\n ) {\n updateDataFlows(\n input: {\n airgapBundleId: $airgapBundleId\n dataFlows: $dataFlows\n classifyService: $classifyService\n }\n ) {\n dataFlows { ${DATA_FLOW_RESPONSE_FIELDS} }\n }\n }\n`;\n\n// ---- DATA_FLOWS query ----\n\n/** Full response from the DATA_FLOWS query */\nexport interface TranscendCliDataFlowsResponse {\n /** Data flows result */\n dataFlows: {\n /** List of data flow nodes */\n nodes: TranscendDataFlowGql[];\n /** Total count of matching data flows */\n totalCount: number;\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_FLOWS = gql`\n query TranscendCliDataFlows(\n $input: AirgapBundleInput!\n $first: Int!\n $offset: Int!\n $filterBy: DataFlowsFiltersInput\n $orderBy: [DataFlowOrder!]\n ) {\n dataFlows(\n input: $input\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: $orderBy\n useMaster: false\n ) {\n totalCount\n nodes {\n id\n value\n type\n description\n trackingType\n purposes { ${TRACKING_PURPOSE_FIELDS} }\n frequency\n service { ${SERVICE_FIELDS} }\n isJunk\n source\n status\n owners { ${OWNER_FIELDS} }\n teams { ${TEAM_FIELDS} }\n attributeValues { ${ATTRIBUTE_VALUE_FIELDS} }\n createdAt\n updatedAt\n lastDiscoveredAt\n occurrences\n consentSiteCountAllTime\n consentSiteCountLastWeek\n }\n }\n }\n`;\n\n// ---- DELETE_DATA_FLOWS mutation ----\n\n/** Full response from the DELETE_DATA_FLOWS mutation */\nexport interface TranscendCliDeleteDataFlowsResponse {\n /** Mutation result */\n deleteDataFlows: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const DELETE_DATA_FLOWS = gql`\n mutation TranscendCliDeleteDataFlows($input: DeleteDataFlowsInput!) {\n deleteDataFlows(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ConsentTrackerStatus, OrderDirection } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport {\n DATA_FLOWS,\n type TranscendDataFlowGql,\n type TranscendCliDataFlowsResponse,\n} from './gqls/dataFlows.js';\n\n/** Sort order for data flow queries */\nexport interface DataFlowOrder {\n /** Field to sort by */\n field: string;\n /** Sort direction */\n direction: OrderDirection;\n}\n\nconst PAGE_SIZE = 20;\n\nconst DEFAULT_DATA_FLOW_ORDER: DataFlowOrder[] = [\n { field: 'createdAt', direction: OrderDirection.Asc },\n { field: 'value', direction: OrderDirection.Asc },\n];\n\n/**\n * Fetch all DataFlows in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All DataFlows in the organization\n */\nexport async function fetchAllDataFlows(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** The status to fetch */\n status?: ConsentTrackerStatus;\n };\n /** Sort ordering (defaults to createdAt ASC, value ASC) */\n orderBy?: DataFlowOrder[];\n } = {},\n): Promise<TranscendDataFlowGql[]> {\n const {\n logger,\n filterBy: { status = ConsentTrackerStatus.Live } = {},\n orderBy = DEFAULT_DATA_FLOW_ORDER,\n } = options;\n const dataFlows: TranscendDataFlowGql[] = [];\n let offset = 0;\n\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n let shouldContinue = false;\n do {\n const {\n dataFlows: { nodes },\n } = await makeGraphQLRequest<TranscendCliDataFlowsResponse>(client, DATA_FLOWS, {\n variables: {\n input: { airgapBundleId },\n first: PAGE_SIZE,\n offset,\n filterBy: {\n status,\n ...(status === ConsentTrackerStatus.NeedsReview ? { showZeroActivity: true } : {}),\n },\n orderBy,\n },\n logger,\n });\n dataFlows.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return dataFlows.sort((a, b) => a.value.localeCompare(b.value));\n}\n","import { gql } from 'graphql-request';\n\nexport const FETCH_PRIVACY_CENTER_ID = gql`\n query TranscendCliFetchPrivacyCenterId($url: String!) {\n privacyCenter(lookup: { url: $url }) {\n id\n }\n }\n`;\n\nexport const DEPLOYED_PRIVACY_CENTER_URL = gql`\n query TranscendCliDeployedPrivacyCenterUrl {\n organization {\n deployedPrivacyCenterUrl\n }\n }\n`;\n\nexport const PRIVACY_CENTER = gql`\n query TranscendCliFetchPrivacyCenters($url: String!) {\n privacyCenter(lookup: { url: $url }) {\n id\n url\n isDisabled\n showPrivacyRequestButton\n showPolicies\n showTrackingTechnologies\n showCookies\n showDataFlows\n showConsentManager\n showManageYourPrivacy\n showMarketingPreferences\n locales\n defaultLocale\n preferBrowserDefaultLocale\n supportEmail\n replyToEmail\n useNoReplyEmailAddress\n useCustomEmailDomain\n transformAccessReportJsonToCsv\n home {\n defaultMessage\n }\n expandSideMenuByDefault\n workflowsCustomFieldsRequired\n footerLayout\n themeStr\n childOrganizations {\n id\n uri\n name\n }\n footerLinks {\n id\n displayOrder\n title {\n defaultMessage\n }\n url {\n defaultMessage\n }\n icon {\n id\n src\n key\n size\n mimetype\n }\n iconOnly\n }\n }\n }\n`;\n\nexport const UPDATE_PRIVACY_CENTER = gql`\n mutation TranscendCliUpdatePrivacyCenter($input: UpdatePrivacyCenterInput!) {\n updatePrivacyCenter(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_PRIVACY_CENTER_FOOTER_LINKS = gql`\n mutation TranscendCliUpdatePrivacyCenterFooterLinks(\n $input: UpdatePrivacyCenterFooterLinksInput!\n ) {\n updatePrivacyCenterFooterLinks(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const DELETE_PRIVACY_CENTER_FOOTER_LINKS = gql`\n mutation TranscendCliDeletePrivacyCenterFooterLinks(\n $input: DeletePrivacyCenterFooterLinksInput!\n ) {\n deletePrivacyCenterFooterLinks(input: $input) {\n clientMutationId\n success\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DEPLOYED_PRIVACY_CENTER_URL, FETCH_PRIVACY_CENTER_ID } from './gqls/privacyCenter.js';\n\n/**\n * Fetch default privacy center URL\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Privacy center URL in organization\n */\nexport async function fetchPrivacyCenterUrl(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<string> {\n const { organization } = await makeGraphQLRequest<{\n /** Organization */\n organization: {\n /** URL */\n deployedPrivacyCenterUrl: string;\n };\n }>(client, DEPLOYED_PRIVACY_CENTER_URL, { logger: options.logger });\n return organization.deployedPrivacyCenterUrl;\n}\n\n/**\n * Fetch privacy center ID\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Privacy center ID in organization\n */\nexport async function fetchPrivacyCenterId(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** URL to look up */\n url?: string;\n };\n } = {},\n): Promise<string> {\n const { logger, filterBy: { url } = {} } = options;\n let urlToUse = url;\n if (!urlToUse) {\n urlToUse = await fetchPrivacyCenterUrl(client, { logger });\n }\n const { privacyCenter } = await makeGraphQLRequest<{\n /** Privacy center query */\n privacyCenter: {\n /** ID of bundle */\n id: string;\n };\n }>(client, FETCH_PRIVACY_CENTER_ID, {\n variables: { url: urlToUse },\n logger,\n });\n return privacyCenter.id;\n}\n","import { gql } from 'graphql-request';\n\nexport const POLICIES = gql`\n query TranscendCliFetchPolicies($url: String!) {\n privacyCenterPolicies(lookup: { url: $url }) {\n id\n title {\n defaultMessage\n }\n type\n disableEffectiveOn\n disabledLocales\n versions {\n effectiveOn\n content {\n defaultMessage\n }\n }\n }\n }\n`;\n\nexport const UPDATE_POLICIES = gql`\n mutation TranscendCliUpdatePolicies($policies: [PolicyInput!]!, $privacyCenterId: ID!) {\n updatePolicies(\n input: { privacyCenterId: $privacyCenterId, policies: $policies, skipPublish: true }\n ) {\n clientMutationId\n }\n }\n`;\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type { PolicyType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchPrivacyCenterUrl } from './fetchPrivacyCenterId.js';\nimport { POLICIES } from './gqls/policy.js';\n\nexport interface Policy {\n /** ID of policy */\n id: string;\n /** Title of policy */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** Policy type */\n type: PolicyType;\n /** Disabled locales */\n disabledLocales: LocaleValue[];\n /** Versions */\n versions: {\n /** Message content */\n content: {\n /** Default message */\n defaultMessage: string;\n };\n }[];\n}\n\n/**\n * Fetch all policies in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All policies in the organization\n */\nexport async function fetchAllPolicies(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Policy[]> {\n const { logger } = options;\n const deployedPrivacyCenterUrl = await fetchPrivacyCenterUrl(client, { logger });\n const { privacyCenterPolicies } = await makeGraphQLRequest<{\n /** Policies */\n privacyCenterPolicies: Policy[];\n }>(client, POLICIES, {\n variables: { url: deployedPrivacyCenterUrl },\n logger,\n });\n\n return privacyCenterPolicies.sort((a, b) =>\n a.title.defaultMessage.localeCompare(b.title.defaultMessage),\n );\n}\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type {\n PrivacyCenterFooterLayout,\n PrivacyCenterThemePartial,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchPrivacyCenterUrl } from './fetchPrivacyCenterId.js';\nimport { PRIVACY_CENTER } from './gqls/privacyCenter.js';\n\nexport interface PrivacyCenterChildOrganization {\n /** Organization ID */\n id: string;\n /** Organization URI */\n uri: string;\n /** Organization name */\n name: string;\n}\n\nexport interface PrivacyCenterFooterLinkIcon {\n /** Asset file ID */\n id: string;\n /** Public URL where the asset can be downloaded */\n src: string;\n /** Storage key for the asset */\n key: string;\n /** File size in bytes */\n size: number;\n /** MIME type of the asset */\n mimetype: string;\n}\n\nexport interface PrivacyCenterFooterLink {\n /** Footer link ID */\n id: string;\n /** Display order */\n displayOrder: number;\n /** Link title */\n title: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Link URL (default locale) */\n url: string;\n /** Optional icon image for the footer link */\n icon?: PrivacyCenterFooterLinkIcon;\n /**\n * When true with an icon set, render as icon-only (title is used for\n * accessibility). When false with an icon set, render icon beside the\n * visible label.\n */\n iconOnly: boolean;\n}\n\nexport interface PrivacyCenter {\n /** ID of the privacy center */\n id: string;\n /** The URL of the privacy center */\n url: string;\n /** Whether or not the entire privacy center is enabled or disabled */\n isDisabled: boolean;\n /** Whether or not to show the privacy requests button */\n showPrivacyRequestButton: boolean;\n /** Whether or not to show the policies page */\n showPolicies: boolean;\n /** Whether or not to show the tracking technologies page */\n showTrackingTechnologies: boolean;\n /** Whether or not to show the cookies on the tracking technologies page */\n showCookies: boolean;\n /** Whether or not to show the data flows on the tracking technologies page */\n showDataFlows: boolean;\n /** Whether or not to show the consent manager opt out options on the tracking technologies page */\n showConsentManager: boolean;\n /** Whether or not to show the manage your privacy page */\n showManageYourPrivacy: boolean;\n /** Whether or not to show the marketing preferences page */\n showMarketingPreferences: boolean;\n /** What languages are supported for the privacy center */\n locales: LocaleValue[];\n /** The default locale for the privacy center */\n defaultLocale: LocaleValue;\n /** Whether or not to prefer the browser default locale */\n preferBrowserDefaultLocale: boolean;\n /** The email addresses of the employees within your company that are the go-to individuals for managing this privacy center */\n supportEmail: string;\n /** The email addresses of the employees within your company that are the go-to individuals for managing this privacy center */\n replyToEmail: string;\n /** Whether or not to send emails from a no reply email */\n useNoReplyEmailAddress: boolean;\n /** Whether or not to use a custom email domain */\n useCustomEmailDomain: boolean;\n /** Whether or not to transcend access requests from JSON to CSV */\n transformAccessReportJsonToCsv: boolean;\n /**\n * Privacy Center \"home\" / back-to-site link URL shown in the privacy center\n * header\n */\n home: string;\n /** Whether the side menu is expanded by default on the privacy center */\n expandSideMenuByDefault: boolean;\n /** Whether custom fields are required on privacy center workflows */\n workflowsCustomFieldsRequired: boolean;\n /** Footer layout */\n footerLayout: PrivacyCenterFooterLayout;\n /** Child organizations displayed on this privacy center */\n childOrganizations: PrivacyCenterChildOrganization[];\n /** Footer links displayed on this privacy center */\n footerLinks: PrivacyCenterFooterLink[];\n /** The theme object of colors to display on the privacy center */\n theme: PrivacyCenterThemePartial;\n}\n\n/**\n * Fetch all privacy centers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All privacy centers in the organization\n */\nexport async function fetchAllPrivacyCenters(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PrivacyCenter[]> {\n const { logger } = options;\n const deployedPrivacyCenterUrl = await fetchPrivacyCenterUrl(client, { logger });\n const {\n privacyCenter: { themeStr, footerLinks, home, ...rest },\n } = await makeGraphQLRequest<{\n /** Privacy centers */\n privacyCenter: Omit<PrivacyCenter, 'theme' | 'footerLinks' | 'home'> & {\n /** Theme string */\n themeStr: string;\n /** Home / back-to-site link as returned by GraphQL */\n home: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Footer links as returned by GraphQL */\n footerLinks: {\n /** Footer link ID */\n id: string;\n /** Display order */\n displayOrder: number;\n /** Link title */\n title: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Link URL */\n url: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Optional icon image for the footer link */\n icon: PrivacyCenterFooterLinkIcon | null;\n /** Whether the link renders as icon-only */\n iconOnly: boolean;\n }[];\n };\n }>(client, PRIVACY_CENTER, {\n variables: { url: deployedPrivacyCenterUrl },\n logger,\n });\n\n return [\n {\n ...rest,\n home: home.defaultMessage,\n footerLinks: footerLinks.map((link) => ({\n id: link.id,\n displayOrder: link.displayOrder,\n title: link.title,\n url: link.url.defaultMessage,\n ...(link.icon ? { icon: link.icon } : {}),\n iconOnly: link.iconOnly,\n })),\n theme: JSON.parse(themeStr),\n },\n ];\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: name, direction: ASC }\n// ]\nexport const PROCESSING_PURPOSE_SUB_CATEGORIES = gql`\n query TranscendCliProcessingPurposeSubCategories($first: Int!, $offset: Int!) {\n processingPurposeSubCategories(\n first: $first\n offset: $offset\n isExportCsv: true\n useMaster: false\n ) {\n nodes {\n id\n name\n purpose\n description\n teams {\n name\n }\n owners {\n email\n }\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_PROCESSING_PURPOSE_SUB_CATEGORY = gql`\n mutation TranscendCliCreateProcessingPurposeSubCategory(\n $input: CreateProcessingPurposeCategoryInput!\n ) {\n createProcessingPurposeSubCategory(input: $input) {\n processingPurposeSubCategory {\n id\n name\n purpose\n }\n }\n }\n`;\n\nexport const UPDATE_PROCESSING_PURPOSE_SUB_CATEGORIES = gql`\n mutation TranscendCliUpdateProcessingPurposeSubCategories(\n $input: UpdateProcessingPurposeSubCategoriesInput!\n ) {\n updateProcessingPurposeSubCategories(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ProcessingPurpose } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PROCESSING_PURPOSE_SUB_CATEGORIES } from './gqls/processingPurpose.js';\n\nexport interface ProcessingPurposeSubCategory {\n /** ID of processing purpose */\n id: string;\n /** Name of processing purpose */\n name: string;\n /** Type of processing purpose */\n purpose: ProcessingPurpose;\n /** Description of processing purpose */\n description?: string;\n /** Assigned teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Assigned owners */\n owners: {\n /** Email */\n email: string;\n }[];\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all processingPurposeSubCategories in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All processingPurposeSubCategories in the organization\n */\nexport async function fetchAllProcessingPurposes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ProcessingPurposeSubCategory[]> {\n const processingPurposeSubCategories: ProcessingPurposeSubCategory[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n processingPurposeSubCategories: { nodes },\n } = await makeGraphQLRequest<{\n /** DataCategories */\n processingPurposeSubCategories: {\n /** List */\n nodes: ProcessingPurposeSubCategory[];\n };\n }>(client, PROCESSING_PURPOSE_SUB_CATEGORIES, {\n variables: { first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n processingPurposeSubCategories.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return processingPurposeSubCategories.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import {\n ConsentPrecedenceOption,\n UnknownRequestPolicy,\n TelemetryPartitionStrategy,\n SignedIabAgreementOption,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { FETCH_CONSENT_MANAGER } from './gqls/consentManager.js';\n\nexport interface ConsentManager {\n /** ID of consent manager */\n id: string;\n /** Production bundle URL */\n bundleURL: string;\n /** Test bundle URL */\n testBundleURL: string;\n /** Configuration of consent manager */\n configuration: {\n /** Domain list */\n domains: string[];\n /** Consent precedence of user vs signal */\n consentPrecedence: ConsentPrecedenceOption;\n /** Unknown request policy */\n unknownRequestPolicy: UnknownRequestPolicy;\n /** Unknown cookie policy */\n unknownCookiePolicy: UnknownRequestPolicy;\n /** Sync endpoint */\n syncEndpoint: string;\n /** Telemetry partitioning */\n telemetryPartitioning: TelemetryPartitionStrategy;\n /** Signed IAB agreement */\n signedIabAgreement: SignedIabAgreementOption;\n /** Sync groups */\n syncGroups: string;\n /** Partition parameter */\n partition: string;\n };\n /** When using a custom partition, this is the partition value */\n partition?: {\n /** Partition value */\n partition: string;\n };\n}\n\n/**\n * Fetch consent manager\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager ID in organization\n */\nexport async function fetchConsentManager(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentManager> {\n const {\n consentManager: { consentManager },\n } = await makeGraphQLRequest<{\n /** Consent manager query */\n consentManager: {\n /** Consent manager object */\n consentManager: ConsentManager;\n };\n }>(client, FETCH_CONSENT_MANAGER, { logger: options.logger });\n return consentManager;\n}\n","import { gql } from 'graphql-request';\n\nexport const CONSENT_MANAGER_ANALYTICS_DATA = gql`\n query TranscendCliConsentManagerAnalyticsData($input: AnalyticsInput!) {\n analyticsData(input: $input) {\n series {\n name\n points {\n key\n value\n }\n }\n }\n }\n`;\n","import { ConsentManagerMetricBin } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CONSENT_MANAGER_ANALYTICS_DATA } from './gqls/consentManagerMetrics.js';\n\nexport interface ConsentManagerMetric {\n /** Name of metric */\n name: string;\n /** The metrics */\n points: {\n /** Key of metric */\n key: string;\n /** Value of metric */\n value: string;\n }[];\n}\n\n/**\n * Fetch consent manager analytics data\n *\n * @param client - GraphQL client\n * @param input - Input for fetching data\n * @param options - Options\n * @returns Consent manager purposes in the organization\n */\nexport async function fetchConsentManagerAnalyticsData(\n client: GraphQLClient,\n input: {\n /** Data source */\n dataSource:\n | 'PRIVACY_SIGNAL_TIMESERIES'\n | 'CONSENT_CHANGES_TIMESERIES'\n | 'CONSENT_SESSIONS_BY_REGIME';\n /** Start date, in ISO string format */\n startDate: string;\n /** End date, in ISO string format */\n endDate: string;\n /** Force refetching */\n forceRefetch?: boolean;\n /** Airgap bundle ID */\n airgapBundleId: string;\n /** Bin interval */\n binInterval: ConsentManagerMetricBin;\n /** Whether or not to smooth the time series */\n smoothTimeseries: false;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentManagerMetric[]> {\n const {\n analyticsData: { series },\n } = await makeGraphQLRequest<{\n /** Analytics data response */\n analyticsData: {\n /** Consent manager metrics */\n series: ConsentManagerMetric[];\n };\n }>(client, CONSENT_MANAGER_ANALYTICS_DATA, {\n variables: { input },\n logger: options.logger,\n });\n return series;\n}\n","import { gql } from 'graphql-request';\n\n// ---- Shared experience types ----\n\n/** Region associated with a consent experience */\nexport interface TranscendExperienceRegionGql {\n /** ISO country subdivision code */\n countrySubDivision?: string;\n /** ISO country code */\n country?: string;\n}\n\n/** Experience purpose reference */\nexport interface TranscendExperiencePurposeGql {\n /** Purpose display name */\n name: string;\n /** Purpose slug */\n trackingType: string;\n}\n\n/** Consent experience (regional regime) returned by EXPERIENCES query */\nexport interface TranscendExperienceGql {\n /** Experience ID */\n id: string;\n /** Internal name */\n name: string;\n /** Display name shown to users */\n displayName?: string;\n /** Regions where this experience applies */\n regions: TranscendExperienceRegionGql[];\n /** Whether regions list is inclusive or exclusive */\n operator: string;\n /** Display priority for ordering */\n displayPriority: number;\n /** Behavior on consent expiry */\n onConsentExpiry: string;\n /** Consent expiry duration */\n consentExpiry: number;\n /** Initial view state for auto-prompting */\n viewState: string;\n /** Purposes that can be opted out of */\n purposes: TranscendExperiencePurposeGql[];\n /** Purposes opted out by default */\n optedOutPurposes: TranscendExperiencePurposeGql[];\n /** Browser languages for this experience */\n browserLanguages: string[];\n /** Browser time zones for this experience */\n browserTimeZones: string[];\n /** Consent UI variant assigned to this experience */\n consentUiVariant?: {\n /** Variant ID */\n id: string;\n };\n}\n\n// ---- EXPERIENCES query ----\n\n/** Full response from the EXPERIENCES query */\nexport interface TranscendCliExperiencesResponse {\n /** Experiences result */\n experiences: {\n /** Total count of experiences */\n totalCount: number;\n /** List of experience nodes */\n nodes: TranscendExperienceGql[];\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const EXPERIENCES = gql`\n query TranscendCliExperiences($first: Int!, $offset: Int!) {\n experiences(first: $first, offset: $offset, useMaster: false) {\n totalCount\n nodes {\n id\n name\n displayName\n regions {\n countrySubDivision\n country\n }\n operator\n displayPriority\n onConsentExpiry\n consentExpiry\n viewState\n purposes {\n name\n trackingType\n }\n optedOutPurposes {\n name\n trackingType\n }\n browserLanguages\n browserTimeZones\n consentUiVariant {\n id\n slug\n }\n }\n }\n }\n`;\n\n// ---- UPDATE_CONSENT_EXPERIENCE mutation ----\n\n/** Full response from the UPDATE_CONSENT_EXPERIENCE mutation */\nexport interface TranscendCliUpdateConsentExperienceResponse {\n /** Mutation result */\n updateExperience: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const UPDATE_CONSENT_EXPERIENCE = gql`\n mutation TranscendCliUpdateConsentExperience($input: UpdateExperienceInput!) {\n updateExperience(input: $input) {\n clientMutationId\n }\n }\n`;\n\n// ---- CREATE_CONSENT_EXPERIENCE mutation ----\n\n/** Full response from the CREATE_CONSENT_EXPERIENCE mutation */\nexport interface TranscendCliCreateConsentExperienceResponse {\n /** Mutation result */\n createExperience: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const CREATE_CONSENT_EXPERIENCE = gql`\n mutation TranscendCliCreateConsentExperience($input: CreateExperienceInput!) {\n createExperience(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { InitialViewState, BrowserLanguage, OnConsentExpiry } from '@transcend-io/airgap.js-types';\nimport {\n RegionsOperator,\n IsoCountrySubdivisionCode,\n IsoCountryCode,\n BrowserTimeZone,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { EXPERIENCES } from './gqls/experiences.js';\n\nconst PAGE_SIZE = 50;\n\nexport interface ConsentExperience {\n /** ID of experience */\n id: string;\n /** Name of experience */\n name: string;\n /** Experience display name */\n displayName?: string;\n /** Region that define this regional experience */\n regions: {\n /** Sub division */\n countrySubDivision?: IsoCountrySubdivisionCode;\n /** Country */\n country?: IsoCountryCode;\n }[];\n /** In vs not in operator */\n operator: RegionsOperator;\n /** Priority of experience */\n displayPriority: number;\n /** View state to prompt when auto prompting is enabled */\n viewState: InitialViewState;\n /** Consent expiry setting */\n onConsentExpiry: OnConsentExpiry;\n /** Consent expiry */\n consentExpiry: number;\n /** Purposes that can be opted out of in a particular experience */\n purposes: {\n /** Name of purpose */\n name: string;\n /** Purpose slug */\n trackingType: string;\n }[];\n /** Purposes that are opted out by default in a particular experience */\n optedOutPurposes: {\n /** Name of purpose */\n name: string;\n /** Purpose slug */\n trackingType: string;\n }[];\n /** Browser languages that define this regional experience */\n browserLanguages: BrowserLanguage[];\n /** Browser time zones that define this regional experience */\n browserTimeZones: BrowserTimeZone[];\n /** Consent UI variant assigned to this experience */\n consentUiVariant?: {\n /** Variant ID */\n id: string;\n /** Variant slug */\n slug: string;\n };\n}\n\n/**\n * Fetch consent manager experiences\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager experiences in the organization\n */\nexport async function fetchConsentManagerExperiences(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentExperience[]> {\n const experiences: ConsentExperience[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n experiences: { nodes },\n } = await makeGraphQLRequest<{\n /** Consent experience */\n experiences: {\n /** List */\n nodes: ConsentExperience[];\n };\n }>(client, EXPERIENCES, {\n variables: { first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n experiences.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return experiences.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport {\n FETCH_CONSENT_MANAGER_THEME,\n type TranscendConsentManagerThemeGql,\n type TranscendCliFetchConsentManagerThemeResponse,\n} from './gqls/consentManager.js';\n\n/**\n * Fetch consent manager theme\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager theme\n */\nexport async function fetchConsentManagerTheme(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** Airgap bundle ID to fetch for */\n airgapBundleId: string;\n };\n },\n): Promise<TranscendConsentManagerThemeGql> {\n const {\n consentManagerTheme: { theme },\n } = await makeGraphQLRequest<TranscendCliFetchConsentManagerThemeResponse>(\n client,\n FETCH_CONSENT_MANAGER_THEME,\n {\n variables: { airgapBundleId: options.filterBy.airgapBundleId },\n logger: options.logger,\n },\n );\n return theme;\n}\n","import type { ThemeConfiguration } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { FETCH_CONSENT_UI_THEMES } from './gqls/consentManager.js';\n\nconst PAGE_SIZE = 50;\n\n/** A consent UI theme returned by the consentUiThemes query */\nexport interface ConsentUiTheme {\n /** The id of the consent UI theme */\n id: string;\n /** The display name for this UI theme */\n name: string;\n /** The unique identifier for this UI theme */\n slug: string;\n /** Theme styling configuration */\n configuration: ThemeConfiguration;\n}\n\n/** Paginated response from the consentUiThemes GraphQL query */\nexport interface ConsentUiThemesQueryResponse {\n /** Consent UI themes for the airgap bundle */\n nodes: ConsentUiTheme[];\n /** Total number of themes matching the filter */\n totalCount: number;\n}\n\n/**\n * Fetch consent themes\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns consent UI themes\n */\nexport async function fetchConsentThemes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentUiTheme[]> {\n const themes: ConsentUiTheme[] = [];\n let offset = 0;\n const airgapBundleId = await fetchConsentManagerId(client, { logger: options.logger });\n\n let shouldContinue = false;\n do {\n const {\n consentUiThemes: { nodes },\n } = await makeGraphQLRequest<{\n consentUiThemes: ConsentUiThemesQueryResponse;\n }>(client, FETCH_CONSENT_UI_THEMES, {\n variables: { airgapBundleId, first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n themes.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return themes;\n}\n","import type {\n ConsentUiUserFlow,\n UIConfiguration,\n UiVariantStatus,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { FETCH_CONSENT_UI_VARIANTS } from './gqls/consentManager.js';\n\nconst PAGE_SIZE = 50;\n\n/** A consent UI variant returned by the consentUiVariants query */\nexport interface ConsentUiVariant {\n /** The id of the consent UI variant */\n id: string;\n /** The display name for this UI variant */\n name: string;\n /** The unique identifier for this UI variant */\n slug: string;\n /** Description of this UI variant (null when unset) */\n description?: string | null;\n /** Locales this UI variant applies to */\n locales: string[];\n /** Variant behavior and layer configuration */\n configuration: UIConfiguration;\n /** Status of this UI variant */\n status: UiVariantStatus;\n /** The consent UI theme associated with this UI variant */\n theme?: {\n /** The id of the consent UI theme */\n id: string;\n /** The slug of the consent UI theme */\n slug: string;\n } | null;\n /** The user flow for this UI variant (null when unset) */\n userFlow?: ConsentUiUserFlow | null;\n}\n\n/** Paginated response from the consentUiVariants GraphQL query */\nexport interface ConsentUiVariantsQueryResponse {\n /** Consent UI variants for the airgap bundle */\n nodes: ConsentUiVariant[];\n /** Total number of variants matching the filter */\n totalCount: number;\n}\n\n/**\n * Fetch consent variants\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns consent UI variants\n */\nexport async function fetchConsentVariants(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentUiVariant[]> {\n const variants: ConsentUiVariant[] = [];\n let offset = 0;\n const airgapBundleId = await fetchConsentManagerId(client, { logger: options.logger });\n\n let shouldContinue = false;\n do {\n const {\n consentUiVariants: { nodes },\n } = await makeGraphQLRequest<{\n consentUiVariants: ConsentUiVariantsQueryResponse;\n }>(client, FETCH_CONSENT_UI_VARIANTS, {\n variables: { airgapBundleId, first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n variants.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return variants;\n}\n","import { gql } from 'graphql-request';\n\n/** Dimension values on an aggregate analytics row */\nexport interface AirgapBundleAggregateAnalyticsDimensionsGql {\n /** Consent value (true = opted in, false = opted out) */\n NEW_VALUE?: string;\n /** Privacy regime */\n REGIME?: string;\n /** Tracking purpose */\n PURPOSE?: string;\n}\n\n/** Single row from airgapBundleAggregateAnalytics */\nexport interface AirgapBundleAggregateAnalyticsItemGql {\n /** Count or measure value */\n measure: string;\n /** Breakdown dimensions when requested */\n dimensions?: AirgapBundleAggregateAnalyticsDimensionsGql;\n}\n\n/** Single row from airgapBundleTimeseriesAnalytics */\nexport interface AirgapBundleTimeseriesAnalyticsItemGql {\n /** Bin timestamp */\n time: string;\n /** Metric name */\n metric: string;\n /** Count or measure value */\n measure: string;\n}\n\n/** Full response from AIRGAP_BUNDLE_AGGREGATE_ANALYTICS */\nexport interface TranscendCliAirgapBundleAggregateAnalyticsResponse {\n /** Aggregate analytics result */\n airgapBundleAggregateAnalytics: {\n /** Analytics rows */\n items: AirgapBundleAggregateAnalyticsItemGql[];\n };\n}\n\n/** Full response from AIRGAP_BUNDLE_TIMESERIES_ANALYTICS */\nexport interface TranscendCliAirgapBundleTimeseriesAnalyticsResponse {\n /** Timeseries analytics result */\n airgapBundleTimeseriesAnalytics: {\n /** Timeseries rows */\n items: AirgapBundleTimeseriesAnalyticsItemGql[];\n };\n}\n\nexport const AIRGAP_BUNDLE_AGGREGATE_ANALYTICS = gql`\n query TranscendCliAirgapBundleAggregateAnalytics(\n $id: ID!\n $input: AirgapBundleAggregateAnalyticsInput!\n ) {\n airgapBundleAggregateAnalytics(id: $id, input: $input) {\n items {\n measure\n dimensions {\n NEW_VALUE\n REGIME\n PURPOSE\n }\n }\n }\n }\n`;\n\nexport const AIRGAP_BUNDLE_TIMESERIES_ANALYTICS = gql`\n query TranscendCliAirgapBundleTimeseriesAnalytics(\n $id: ID!\n $input: AirgapBundleTimeseriesAnalyticsInput!\n ) {\n airgapBundleTimeseriesAnalytics(id: $id, input: $input) {\n items {\n time\n metric\n measure\n }\n }\n }\n`;\n","import type {\n AirgapBundleAnalyticsBinInterval,\n AirgapBundleAnalyticsDimension,\n AirgapBundleAnalyticsMetric,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport {\n AIRGAP_BUNDLE_AGGREGATE_ANALYTICS,\n AIRGAP_BUNDLE_TIMESERIES_ANALYTICS,\n type AirgapBundleAggregateAnalyticsItemGql,\n type AirgapBundleTimeseriesAnalyticsItemGql,\n type TranscendCliAirgapBundleAggregateAnalyticsResponse,\n type TranscendCliAirgapBundleTimeseriesAnalyticsResponse,\n} from './gqls/airgapBundleAnalytics.js';\n\n/**\n * Fetch aggregate consent analytics for an airgap bundle.\n *\n * @see https://docs.transcend.io/docs/articles/consent-management/configuration/consent-stats-with-gql-api\n */\nexport async function fetchAirgapBundleAggregateAnalytics(\n client: GraphQLClient,\n input: {\n /** Airgap bundle ID */\n airgapBundleId: string;\n /** Analytics metric to query */\n metric: AirgapBundleAnalyticsMetric;\n /** Start time (epoch seconds) */\n start: number;\n /** End time (epoch seconds) */\n end: number;\n /** Optional dimension breakdowns */\n includeDimensions?: AirgapBundleAnalyticsDimension[];\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AirgapBundleAggregateAnalyticsItemGql[]> {\n const {\n airgapBundleAggregateAnalytics: { items },\n } = await makeGraphQLRequest<TranscendCliAirgapBundleAggregateAnalyticsResponse>(\n client,\n AIRGAP_BUNDLE_AGGREGATE_ANALYTICS,\n {\n variables: {\n id: input.airgapBundleId,\n input: {\n metric: input.metric,\n start: input.start,\n end: input.end,\n ...(input.includeDimensions?.length\n ? { includeDimensions: input.includeDimensions }\n : {}),\n },\n },\n logger: options.logger,\n },\n );\n return items;\n}\n\n/**\n * Fetch timeseries consent analytics for an airgap bundle.\n *\n * @see https://docs.transcend.io/docs/articles/consent-management/configuration/consent-stats-with-gql-api\n */\nexport async function fetchAirgapBundleTimeseriesAnalytics(\n client: GraphQLClient,\n input: {\n /** Airgap bundle ID */\n airgapBundleId: string;\n /** Analytics metric to query */\n metric: AirgapBundleAnalyticsMetric;\n /** Start time (epoch seconds) */\n start: number;\n /** End time (epoch seconds) */\n end: number;\n /** Time bin size */\n binInterval: AirgapBundleAnalyticsBinInterval;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AirgapBundleTimeseriesAnalyticsItemGql[]> {\n const {\n airgapBundleTimeseriesAnalytics: { items },\n } = await makeGraphQLRequest<TranscendCliAirgapBundleTimeseriesAnalyticsResponse>(\n client,\n AIRGAP_BUNDLE_TIMESERIES_ANALYTICS,\n {\n variables: {\n id: input.airgapBundleId,\n input: {\n metric: input.metric,\n start: input.start,\n end: input.end,\n binInterval: input.binInterval,\n },\n },\n logger: options.logger,\n },\n );\n return items;\n}\n","import { gql } from 'graphql-request';\n\n/** Preference topic title translation */\nexport interface TranscendPreferenceTopicTitle {\n /** Translated description */\n description: string;\n}\n\n/** Preference topic linked to a purpose */\nexport interface TranscendPreferenceTopicGql {\n /** Topic ID */\n id: string;\n /** Topic slug */\n slug: string;\n /** Topic title with translations */\n title: TranscendPreferenceTopicTitle;\n /** Display color */\n color: string;\n}\n\n/** Data silo (integration) mapped to a purpose */\nexport interface TranscendMappedDataSiloGql {\n /** Data silo ID */\n id: string;\n /** Data silo display title */\n title: string;\n /** Data silo type */\n type: string;\n}\n\n/** Tracking purpose returned by the PURPOSES query */\nexport interface TranscendPurposeGql {\n /** Purpose ID */\n id: string;\n /** Purpose display name */\n name: string;\n /** Purpose description */\n description: string | null;\n /** Default consent state */\n defaultConsent: string;\n /** Purpose slug */\n trackingType: string;\n /** Whether the purpose is user-configurable */\n configurable: boolean;\n /** Whether the purpose is essential (non-optional) */\n essential: boolean;\n /** Whether the purpose appears in the consent manager UI */\n showInConsentManager: boolean;\n /** Whether the purpose is currently active */\n isActive: boolean;\n /** Display ordering weight */\n displayOrder: number;\n /** Signals that trigger automatic opt-out (e.g. GPC) */\n optOutSignals: string[];\n /** Soft-deletion timestamp, null if active */\n deletedAt: string | null;\n /** Authentication level required */\n authLevel: string;\n /** Whether the purpose appears in the privacy center */\n showInPrivacyCenter: boolean;\n /** Localized display title */\n title: string | null;\n /** Linked preference topics */\n preferenceTopics: TranscendPreferenceTopicGql[];\n /** Number of data points tracked under this purpose */\n purposeDataPointCount: number;\n /** Number of preference topic option value data points */\n preferenceTopicOptionValueDataPointCount: number;\n /** Data silos mapped to this purpose */\n mappedDataSilos: TranscendMappedDataSiloGql[];\n}\n\n/** Full response from the PURPOSES query */\nexport interface TranscendCliPurposesResponse {\n /** Purposes result */\n purposes: {\n /** List of purpose nodes */\n nodes: TranscendPurposeGql[];\n /** Total count of purposes */\n totalCount: number;\n };\n}\n\nexport const PURPOSES = gql`\n query TranscendCliPurposes($first: Int!) {\n purposes(first: $first) {\n nodes {\n id\n name\n description\n defaultConsent\n trackingType\n configurable\n essential\n showInConsentManager\n isActive\n displayOrder\n optOutSignals\n deletedAt\n authLevel\n showInPrivacyCenter\n title\n preferenceTopics {\n id\n slug\n title {\n description\n }\n color\n }\n purposeDataPointCount\n preferenceTopicOptionValueDataPointCount\n mappedDataSilos {\n id\n title\n type\n }\n }\n totalCount\n }\n }\n`;\n","import { gql } from 'graphql-request';\n\n// ---- Shared partition types ----\n\n/** Consent partition node */\nexport interface TranscendConsentPartitionGql {\n /** Partition ID */\n id: string;\n /** Partition name */\n name: string;\n /** Partition key */\n partition: string;\n}\n\n// ---- CONSENT_PARTITIONS query ----\n\n/** Full response from the CONSENT_PARTITIONS query */\nexport interface TranscendCliConsentPartitionsResponse {\n /** Partitions result */\n consentPartitions: {\n /** List of partition nodes */\n nodes: TranscendConsentPartitionGql[];\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// useMaster: false\n// isExportCsv: true\nexport const CONSENT_PARTITIONS = gql`\n query TranscendCliConsentPartitions($first: Int!, $offset: Int!) {\n consentPartitions(first: $first, offset: $offset) {\n nodes {\n id\n name\n partition\n }\n }\n }\n`;\n\n// ---- CREATE_CONSENT_PARTITION mutation ----\n\n/** Full response from the CREATE_CONSENT_PARTITION mutation */\nexport interface TranscendCliCreateConsentPartitionResponse {\n /** Mutation result */\n createConsentPartition: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const CREATE_CONSENT_PARTITION = gql`\n mutation TranscendCliCreateConsentPartition($input: CreateConsentPartitionInput!) {\n createConsentPartition(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { gql } from 'graphql-request';\n\n/** Stats counts returned by COOKIE_STATS and DATA_FLOW_STATS */\nexport interface TranscendTrackerStatsGql {\n /** Number of live (approved) items */\n liveCount: number;\n /** Number of items needing review */\n needReviewCount: number;\n /** Number of junked items */\n junkCount: number;\n}\n\n/** Full response from the COOKIE_STATS query */\nexport interface TranscendCliCookieStatsResponse {\n /** Cookie stats result */\n cookieStats: TranscendTrackerStatsGql;\n}\n\nexport const COOKIE_STATS = gql`\n query TranscendCliCookieStats($input: AirgapBundleInput!) {\n cookieStats(input: $input) {\n liveCount\n needReviewCount\n junkCount\n }\n }\n`;\n\n/** Full response from the DATA_FLOW_STATS query */\nexport interface TranscendCliDataFlowStatsResponse {\n /** Data flow stats result */\n dataFlowStats: TranscendTrackerStatsGql;\n}\n\nexport const DATA_FLOW_STATS = gql`\n query TranscendCliDataFlowStats($input: AirgapBundleInput!) {\n dataFlowStats(input: $input) {\n liveCount\n needReviewCount\n junkCount\n }\n }\n`;\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const CONSENT_WORKFLOW_TRIGGERS: DocumentNode = parse(gql`\n query TranscendCliConsentWorkflowTriggers($first: Int!, $offset: Int!) {\n consentWorkflowTriggers(first: $first, offset: $offset) {\n nodes {\n id\n name\n triggerCondition\n isSilent\n allowUnauthenticated\n isActive\n workflowConfigId\n action {\n type\n }\n subject {\n type\n }\n dataSilos {\n title\n }\n }\n totalCount\n }\n }\n`);\n\nexport const CREATE_OR_UPDATE_CONSENT_WORKFLOW_TRIGGER: DocumentNode = parse(gql`\n mutation TranscendCliCreateOrUpdateConsentWorkflowTrigger(\n $input: CreateOrUpdateConsentWorkflowTriggerInput!\n ) {\n createOrUpdateConsentWorkflowTrigger(input: $input) {\n consentWorkflowTrigger {\n id\n name\n }\n }\n }\n`);\n","/**\n * Resolve displayed child organization URIs or IDs to organization IDs.\n *\n * Each value may be either an organization ID or URI. IDs are matched first,\n * then URIs. Throws if any value cannot be resolved.\n *\n * @param childOrganizations - Child organizations available on the privacy center\n * @param urisOrIds - URIs and/or IDs from transcend.yml\n * @returns Resolved organization IDs (same order as input)\n */\nexport function resolveDisplayedChildOrganizationIds(\n childOrganizations: Array<{\n /** Organization ID */\n id: string;\n /** Organization URI */\n uri: string;\n }>,\n urisOrIds: string[],\n): string[] {\n return urisOrIds.map((value) => {\n const byId = childOrganizations.find((child) => child.id === value);\n if (byId) {\n return byId.id;\n }\n const byUri = childOrganizations.find((child) => child.uri === value);\n if (byUri) {\n return byUri.id;\n }\n const available =\n childOrganizations.length > 0\n ? childOrganizations.map((child) => `${child.uri} (${child.id})`).join(', ')\n : '(none)';\n throw new Error(\n `Failed to resolve displayed child organization URI or ID: \"${value}\". Available: ${available}`,\n );\n });\n}\n","import type { ConsentThemeInput, ConsentVariantInput } from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentThemes, type ConsentUiTheme } from './fetchConsentThemes.js';\nimport { fetchConsentVariants, type ConsentUiVariant } from './fetchConsentVariants.js';\nimport {\n CREATE_CONSENT_UI_THEME,\n CREATE_CONSENT_UI_VARIANT,\n UPDATE_CONSENT_UI_THEME,\n UPDATE_CONSENT_UI_VARIANT,\n} from './gqls/consentManager.js';\n\n/**\n * Parse a JSON configuration string from transcend.yml\n *\n * @param configuration - JSON configuration string\n * @param resourceLabel - Label for error messages\n * @returns Parsed configuration object\n */\nfunction parseConsentUiConfiguration(\n configuration: string,\n resourceLabel: string,\n): Record<string, unknown> {\n try {\n return JSON.parse(configuration) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON configuration for ${resourceLabel}`);\n }\n}\n\n/** Consent UI theme synced to Transcend (id + slug for variant theme assignment) */\nexport type SyncedConsentUiTheme = Pick<ConsentUiTheme, 'id' | 'slug'>;\n\n/** Consent UI variant synced to Transcend (id + slug for experience consentUiVariantSlug assignment) */\nexport type SyncedConsentUiVariant = Pick<ConsentUiVariant, 'id' | 'slug'>;\n\n/**\n * Sync consent UI themes from transcend.yml\n *\n * @param client - GraphQL client\n * @param airgapBundleId - Airgap bundle ID\n * @param yamlThemes - Theme inputs from transcend.yml\n * @param options - Options\n * @returns Synced themes (id + slug) for resolving variant themeSlug assignments\n */\nexport async function syncConsentUiThemes(\n client: GraphQLClient,\n airgapBundleId: string,\n yamlThemes: ConsentThemeInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SyncedConsentUiTheme[]> {\n const { logger = NOOP_LOGGER } = options;\n const remoteThemes = await fetchConsentThemes(client, { logger });\n const remoteThemeBySlug = keyBy(remoteThemes, 'slug');\n const syncedThemes: SyncedConsentUiTheme[] = [];\n\n await map(\n yamlThemes,\n async (yamlTheme, index) => {\n const resourceLabel = `consentManager.consentThemes[${index}]`;\n const configuration = parseConsentUiConfiguration(yamlTheme.configuration, resourceLabel);\n const remoteTheme = remoteThemeBySlug[yamlTheme.slug];\n\n if (remoteTheme) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_UI_THEME, {\n variables: {\n input: {\n id: remoteTheme.id,\n airgapBundleId,\n name: yamlTheme.name,\n configuration,\n },\n },\n logger,\n });\n syncedThemes.push({ id: remoteTheme.id, slug: yamlTheme.slug });\n logger.info(`Successfully synced consent UI theme \"${yamlTheme.name}\"!`);\n } else {\n const result = await makeGraphQLRequest<{\n createConsentUiTheme: {\n consentUiTheme: Pick<ConsentUiTheme, 'id' | 'name'>;\n };\n }>(client, CREATE_CONSENT_UI_THEME, {\n variables: {\n input: {\n airgapBundleId,\n name: yamlTheme.name,\n slug: yamlTheme.slug,\n configuration,\n },\n },\n logger,\n });\n syncedThemes.push({\n id: result.createConsentUiTheme.consentUiTheme.id,\n slug: yamlTheme.slug,\n });\n logger.info(`Successfully created consent UI theme \"${yamlTheme.name}\"!`);\n }\n },\n { concurrency: 5 },\n );\n\n return syncedThemes;\n}\n\n/**\n * Sync consent UI variants from transcend.yml\n *\n * @param client - GraphQL client\n * @param airgapBundleId - Airgap bundle ID\n * @param yamlVariants - Variant inputs from transcend.yml\n * @param syncedThemes - Themes synced by syncConsentUiThemes, used to resolve themeSlug\n * @param options - Options\n * @returns Synced variants (id + slug) for resolving experience consentUiVariantSlug assignments\n */\nexport async function syncConsentUiVariants(\n client: GraphQLClient,\n airgapBundleId: string,\n yamlVariants: ConsentVariantInput[],\n syncedThemes: SyncedConsentUiTheme[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SyncedConsentUiVariant[]> {\n const { logger = NOOP_LOGGER } = options;\n const remoteVariants = await fetchConsentVariants(client, { logger });\n const remoteVariantBySlug = keyBy(remoteVariants, 'slug');\n const syncedThemeBySlug = keyBy(syncedThemes, 'slug');\n const syncedVariants: SyncedConsentUiVariant[] = [];\n\n await map(\n yamlVariants,\n async (yamlVariant, index) => {\n const resourceLabel = `consentManager.consentVariants[${index}]`;\n const configuration = parseConsentUiConfiguration(yamlVariant.configuration, resourceLabel);\n const remoteThemeId = yamlVariant.themeSlug\n ? syncedThemeBySlug[yamlVariant.themeSlug]?.id\n : undefined;\n const remoteVariant = remoteVariantBySlug[yamlVariant.slug];\n\n // Missing/null description means leave unset — do not clear on sync\n const descriptionInput =\n yamlVariant.description != null ? { description: yamlVariant.description } : {};\n\n if (remoteVariant) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_UI_VARIANT, {\n variables: {\n input: {\n id: remoteVariant.id,\n airgapBundleId,\n name: yamlVariant.name,\n ...descriptionInput,\n locales: yamlVariant.locales,\n configuration,\n status: yamlVariant.status,\n ...(remoteThemeId ? { themeId: remoteThemeId } : {}),\n },\n },\n logger,\n });\n syncedVariants.push({ id: remoteVariant.id, slug: yamlVariant.slug });\n logger.info(`Successfully synced consent UI variant \"${yamlVariant.name}\"!`);\n } else {\n const result = await makeGraphQLRequest<{\n createConsentUiVariant: {\n consentUiVariant: Pick<ConsentUiVariant, 'id'>;\n };\n }>(client, CREATE_CONSENT_UI_VARIANT, {\n variables: {\n input: {\n airgapBundleId,\n name: yamlVariant.name,\n slug: yamlVariant.slug,\n ...descriptionInput,\n locales: yamlVariant.locales,\n configuration,\n status: yamlVariant.status,\n ...(remoteThemeId ? { themeId: remoteThemeId } : {}),\n },\n },\n logger,\n });\n syncedVariants.push({\n id: result.createConsentUiVariant.consentUiVariant.id,\n slug: yamlVariant.slug,\n });\n logger.info(`Successfully created consent UI variant \"${yamlVariant.name}\"!`);\n }\n },\n { concurrency: 5 },\n );\n\n return syncedVariants;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { difference } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport {\n CONSENT_PARTITIONS,\n CREATE_CONSENT_PARTITION,\n type TranscendConsentPartitionGql,\n type TranscendCliConsentPartitionsResponse,\n} from './gqls/partitions.js';\n\nconst PAGE_SIZE = 50;\n\nexport interface PartitionInput {\n /** Name of partition */\n name: string;\n /** Value of partition, cannot be pushed, can only be pulled */\n partition?: string;\n}\n\n/**\n * Fetch the list of partitions\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Partition list\n */\nexport async function fetchPartitions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<TranscendConsentPartitionGql[]> {\n const { logger = NOOP_LOGGER } = options;\n const partitions: TranscendConsentPartitionGql[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n consentPartitions: { nodes },\n } = await makeGraphQLRequest<TranscendCliConsentPartitionsResponse>(\n client,\n CONSENT_PARTITIONS,\n {\n variables: { first: PAGE_SIZE, offset },\n logger,\n },\n );\n partitions.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return partitions.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Sync the consent partitions\n *\n * @param client - GraphQL client\n * @param partitionInputs - The partition inputs\n * @param options - Options\n * @returns True on success\n */\nexport async function syncPartitions(\n client: GraphQLClient,\n partitionInputs: PartitionInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n let encounteredError = false;\n const partitions = await fetchPartitions(client, { logger });\n const newPartitionNames = difference(\n partitionInputs.map(({ name }) => name),\n partitions.map(({ name }) => name),\n );\n await mapSeries(newPartitionNames, async (name) => {\n try {\n await makeGraphQLRequest(client, CREATE_CONSENT_PARTITION, {\n variables: {\n input: {\n id: airgapBundleId,\n name,\n },\n },\n logger,\n });\n logger.info(`Successfully created consent partition: ${name}!`);\n } catch (err) {\n logger.error(`Failed to create consent partition: ${name}! - ${(err as Error).message}`);\n encounteredError = true;\n }\n });\n return !encounteredError;\n}\n","import { InitialViewState, OnConsentExpiry, BrowserLanguage } from '@transcend-io/airgap.js-types';\nimport {\n ConsentBundleType,\n ConsentPrecedenceOption,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RegionsOperator,\n UnknownRequestPolicy,\n TelemetryPartitionStrategy,\n SignedIabAgreementOption,\n BrowserTimeZone,\n type ConsentThemeInput,\n type ConsentVariantInput,\n} from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllPurposes } from '../preference-management/fetchAllPurposes.js';\nimport { fetchConsentManagerExperiences } from './fetchConsentManagerExperiences.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { fetchPrivacyCenterId } from './fetchPrivacyCenterId.js';\nimport {\n UPDATE_CONSENT_MANAGER_DOMAINS,\n CREATE_CONSENT_MANAGER,\n UPDATE_LOAD_OPTIONS,\n UPDATE_CONSENT_MANAGER_PARTITION,\n UPDATE_CONSENT_MANAGER_VERSION,\n TOGGLE_TELEMETRY_PARTITION_STRATEGY,\n TOGGLE_UNKNOWN_COOKIE_POLICY,\n TOGGLE_CONSENT_PRECEDENCE,\n TOGGLE_UNKNOWN_REQUEST_POLICY,\n UPDATE_CONSENT_MANAGER_THEME,\n} from './gqls/consentManager.js';\nimport { UPDATE_CONSENT_EXPERIENCE, CREATE_CONSENT_EXPERIENCE } from './gqls/experiences.js';\nimport {\n syncConsentUiThemes,\n syncConsentUiVariants,\n type SyncedConsentUiVariant,\n} from './syncConsentUi.js';\nimport { fetchPartitions } from './syncPartitions.js';\n\nconst PURPOSES_LINK = 'https://app.transcend.io/consent-manager/regional-experiences/purposes';\n\nexport interface ConsentManageExperienceInput {\n /** Name of experience */\n name: string;\n /** Display name of experience */\n displayName?: string;\n /** Regions that define this regional experience */\n regions?: {\n /** Country */\n country?: IsoCountryCode;\n /** Country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n }[];\n /** How to handle consent expiry */\n onConsentExpiry?: OnConsentExpiry;\n /** Consent expiration value */\n consentExpiry?: number;\n /** In vs not in operator */\n operator?: RegionsOperator;\n /** Priority of experience */\n displayPriority?: number;\n /** View state to prompt when auto prompting is enabled */\n viewState?: InitialViewState;\n /** Purposes that can be opted out of in a particular experience */\n purposes?: {\n /** Slug of purpose */\n trackingType: string;\n }[];\n /** Purposes that are opted out by default in a particular experience */\n optedOutPurposes?: {\n /** Slug of purpose */\n trackingType: string;\n }[];\n /** Browser languages that define this regional experience */\n browserLanguages?: BrowserLanguage[];\n /** Browser time zones that define this regional experience */\n browserTimeZones?: BrowserTimeZone[];\n /** Slug of the consent UI variant assigned to this experience */\n consentUiVariantSlug?: string;\n}\n\nexport interface ConsentManagerInput {\n /** Airgap version */\n version?: string;\n /** Bundle URLs per bundle type */\n bundleUrls?: Partial<Record<ConsentBundleType, string>>;\n /** The consent manager domains */\n domains?: string[];\n /** Key used to partition consent records */\n partition?: string;\n /** Precedence of signals vs user input */\n consentPrecedence?: ConsentPrecedenceOption;\n /** The consent manager unknown request policy */\n unknownRequestPolicy?: UnknownRequestPolicy;\n /** The consent manager unknown cookie policy */\n unknownCookiePolicy?: UnknownRequestPolicy;\n /** The XDI sync endpoint */\n syncEndpoint?: string;\n /** The telemetry partitioning strategy */\n telemetryPartitioning?: TelemetryPartitionStrategy;\n /** Whether the site owner has signed the IAB agreement */\n signedIabAgreement?: SignedIabAgreementOption;\n /** Regional experience configurations */\n experiences?: ConsentManageExperienceInput[];\n /** Theme configuration */\n theme?: {\n /** Primary color */\n primaryColor?: string;\n /** Font color */\n fontColor?: string;\n /** Privacy policy URL */\n privacyPolicy?: string;\n /** Auto-prompt setting */\n prompt?: number;\n };\n /** The Shared XDI host sync groups config (JSON) */\n syncGroups?: string;\n /** Consent UI variant configurations */\n consentVariants?: ConsentVariantInput[];\n /** Consent UI theme configurations */\n consentThemes?: ConsentThemeInput[];\n}\n\n/**\n * Sync consent manager experiences up to Transcend\n *\n * @param client - GraphQL client\n * @param experiences - The experience inputs\n * @param syncedVariants - Variants synced by syncConsentUiVariants, used to resolve consentUiVariantSlug\n * @param options - Options\n */\nexport async function syncConsentManagerExperiences(\n client: GraphQLClient,\n experiences: ConsentManageExperienceInput[],\n syncedVariants: SyncedConsentUiVariant[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n\n const existingExperiences = await fetchConsentManagerExperiences(client, {\n logger,\n });\n const experienceLookup = keyBy(existingExperiences, 'name');\n\n const purposes = await fetchAllPurposes(client, { logger });\n const purposeLookup = keyBy(purposes, 'trackingType');\n\n const syncedVariantBySlug = keyBy(syncedVariants, 'slug');\n\n await map(\n experiences,\n async (exp, ind) => {\n const purposeIds = exp.purposes?.map((purpose, ind2) => {\n const existingPurpose = purposeLookup[purpose.trackingType];\n if (!existingPurpose) {\n throw new Error(\n `Invalid purpose trackingType provided at consentManager.experiences[${ind}].purposes[${ind2}]: ` +\n `${purpose.trackingType}. See list of valid purposes ${PURPOSES_LINK}`,\n );\n }\n return existingPurpose.id;\n });\n const optedOutPurposeIds = exp.optedOutPurposes?.map((purpose, ind2) => {\n const existingPurpose = purposeLookup[purpose.trackingType];\n if (!existingPurpose) {\n throw new Error(\n `Invalid purpose trackingType provided at consentManager.experiences[${ind}].optedOutPurposes[${ind2}]: ` +\n `${purpose.trackingType}. See list of valid purposes ${PURPOSES_LINK}`,\n );\n }\n return existingPurpose.id;\n });\n\n const remoteConsentUiVariantId = exp.consentUiVariantSlug\n ? syncedVariantBySlug[exp.consentUiVariantSlug]?.id\n : undefined;\n const existingExperience = experienceLookup[exp.name];\n if (existingExperience) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_EXPERIENCE, {\n variables: {\n input: {\n id: existingExperience.id,\n name: exp.displayName,\n regions: exp.regions,\n operator: exp.operator,\n onConsentExpiry: exp.onConsentExpiry,\n consentExpiry: exp.consentExpiry,\n displayPriority:\n exp.displayPriority !== existingExperience.displayPriority\n ? exp.displayPriority\n : undefined,\n viewState: exp.viewState,\n purposes: purposeIds,\n optedOutPurposes: optedOutPurposeIds,\n browserLanguages: exp.browserLanguages,\n browserTimeZones: exp.browserTimeZones,\n ...(remoteConsentUiVariantId ? { consentUiVariantId: remoteConsentUiVariantId } : {}),\n },\n },\n logger,\n });\n logger.info(`Successfully synced consent experience \"${exp.name}\"!`);\n } else {\n await makeGraphQLRequest(client, CREATE_CONSENT_EXPERIENCE, {\n variables: {\n input: {\n name: exp.name,\n displayName: exp.displayName,\n regions: exp.regions,\n operator: exp.operator,\n onConsentExpiry: exp.onConsentExpiry || OnConsentExpiry.Prompt,\n consentExpiry: exp.consentExpiry,\n displayPriority: exp.displayPriority,\n viewState: exp.viewState || InitialViewState.Hidden,\n purposes: purposeIds || [],\n optedOutPurposes: optedOutPurposeIds || [],\n browserLanguages: exp.browserLanguages,\n browserTimeZones: exp.browserTimeZones,\n ...(remoteConsentUiVariantId ? { consentUiVariantId: remoteConsentUiVariantId } : {}),\n },\n },\n logger,\n });\n logger.info(`Successfully created consent experience \"${exp.name}\"!`);\n }\n },\n {\n concurrency: 10,\n },\n );\n}\n\n/**\n * Sync the consent manager\n *\n * @param client - GraphQL client\n * @param consentManager - The consent manager input\n * @param options - Options\n */\nexport async function syncConsentManager(\n client: GraphQLClient,\n consentManager: ConsentManagerInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n let airgapBundleId: string;\n\n try {\n airgapBundleId = await fetchConsentManagerId(client, {\n logger,\n maxRequests: 1,\n });\n } catch (err) {\n // TODO: https://transcend.height.app/T-23778\n if ((err as Error).message.includes('AirgapBundle not found')) {\n const privacyCenterId = await fetchPrivacyCenterId(client, {\n logger,\n });\n\n const { createConsentManager } = await makeGraphQLRequest<{\n /** Create consent manager */\n createConsentManager: {\n /** Consent manager */\n consentManager: {\n /** ID */\n id: string;\n };\n };\n }>(client, CREATE_CONSENT_MANAGER, {\n variables: { privacyCenterId },\n logger,\n });\n airgapBundleId = createConsentManager.consentManager.id;\n } else {\n throw err;\n }\n }\n\n if (consentManager.domains) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_DOMAINS, {\n variables: { domains: consentManager.domains, airgapBundleId },\n logger,\n });\n }\n\n if (consentManager.partition) {\n const partitions = await fetchPartitions(client, { logger });\n const partitionToUpdate = partitions.find((part) => part.name === consentManager.partition);\n if (!partitionToUpdate) {\n throw new Error(\n `Partition \"${consentManager.partition}\" not found. Please create the partition first.`,\n );\n }\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_PARTITION, {\n variables: { partitionId: partitionToUpdate.id, airgapBundleId },\n logger,\n });\n }\n\n if (consentManager.version) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_VERSION, {\n variables: { airgapBundleId, version: consentManager.version },\n logger,\n });\n }\n\n if (consentManager.signedIabAgreement) {\n await makeGraphQLRequest(client, UPDATE_LOAD_OPTIONS, {\n variables: {\n input: {\n id: airgapBundleId,\n ...(consentManager.signedIabAgreement\n ? { signedIabAgreement: consentManager.signedIabAgreement }\n : {}),\n },\n },\n logger,\n });\n }\n\n if (consentManager.unknownRequestPolicy) {\n await makeGraphQLRequest(client, TOGGLE_UNKNOWN_REQUEST_POLICY, {\n variables: {\n input: {\n id: airgapBundleId,\n unknownRequestPolicy: consentManager.unknownRequestPolicy,\n },\n },\n logger,\n });\n }\n\n if (consentManager.unknownRequestPolicy) {\n await makeGraphQLRequest(client, TOGGLE_UNKNOWN_COOKIE_POLICY, {\n variables: {\n input: {\n id: airgapBundleId,\n unknownCookiePolicy: consentManager.unknownCookiePolicy,\n },\n },\n logger,\n });\n }\n\n if (consentManager.telemetryPartitioning) {\n await makeGraphQLRequest(client, TOGGLE_TELEMETRY_PARTITION_STRATEGY, {\n variables: {\n input: {\n id: airgapBundleId,\n strategy: consentManager.telemetryPartitioning,\n },\n },\n logger,\n });\n }\n\n if (consentManager.consentPrecedence) {\n await makeGraphQLRequest(client, TOGGLE_CONSENT_PRECEDENCE, {\n variables: {\n input: {\n id: airgapBundleId,\n consentPrecedence: consentManager.consentPrecedence,\n },\n },\n logger,\n });\n }\n\n if (\n consentManager.consentThemes ||\n consentManager.consentVariants ||\n consentManager.experiences\n ) {\n const syncedThemes = consentManager.consentThemes\n ? await syncConsentUiThemes(client, airgapBundleId, consentManager.consentThemes, {\n logger,\n })\n : [];\n\n const syncedVariants = consentManager.consentVariants\n ? await syncConsentUiVariants(\n client,\n airgapBundleId,\n consentManager.consentVariants,\n syncedThemes,\n { logger },\n )\n : [];\n\n if (consentManager.experiences) {\n await syncConsentManagerExperiences(client, consentManager.experiences, syncedVariants, {\n logger,\n });\n }\n }\n\n if (consentManager.theme) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_THEME, {\n variables: {\n input: {\n airgapBundleId,\n ...consentManager.theme,\n },\n },\n logger,\n });\n }\n\n // TODO: https://transcend.height.app/T-23875\n // syncEndpoint: string;\n // TODO: https://transcend.height.app/T-23919\n // syncGroups: string;\n}\n","import { ConsentTrackerStatus } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { UPDATE_OR_CREATE_COOKIES } from './gqls/cookies.js';\n\nexport interface CookieInput {\n /** Name of cookie */\n name: string;\n /** Whether or not the cookie is a regular expression */\n isRegex?: boolean;\n /** Description of cookie */\n description?: string;\n /** The tracking purposes that are required to be opted in for this cookie */\n trackingPurposes?: string[];\n /** Name of the consent service attached */\n service?: string;\n /** Status of the tracker (approved vs triage) */\n status?: ConsentTrackerStatus;\n /** Email addresses of owners */\n owners?: string[];\n /** Names of teams responsible for managing this cookie */\n teams?: string[];\n /** Attribute key-value pairs */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Update or create cookies\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateOrCreateCookies(\n client: GraphQLClient,\n options: {\n /** List of cookie inputs */\n input: CookieInput[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: cookies, logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n await mapSeries(chunk(cookies, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_OR_CREATE_COOKIES, {\n variables: {\n airgapBundleId,\n cookies: page.map((cookie) => ({\n name: cookie.name,\n trackingPurposes:\n cookie.trackingPurposes && cookie.trackingPurposes.length > 0\n ? cookie.trackingPurposes\n : undefined,\n description: cookie.description,\n service: cookie.service,\n status: cookie.status,\n attributes: cookie.attributes,\n isRegex: cookie.isRegex,\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the set of cookies into the product\n *\n * @param client - GraphQL client\n * @param cookies - Cookies to sync\n * @param options - Options\n * @returns True upon success, false upon failure\n */\nexport async function syncCookies(\n client: GraphQLClient,\n cookies: CookieInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${cookies.length}\" cookies...`);\n\n const notUnique = cookies.filter(\n (cookie) =>\n cookies.filter((cook) => cookie.name === cook.name && cookie.isRegex === cook.isRegex)\n .length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload cookies as there were non-unique entries found: ${notUnique\n .map(({ name }) => name)\n .join(',')}`,\n );\n }\n\n try {\n logger.info(`Upserting \"${cookies.length}\" new cookies...`);\n await updateOrCreateCookies(client, { input: cookies, logger });\n logger.info(`Successfully synced ${cookies.length} cookies!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create cookies! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { ConsentTrackerStatus, DataFlowScope } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataFlows } from './fetchAllDataFlows.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { CREATE_DATA_FLOWS, UPDATE_DATA_FLOWS } from './gqls/dataFlows.js';\n\n/** Attribute key-value pair for a data flow */\nexport interface DataFlowAttributeInput {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n}\n\n/**\n * Input to define a data flow for sync\n *\n * @see https://app.transcend.io/consent-manager/data-flows/approved\n */\nexport interface DataFlowInput {\n /** Value of data flow */\n value: string;\n /** Type of data flow */\n type: DataFlowScope;\n /** Description of data flow */\n description?: string;\n /** The tracking purposes that are required to be opted in for this data flow */\n trackingPurposes?: string[];\n /** Name of the consent service attached */\n service?: string;\n /** Status of the tracker (approved vs triage) */\n status?: ConsentTrackerStatus;\n /** Email addresses of owners */\n owners?: string[];\n /** Names of teams responsible for managing this data flow */\n teams?: string[];\n /** Attribute key-value pairs */\n attributes?: DataFlowAttributeInput[];\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Update data flows that already existed\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateDataFlows(\n client: GraphQLClient,\n options: {\n /** [DataFlowInput, Data Flow ID] mappings to update */\n input: [DataFlowInput, string][];\n /** Classify service if missing */\n classifyService?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataFlows, classifyService = false, logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // const purposes = await fetchAllPurposes(client);\n // const purposeNameToId = keyBy(purposes, 'name');\n\n await mapSeries(chunk(dataFlows, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_DATA_FLOWS, {\n variables: {\n airgapBundleId,\n dataFlows: page.map(([flow, id]) => ({\n id,\n value: flow.value,\n type: flow.type,\n trackingType:\n flow.trackingPurposes && flow.trackingPurposes.length > 0\n ? flow.trackingPurposes\n : undefined,\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // purposeIds: flow.trackingPurposes\n // ? flow.trackingPurposes\n // .filter((purpose) => purpose !== 'Unknown')\n // .map((purpose) => purposeNameToId[purpose].id)\n // : undefined,\n description: flow.description,\n service: flow.service,\n status: flow.status,\n attributes: flow.attributes,\n // TODO: https://transcend.height.app/T-23718\n // owners,\n // teams,\n })),\n classifyService,\n },\n logger,\n });\n });\n}\n\n/**\n * Create new data flows\n *\n * @param client - GraphQL client\n * @param dataFlowInputs - List of data flows to create\n * @param options - Options\n */\nexport async function createDataFlows(\n client: GraphQLClient,\n options: {\n /** Data flow inputs to create */\n input: DataFlowInput[];\n /** Classify service if missing */\n classifyService?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataFlowInputs, classifyService = false, logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // const purposes = await fetchAllPurposes(client);\n // const purposeNameToId = keyBy(purposes, 'name');\n\n await mapSeries(chunk(dataFlowInputs, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, CREATE_DATA_FLOWS, {\n variables: {\n airgapBundleId,\n dataFlows: page.map((flow) => ({\n value: flow.value,\n type: flow.type,\n trackingType:\n flow.trackingPurposes && flow.trackingPurposes.length > 0\n ? flow.trackingPurposes\n : undefined,\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // purposeIds: flow.trackingPurposes\n // ? flow.trackingPurposes\n // .filter((purpose) => purpose !== 'Unknown')\n // .map((purpose) => purposeNameToId[purpose].id)\n // : undefined,\n description: flow.description,\n service: flow.service,\n status: flow.status,\n attributes: flow.attributes,\n // TODO: https://transcend.height.app/T-23718\n // owners,\n // teams,\n })),\n classifyService,\n },\n logger,\n });\n });\n}\n\n/**\n * Sync data flow configurations into Transcend\n *\n * @param client - GraphQL client\n * @param dataFlows - The data flows to upload\n * @param options - Options\n * @returns True if the command ran successfully, returns false if an error occurred\n */\nexport async function syncDataFlows(\n client: GraphQLClient,\n dataFlows: DataFlowInput[],\n options: {\n /** When true, auto classify the service based on the data flow value */\n classifyService?: boolean;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { classifyService = false, logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${dataFlows.length}\" data flows...`);\n\n const notUnique = dataFlows.filter(\n (dataFlow) =>\n dataFlows.filter((flow) => dataFlow.value === flow.value && dataFlow.type === flow.type)\n .length > 1,\n );\n\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload data flows as there were non-unique entries found: ${notUnique\n .map(({ value }) => value)\n .join(',')}`,\n );\n }\n\n logger.info('Fetching data flows...');\n const [existingLiveDataFlows, existingInReviewDataFlows] = await Promise.all([\n fetchAllDataFlows(client, {\n logger,\n filterBy: { status: ConsentTrackerStatus.Live },\n }),\n fetchAllDataFlows(client, {\n logger,\n filterBy: { status: ConsentTrackerStatus.NeedsReview },\n }),\n ]);\n const allDataFlows = [...existingLiveDataFlows, ...existingInReviewDataFlows];\n\n const mapDataFlowsToExisting = dataFlows.map((dataFlow) => [\n dataFlow,\n allDataFlows.find((flow) => dataFlow.value === flow.value && dataFlow.type === flow.type)?.id,\n ]);\n\n const newDataFlows = mapDataFlowsToExisting\n .filter(([, existing]) => !existing)\n .map(([flow]) => flow as DataFlowInput);\n try {\n logger.info(`Creating \"${newDataFlows.length}\" new data flows...`);\n await createDataFlows(client, { input: newDataFlows, classifyService, logger });\n logger.info(`Successfully synced ${newDataFlows.length} data flows!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create data flows! - ${(err as Error).message}`);\n }\n\n const existingDataFlows = mapDataFlowsToExisting.filter(\n (x): x is [DataFlowInput, string] => !!x[1],\n );\n try {\n logger.info(`Updating \"${existingDataFlows.length}\" data flows...`);\n await updateDataFlows(client, { input: existingDataFlows, classifyService, logger });\n logger.info(`Successfully updated \"${existingDataFlows.length}\" data flows!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to update data flows! - ${(err as Error).message}`);\n }\n\n logger.info(`Synced \"${dataFlows.length}\" data flows!`);\n\n return !encounteredError;\n}\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport { PolicyType } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllPolicies } from './fetchAllPolicies.js';\nimport { fetchPrivacyCenterId } from './fetchPrivacyCenterId.js';\nimport { UPDATE_POLICIES } from './gqls/policy.js';\n\nexport interface PolicyInput {\n /** The title of the policy */\n title: string;\n /**\n * Policy type. Required by the API on create; optional on update.\n * When omitted on create, inferred from title.\n */\n type?: PolicyType;\n /** Effective date of policy */\n effectiveOn?: string;\n /** Whether or not to disable the effective date */\n disableEffectiveOn?: boolean;\n /** Content of the policy */\n content?: string;\n /** The languages for which the policy is disabled for */\n disabledLocales?: LocaleValue[];\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Infer PolicyType from a policy title when type is not provided on create.\n *\n * @param title - Policy title\n * @returns Inferred policy type\n */\nexport function inferPolicyTypeFromTitle(title: string): PolicyType {\n if (title === 'Privacy Policy') {\n return PolicyType.PrivacyPolicy;\n }\n if (title === 'Cookie Policy') {\n return PolicyType.CookiePolicy;\n }\n return PolicyType.Custom;\n}\n\n/**\n * Update or create policies\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updatePolicies(\n client: GraphQLClient,\n options: {\n /** [PolicyInput, policyId] list */\n input: [PolicyInput, string | undefined][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: policyInputs, logger = NOOP_LOGGER } = options;\n const privacyCenterId = await fetchPrivacyCenterId(client, { logger });\n\n await mapSeries(chunk(policyInputs, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_POLICIES, {\n variables: {\n privacyCenterId,\n policies: page.map(([policy, policyId]) => ({\n id: policyId,\n title: policy.title,\n // Backend requires type on create; leave existing type alone on update\n ...(!policyId\n ? {\n type: policy.type ?? inferPolicyTypeFromTitle(policy.title),\n }\n : {}),\n disableEffectiveOn: policy.disableEffectiveOn,\n disabledLocales: policy.disabledLocales,\n ...(policy.effectiveOn || policy.content\n ? {\n version: {\n ...(policy.effectiveOn ? { effectiveOn: policy.effectiveOn } : {}),\n ...(policy.content\n ? {\n content: {\n defaultMessage: policy.content,\n },\n }\n : {}),\n },\n }\n : {}),\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the set of policies from the YML interface into the product\n *\n * @param client - GraphQL client\n * @param policies - policies to sync\n * @param options - Options\n * @returns True upon success, false upon failure\n */\nexport async function syncPolicies(\n client: GraphQLClient,\n policies: PolicyInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${policies.length}\" policies...`);\n\n const notUnique = policies.filter(\n (policy) => policies.filter((pol) => policy.title === pol.title).length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload policies as there were non-unique entries found: ${notUnique\n .map(({ title }) => title)\n .join(',')}`,\n );\n }\n\n const existingPolicies = await fetchAllPolicies(client, { logger });\n const policiesById = keyBy(existingPolicies, ({ title }) => title.defaultMessage);\n\n try {\n logger.info(`Upserting \"${policies.length}\" new policies...`);\n await updatePolicies(client, {\n input: policies.map((policy) => [policy, policiesById[policy.title]?.id]),\n logger,\n });\n logger.info(`Successfully synced ${policies.length} policies!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create policies! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { PrivacyCenterFooterLink } from './fetchAllPrivacyCenters.js';\nimport {\n DELETE_PRIVACY_CENTER_FOOTER_LINKS,\n UPDATE_PRIVACY_CENTER_FOOTER_LINKS,\n} from './gqls/privacyCenter.js';\n\nexport interface PrivacyCenterFooterLinkInput {\n /** Link title (default locale) */\n title: string;\n /** Link URL (default locale); optional for icon-only links */\n url?: string;\n /**\n * When true with an icon set, render as icon-only (title is used for\n * accessibility). When false with an icon set, render icon beside the\n * visible label.\n */\n iconOnly?: boolean;\n}\n\n/**\n * Sync privacy center footer links. Matches existing links by title, upserts\n * the provided list, and deletes any existing links omitted from the YAML.\n *\n * @param client - GraphQL client\n * @param privacyCenterId - Privacy center ID\n * @param footerLinks - Desired footer links\n * @param existingFooterLinks - Existing footer links from the privacy center\n * @param options - Options\n */\nexport async function syncPrivacyCenterFooterLinks(\n client: GraphQLClient,\n privacyCenterId: string,\n footerLinks: PrivacyCenterFooterLinkInput[],\n existingFooterLinks: PrivacyCenterFooterLink[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n\n const notUnique = footerLinks.filter(\n (link) => footerLinks.filter((other) => other.title === link.title).length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to sync privacy center footer links as there were non-unique titles: ${[\n ...new Set(notUnique.map(({ title }) => title)),\n ].join(', ')}`,\n );\n }\n\n const existingByTitle = keyBy(existingFooterLinks, ({ title }) => title.defaultMessage);\n\n const resolved = footerLinks.map((link) => {\n const byTitle = existingByTitle[link.title];\n return {\n id: byTitle?.id,\n title: link.title,\n ...(link.url !== undefined ? { url: link.url } : {}),\n ...(link.iconOnly !== undefined ? { iconOnly: link.iconOnly } : {}),\n };\n });\n\n const keptIds = new Set(resolved.map((link) => link.id).filter((id): id is string => !!id));\n const idsToDelete = existingFooterLinks.map(({ id }) => id).filter((id) => !keptIds.has(id));\n\n if (idsToDelete.length > 0) {\n logger.info(`Deleting \"${idsToDelete.length}\" privacy center footer links...`);\n await makeGraphQLRequest(client, DELETE_PRIVACY_CENTER_FOOTER_LINKS, {\n variables: {\n input: {\n privacyCenterId,\n ids: idsToDelete,\n },\n },\n logger,\n });\n }\n\n if (resolved.length > 0) {\n logger.info(`Upserting \"${resolved.length}\" privacy center footer links...`);\n await makeGraphQLRequest(client, UPDATE_PRIVACY_CENTER_FOOTER_LINKS, {\n variables: {\n input: {\n privacyCenterId,\n footerLinks: resolved,\n },\n },\n logger,\n });\n }\n}\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type { PrivacyCenterFooterLayout } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllPrivacyCenters } from './fetchAllPrivacyCenters.js';\nimport { fetchPrivacyCenterId } from './fetchPrivacyCenterId.js';\nimport { UPDATE_PRIVACY_CENTER } from './gqls/privacyCenter.js';\nimport { resolveDisplayedChildOrganizationIds } from './resolveDisplayedChildOrganizationIds.js';\nimport {\n syncPrivacyCenterFooterLinks,\n type PrivacyCenterFooterLinkInput,\n} from './syncPrivacyCenterFooterLinks.js';\n\nexport interface PrivacyCenterInput {\n /** Whether or not the entire privacy center is enabled or disabled */\n isDisabled?: boolean;\n /** Whether or not to show the privacy requests button */\n showPrivacyRequestButton?: boolean;\n /** Whether or not to show the policies page */\n showPolicies?: boolean;\n /** Whether or not to show the tracking technologies page */\n showTrackingTechnologies?: boolean;\n /** Whether or not to show the cookies on the tracking technologies page */\n showCookies?: boolean;\n /** Whether or not to show the data flows on the tracking technologies page */\n showDataFlows?: boolean;\n /** Whether or not to show the consent manager opt out options on the tracking technologies page */\n showConsentManager?: boolean;\n /** Whether or not to show the manage your privacy page */\n showManageYourPrivacy?: boolean;\n /** Whether or not to show the marketing preferences page */\n showMarketingPreferences?: boolean;\n /** What languages are supported for the privacy center */\n locales?: LocaleValue[];\n /** The default locale for the privacy center */\n defaultLocale?: LocaleValue;\n /** Whether or not to prefer the browser default locale */\n preferBrowserDefaultLocale?: boolean;\n /** The support email address */\n supportEmail?: string;\n /** The reply-to email address */\n replyToEmail?: string;\n /** Whether or not to send emails from a no reply email */\n useNoReplyEmailAddress?: boolean;\n /** Whether or not to use a custom email domain */\n useCustomEmailDomain?: boolean;\n /** Whether or not to transcend access requests from JSON to CSV */\n transformAccessReportJsonToCsv?: boolean;\n /**\n * Privacy Center \"home\" / back-to-site link URL shown in the privacy center\n * header\n */\n home?: string;\n /** Whether the side menu is expanded by default on the privacy center */\n expandSideMenuByDefault?: boolean;\n /** Whether custom fields are required on privacy center workflows */\n workflowsCustomFieldsRequired?: boolean;\n /**\n * Child organization URIs and/or IDs to display on a unified multi-brand\n * privacy center\n */\n displayedChildOrganizationUris?: string[];\n /** Footer layout for privacy center footer links */\n footerLayout?: PrivacyCenterFooterLayout;\n /** Footer links displayed on the privacy center */\n footerLinks?: PrivacyCenterFooterLinkInput[];\n /** The theme object of colors to display on the privacy center */\n theme?: {\n /** The theme colors */\n colors?: Record<string, string | undefined>;\n /** Styles to apply to components */\n componentStyles?: Record<string, unknown>;\n /** Override styles */\n textStyles?: Record<string, unknown>;\n };\n}\n\n/**\n * Sync the privacy center\n *\n * @param client - GraphQL client\n * @param privacyCenter - The privacy center input\n * @param options - Options\n * @returns Whether the privacy center was synced successfully\n */\nexport async function syncPrivacyCenter(\n client: GraphQLClient,\n privacyCenter: PrivacyCenterInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** When true, skip publishing the privacy center after update */\n skipPublish?: boolean;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER, skipPublish } = options;\n let encounteredError = false;\n logger.info('Syncing privacy center...');\n\n const privacyCenterId = await fetchPrivacyCenterId(client, { logger });\n\n const { displayedChildOrganizationUris, footerLinks } = privacyCenter;\n const needsExisting = !!displayedChildOrganizationUris || footerLinks !== undefined;\n const [existing] = needsExisting ? await fetchAllPrivacyCenters(client, { logger }) : [];\n\n let displayedChildOrganizationIds: string[] | undefined;\n if (displayedChildOrganizationUris) {\n displayedChildOrganizationIds = resolveDisplayedChildOrganizationIds(\n existing?.childOrganizations ?? [],\n displayedChildOrganizationUris,\n );\n }\n\n try {\n await makeGraphQLRequest(client, UPDATE_PRIVACY_CENTER, {\n variables: {\n input: {\n privacyCenterId,\n transformAccessReportJsonToCsv: privacyCenter.transformAccessReportJsonToCsv,\n useCustomEmailDomain: privacyCenter.useCustomEmailDomain,\n useNoReplyEmailAddress: privacyCenter.useNoReplyEmailAddress,\n replyToEmail: privacyCenter.replyToEmail,\n supportEmail: privacyCenter.supportEmail,\n preferBrowserDefaultLocale: privacyCenter.preferBrowserDefaultLocale,\n defaultLocale: privacyCenter.defaultLocale,\n locales: privacyCenter.locales,\n showMarketingPreferences: privacyCenter.showMarketingPreferences,\n showManageYourPrivacy: privacyCenter.showManageYourPrivacy,\n showPolicies: privacyCenter.showPolicies,\n showConsentManager: privacyCenter.showConsentManager,\n showDataFlows: privacyCenter.showDataFlows,\n showCookies: privacyCenter.showCookies,\n showTrackingTechnologies: privacyCenter.showTrackingTechnologies,\n showPrivacyRequestButton: privacyCenter.showPrivacyRequestButton,\n isDisabled: privacyCenter.isDisabled,\n home: privacyCenter.home,\n expandSideMenuByDefault: privacyCenter.expandSideMenuByDefault,\n workflowsCustomFieldsRequired: privacyCenter.workflowsCustomFieldsRequired,\n footerLayout: privacyCenter.footerLayout,\n ...(displayedChildOrganizationIds ? { displayedChildOrganizationIds } : {}),\n ...(skipPublish !== undefined ? { skipPublish } : {}),\n ...(privacyCenter.theme\n ? {\n colorPalette: privacyCenter.theme.colors,\n componentStyles: privacyCenter.theme.componentStyles,\n textStyles: privacyCenter.theme.textStyles,\n }\n : {}),\n },\n },\n logger,\n });\n\n if (footerLinks !== undefined) {\n await syncPrivacyCenterFooterLinks(\n client,\n privacyCenterId,\n footerLinks,\n existing?.footerLinks ?? [],\n { logger },\n );\n }\n\n logger.info('Successfully synced privacy center!');\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync privacy center! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import type { RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CONSENT_WORKFLOW_TRIGGERS } from './gqls/consentWorkflowTrigger.js';\n\nexport interface ConsentWorkflowTrigger {\n /** ID of the trigger */\n id: string;\n /** Name of the trigger */\n name: string;\n /** JSON string of the trigger condition */\n triggerCondition: string | null;\n /** Whether the trigger runs silently */\n isSilent: boolean;\n /** Whether unauthenticated requests are allowed */\n allowUnauthenticated: boolean;\n /** Whether the trigger is active */\n isActive: boolean;\n /** The workflow config ID */\n workflowConfigId: string | null;\n /** The request action associated with the trigger */\n action: {\n /** Action type (e.g. ERASURE, ACCESS) */\n type: RequestAction;\n };\n /** The data subject associated with the trigger */\n subject: {\n /** Data subject type */\n type: string;\n };\n /** Data silos associated with this trigger */\n dataSilos: {\n /** Title of data silo */\n title: string;\n }[];\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all consent workflow triggers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All consent workflow triggers in the organization\n */\nexport async function fetchAllConsentWorkflowTriggers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentWorkflowTrigger[]> {\n const { logger } = options;\n const triggers: ConsentWorkflowTrigger[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n consentWorkflowTriggers: { nodes },\n } = await makeGraphQLRequest<{\n /** Consent workflow triggers */\n consentWorkflowTriggers: {\n /** List */\n nodes: ConsentWorkflowTrigger[];\n };\n }>(client, CONSENT_WORKFLOW_TRIGGERS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n triggers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return triggers.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\nexport const ACTIONS = gql`\n query TranscendCliActions($first: Int!, $offset: Int!) {\n actions(\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: type, direction: ASC }]\n ) {\n nodes {\n id\n type\n skipSecondaryIfNoFiles\n skipDownloadableStep\n requiresReview\n regionList\n regionDetectionMethod\n waitingPeriod\n }\n }\n }\n`;\n\nexport const UPDATE_ACTION = gql`\n mutation TranscendCliUpdateAction($input: UpdateActionInput!) {\n updateAction(input: $input) {\n clientMutationId\n }\n }\n`;\n","import {\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RegionDetectionMethod,\n RequestAction,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ACTIONS } from './gqls/action.js';\n\nexport interface Action {\n /** ID of identifier */\n id: string;\n /** Type of action */\n type: RequestAction;\n /** Whether to skip secondary when no files exist */\n skipSecondaryIfNoFiles: boolean;\n /** Whether to skip downloadable step */\n skipDownloadableStep: boolean;\n /** Whether action requires review */\n requiresReview: boolean;\n /** Waiting period for action */\n waitingPeriod: number;\n /** Method in which the data subject's region is detected */\n regionDetectionMethod: RegionDetectionMethod;\n /** The list of regions to show in the form */\n regionList: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all actions in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All actions in the organization\n */\nexport async function fetchAllActions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Action[]> {\n const { logger } = options;\n const actions: Action[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n actions: { nodes },\n } = await makeGraphQLRequest<{\n /** Actions */\n actions: {\n /** List */\n nodes: Action[];\n };\n }>(client, ACTIONS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n actions.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return actions.sort((a, b) => a.type.localeCompare(b.type));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const WORKFLOW_CONFIGS: DocumentNode = parse(gql`\n query TranscendCliWorkflowConfigs($first: Int!, $offset: Int!, $filterBy: WorkflowFilterInput) {\n workflows(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n title {\n defaultMessage\n }\n subtitle {\n defaultMessage\n }\n description {\n defaultMessage\n }\n internalName\n workflowConfigVisibility\n workflowConfigType\n collectDataSubjectRegions\n regionList\n expiryTime {\n region\n value\n }\n action {\n id\n type\n }\n subject {\n id\n type\n }\n WorkflowConfigAttributeKeys {\n attributeKey {\n id\n name\n }\n }\n }\n totalCount\n }\n }\n`);\n\nexport const CREATE_WORKFLOW: DocumentNode = parse(gql`\n mutation TranscendCliCreateWorkflow($input: CreateWorkflowInput!) {\n createWorkflow(input: $input) {\n workflowConfig {\n id\n internalName\n }\n }\n }\n`);\n\nexport const UPDATE_WORKFLOW_CONFIG: DocumentNode = parse(gql`\n mutation TranscendCliUpdateWorkflowConfig($input: UpdateWorkflowConfigInput!) {\n updateWorkflowConfig(input: $input) {\n success\n }\n }\n`);\n","import {\n CollectDataSubjectRegions,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RequestAction,\n WorkflowConfigType,\n WorkflowConfigVisibility,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { WORKFLOW_CONFIGS } from './gqls/workflowConfig.js';\n\n/** Raw workflow config node returned by GraphQL */\nexport interface WorkflowConfigNode {\n /** Workflow config ID */\n id: string;\n /** Title */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** Subtitle */\n subtitle: {\n /** Default message */\n defaultMessage: string;\n } | null;\n /** Description */\n description: {\n /** Default message */\n defaultMessage: string;\n } | null;\n /** Internal name */\n internalName: string | null;\n /** Visibility tier */\n workflowConfigVisibility: WorkflowConfigVisibility;\n /** Workflow type (DSR or PREFERENCE_MANAGEMENT) */\n workflowConfigType: WorkflowConfigType;\n /** Whether the workflow collects the data subject's region */\n collectDataSubjectRegions: CollectDataSubjectRegions | null;\n /** Region allow list */\n regionList: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** Per-region request expiry times */\n expiryTime:\n | {\n /** Region code (or 'default') */\n region: 'default' | IsoCountryCode | IsoCountrySubdivisionCode;\n /** Expiry time in days */\n value: number;\n }[]\n | null;\n /** Request action */\n action: {\n /** Action ID */\n id: string;\n /** Action type */\n type: RequestAction;\n };\n /** Data subject */\n subject: {\n /** Data subject ID */\n id: string;\n /** Data subject type */\n type: string;\n } | null;\n /** Attribute keys (custom fields) associated with the workflow */\n WorkflowConfigAttributeKeys:\n | {\n /** Attribute key */\n attributeKey: {\n /** Attribute key ID */\n id: string;\n /** Attribute key name */\n name: string;\n };\n }[]\n | null;\n}\n\n/** Alias used by consent-workflow-trigger helpers */\nexport type WorkflowConfigSummary = WorkflowConfigNode;\n\nconst PAGE_SIZE = 50;\n\n/**\n * Display title for a workflow config, matching the admin UI:\n * `internalName` when set, otherwise the external title.\n *\n * @param workflow - Workflow config\n * @returns Display title used in Preference Workflows dropdown\n */\nexport function getWorkflowConfigDisplayTitle(workflow: WorkflowConfigNode): string {\n return workflow.internalName || workflow.title.defaultMessage;\n}\n\n/**\n * Fetch all workflow configs in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All workflow configs\n */\nexport async function fetchAllWorkflowConfigs(\n client: GraphQLClient,\n options: {\n /** Filter by workflow config type */\n workflowConfigType?: WorkflowConfigType;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<WorkflowConfigNode[]> {\n const { logger, workflowConfigType } = options;\n const configs: WorkflowConfigNode[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n workflows: { nodes },\n } = await makeGraphQLRequest<{\n /** Workflows */\n workflows: {\n /** List */\n nodes: WorkflowConfigNode[];\n };\n }>(client, WORKFLOW_CONFIGS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n filterBy: workflowConfigType ? { workflowConfigType } : undefined,\n },\n logger,\n });\n\n configs.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return configs.sort((a, b) =>\n getWorkflowConfigDisplayTitle(a).localeCompare(getWorkflowConfigDisplayTitle(b)),\n );\n}\n\n/**\n * Resolve a YAML `workflow-title` to a DSR workflow config.\n * Matches against display title (`internalName` or external title).\n *\n * @param workflows - Candidate workflow configs\n * @param workflowTitle - Title from transcend.yml\n * @returns Matching workflow\n */\nexport function resolveWorkflowConfigByTitle(\n workflows: WorkflowConfigNode[],\n workflowTitle: string,\n): WorkflowConfigNode {\n const matches = workflows.filter(\n (workflow) => getWorkflowConfigDisplayTitle(workflow) === workflowTitle,\n );\n if (matches.length === 0) {\n throw new Error(\n `Failed to find DSR workflow with title: ${workflowTitle}. ` +\n `Use the workflow internal name when set, otherwise the external title.`,\n );\n }\n if (matches.length > 1) {\n throw new Error(\n `Found multiple DSR workflows with title: ${workflowTitle}. ` +\n `Set a unique internalName on the workflow in the Admin Dashboard.`,\n );\n }\n const [match] = matches;\n if (!match) {\n throw new Error(`Failed to find DSR workflow with title: ${workflowTitle}.`);\n }\n return match;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const DATA_SUBJECTS: DocumentNode = parse(gql`\n query TranscendCliDataSubjects {\n internalSubjects {\n id\n title {\n defaultMessage\n }\n active\n type\n adminDashboardDefaultSilentMode\n supportsAuthorizedAgent\n actions {\n type\n }\n }\n }\n`);\n\nexport const CREATE_DATA_SUBJECT: DocumentNode = parse(gql`\n mutation TranscendCliCreateDataSubject($type: String!) {\n createSubject(input: { type: $type, title: $type, subjectClass: OTHER }) {\n subject {\n id\n type\n }\n }\n }\n`);\n\nexport const UPDATE_DATA_SUBJECT: DocumentNode = parse(gql`\n mutation TranscendCliUpdateDataSubject($input: UpdateSubjectInput!) {\n updateSubject(input: $input) {\n clientMutationId\n }\n }\n`);\n\nexport const TOGGLE_DATA_SUBJECT: DocumentNode = parse(gql`\n mutation TranscendCliToggleDataSubject($input: ToggleSubjectInput!) {\n toggleSubject(input: $input) {\n clientMutationId\n }\n }\n`);\n","import { RequestActionObjectResolver } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { CREATE_DATA_SUBJECT, DATA_SUBJECTS } from './gqls/dataSubject.js';\n\nexport interface DataSubject {\n /** ID of data subject */\n id: string;\n /** Type of data subject */\n type: string;\n /** Whether active */\n active: boolean;\n /** Title of data subject */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** Whether silent mode is enabled by default */\n adminDashboardDefaultSilentMode: boolean;\n /** Whether requests can be submitted on behalf of this data subject by an authorized agent */\n supportsAuthorizedAgent: boolean;\n /** Enabled actions */\n actions: {\n /** Type of action */\n type: RequestActionObjectResolver;\n }[];\n}\n\n/**\n * Fetch all data subjects in an organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of data subject configurations\n */\nexport async function fetchAllDataSubjects(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<DataSubject[]> {\n const { logger = NOOP_LOGGER } = options;\n const { internalSubjects } = await makeGraphQLRequest<{\n /** Query response */\n internalSubjects: DataSubject[];\n }>(client, DATA_SUBJECTS, { logger });\n return internalSubjects;\n}\n\n/**\n * Create a single data subject\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Created data subject\n */\nexport async function createDataSubject(\n client: GraphQLClient,\n options: {\n /** The type string for the new subject */\n input: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<DataSubject> {\n const { input: dataSubjectType, logger = NOOP_LOGGER } = options;\n const { createSubject } = await makeGraphQLRequest<{\n /** Create Subject Response */\n createSubject: {\n /** Created Data Subject */\n subject: DataSubject;\n };\n }>(client, CREATE_DATA_SUBJECT, {\n variables: { type: dataSubjectType, skipPublish: true },\n logger,\n });\n return createSubject.subject;\n}\n\n/**\n * Convert a list of data subject types into the block list of IDs to assign to the data silo\n *\n * @param dataSubjectTypes - The list of data subject types that the data silo should be for\n * @param allDataSubjects - All data subjects in the organization\n * @returns The block list of data subject ids to not process against this data silo\n */\nexport function convertToDataSubjectBlockList(\n dataSubjectTypes: string[],\n allDataSubjects: { [type in string]: DataSubject },\n): string[] {\n dataSubjectTypes.forEach((type) => {\n if (!allDataSubjects[type]) {\n throw new Error(`Expected to find data subject definition: ${type}`);\n }\n });\n\n return Object.values(allDataSubjects)\n .filter((silo) => !dataSubjectTypes.includes(silo.type))\n .map(({ id }) => id);\n}\n\n/**\n * Convert a list of data subject types into the allow list of types\n *\n * @param dataSubjectTypes - The list of data subject types that the data silo should be for\n * @param allDataSubjects - All data subjects in the organization\n * @returns The allow list of data subjects for that silo\n */\nexport function convertToDataSubjectAllowlist(\n dataSubjectTypes: string[],\n allDataSubjects: { [type in string]: DataSubject },\n): string[] {\n dataSubjectTypes.forEach((type) => {\n if (!allDataSubjects[type]) {\n throw new Error(`Expected to find data subject definition: ${type}`);\n }\n });\n\n return Object.values(allDataSubjects)\n .filter((silo) => !dataSubjectTypes.includes(silo.type))\n .map(({ type }) => type);\n}\n","import { RequestAction, WorkflowConfigType } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataSilos } from '../data-inventory/fetchAllDataSilos.js';\nimport { fetchAllActions, type Action } from '../dsr-automation/fetchAllActions.js';\nimport {\n fetchAllWorkflowConfigs,\n getWorkflowConfigDisplayTitle,\n resolveWorkflowConfigByTitle,\n type WorkflowConfigSummary,\n} from '../dsr-automation/fetchAllWorkflowConfigs.js';\nimport { fetchAllDataSubjects, type DataSubject } from '../dsr-automation/fetchDataSubjects.js';\nimport { fetchAllPurposes, type Purpose } from '../preference-management/fetchAllPurposes.js';\nimport { fetchAllConsentWorkflowTriggers } from './fetchAllConsentWorkflowTriggers.js';\nimport { CREATE_OR_UPDATE_CONSENT_WORKFLOW_TRIGGER } from './gqls/consentWorkflowTrigger.js';\n\nexport interface ConsentWorkflowTriggerPurposeInput {\n /** The tracking type slug of the purpose */\n 'tracking-type': string;\n /** The matching consent state for the purpose */\n 'matching-state': boolean;\n}\n\nexport interface ConsentWorkflowTriggerSyncInput {\n /** The name of the consent workflow trigger */\n name: string;\n /**\n * Purposes and their matching consent states.\n * Required (non-empty). Used to derive triggerCondition.\n */\n purposes: ConsentWorkflowTriggerPurposeInput[];\n /**\n * DSR workflow display title (internalName or external title).\n * V2 mode — mutually exclusive with action-type / data-silo-titles.\n */\n 'workflow-title'?: string;\n /** The action type (e.g. ERASURE, ACCESS). Legacy mode; required on create when no workflow-title. */\n 'action-type'?: RequestAction;\n /** The data subject type. Required on create (legacy) or always when set. */\n 'data-subject-type'?: string;\n /** Whether the trigger runs silently */\n 'is-silent'?: boolean;\n /** Whether unauthenticated requests are allowed */\n 'allow-unauthenticated'?: boolean;\n /** Whether the trigger is active */\n 'is-active'?: boolean;\n /** Titles of data silos associated with this trigger (legacy mode only) */\n 'data-silo-titles'?: string[];\n}\n\n/**\n * Build the GraphQL triggerCondition JSON string from purposes,\n * matching the admin dashboard Preferences → Consent Workflows UI.\n *\n * @param purposes - Purpose tracking types and matching states\n * @returns JSON string of `{ And: [{ [trackingType]: matchingState }, ...] }`\n */\nexport function buildTriggerConditionFromPurposes(\n purposes: ConsentWorkflowTriggerPurposeInput[],\n): string {\n return JSON.stringify({\n And: purposes.map((purpose) => ({\n [purpose['tracking-type']]: purpose['matching-state'],\n })),\n });\n}\n\n/**\n * Parse GraphQL triggerCondition JSON into purposes for transcend.yml.\n * Expects ConditionalExpression shape: `{ And: [{ [trackingType]: boolean }, ...] }`.\n *\n * @param triggerCondition - JSON string from GraphQL (or null)\n * @returns Purpose entries for YAML, or empty if unparseable\n */\nexport function parsePurposesFromTriggerCondition(\n triggerCondition: string | null | undefined,\n): ConsentWorkflowTriggerPurposeInput[] {\n if (!triggerCondition) {\n return [];\n }\n\n try {\n const parsed: unknown = JSON.parse(triggerCondition);\n if (!parsed || typeof parsed !== 'object') {\n return [];\n }\n\n const andEntries = (parsed as { And?: unknown }).And;\n if (!Array.isArray(andEntries)) {\n return [];\n }\n\n const purposes: ConsentWorkflowTriggerPurposeInput[] = [];\n for (const entry of andEntries) {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n continue;\n }\n for (const [trackingType, matchingState] of Object.entries(\n entry as Record<string, unknown>,\n )) {\n if (typeof matchingState === 'boolean') {\n purposes.push({\n 'tracking-type': trackingType,\n 'matching-state': matchingState,\n });\n }\n }\n }\n return purposes;\n } catch {\n return [];\n }\n}\n\n/**\n * Whether this YAML entry is Workflows V2 mode (links a DSR workflow config).\n *\n * @param trigger - Sync input\n * @returns True when workflow-title is set\n */\nexport function isConsentWorkflowTriggerV2Mode(\n trigger: Pick<ConsentWorkflowTriggerSyncInput, 'workflow-title'>,\n): boolean {\n return Boolean(trigger['workflow-title']);\n}\n\n/**\n * Validate mutual exclusivity and required fields for a trigger input.\n * Pure helper for unit tests and sync.\n *\n * @param trigger - Sync input\n * @param options - Whether the trigger already exists in the org\n */\nexport function validateConsentWorkflowTriggerMode(\n trigger: ConsentWorkflowTriggerSyncInput,\n options: {\n /** True when updating an existing trigger by name */\n isUpdate: boolean;\n },\n): void {\n if (!trigger.purposes?.length) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" requires a non-empty \"purposes\" list ` +\n `(each entry needs tracking-type and matching-state). ` +\n `See Preferences → Preference Workflows.`,\n );\n }\n\n const isV2 = isConsentWorkflowTriggerV2Mode(trigger);\n const hasLegacyAction = Boolean(trigger['action-type']);\n const hasLegacySilos = Boolean(trigger['data-silo-titles']?.length);\n\n if (isV2 && (hasLegacyAction || hasLegacySilos)) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" cannot set \"workflow-title\" together with ` +\n `\"action-type\" or \"data-silo-titles\". Use workflow-title for Workflows V2, ` +\n `or action-type / data-silo-titles for legacy mode.`,\n );\n }\n\n if (!options.isUpdate) {\n if (isV2) {\n // workflow-title already present; data-subject-type may come from the workflow\n return;\n }\n if (!trigger['action-type']) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" requires \"action-type\" when creating ` +\n `a legacy trigger (or set \"workflow-title\" for Workflows V2).`,\n );\n }\n if (!trigger['data-subject-type']) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" requires \"data-subject-type\" when creating a new trigger.`,\n );\n }\n }\n}\n\n/**\n * Resolve workflow display title from a workflowConfigId.\n *\n * @param workflows - DSR workflows\n * @param workflowConfigId - Linked workflow config ID\n * @returns Display title, or undefined if not found\n */\nexport function resolveWorkflowTitleFromId(\n workflows: WorkflowConfigSummary[],\n workflowConfigId: string | null | undefined,\n): string | undefined {\n if (!workflowConfigId) {\n return undefined;\n }\n const workflow = workflows.find((candidate) => candidate.id === workflowConfigId);\n return workflow ? getWorkflowConfigDisplayTitle(workflow) : undefined;\n}\n\n/**\n * Sync consent workflow triggers to Transcend\n *\n * @param client - GraphQL client\n * @param inputs - Consent workflow trigger inputs from YAML\n * @param options - Options\n * @returns True if run without error\n */\nexport async function syncConsentWorkflowTriggers(\n client: GraphQLClient,\n inputs: ConsentWorkflowTriggerSyncInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Page size when fetching data silos */\n pageSize?: number;\n } = {},\n): Promise<boolean> {\n const { logger, pageSize = 50 } = options;\n logger?.info(`Syncing \"${inputs.length}\" consent workflow triggers...`);\n\n let encounteredError = false;\n\n const needsV2 = inputs.some((t) => isConsentWorkflowTriggerV2Mode(t));\n const needsActions = inputs.some((t) => t['action-type']);\n const needsSubjects = inputs.some((t) => t['data-subject-type']);\n const needsDataSilos = inputs.some((t) => t['data-silo-titles']?.length);\n\n const [existingTriggers, actions, dataSubjects, purposes, dataSilos, dsrWorkflows] =\n await Promise.all([\n fetchAllConsentWorkflowTriggers(client, { logger }),\n needsActions ? fetchAllActions(client, { logger }) : ([] as Action[]),\n needsSubjects ? fetchAllDataSubjects(client, { logger }) : ([] as DataSubject[]),\n // Purposes are always required to resolve purposeId for the mutation\n fetchAllPurposes(client, { logger }),\n needsDataSilos ? fetchAllDataSilos(client, { pageSize, logger }) : [],\n needsV2\n ? fetchAllWorkflowConfigs(client, {\n logger,\n workflowConfigType: WorkflowConfigType.DSR,\n })\n : ([] as WorkflowConfigSummary[]),\n ]);\n\n const triggerByName = keyBy(existingTriggers, 'name');\n const actionByType = keyBy(actions, 'type') as Record<string, Action>;\n const dataSubjectByType = keyBy(dataSubjects, 'type') as Record<string, DataSubject>;\n const purposeByTrackingType = keyBy(purposes, 'trackingType') as Record<string, Purpose>;\n const dataSiloByTitle = keyBy(dataSilos, 'title') as Record<\n string,\n { id: string; title: string }\n >;\n\n await mapSeries(inputs, async (trigger) => {\n try {\n const existingTrigger = triggerByName[trigger.name];\n validateConsentWorkflowTriggerMode(trigger, { isUpdate: Boolean(existingTrigger) });\n\n const consentWorkflowTriggerPurposes = trigger.purposes.map((purposeInput) => {\n const purpose = purposeByTrackingType[purposeInput['tracking-type']];\n if (!purpose) {\n throw new Error(\n `Failed to find purpose with trackingType: ${purposeInput['tracking-type']}`,\n );\n }\n return {\n purposeId: purpose.id,\n matchingState: purposeInput['matching-state'],\n };\n });\n\n const mutationInput: Record<string, unknown> = {\n name: trigger.name,\n ...(existingTrigger ? { id: existingTrigger.id } : {}),\n triggerCondition: buildTriggerConditionFromPurposes(trigger.purposes),\n consentWorkflowTriggerPurposes,\n ...(trigger['is-silent'] !== undefined ? { isSilent: trigger['is-silent'] } : {}),\n ...(trigger['allow-unauthenticated'] !== undefined\n ? { allowUnauthenticated: trigger['allow-unauthenticated'] }\n : {}),\n ...(trigger['is-active'] !== undefined ? { isActive: trigger['is-active'] } : {}),\n };\n\n if (isConsentWorkflowTriggerV2Mode(trigger)) {\n const workflow = resolveWorkflowConfigByTitle(dsrWorkflows, trigger['workflow-title']!);\n mutationInput.workflowConfigId = workflow.id;\n\n // Backend create still requires actionId + dataSubjectId — take from the workflow\n if (!existingTrigger) {\n mutationInput.actionId = workflow.action.id;\n if (!workflow.subject) {\n throw new Error(\n `DSR workflow \"${trigger['workflow-title']}\" has no data subject; ` +\n `cannot create consent workflow trigger \"${trigger.name}\".`,\n );\n }\n mutationInput.dataSubjectId = workflow.subject.id;\n } else if (trigger['data-subject-type']) {\n // Allow updating data-subject-type explicitly on V2 updates if provided\n const subject = dataSubjectByType[trigger['data-subject-type']];\n if (!subject) {\n // May need to fetch subjects if only V2 creates were expected — fetch on demand via workflow\n throw new Error(\n `Failed to find data subject with type: ${trigger['data-subject-type']}`,\n );\n }\n mutationInput.dataSubjectId = subject.id;\n }\n } else {\n // Legacy mode\n let actionId: string | undefined;\n if (trigger['action-type']) {\n const action = actionByType[trigger['action-type']];\n if (!action) {\n throw new Error(`Failed to find action with type: ${trigger['action-type']}`);\n }\n actionId = action.id;\n }\n\n let dataSubjectId: string | undefined;\n if (trigger['data-subject-type']) {\n const subject = dataSubjectByType[trigger['data-subject-type']];\n if (!subject) {\n throw new Error(\n `Failed to find data subject with type: ${trigger['data-subject-type']}`,\n );\n }\n dataSubjectId = subject.id;\n }\n\n let dataSiloIds: string[] | undefined;\n if (trigger['data-silo-titles']?.length) {\n dataSiloIds = trigger['data-silo-titles'].map((title) => {\n const dataSilo = dataSiloByTitle[title];\n if (!dataSilo) {\n throw new Error(`Failed to find data silo with title: ${title}`);\n }\n return dataSilo.id;\n });\n }\n\n if (actionId) {\n mutationInput.actionId = actionId;\n }\n if (dataSubjectId) {\n mutationInput.dataSubjectId = dataSubjectId;\n }\n if (dataSiloIds) {\n mutationInput.dataSiloIds = dataSiloIds;\n }\n }\n\n await makeGraphQLRequest<{\n /** Mutation result */\n createOrUpdateConsentWorkflowTrigger: {\n /** Created or updated trigger */\n consentWorkflowTrigger: {\n /** Trigger ID */\n id: string;\n };\n };\n }>(client, CREATE_OR_UPDATE_CONSENT_WORKFLOW_TRIGGER, {\n variables: { input: mutationInput },\n logger,\n });\n\n logger?.info(`Successfully synced consent workflow trigger \"${trigger.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger?.error(\n `Failed to sync consent workflow trigger \"${trigger.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n logger?.info(`Synced \"${inputs.length}\" consent workflow triggers!`);\n return !encounteredError;\n}\n","import { ProcessingPurpose } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n fetchAllProcessingPurposes,\n type ProcessingPurposeSubCategory,\n} from './fetchAllProcessingPurposes.js';\nimport {\n UPDATE_PROCESSING_PURPOSE_SUB_CATEGORIES,\n CREATE_PROCESSING_PURPOSE_SUB_CATEGORY,\n} from './gqls/processingPurpose.js';\n\nexport interface ProcessingPurposeInput {\n /** Name of processing purpose */\n name: string;\n /** Type of processing purpose */\n purpose: ProcessingPurpose;\n /** Description of processing purpose */\n description?: string;\n /** Owner email addresses */\n owners?: string[];\n /** Team names */\n teams?: string[];\n /** Attribute key-value pairs */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\n/**\n * Input to create a new processing purpose\n *\n * @param client - GraphQL client\n * @param processingPurpose - Input\n * @param options - Options\n * @returns Created processing purpose\n */\nexport async function createProcessingPurpose(\n client: GraphQLClient,\n options: {\n /** Processing purpose to create */\n input: ProcessingPurposeInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<ProcessingPurposeSubCategory, 'id' | 'name' | 'purpose'>> {\n const { input: processingPurpose, logger } = options;\n const gqlInput = {\n name: processingPurpose.name,\n purpose: processingPurpose.purpose,\n description: processingPurpose.description,\n // TODO: https://transcend.height.app/T-31994 - add attributes, teams, owners\n };\n\n const { createProcessingPurposeSubCategory } = await makeGraphQLRequest<{\n /** Create processing purpose mutation */\n createProcessingPurposeSubCategory: {\n /** Created processing purpose */\n processingPurposeSubCategory: ProcessingPurposeSubCategory;\n };\n }>(client, CREATE_PROCESSING_PURPOSE_SUB_CATEGORY, {\n variables: { input: gqlInput },\n logger,\n });\n return createProcessingPurposeSubCategory.processingPurposeSubCategory;\n}\n\n/**\n * Input to update processing purposes\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateProcessingPurposes(\n client: GraphQLClient,\n options: {\n /** [ProcessingPurposeInput, processingPurposeId] list */\n input: [ProcessingPurposeInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: processingPurposes, logger } = options;\n await makeGraphQLRequest(client, UPDATE_PROCESSING_PURPOSE_SUB_CATEGORIES, {\n variables: {\n input: {\n processingPurposeSubCategories: processingPurposes.map(([processingPurpose, id]) => ({\n id,\n description: processingPurpose.description,\n // TODO: https://transcend.height.app/T-31994 - add teams, owners\n attributes: processingPurpose.attributes,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory processing purposes\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncProcessingPurposes(\n client: GraphQLClient,\n inputs: ProcessingPurposeInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n\n logger.info(`Syncing \"${inputs.length}\" processing purposes...`);\n\n let encounteredError = false;\n\n const existingProcessingPurposes = await fetchAllProcessingPurposes(client, { logger });\n\n const processingPurposeByName: {\n [k in string]: Pick<ProcessingPurposeSubCategory, 'id' | 'name'>;\n } = keyBy(existingProcessingPurposes, ({ name, purpose }) => `${name}:${purpose}`);\n\n const newProcessingPurposes = inputs.filter(\n (input) => !processingPurposeByName[`${input.name}:${input.purpose}`],\n );\n\n await mapSeries(newProcessingPurposes, async (processingPurpose) => {\n try {\n const newProcessingPurpose = await createProcessingPurpose(client, {\n input: processingPurpose,\n logger,\n });\n processingPurposeByName[`${newProcessingPurpose.name}:${newProcessingPurpose.purpose}`] =\n newProcessingPurpose;\n logger.info(`Successfully synced processing purpose \"${processingPurpose.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync processing purpose \"${processingPurpose.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" processing purposes!`);\n await updateProcessingPurposes(client, {\n input: inputs.map((input) => [\n input,\n processingPurposeByName[`${input.name}:${input.purpose}`]!.id,\n ]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" processing purposes!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync \"${inputs.length}\" processing purposes ! - ${(err as Error).message}`,\n );\n }\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const REQUEST_ENRICHERS = gql`\n query TranscendCliRequestEnrichers($first: Int!, $offset: Int!, $requestId: ID!) {\n requestEnrichers(\n input: { requestId: $requestId }\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [\n { field: createdAt, direction: ASC }\n { field: title, direction: ASC, model: enricher }\n ]\n ) {\n nodes {\n id\n status\n enricher {\n id\n type\n title\n }\n }\n totalCount\n }\n }\n`;\n\nexport const RETRY_REQUEST_ENRICHER = gql`\n mutation TranscendCliRetryRequestEnricher($requestEnricherId: ID!) {\n retryRequestEnricher(id: $requestEnricherId) {\n requestEnricher {\n id\n }\n }\n }\n`;\n\nexport const SKIP_REQUEST_ENRICHER = gql`\n mutation TranscendCliSkipRequestEnricher($requestEnricherId: ID!) {\n skipRequestEnricher(id: $requestEnricherId) {\n requestEnricher {\n id\n }\n }\n }\n`;\n","import { RequestEnricherStatus } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { REQUEST_ENRICHERS } from './gqls/requestEnricher.js';\n\nexport interface RequestEnricher {\n /** ID of request enricher */\n id: string;\n /** Name of identifier */\n enricher: {\n /** ID of enricher */\n id: string;\n /** Title of enricher */\n title: string;\n /** Typeof of enricher */\n type: string;\n };\n /** The status of the enricher */\n status: RequestEnricherStatus;\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all request enrichers for a particular request\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of request enrichers\n */\nexport async function fetchAllRequestEnrichers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** ID of request to filter on */\n requestId: string;\n };\n },\n): Promise<RequestEnricher[]> {\n const {\n logger = NOOP_LOGGER,\n filterBy: { requestId },\n } = options;\n const requestEnrichers: RequestEnricher[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n requestEnrichers: { nodes },\n } = await makeGraphQLRequest<{\n /** Request Enrichers */\n requestEnrichers: {\n /** List */\n nodes: RequestEnricher[];\n };\n }>(client, REQUEST_ENRICHERS, {\n variables: { first: PAGE_SIZE, offset, requestId },\n logger,\n });\n requestEnrichers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return requestEnrichers;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const SOMBRA_VERSION: DocumentNode = parse(gql`\n query TranscendSombraVersion {\n organization {\n sombra {\n version\n }\n }\n }\n`);\n","import { IdentifierType } from '@transcend-io/privacy-types';\nimport { decodeCodec, valuesOf } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\nimport { GraphQLClient } from 'graphql-request';\nimport * as t from 'io-ts';\nimport semver from 'semver';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { SOMBRA_VERSION } from './gqls/sombraVersion.js';\n\nconst MIN_SOMBRA_VERSION_TO_DECRYPT = '7.180.0';\n\nconst RequestIdentifier = t.type({\n /** ID of request */\n id: t.string,\n /** Name of identifier */\n name: t.string,\n /** The underlying identifier value */\n value: t.string,\n /** Type of identifier */\n type: valuesOf(IdentifierType),\n});\n\n/** Type override */\nexport type RequestIdentifier = t.TypeOf<typeof RequestIdentifier>;\n\nconst PAGE_SIZE = 50;\n\nexport const RequestIdentifiersResponse = t.type({\n identifiers: t.array(RequestIdentifier),\n});\n\n/**\n * Validate that the Sombra version meets the minimum requirement for\n * decrypting request identifiers. Call once before bulk-fetching identifiers\n * to avoid repeating this check on every request.\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function validateSombraVersion(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n const {\n organization: {\n sombra: { version },\n },\n } = await makeGraphQLRequest<{\n /** The organization */\n organization: {\n /** Sombra */\n sombra: {\n /** Version string */\n version: string;\n };\n };\n }>(client, SOMBRA_VERSION, { logger });\n\n if (version && semver.lt(version, MIN_SOMBRA_VERSION_TO_DECRYPT)) {\n throw new Error(\n `Please upgrade Sombra to ${MIN_SOMBRA_VERSION_TO_DECRYPT} or greater to use this command.`,\n );\n }\n}\n\n/**\n * Fetch all request identifiers for a particular request\n *\n * @param client - GraphQL client\n * @param sombra - Sombra client\n * @param options - Options\n * @returns List of request identifiers\n */\nexport async function fetchAllRequestIdentifiers(\n client: GraphQLClient,\n sombra: Got,\n options: {\n /** Filter options */\n filterBy: {\n /** ID of request to filter on */\n requestId: string;\n };\n /** Skip the Sombra version check (caller already validated) */\n skipSombraCheck?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<RequestIdentifier[]> {\n const {\n filterBy: { requestId },\n skipSombraCheck = false,\n logger = NOOP_LOGGER,\n } = options;\n const requestIdentifiers: RequestIdentifier[] = [];\n let offset = 0;\n let shouldContinue = false;\n\n if (!skipSombraCheck) {\n await validateSombraVersion(client, { logger });\n }\n\n do {\n // `POST /v1/request-identifiers` is effectively a paginated read (the server\n // never mutates state from this call), so it is safe to retry on transient\n // gateway / network errors. Retries matter here because a single export can\n // span thousands of pages and occasional 502s from the Sombra reverse\n // tunnel or upstream load balancer would otherwise abort the whole chunk.\n const response = await withTransientRetry(\n 'Failed to fetch request identifiers',\n () =>\n sombra!\n .post<{\n /** Decrypted identifiers */\n identifiers: RequestIdentifier[];\n }>('v1/request-identifiers', {\n json: {\n first: PAGE_SIZE,\n offset,\n requestId,\n },\n })\n .json(),\n { logger, maxAttempts: 6, baseDelayMs: 500 },\n );\n\n const { identifiers: nodes } = decodeCodec(RequestIdentifiersResponse, response);\n\n requestIdentifiers.push(...nodes);\n\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return requestIdentifiers;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const REMOVE_REQUEST_IDENTIFIERS: DocumentNode = parse(gql`\n mutation TranscendCliRemoveRequestIdentifiers($input: RemoveRequestIdentifiersInput!) {\n removeRequestIdentifiers(input: $input) {\n count\n }\n }\n`);\n\nexport const REQUEST_IDENTIFIERS: DocumentNode = parse(gql`\n query TranscendCliRequestIdentifiers(\n $first: Int!\n $offset: Int!\n $requestIds: [ID!]\n $updatedAtBefore: Date\n $updatedAtAfter: Date\n ) {\n requestIdentifiers(\n input: {\n requestIds: $requestIds\n updatedAtBefore: $updatedAtBefore\n updatedAtAfter: $updatedAtAfter\n }\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n isVerifiedAtLeastOnce\n }\n totalCount\n }\n }\n`);\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { REQUEST_IDENTIFIERS } from './gqls/requestIdentifier.js';\n\nexport interface RequestIdentifierMetadata {\n /** ID of request identifier */\n id: string;\n /** Name of identifier */\n name: string;\n /** Status of identifier */\n isVerifiedAtLeastOnce: boolean;\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all request identifier metadata for a particular request\n *\n * @param client - GraphQL client\n * @param options - Filter options\n * @returns List of request identifiers\n */\nexport async function fetchAllRequestIdentifierMetadata(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** ID of request to filter on */\n requestId?: string;\n /** IDs of requests to filter on */\n requestIds?: string[];\n /** Filter for request identifiers updated before this date */\n updatedAtBefore?: Date;\n /** Filter for request identifiers updated after this date */\n updatedAtAfter?: Date;\n };\n } = {},\n): Promise<RequestIdentifierMetadata[]> {\n const { logger = NOOP_LOGGER, filterBy } = options;\n const { requestId, requestIds, updatedAtBefore, updatedAtAfter } = filterBy ?? {};\n const resolvedRequestIds = requestIds ?? (requestId ? [requestId] : undefined);\n const requestIdentifiers: RequestIdentifierMetadata[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n requestIdentifiers: { nodes },\n } = await makeGraphQLRequest<{\n /** Request Identifiers */\n requestIdentifiers: {\n /** List */\n nodes: RequestIdentifierMetadata[];\n };\n }>(client, REQUEST_IDENTIFIERS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n requestIds: resolvedRequestIds,\n updatedAtBefore: updatedAtBefore ? updatedAtBefore.toISOString() : undefined,\n updatedAtAfter: updatedAtAfter ? updatedAtAfter.toISOString() : undefined,\n },\n logger,\n });\n requestIdentifiers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return requestIdentifiers;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const NEW_IDENTIFIER_TYPES: DocumentNode = parse(gql`\n query TranscendCliNewIdentifierTypes {\n newIdentifierTypes {\n name\n }\n }\n`);\n\nexport const CREATE_IDENTIFIER: DocumentNode = parse(gql`\n mutation TranscendCliCreateIdentifier($input: IdentifierInput!) {\n createIdentifier(input: $input) {\n identifier {\n id\n name\n }\n }\n }\n`);\n\nexport const UPDATE_IDENTIFIER: DocumentNode = parse(gql`\n mutation TranscendCliUpdateIdentifier($input: UpdateIdentifierInput!) {\n updateIdentifier(input: $input) {\n clientMutationId\n }\n }\n`);\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, uniq, flatten, difference } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllIdentifiers, type Identifier } from '../data-inventory/fetchAllIdentifiers.js';\nimport { CREATE_IDENTIFIER, NEW_IDENTIFIER_TYPES } from './gqls/dsrIdentifier.js';\n\nexport interface IdentifiersAndCreateMissingInput {\n /** Enricher configurations */\n enrichers?: {\n /** Input identifier name */\n 'input-identifier'?: string;\n /** Output identifier names */\n 'output-identifiers': string[];\n }[];\n /** Data silo configurations */\n 'data-silos'?: {\n /** Identity keys */\n 'identity-keys'?: string[];\n }[];\n /** Identifier configurations */\n identifiers?: {\n /** Identifier name */\n name: string;\n }[];\n}\n\n/**\n * Fetch all identifiers and if any are found in the config that are\n * missing, create those identifiers.\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns A map from identifier name to Identifier\n */\nexport async function fetchIdentifiersAndCreateMissing(\n client: GraphQLClient,\n options: {\n /** Transcend input configuration */\n input: IdentifiersAndCreateMissingInput;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<{ [k in string]: Identifier }> {\n const { input, skipPublish = false, logger = NOOP_LOGGER } = options;\n const { enrichers = [], 'data-silos': dataSilos = [], identifiers = [] } = input;\n const allIdentifiers = await fetchAllIdentifiers(client, { logger });\n const identifiersByName = keyBy(allIdentifiers, 'name');\n\n const expectedIdentifiers = uniq([\n ...flatten(\n enrichers.map((enricher) => [\n enricher['input-identifier'],\n ...enricher['output-identifiers'],\n ]),\n ),\n ...flatten(dataSilos.map((dataSilo) => dataSilo['identity-keys'])),\n ...identifiers.map(({ name }) => name),\n ]).filter((x) => !!x);\n const missingIdentifiers = difference(\n expectedIdentifiers,\n allIdentifiers.map(({ name }) => name),\n );\n\n if (missingIdentifiers.length > 0) {\n logger.info(`Creating ${missingIdentifiers.length} new identifiers...`);\n const { newIdentifierTypes } = await makeGraphQLRequest<{\n /** Query response */\n newIdentifierTypes: {\n /** Name of identifier type remaining */\n name: string;\n }[];\n }>(client, NEW_IDENTIFIER_TYPES, { logger });\n const nativeTypesRemaining = newIdentifierTypes.map(({ name }) => name);\n await mapSeries(missingIdentifiers, async (identifier) => {\n logger.info(`Creating identifier ${identifier}...`);\n try {\n const { createIdentifier } = await makeGraphQLRequest<{\n /** createIdentifier Response */\n createIdentifier: {\n /** Created identifier */\n identifier: Identifier;\n };\n }>(client, CREATE_IDENTIFIER, {\n variables: {\n input: {\n name: identifier,\n type: nativeTypesRemaining.includes(identifier!) ? identifier : 'custom',\n skipPublish,\n },\n },\n logger,\n });\n logger.info(`Created identifier ${identifier}!`);\n\n identifiersByName[identifier!] = createIdentifier.identifier;\n } catch (err) {\n logger.error(`Failed to create identifier \"${identifier}\" - ${(err as Error).message}`);\n }\n });\n }\n return identifiersByName;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { RETRY_REQUEST_ENRICHER } from './gqls/requestEnricher.js';\n\n/**\n * Retry a request enricher\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function retryRequestEnricher(\n client: GraphQLClient,\n options: {\n /** The ID of the request enricher to restart */\n id: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { id, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, RETRY_REQUEST_ENRICHER, {\n variables: { requestEnricherId: id },\n logger,\n });\n}\n","import { RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { TOGGLE_DATA_SUBJECT, UPDATE_DATA_SUBJECT } from './gqls/dataSubject.js';\n\nexport interface DataSubjectInput {\n /** The type of the data subject */\n type: string;\n /** Whether the data subject is active on the Privacy Center & DSR API */\n active?: boolean;\n /** The title of the data subject */\n title?: string;\n /** Whether or not to default new requests made in the admin dashboard to silent mode */\n adminDashboardDefaultSilentMode?: boolean;\n /** Whether requests can be submitted on behalf of this data subject by an authorized agent */\n supportsAuthorizedAgent?: boolean;\n /** Enabled request actions for the data subject */\n actions?: RequestAction[];\n}\n\n/**\n * Sync the data subjects\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function syncDataSubject(\n client: GraphQLClient,\n options: {\n /** DataSubject update input */\n input: DataSubjectInput;\n /** Existing data subject Id */\n dataSubjectId: string;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataSubject, dataSubjectId, skipPublish = false, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_DATA_SUBJECT, {\n variables: {\n input: {\n id: dataSubjectId,\n title: dataSubject.title,\n adminDashboardDefaultSilentMode: dataSubject.adminDashboardDefaultSilentMode,\n supportsAuthorizedAgent: dataSubject.supportsAuthorizedAgent,\n actions: dataSubject.actions,\n skipPublish: skipPublish && typeof dataSubject.active === 'undefined',\n },\n },\n logger,\n });\n\n if (typeof dataSubject.active === 'boolean') {\n await makeGraphQLRequest(client, TOGGLE_DATA_SUBJECT, {\n variables: {\n input: {\n id: dataSubjectId,\n active: dataSubject.active,\n skipPublish,\n },\n },\n logger,\n });\n }\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const ENRICHERS = gql`\n query TranscendCliEnrichers($title: String, $first: Int!, $offset: Int!) {\n enrichers(\n filterBy: { text: $title }\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n ) {\n nodes {\n id\n title\n url\n type\n expirationDuration\n lookerQueryTitle\n testRegex\n transitionRequestStatus\n phoneNumbers\n regionList\n inputIdentifier {\n name\n }\n identifiers {\n name\n }\n dataSubjects {\n type\n }\n actions\n }\n }\n }\n`;\n\nexport interface Initializer {\n /** ID of enricher */\n id: string;\n /** Identifiers */\n identifiers: {\n /** Name of identifier */\n name: string;\n }[];\n}\n\nexport const INITIALIZER = gql`\n query TranscendCliInitializer {\n initializer {\n id\n identifiers {\n name\n }\n }\n }\n`;\n\nexport const CREATE_ENRICHER = gql`\n mutation TranscendCliCreateEnricher($input: EnricherInput!) {\n createEnricher(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_ENRICHER = gql`\n mutation TranscendCliUpdateEnricher($input: UpdateEnricherInput!) {\n updateEnricher(input: $input) {\n clientMutationId\n }\n }\n`;\n","import {\n EnricherType,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n PreflightRequestStatus,\n RequestAction,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { Identifier } from '../data-inventory/fetchAllIdentifiers.js';\nimport { ENRICHERS, CREATE_ENRICHER, UPDATE_ENRICHER } from './gqls/enricher.js';\n\nexport interface EnricherInput {\n /** The display title of the enricher */\n title: string;\n /** The names of the identifiers that can be resolved by this enricher */\n 'output-identifiers': string[];\n /** Internal description for why the enricher is needed */\n description?: string;\n /** The URL of the enricher */\n url?: string;\n /** The type of enricher */\n type?: EnricherType;\n /** The name of the identifier that will be the input to this enricher */\n 'input-identifier'?: string;\n /** A regular expression that can be used to match on for cancellation */\n testRegex?: string;\n /** For looker integration - the title of the looker query to run */\n lookerQueryTitle?: string;\n /** The duration (in ms) that the enricher should take to execute */\n expirationDuration?: number;\n /** The status that the enricher should transfer to when condition is met */\n transitionRequestStatus?: PreflightRequestStatus;\n /** For twilio integration - the phone numbers that can be used to send text codes */\n phoneNumbers?: string[];\n /** The list of regions that should trigger the preflight check */\n regionList?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** Specify which data subjects the enricher should run for */\n 'data-subjects'?: string[];\n /** Headers to include in the webhook */\n headers?: {\n /** Header name */\n name: string;\n /** Header value */\n value: string;\n /** Whether the value is a secret */\n isSecret?: boolean;\n }[];\n /** The privacy actions that the enricher should run against */\n 'privacy-actions'?: RequestAction[];\n}\n\nexport interface Enricher {\n /** ID of enricher */\n id: string;\n /** Title of enricher */\n title: string;\n /** URL of enricher */\n url: string;\n /** Server silo */\n type: EnricherType;\n /** Input identifier */\n inputIdentifier: {\n /** Identifier name */\n name: string;\n };\n /** The selected actions */\n actions: RequestAction[];\n /** Output identifiers */\n identifiers: {\n /** Identifier name */\n name: string;\n }[];\n /** Data subjects that the preflight check is configured for */\n dataSubjects: {\n /** Data subject type */\n type: string;\n }[];\n /** The duration (in ms) that the enricher should take to execute. - BigInt */\n expirationDuration: string;\n /** Looker query title */\n lookerQueryTitle?: string;\n /** A regular expression that can be used to match on for cancelation */\n testRegex?: string;\n /** The status that the enricher should transfer to when condition is met. */\n transitionRequestStatus?: PreflightRequestStatus;\n /** The twilio phone number to send from */\n phoneNumbers: string[];\n /** The list of regions that should trigger the enrichment condition */\n regionList: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n}\n\nexport interface DataSubjectRef {\n /** ID of data subject */\n id: string;\n /** Type of data subject */\n type: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all enrichers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All enrichers in the organization\n */\nexport async function fetchAllEnrichers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter by title */\n title?: string;\n };\n } = {},\n): Promise<Enricher[]> {\n const { logger = NOOP_LOGGER, filterBy } = options;\n const { title } = filterBy ?? {};\n const enrichers: Enricher[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n enrichers: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n enrichers: {\n /** List of matches */\n nodes: Enricher[];\n };\n }>(client, ENRICHERS, {\n variables: { first: PAGE_SIZE, offset, title },\n logger,\n });\n enrichers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return enrichers.sort((a, b) => a.title.localeCompare(b.title));\n}\n\n/**\n * Sync an enricher configuration\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function syncEnricher(\n client: GraphQLClient,\n options: {\n /** The enricher input */\n input: EnricherInput;\n /** Index of identifiers in the organization */\n identifierByName: { [name in string]: Identifier };\n /** Lookup data subject by name */\n dataSubjectsByName: { [name in string]: DataSubjectRef };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: enricher, identifierByName, dataSubjectsByName, logger = NOOP_LOGGER } = options;\n const matches = await fetchAllEnrichers(client, {\n filterBy: { title: enricher.title },\n logger,\n });\n const existingEnricher = matches.find(({ title }) => title === enricher.title);\n\n const dataSubjectIds = enricher['data-subjects']?.map((subject) => {\n const existing = dataSubjectsByName[subject];\n if (!existing) {\n throw new Error(`Failed to find a data subject with name: ${subject}`);\n }\n return existing.id;\n });\n\n const inputIdentifier = enricher['input-identifier'];\n const actionUpdates = enricher['privacy-actions'] || Object.values(RequestAction);\n if (existingEnricher) {\n await makeGraphQLRequest(client, UPDATE_ENRICHER, {\n variables: {\n input: {\n id: existingEnricher.id,\n title: enricher.title,\n url: enricher.url,\n headers: enricher.headers,\n testRegex: enricher.testRegex,\n lookerQueryTitle: enricher.lookerQueryTitle,\n expirationDuration:\n typeof enricher.expirationDuration === 'number'\n ? enricher.expirationDuration.toString()\n : undefined,\n transitionRequestStatus: enricher.transitionRequestStatus,\n phoneNumbers: enricher.phoneNumbers,\n regionList: enricher.regionList,\n dataSubjectIds,\n description: enricher.description || '',\n inputIdentifier: inputIdentifier ? identifierByName[inputIdentifier]!.id : undefined,\n identifiers: enricher['output-identifiers']!.map((id) => identifierByName[id]!.id),\n ...(existingEnricher.type === EnricherType.Sombra ? {} : { actions: actionUpdates }),\n },\n },\n logger,\n });\n } else if (inputIdentifier) {\n await makeGraphQLRequest(client, CREATE_ENRICHER, {\n variables: {\n input: {\n title: enricher.title,\n url: enricher.url,\n type: enricher.type || EnricherType.Server,\n headers: enricher.headers,\n testRegex: enricher.testRegex,\n lookerQueryTitle: enricher.lookerQueryTitle,\n expirationDuration:\n typeof enricher.expirationDuration === 'number'\n ? enricher.expirationDuration.toString()\n : undefined,\n transitionRequestStatus: enricher.transitionRequestStatus,\n phoneNumbers: enricher.phoneNumbers,\n dataSubjectIds,\n regionList: enricher.regionList,\n description: enricher.description || '',\n inputIdentifier: identifierByName[inputIdentifier]!.id,\n identifiers: enricher['output-identifiers'].map((id) => identifierByName[id]!.id),\n actions: actionUpdates,\n },\n },\n logger,\n });\n }\n}\n","import { RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSubject } from './fetchDataSubjects.js';\nimport { UPDATE_IDENTIFIER } from './gqls/dsrIdentifier.js';\n\nexport interface IdentifierInput {\n /** The name of the identifier */\n name: string;\n /** The type of the identifier */\n type: string;\n /** Regular expression to verify the identifier */\n regex?: string;\n /** The fixed set of options that an identifier can take on */\n selectOptions?: string[];\n /** Whether or not the identifier is shown in the privacy center form */\n privacyCenterVisibility?: RequestAction[];\n /** The set of data subjects that this identifier is enabled for */\n dataSubjects?: string[];\n /** When true, the identifier is a required field on the privacy center form */\n isRequiredInForm?: boolean;\n /** Placeholder message for identifier */\n placeholder?: string;\n /** Display title for identifier */\n displayTitle?: string;\n /** Display description for identifier */\n displayDescription?: string;\n /** The display order for the identifier */\n displayOrder?: number;\n /** Whether or not the identifier is unique on the preference store */\n isUniqueOnPreferenceStore?: boolean;\n}\n\n/**\n * Sync a single identifier\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function syncIdentifier(\n client: GraphQLClient,\n options: {\n /** Identifier update input */\n input: IdentifierInput;\n /** Data subject lookup by name */\n dataSubjectsByName: { [k in string]: DataSubject };\n /** Existing identifier Id */\n identifierId: string;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const {\n input: identifier,\n dataSubjectsByName,\n identifierId,\n skipPublish = false,\n logger = NOOP_LOGGER,\n } = options;\n await makeGraphQLRequest(client, UPDATE_IDENTIFIER, {\n variables: {\n input: {\n id: identifierId,\n selectOptions: identifier.selectOptions,\n isRequiredInForm: identifier.isRequiredInForm,\n regex: identifier.regex,\n placeholder: identifier.placeholder,\n displayTitle: identifier.displayTitle,\n displayDescription: identifier.displayDescription,\n displayOrder: identifier.displayOrder,\n isUniqueOnPreferenceStore: identifier.isUniqueOnPreferenceStore,\n privacyCenterVisibility: identifier.privacyCenterVisibility,\n dataSubjectIds: identifier.dataSubjects\n ? identifier.dataSubjects.map((type) => dataSubjectsByName[type]!.id)\n : undefined,\n skipPublish,\n },\n },\n logger,\n });\n}\n","import { gql } from 'graphql-request';\n\nexport const ADD_SILO_DISCOVERY_RESULTS = gql`\n mutation AddSiloDiscoveryResults($pluginId: ID!, $rawResults: [SiloDiscoveryRawResultInput!]!) {\n addSiloDiscoveryResults(input: { pluginId: $pluginId, rawResults: $rawResults }) {\n success\n }\n }\n`;\n\nexport const ENABLED_PLUGINS = gql`\n query Plugins($dataSiloId: String!, $type: PluginType!) {\n plugins(filterBy: { dataSiloId: $dataSiloId, type: $type, enabled: true }) {\n plugins {\n id\n dataSilo {\n type\n }\n }\n totalCount\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { ENABLED_PLUGINS } from './gqls/siloDiscovery.js';\n\nexport interface Plugin {\n /** Associated data silo */\n dataSilo: {\n /** The type of plugin */\n type: string;\n };\n /** The ID of this plugin */\n id: string;\n}\n\nexport interface PluginResponse {\n /** The key object of the response */\n plugins: {\n /** The total count */\n totalCount: number;\n /** The list of plugins */\n plugins: Plugin[];\n };\n}\n\n/**\n * Fetch a data silo discovery plugin\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns An active data silo plugin (if multiple, returns the first)\n */\nexport async function fetchActiveSiloDiscoPlugin(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** The data silo to look up plugins for */\n dataSiloId: string;\n };\n },\n): Promise<Plugin> {\n const {\n logger = NOOP_LOGGER,\n filterBy: { dataSiloId },\n } = options;\n const response = await makeGraphQLRequest<PluginResponse>(client, ENABLED_PLUGINS, {\n variables: {\n dataSiloId,\n type: 'DATA_SILO_DISCOVERY',\n },\n logger,\n });\n\n const { plugins, totalCount } = response.plugins;\n if (totalCount === 0) {\n logger.error('No active data silo plugins found for this data silo.');\n process.exit(1);\n }\n\n return plugins[0]!;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { ATTRIBUTE_KEYS_REQUESTS } from '../administration/gqls/attributeKey.js';\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\n\nexport interface AttributeKey {\n /** ID of attribute key */\n id: string;\n /** Name of attribute key */\n name: string;\n /** Attribute key type */\n type: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all attribute keys enabled for privacy requests\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All attribute keys in the organization\n */\nexport async function fetchAllRequestAttributeKeys(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AttributeKey[]> {\n const { logger } = options;\n const attributeKeys: AttributeKey[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n attributeKeys: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n attributeKeys: {\n /** List of matches */\n nodes: AttributeKey[];\n };\n }>(client, ATTRIBUTE_KEYS_REQUESTS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n attributeKeys.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return attributeKeys.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const SILO_DISCOVERY_RESULTS = gql`\n query TranscendCliSiloDiscoveryResults($first: Int!, $offset: Int!) {\n siloDiscoveryResults(first: $first, offset: $offset) {\n nodes {\n title\n resourceId\n country\n countrySubDivision\n plaintextContext\n status\n containsSensitiveData\n suggestedCatalog {\n title\n }\n plugin {\n dataSilo {\n title\n }\n }\n }\n }\n }\n`;\n","import type { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { SILO_DISCOVERY_RESULTS } from './gqls/siloDiscoveryResult.js';\n\nexport interface SiloDiscoveryResult {\n /** Title of silo discovery result */\n title?: string;\n /** Resource ID of silo discovery result */\n resourceId: string;\n /** Suggested catalog */\n suggestedCatalog: {\n /** Title for the suggested catalog */\n title: string;\n };\n /** The likelihood that data is sensitive for this results */\n containsSensitiveData: string;\n /** The status of silo discovery triage */\n status: string;\n /** Hosting country of data silo discovery result */\n country?: IsoCountryCode;\n /** Hosting subdivision data silo discovery result */\n countrySubDivision?: IsoCountrySubdivisionCode;\n /** Plaintext context data silo discovery result */\n plaintextContext: string;\n /** The plugin that found this result */\n plugin: {\n /** The data silo the plugin belongs to */\n dataSilo: {\n /** The internal display title */\n title: string;\n };\n };\n}\n\nconst PAGE_SIZE = 30;\n\n/**\n * Fetch all silo discovery results in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All silo discovery results in the organization\n */\nexport async function fetchAllSiloDiscoveryResults(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SiloDiscoveryResult[]> {\n const { logger } = options;\n const siloDiscoveryResults: SiloDiscoveryResult[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n siloDiscoveryResults: { nodes },\n } = await makeGraphQLRequest<{\n /** Discovery results */\n siloDiscoveryResults: {\n /** Nodes */\n nodes: SiloDiscoveryResult[];\n };\n }>(client, SILO_DISCOVERY_RESULTS, {\n variables: { first: PAGE_SIZE, offset, input: {}, filterBy: {} },\n logger,\n });\n\n const titledNodes = nodes.map((node) =>\n node.title === null && node.suggestedCatalog?.title\n ? { ...node, title: node.suggestedCatalog.title }\n : node,\n );\n\n siloDiscoveryResults.push(...titledNodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return siloDiscoveryResults;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const CATALOGS = gql`\n query TranscendCliCatalogs($first: Int!, $offset: Int!) {\n catalogs(first: $first, offset: $offset, filterBy: {}, useMaster: false) {\n nodes {\n integrationName\n title\n hasApiFunctionality\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CATALOGS } from './gqls/catalog.js';\n\nexport interface Catalog {\n /** Integration name */\n integrationName: string;\n /** Title of Data Silo */\n title: string;\n /** Whether API is supported */\n hasApiFunctionality: boolean;\n}\n\nconst PAGE_SIZE = 100;\n\n/**\n * Fetch all integration catalogs in an organization\n *\n * @param client - Client\n * @param options - Options\n * @returns Integration catalogs\n */\nexport async function fetchAllCatalogs(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Catalog[]> {\n const { logger } = options;\n const catalogs: Catalog[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n catalogs: { nodes },\n } = await makeGraphQLRequest<{\n /** integration catalogs */\n catalogs: {\n /** List */\n nodes: Catalog[];\n };\n }>(client, CATALOGS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n catalogs.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n return catalogs.sort((a, b) => a.integrationName.localeCompare(b.integrationName));\n}\n\nexport interface IndexedCatalogs {\n /** Mapping from service name to service title */\n serviceToTitle: { [k in string]: string };\n /** Mapping from service name to boolean indicate if service has API integration support */\n serviceToSupportedIntegration: { [k in string]: boolean };\n}\n\n/**\n * Fetch all integration catalogs and index them for usage in common utility manners\n *\n * @param client - Client\n * @param options - Options\n * @returns Integration catalogs\n */\nexport async function fetchAndIndexCatalogs(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<\n {\n /** List of all catalogs */\n catalogs: Catalog[];\n } & IndexedCatalogs\n> {\n const catalogs = await fetchAllCatalogs(client, options);\n\n const serviceToTitle = catalogs.reduce(\n (acc, catalog) => Object.assign(acc, { [catalog.integrationName]: catalog.title }),\n {} as { [k in string]: string },\n );\n\n const serviceToSupportedIntegration = catalogs.reduce(\n (acc, catalog) =>\n Object.assign(acc, {\n [catalog.integrationName]: catalog.hasApiFunctionality,\n }),\n {} as { [k in string]: boolean },\n );\n\n return {\n catalogs,\n serviceToTitle,\n serviceToSupportedIntegration,\n };\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const REQUEST_DATA_SILOS = gql`\n query TranscendCliRequestDataSilos(\n $first: Int!\n $offset: Int!\n $filterBy: RequestDataSiloFiltersInput!\n ) {\n requestDataSilos(\n filterBy: $filterBy\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [\n { field: createdAt, direction: DESC }\n { field: title, direction: ASC, model: dataSilo }\n ]\n ) {\n nodes {\n id\n request {\n type\n }\n }\n totalCount\n }\n }\n`;\n\nexport const CHANGE_REQUEST_DATA_SILO_STATUS = gql`\n mutation TranscendCliMarkRequestDataSiloCompleted(\n $requestDataSiloId: ID!\n $status: UpdateRequestDataSiloStatus!\n ) {\n changeRequestDataSiloStatus(input: { id: $requestDataSiloId, status: $status }) {\n requestDataSilo {\n id\n }\n }\n }\n`;\n\nexport const RETRY_REQUEST_DATA_SILO = gql`\n mutation TranscendCliRetryRequestDataSilo($requestDataSiloId: ID!) {\n retryRequestDataSilo(id: $requestDataSiloId) {\n requestDataSilo {\n id\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: DESC }\n// { field: title, direction: ASC, model: dataSilo }\n// ]\nexport const REDUCED_REQUESTS_FOR_DATA_SILO_COUNT = gql`\n query TranscendCliListReducedRequestsForDataSiloCount(\n $input: BulkCompletionReducedRequestInput!\n ) {\n listReducedRequestsForDataSilo(input: $input) {\n totalCount\n }\n }\n`;\n","import { RequestAction, RequestDataSiloStatus, RequestStatus } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { REQUEST_DATA_SILOS } from './gqls/requestDataSilo.js';\n\nexport interface RequestDataSilo {\n /** ID of RequestDataSilo */\n id: string;\n /** Request */\n request: {\n /** Type of request */\n type: RequestAction;\n };\n}\n\nexport interface RequestDataSiloFilters {\n /** ID of request to filter on */\n requestId?: string;\n /** Data silo ID */\n dataSiloId?: string;\n /** The statuses to filter on */\n statuses?: RequestDataSiloStatus[];\n /** The request statuses to filter on */\n requestStatuses?: RequestStatus[];\n}\n\n/**\n * Fetch a count of request data silos\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Count of request data silos\n */\nexport async function fetchRequestDataSilosCount(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: RequestDataSiloFilters;\n } = {},\n): Promise<number> {\n const { logger, filterBy: { requestId, dataSiloId, requestStatuses, statuses } = {} } = options;\n const {\n requestDataSilos: { totalCount },\n } = await makeGraphQLRequest<{\n /** Request Data Silos */\n requestDataSilos: {\n /** List */\n nodes: RequestDataSilo[];\n /** Total count */\n totalCount: number;\n };\n }>(client, REQUEST_DATA_SILOS, {\n variables: {\n first: 1,\n offset: 0,\n filterBy: {\n dataSiloId,\n requestId,\n status: statuses,\n requestStatus: requestStatuses,\n },\n },\n logger,\n });\n\n return totalCount;\n}\n\nconst PAGE_SIZE = 100;\n\n/**\n * Fetch all request data silos by some filter criteria\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of request data silos\n */\nexport async function fetchRequestDataSilos(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: RequestDataSiloFilters;\n /** Limit on number of requests */\n limit?: number;\n /** Handle progress updates */\n onProgress?: (numUpdated: number) => void;\n } = {},\n): Promise<RequestDataSilo[]> {\n const {\n logger,\n filterBy: { requestId, dataSiloId, requestStatuses, statuses } = {},\n limit,\n onProgress,\n } = options;\n const requestDataSilos: RequestDataSilo[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n requestDataSilos: { nodes },\n } = await makeGraphQLRequest<{\n /** Request Data Silos */\n requestDataSilos: {\n /** List */\n nodes: RequestDataSilo[];\n /** Total count */\n totalCount: number;\n };\n }>(client, REQUEST_DATA_SILOS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n filterBy: {\n dataSiloId,\n requestId,\n status: statuses,\n requestStatus: requestStatuses,\n },\n },\n logger,\n });\n requestDataSilos.push(...nodes);\n\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n onProgress?.(nodes.length);\n } while (shouldContinue && (!limit || offset < limit));\n\n return requestDataSilos;\n}\n\n/**\n * Fetch a single request data silo by request and data silo IDs\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns The matching request data silo\n */\nexport async function fetchRequestDataSilo(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** ID of request to filter on */\n requestId: string;\n /** Data silo ID */\n dataSiloId: string;\n };\n },\n): Promise<RequestDataSilo> {\n const {\n logger,\n filterBy: { requestId, dataSiloId },\n } = options;\n const nodes = await fetchRequestDataSilos(client, {\n logger,\n filterBy: { requestId, dataSiloId },\n });\n if (nodes.length !== 1) {\n throw new Error(\n `Failed to find RequestDataSilo with requestId:${requestId},dataSiloId:${dataSiloId}`,\n );\n }\n\n return nodes[0]!;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const REQUEST_FILES = gql`\n query TranscendCliRequestFiles($first: Int!, $offset: Int!, $filterBy: RequestFileFiltersInput!) {\n requestFiles(\n filterBy: $filterBy\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: id, direction: ASC }]\n ) {\n nodes {\n remoteId\n fileName\n }\n }\n }\n`;\n\nexport const BULK_REQUEST_FILES = gql`\n query TranscendCliBulkRequestFiles(\n $filterBy: BulkRequestFilesFiltersInput!\n $first: Int!\n $after: String\n ) {\n bulkRequestFiles(filterBy: $filterBy, first: $first, after: $after) {\n nodes {\n remoteId\n fileName\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { BULK_REQUEST_FILES } from './gqls/requestFile.js';\n\nexport interface RequestFileCursor {\n /** The ID of the request file */\n id: string;\n /** The created at timestamp */\n createdAt: string;\n}\n\nexport interface RequestFile {\n /** The remote ID */\n remoteId: string;\n /** The file name */\n fileName: string;\n}\n\nexport interface RequestFileResponse {\n /** RequestFiles */\n bulkRequestFiles: {\n /** List */\n nodes: RequestFile[];\n /** The page info */\n pageInfo: {\n /** Whether there is a next page */\n hasNextPage: boolean;\n /** The end cursor */\n endCursor: string;\n };\n };\n}\n\n/**\n * Fetch all RequestFiles for a single request\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All RequestFiles in the organization\n */\nexport async function fetchRequestFilesForRequest(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** How many request files to fetch per API call */\n pageSize?: number;\n /** Filter options */\n filterBy: {\n /** Filter by request IDs */\n requestIds: string[];\n /** Filter by data silo ID */\n dataSiloIds: string[];\n };\n },\n): Promise<RequestFile[]> {\n const { logger, pageSize = 100, filterBy } = options;\n const requestFiles: RequestFile[] = [];\n let cursor: string | null = null;\n\n let shouldContinue = false;\n do {\n const response: RequestFileResponse = await makeGraphQLRequest<RequestFileResponse>(\n client,\n BULK_REQUEST_FILES,\n {\n variables: {\n filterBy: {\n ...filterBy,\n },\n first: pageSize,\n after: cursor ?? undefined,\n },\n logger,\n },\n );\n const {\n bulkRequestFiles: { nodes, pageInfo },\n } = response;\n requestFiles.push(...nodes);\n shouldContinue = pageInfo.hasNextPage;\n cursor = pageInfo.endCursor;\n } while (shouldContinue);\n\n return requestFiles.sort((a, b) => a.remoteId.localeCompare(b.remoteId));\n}\n","export interface DataSiloAttributeValue {\n /** Key associated to value */\n attributeKey: {\n /** Name of key */\n name: string;\n };\n /** Name of value */\n name: string;\n}\n\nexport interface FormattedAttribute {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n}\n\n/**\n * Format attribute value objects to key-pair values\n *\n * @param vals - Attribute values\n * @returns formatted attributes\n */\nexport function formatAttributeValues(vals: DataSiloAttributeValue[]): FormattedAttribute[] {\n const attributes: FormattedAttribute[] = [];\n\n vals.map((val) => {\n let foundKey = attributes.find((att) => att.key === val.attributeKey.name);\n\n if (foundKey === undefined) {\n foundKey = {\n key: val.attributeKey.name,\n values: [val.name],\n };\n attributes.push(foundKey);\n } else {\n foundKey.values.push(val.name);\n }\n return attributes;\n });\n\n return attributes;\n}\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { difference } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { UPDATE_ACTION } from './gqls/action.js';\n\nconst ALL_COUNTRIES_AND_SUBDIVISIONS = [\n ...Object.values(IsoCountryCode),\n ...Object.values(IsoCountrySubdivisionCode),\n];\n\nexport interface SyncActionInput {\n /** Whether to skip secondary when no files exist */\n skipSecondaryIfNoFiles?: boolean;\n /** Whether to skip downloadable step */\n skipDownloadableStep?: boolean;\n /** Whether the request action requires review */\n requiresReview?: boolean;\n /** The wait period for the action */\n waitingPeriod?: number;\n /** The method in which the data subject's region is detected */\n regionDetectionMethod?: string;\n /** The list of regions to show in the form */\n regionList?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** The list of regions NOT to show in the form */\n regionBlockList?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n}\n\n/**\n * Sync the consent manager\n *\n * @param client - GraphQL client\n * @param actionInput - Action update details\n * @param options - Options\n */\nexport async function syncAction(\n client: GraphQLClient,\n actionInput: {\n /** Action update input */\n action: SyncActionInput;\n /** Existing action Id */\n actionId: string;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger } = options;\n const { action, actionId, skipPublish = false } = actionInput;\n await makeGraphQLRequest(client, UPDATE_ACTION, {\n variables: {\n input: {\n id: actionId,\n skipSecondaryIfNoFiles: action.skipSecondaryIfNoFiles,\n skipDownloadableStep: action.skipDownloadableStep,\n requiresReview: action.requiresReview,\n waitingPeriod: action.waitingPeriod,\n skipPublish,\n regionList: action.regionBlockList\n ? difference(ALL_COUNTRIES_AND_SUBDIVISIONS, action.regionBlockList)\n : action.regionList,\n regionDetectionMethod: action.regionDetectionMethod,\n },\n },\n logger,\n });\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const TEMPLATES = gql`\n query TranscendCliTemplates($title: String, $first: Int!, $offset: Int!) {\n templates(filterBy: { text: $title }, first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n title\n subject {\n defaultMessage\n }\n template {\n defaultMessage\n }\n }\n }\n }\n`;\n\nexport const CREATE_TEMPLATE = gql`\n mutation TranscendCliCreateTemplate($title: String!) {\n createTemplate(input: { title: $title, template: \"\", subject: $title }) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { TEMPLATES, CREATE_TEMPLATE } from './gqls/template.js';\n\nexport interface Template {\n /** ID of Template */\n id: string;\n /** Title of Template */\n title: string;\n /** Template subject (e.g. email subject) */\n subject: {\n /** Default message for template subject */\n defaultMessage: string;\n };\n /** Template body - rich text HTML */\n template: {\n /** Default message for template body */\n defaultMessage: string;\n };\n}\n\nexport interface SyncTemplateInput {\n /** The title of the template */\n title: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all Templates in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All Templates in the organization\n */\nexport async function fetchAllTemplates(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter by title */\n title?: string;\n };\n } = {},\n): Promise<Template[]> {\n const { logger, filterBy: { title } = {} } = options;\n const templates: Template[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n templates: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n templates: {\n /** List of matches */\n nodes: Template[];\n };\n }>(client, TEMPLATES, {\n variables: { first: PAGE_SIZE, offset, title },\n logger,\n });\n templates.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return templates.sort((a, b) => a.title.localeCompare(b.title));\n}\n\n/**\n * Sync an email template configuration\n *\n * @param client - GraphQL client\n * @param template - The email template input\n * @param options - Options\n */\nexport async function syncTemplate(\n client: GraphQLClient,\n template: SyncTemplateInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger } = options;\n const matches = await fetchAllTemplates(client, {\n logger,\n filterBy: { title: template.title },\n });\n const existingTemplate = matches.find(({ title }) => title === template.title);\n\n if (!existingTemplate) {\n await makeGraphQLRequest(client, CREATE_TEMPLATE, {\n variables: { title: template.title },\n logger,\n });\n }\n}\n","import type { WorkflowConfigNode } from './fetchAllWorkflowConfigs.js';\nimport type { WorkflowConfigSyncInput } from './syncWorkflowConfigs.js';\n\n/** Fields used to resolve a workflow config match */\nexport type WorkflowConfigMatchInput = Pick<\n WorkflowConfigSyncInput,\n 'internal-name' | 'title' | 'action-type' | 'data-subject-type' | 'region-list'\n>;\n\n/** Result of resolving a workflow config match */\nexport type WorkflowConfigMatchResolution =\n | {\n /** Matched an existing workflow config */\n kind: 'match';\n /** Matched workflow config */\n config: WorkflowConfigNode;\n }\n | {\n /** No existing workflow config matched; create a new one */\n kind: 'create';\n }\n | {\n /** Multiple workflow configs matched after the full cascade */\n kind: 'ambiguous';\n /** Ambiguous workflow config candidates */\n candidates: WorkflowConfigNode[];\n };\n\n/**\n * Stable key for title + action + data subject + region-list identity.\n *\n * @param config - Workflow config node\n * @returns Match key string\n */\nexport function workflowConfigMatchKey(config: WorkflowConfigNode): string {\n return [\n config.title.defaultMessage,\n config.action.type,\n config.subject?.type ?? '',\n workflowRegionListKey(config.regionList),\n ].join('\\0');\n}\n\n/**\n * Order-independent region list key for workflow matching.\n *\n * @param regions - Region list\n * @returns Sorted comma-separated region codes\n */\nexport function workflowRegionListKey(regions: readonly string[] | undefined | null): string {\n return [...(regions ?? [])].sort().join(',');\n}\n\n/**\n * Human-readable label for logging and errors.\n *\n * @param input - Workflow config sync input\n * @returns Label string\n */\nexport function workflowConfigInputLabel(input: WorkflowConfigSyncInput): string {\n return input['internal-name'] ?? input.title;\n}\n\nfunction sameRegionList(\n inputRegions: WorkflowConfigMatchInput['region-list'],\n configRegions: WorkflowConfigNode['regionList'],\n): boolean {\n return workflowRegionListKey(inputRegions) === workflowRegionListKey(configRegions);\n}\n\nfunction narrowToOneOrContinue(\n candidates: WorkflowConfigNode[],\n next: (remaining: WorkflowConfigNode[]) => WorkflowConfigMatchResolution,\n): WorkflowConfigMatchResolution {\n const [only] = candidates;\n if (candidates.length === 1 && only !== undefined) {\n return { kind: 'match', config: only };\n }\n if (candidates.length === 0) {\n return { kind: 'create' };\n }\n return next(candidates);\n}\n\nfunction continueCascade(\n candidates: WorkflowConfigNode[],\n input: WorkflowConfigMatchInput,\n): WorkflowConfigMatchResolution {\n return narrowToOneOrContinue(\n candidates.filter((config) => config.title.defaultMessage === input.title),\n (byTitle) =>\n narrowToOneOrContinue(\n byTitle.filter((config) => config.action.type === input['action-type']),\n (byAction) => {\n const dataSubjectType = input['data-subject-type'];\n if (dataSubjectType) {\n return narrowToOneOrContinue(\n byAction.filter((config) => config.subject?.type === dataSubjectType),\n (bySubject) => filterByRegions(bySubject, input),\n );\n }\n return filterByRegions(byAction, input);\n },\n ),\n );\n}\n\nfunction filterByRegions(\n candidates: WorkflowConfigNode[],\n input: WorkflowConfigMatchInput,\n): WorkflowConfigMatchResolution {\n const matches = candidates.filter((config) =>\n sameRegionList(input['region-list'], config.regionList),\n );\n const [only] = matches;\n if (matches.length === 1 && only !== undefined) {\n return { kind: 'match', config: only };\n }\n if (matches.length === 0) {\n return { kind: 'create' };\n }\n\n // When YAML omits internal-name, prefer unnamed candidates so a named\n // sibling with the same title/action/subject/regions is not treated as\n // ambiguous (common after pull when only some workflows have internal names).\n // If multiple unnamed remain, pick the first in stable fetch order — sync\n // claims matched ids so later identical YAML rows map to the rest.\n // TODO: https://linear.app/transcend/issue/WAL-10312 - remove once internalName is unique in DB\n if (!input['internal-name']) {\n const unnamed = matches.filter((config) => config.internalName == null);\n const [firstUnnamed] = unnamed;\n if (firstUnnamed !== undefined) {\n return { kind: 'match', config: firstUnnamed };\n }\n }\n\n return { kind: 'ambiguous', candidates: matches };\n}\n\n/**\n * Resolve which existing workflow config a YAML entry should update, if any.\n *\n * Cascade order: internal-name → title → action-type → data-subject-type (when\n * provided) → region-list. A provided internal-name with zero matches creates a\n * new workflow instead of falling through to title. When internal-name is\n * omitted and the cascade still matches multiple configs, prefer the candidate\n * with a null internal name.\n *\n * @param input - Workflow config sync input\n * @param existingConfigs - Existing workflow configs in the org\n * @returns Match resolution\n */\nexport function resolveWorkflowConfigMatch(\n input: WorkflowConfigMatchInput,\n existingConfigs: WorkflowConfigNode[],\n): WorkflowConfigMatchResolution {\n const internalName = input['internal-name'];\n\n if (internalName) {\n const byName = existingConfigs.filter((config) => config.internalName === internalName);\n const [onlyByName] = byName;\n if (byName.length === 1 && onlyByName !== undefined) {\n return { kind: 'match', config: onlyByName };\n }\n if (byName.length === 0) {\n return { kind: 'create' };\n }\n return continueCascade(byName, input);\n }\n\n return continueCascade(existingConfigs, input);\n}\n","import {\n CollectDataSubjectRegions,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RequestAction,\n WorkflowConfigType,\n WorkflowConfigVisibility,\n} from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchAllActions } from './fetchAllActions.js';\nimport { fetchAllRequestAttributeKeys, type AttributeKey } from './fetchAllAttributeKeys.js';\nimport { fetchAllWorkflowConfigs, type WorkflowConfigNode } from './fetchAllWorkflowConfigs.js';\nimport { fetchAllDataSubjects, type DataSubject } from './fetchDataSubjects.js';\nimport { CREATE_WORKFLOW, UPDATE_WORKFLOW_CONFIG } from './gqls/workflowConfig.js';\nimport {\n resolveWorkflowConfigMatch,\n workflowConfigInputLabel,\n} from './resolveWorkflowConfigMatch.js';\n\nexport interface WorkflowConfigSyncInput {\n /** User-facing title */\n title: string;\n /** Request action type */\n 'action-type': RequestAction;\n /**\n * Internal name of the workflow config. When provided, used as the first match\n * key; when omitted, matching falls back through title, action, subject, and\n * regions.\n */\n 'internal-name'?: string;\n /** Subtitle */\n subtitle?: string;\n /** Description */\n description?: string;\n /** Data subject type */\n 'data-subject-type'?: string;\n /** Visibility tier (DRAFT, INTERNAL, PUBLISHED) */\n visibility?: WorkflowConfigVisibility;\n /**\n * Workflow config type. Only DSR is supported for sync; preference-management\n * workflows must be managed in the Admin Dashboard.\n */\n type?: WorkflowConfigType;\n /** Whether to collect the data subject's region (COLLECT, DO_NOT_COLLECT) */\n 'collect-data-subject-regions'?: CollectDataSubjectRegions;\n /** Region allow list */\n 'region-list'?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** Per-region request expiry times */\n 'expiry-time'?: {\n /** Region code (or 'default') */\n region: 'default' | IsoCountryCode | IsoCountrySubdivisionCode;\n /** Expiry time in days */\n value: number;\n }[];\n /** Attribute key (custom field) names to associate with the workflow */\n 'attribute-keys'?: string[];\n}\n\n/**\n * Sync workflow configs to Transcend.\n *\n * Matches each input using a cascading key: internal-name → title → action-type\n * → data-subject-type (when provided) → region-list. Missing configs are\n * created via `createWorkflow` (DSR only; starts as Draft with default\n * associations), then updated with the remaining YAML fields. Successfully\n * matched configs are claimed (removed from the match pool) so later YAML\n * entries can resolve remaining duplicates.\n *\n * @param client - GraphQL client\n * @param inputs - Workflow config inputs from YAML\n * @param options - Options\n * @returns True if run without error\n */\nexport async function syncWorkflowConfigs(\n client: GraphQLClient,\n inputs: WorkflowConfigSyncInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger } = options;\n logger?.info(`Syncing \"${inputs.length}\" workflow configs...`);\n\n let encounteredError = false;\n\n const needsSubjects = inputs.some((config) => config['data-subject-type']);\n const needsAttributeKeys = inputs.some((config) => config['attribute-keys']?.length);\n\n const [fetchedConfigs, actions] = await Promise.all([\n fetchAllWorkflowConfigs(client, {\n logger,\n workflowConfigType: WorkflowConfigType.DSR,\n }),\n fetchAllActions(client, { logger }),\n ]);\n\n const existingConfigs: WorkflowConfigNode[] = [...fetchedConfigs];\n\n let dataSubjects: DataSubject[] = [];\n if (needsSubjects) {\n dataSubjects = await fetchAllDataSubjects(client, { logger });\n }\n\n let attributeKeys: AttributeKey[] = [];\n if (needsAttributeKeys) {\n attributeKeys = await fetchAllRequestAttributeKeys(client, { logger });\n }\n\n const actionByType = keyBy(actions, 'type');\n const dataSubjectByType = keyBy(dataSubjects, 'type');\n const attributeKeyByName = keyBy(attributeKeys, 'name');\n\n await mapSeries(inputs, async (config) => {\n const label = workflowConfigInputLabel(config);\n try {\n // TODO: https://linear.app/transcend/issue/WAL-10313 - support preference-management workflow sync\n if (config.type && config.type !== WorkflowConfigType.DSR) {\n throw new Error(\n `Workflow config \"${label}\" has type ` +\n `\"${config.type}\". Only DSR workflow configs can be synced — ` +\n 'preference management workflows have purpose-based semantics and must be managed in the Admin Dashboard.',\n );\n }\n\n const resolution = resolveWorkflowConfigMatch(config, existingConfigs);\n if (resolution.kind === 'ambiguous') {\n throw new Error(\n `Found \"${resolution.candidates.length}\" workflow configs matching ` +\n `\"${label}\" after applying title, action-type, data-subject-type, and region-list. ` +\n 'Add a unique internal-name or disambiguating region-list to sync this workflow.',\n );\n }\n\n const existingConfig = resolution.kind === 'match' ? resolution.config : undefined;\n\n if (existingConfig && existingConfig.workflowConfigType !== WorkflowConfigType.DSR) {\n throw new Error(\n `Workflow config \"${label}\" has type ` +\n `\"${existingConfig.workflowConfigType}\". Only DSR workflow configs can be synced — ` +\n 'preference management workflows have purpose-based semantics and must be managed in the Admin Dashboard.',\n );\n }\n\n const expiryTime = config['expiry-time'];\n if (expiryTime && expiryTime.length > 0) {\n // The backend validates expiryTime against RegionExpiryMap, which requires\n // a `default` entry and all values > 0 — fail fast with a clearer error\n if (!expiryTime.some(({ region }) => region === 'default')) {\n throw new Error(\n `Workflow config \"${label}\" has an expiry-time list ` +\n 'without a \"default\" region entry. A `region: default` entry is required.',\n );\n }\n const invalidEntries = expiryTime.filter(({ value }) => value <= 0);\n if (invalidEntries.length > 0) {\n throw new Error(\n `Workflow config \"${label}\" has expiry-time values that are ` +\n `not positive: ${invalidEntries\n .map(({ region, value }) => `${region}=${value}`)\n .join(', ')}. All expiry times must be greater than 0 days.`,\n );\n }\n }\n\n let subjectId: string | undefined;\n if (config['data-subject-type']) {\n const subject = dataSubjectByType[config['data-subject-type']];\n if (!subject) {\n throw new Error(`Failed to find data subject with type: ${config['data-subject-type']}`);\n }\n subjectId = subject.id;\n }\n\n if (!actionByType[config['action-type']]) {\n throw new Error(`Failed to find action with type: ${config['action-type']}`);\n }\n\n let attributeKeyIds: string[] | undefined;\n if (config['attribute-keys'] !== undefined) {\n attributeKeyIds = config['attribute-keys'].map((name) => {\n const attributeKey = attributeKeyByName[name];\n if (!attributeKey) {\n throw new Error(`Failed to find attribute key with name: ${name}`);\n }\n return attributeKey.id;\n });\n }\n\n let workflowConfigId: string;\n if (!existingConfig) {\n // createWorkflow only accepts title/actionType/internalName (+ type).\n // Remaining fields are applied via updateWorkflowConfig below.\n // New workflows start as Draft with default associations (data silos, receipt email).\n const internalName = config['internal-name'];\n const {\n createWorkflow: { workflowConfig: created },\n } = await makeGraphQLRequest<{\n /** createWorkflow mutation */\n createWorkflow: {\n /** Created workflow config */\n workflowConfig: {\n /** ID */\n id: string;\n /** Internal name */\n internalName: string | null;\n };\n };\n }>(client, CREATE_WORKFLOW, {\n variables: {\n input: {\n title: config.title,\n actionType: config['action-type'],\n ...(internalName ? { internalName } : {}),\n workflowConfigType: WorkflowConfigType.DSR,\n },\n },\n logger,\n });\n workflowConfigId = created.id;\n // Track the new config so a later YAML entry with the same match key\n // updates instead of attempting a second create in this sync run.\n existingConfigs.push({\n id: created.id,\n title: { defaultMessage: config.title },\n subtitle: null,\n description: null,\n internalName: created.internalName ?? internalName ?? null,\n workflowConfigVisibility: WorkflowConfigVisibility.Draft,\n workflowConfigType: WorkflowConfigType.DSR,\n collectDataSubjectRegions: null,\n regionList: config['region-list'] ?? [],\n expiryTime: null,\n // action.id is unused for match-key resolution within this sync run\n action: { id: '', type: config['action-type'] },\n subject: subjectId\n ? { id: subjectId, type: config['data-subject-type'] as string }\n : null,\n WorkflowConfigAttributeKeys: null,\n });\n logger?.info(`Successfully created workflow config \"${label}\" (${created.id})!`);\n } else {\n workflowConfigId = existingConfig.id;\n }\n\n const mutationInput: Record<string, unknown> = {\n workflowConfigId,\n ...(config.title !== undefined ? { title: config.title } : {}),\n ...(config.subtitle !== undefined ? { subtitle: config.subtitle } : {}),\n ...(config.description !== undefined ? { description: config.description } : {}),\n ...(config.visibility !== undefined ? { workflowConfigVisibility: config.visibility } : {}),\n ...(config['internal-name'] !== undefined ? { internalName: config['internal-name'] } : {}),\n actionType: config['action-type'],\n ...(config['collect-data-subject-regions'] !== undefined\n ? { collectDataSubjectRegions: config['collect-data-subject-regions'] }\n : {}),\n ...(subjectId ? { subjectId } : {}),\n ...(config['region-list'] !== undefined ? { regionList: config['region-list'] } : {}),\n ...(config['expiry-time'] !== undefined ? { expiryTime: config['expiry-time'] } : {}),\n ...(attributeKeyIds !== undefined ? { attributeKeyIds } : {}),\n };\n\n await makeGraphQLRequest(client, UPDATE_WORKFLOW_CONFIG, {\n variables: { input: mutationInput },\n logger,\n });\n\n // Claim the matched config so a later YAML entry can resolve remaining\n // duplicates (e.g. named sibling synced first, then unnamed pull row).\n // TODO: https://linear.app/transcend/issue/WAL-10312 - remove once internalName is unique in DB\n if (existingConfig) {\n const claimedIndex = existingConfigs.findIndex((config) => config.id === existingConfig.id);\n if (claimedIndex >= 0) {\n existingConfigs.splice(claimedIndex, 1);\n }\n }\n\n logger?.info(`Successfully synced workflow config \"${label}\" (${workflowConfigId})!`);\n } catch (err) {\n encounteredError = true;\n logger?.error(`Failed to sync workflow config \"${label}\"! - ${(err as Error).message}`);\n }\n });\n\n logger?.info(`Synced \"${inputs.length}\" workflow configs!`);\n return !encounteredError;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { ADD_SILO_DISCOVERY_RESULTS } from './gqls/siloDiscovery.js';\n\nexport interface SiloDiscoveryRawResult {\n /** The name of the potential data silo entry */\n name: string;\n /** A unique UUID (represents the same resource across different silo discovery runs) */\n resourceId: string;\n /** Any hosts associated with the entry */\n host?: string;\n /** Type of data silo */\n type?: string | undefined;\n}\n\nconst CHUNK_SIZE = 1000;\n\n/**\n * Uploads silo discovery results for Transcend to classify\n *\n * @param client - GraphQL Client\n * @param options - Options\n */\nexport async function uploadSiloDiscoveryResults(\n client: GraphQLClient,\n options: {\n /** Plugin ID to associate with the results */\n pluginId: string;\n /** The discovery results to upload */\n results: SiloDiscoveryRawResult[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { pluginId, results, logger = NOOP_LOGGER } = options;\n const chunks = chunk(results, CHUNK_SIZE);\n\n await mapSeries(chunks, async (rawResults) => {\n await makeGraphQLRequest<{\n /** Whether we successfully uploaded the results */\n success: boolean;\n }>(client, ADD_SILO_DISCOVERY_RESULTS, {\n variables: { pluginId, rawResults },\n logger,\n });\n });\n}\n","import { gql } from 'graphql-request';\n\nexport const ADD_MESSAGES_TO_PROMPT_RUN = gql`\n mutation TranscendCliAddMessagesToPromptRun($input: AddMessagesToPromptRunInput!) {\n addMessagesToPromptRun(input: $input) {\n clientMutationId\n promptRun {\n id\n }\n }\n }\n`;\n","import { QueueStatus, ChatCompletionRole } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ADD_MESSAGES_TO_PROMPT_RUN } from './gqls/promptRun.js';\n\nexport interface AddMessagesToPromptRunInput {\n /** ID of run */\n promptRunId:\n | {\n /** Report by prompt run name */\n name: string;\n /** Don't report by ID */\n id?: undefined;\n }\n | {\n /** Don't report by name */\n name?: undefined;\n /** Report by prompt run ID */\n id: string;\n };\n /** Messages to report on */\n promptRunMessages?: {\n /** Message reported */\n content: string;\n /** Role of message */\n role: ChatCompletionRole;\n /** Template used if created from prompt */\n template?: string;\n }[];\n /** Error message (if one exists) */\n error?: string;\n /** The status of the run */\n status?: QueueStatus;\n /** Duration of time that it took to execute the prompt */\n duration?: number;\n}\n\n/**\n * Record a new prompt run\n *\n * @param client - GraphQL client\n * @param input - Prompt input\n * @param options - Options\n * @returns Prompt ID\n */\nexport async function addMessagesToPromptRun(\n client: GraphQLClient,\n { promptRunId, promptRunMessages = [], ...rest }: AddMessagesToPromptRunInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<string> {\n const { logger } = options;\n const {\n addMessagesToPromptRun: { promptRun },\n } = await makeGraphQLRequest<{\n /** addMessagesToPromptRun mutation */\n addMessagesToPromptRun: {\n /** Prompt */\n promptRun: {\n /** ID */\n id: string;\n };\n };\n }>(client, ADD_MESSAGES_TO_PROMPT_RUN, {\n variables: {\n input: {\n ...rest,\n ...promptRunId,\n promptRunMessages: promptRunMessages.map(({ content, ...rest }) => ({\n ...rest,\n message: content,\n })),\n },\n },\n logger,\n });\n return promptRun.id;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const AGENT_FILES = gql`\n query TranscendCliAgentFiles($first: Int!, $offset: Int!, $filterBy: AgentFileFiltersInput) {\n agentFiles(\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n fileId\n size\n purpose\n initialFileName\n }\n }\n }\n`;\n\nexport const CREATE_AGENT_FILE = gql`\n mutation TranscendCliCreateAgentFile($input: CreateAgentFileInput!) {\n createAgentFile(input: $input) {\n agentFile {\n id\n name\n fileId\n initialFileName\n }\n }\n }\n`;\n\nexport const UPDATE_AGENT_FILES = gql`\n mutation TranscendCliUpdateAgentFiles($input: UpdateAgentFilesInput!) {\n updateAgentFiles(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { PromptFilePurpose } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { AGENT_FILES } from './gqls/agentFile.js';\n\nexport interface AgentFile {\n /** ID of agentFile */\n id: string;\n /** Name of agentFile */\n name: string;\n /** Description of the agentFile */\n description: string;\n /** Initial file name, useful to track if a file was split into multiple chunks */\n initialFileName?: string;\n /** File ID */\n fileId: string;\n /** File size */\n size: number;\n /** File purpose */\n purpose: PromptFilePurpose;\n}\n\nexport interface AgentFileFilterBy {\n /** Filter by remote file IDs */\n fileIds?: string[];\n /** Filter by file names */\n names?: string[];\n /** Filter by initial file names (when split into chunks) */\n initialFileNames?: string[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all agent files in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All agent files in the organization\n */\nexport async function fetchAllAgentFiles(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: AgentFileFilterBy;\n } = {},\n): Promise<AgentFile[]> {\n const { logger, filterBy = {} } = options;\n const agentFiles: AgentFile[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n agentFiles: { nodes },\n } = await makeGraphQLRequest<{\n /** AgentFiles */\n agentFiles: {\n /** List */\n nodes: AgentFile[];\n };\n }>(client, AGENT_FILES, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n agentFiles.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return agentFiles.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const AGENT_FUNCTIONS = gql`\n query TranscendCliAgentFunctions($first: Int!, $offset: Int!) {\n agentFunctions(\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n parameters\n }\n }\n }\n`;\n\nexport const CREATE_AGENT_FUNCTION = gql`\n mutation TranscendCliCreateAgentFunction($input: CreateAgentFunctionInput!) {\n createAgentFunction(input: $input) {\n agentFunction {\n id\n name\n }\n }\n }\n`;\n\nexport const UPDATE_AGENT_FUNCTIONS = gql`\n mutation TranscendCliUpdateAgentFunctions($input: UpdateAgentFunctionsInput!) {\n updateAgentFunctions(input: $input) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport type { JSONSchema7 } from 'json-schema';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { AGENT_FUNCTIONS } from './gqls/agentFunction.js';\n\nexport interface AgentFunction {\n /** ID of agentFunction */\n id: string;\n /** Name of agentFunction */\n name: string;\n /** Description of the agentFunction */\n description: string;\n /** The JSON schema */\n parameters: JSONSchema7;\n}\n\ninterface AgentFunctionRaw extends Omit<AgentFunction, 'parameters'> {\n /** Stringified parameters */\n parameters: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all agent functions in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All agent functions in the organization\n */\nexport async function fetchAllAgentFunctions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AgentFunction[]> {\n const { logger } = options;\n const agentFunctions: AgentFunction[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n agentFunctions: { nodes },\n } = await makeGraphQLRequest<{\n /** AgentFunctions */\n agentFunctions: {\n /** List */\n nodes: AgentFunctionRaw[];\n };\n }>(client, AGENT_FUNCTIONS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n agentFunctions.push(\n ...nodes.map((node) => ({\n ...node,\n parameters: JSON.parse(node.parameters),\n })),\n );\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return agentFunctions.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const AGENTS = gql`\n query TranscendCliAgents($first: Int!, $offset: Int!, $filterBy: AgentFiltersInput) {\n agents(\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n agentId\n instructions\n description\n codeInterpreterEnabled\n retrievalEnabled\n prompt {\n title\n }\n largeLanguageModel {\n name\n client\n }\n teams {\n name\n }\n owners {\n email\n }\n agentFunctions {\n name\n }\n agentFiles {\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_AGENT = gql`\n mutation TranscendCliCreateAgent($input: CreateAgentInput!) {\n createAgent(input: $input) {\n agent {\n id\n name\n agentId\n }\n }\n }\n`;\n\nexport const UPDATE_AGENTS = gql`\n mutation TranscendCliUpdateAgents($input: UpdateAgentsInput!) {\n updateAgents(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { LargeLanguageModelClient } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { AGENTS } from './gqls/agent.js';\n\nexport interface Agent {\n /** ID of agent */\n id: string;\n /** Name of agent */\n name: string;\n /** Agent instructions */\n instructions: string;\n /** The ID of the agent */\n agentId: string;\n /** Description of the agent */\n description: string;\n /** Whether the agent has code interpreter enabled */\n codeInterpreterEnabled: boolean;\n /** Whether the agent has retrieval enabled */\n retrievalEnabled: boolean;\n /** The prompt that the agent is based on */\n prompt?: {\n /** Title of the prompt */\n title: string;\n };\n /** Large language model that the agent is based on */\n largeLanguageModel: {\n /** Name of model */\n name: string;\n /** Client */\n client: LargeLanguageModelClient;\n };\n /** Teams assigned to the agent */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Users assigned to the agent */\n owners: {\n /** User email */\n email: string;\n }[];\n /** Functions that the agent has access to */\n agentFunctions: {\n /** Function name */\n name: string;\n }[];\n /** Files that the agent has access to */\n agentFiles: {\n /** File name */\n name: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all agents in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All agents in the organization\n */\nexport async function fetchAllAgents(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: {\n /** Names of the agents to filter for */\n names?: string[];\n /** IDs of agents */\n agentIds?: string[];\n };\n } = {},\n): Promise<Agent[]> {\n const { logger, filterBy = {} } = options;\n const agents: Agent[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n agents: { nodes },\n } = await makeGraphQLRequest<{\n /** Agents */\n agents: {\n /** List */\n nodes: Agent[];\n };\n }>(client, AGENTS, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n agents.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return agents.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const LARGE_LANGUAGE_MODELS = gql`\n query TranscendCliLargeLanguageModels(\n $first: Int!\n $offset: Int!\n $filterBy: LargeLanguageModelFiltersInput\n ) {\n largeLanguageModels(\n first: $first\n orderBy: [\n { field: name, direction: ASC }\n { field: client, direction: ASC }\n { field: isTranscendHosted, direction: ASC }\n ]\n offset: $offset\n filterBy: $filterBy\n ) {\n nodes {\n id\n name\n client\n isTranscendHosted\n }\n }\n }\n`;\n","import { LargeLanguageModelClient } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { LARGE_LANGUAGE_MODELS } from './gqls/largeLanguageModel.js';\n\nexport interface LargeLanguageModel {\n /** ID of prompts */\n id: string;\n /** The name of the large language model. */\n name: string;\n /** The content of the prompt template. */\n client: LargeLanguageModelClient;\n /** Whether hosted by Transcend or not */\n isTranscendHosted: boolean;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all LargeLanguageModels in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All LargeLanguageModels in the organization\n */\nexport async function fetchAllLargeLanguageModels(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<LargeLanguageModel[]> {\n const { logger } = options;\n const largeLanguageModels: LargeLanguageModel[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n largeLanguageModels: { nodes },\n } = await makeGraphQLRequest<{\n /** LargeLanguageModels */\n largeLanguageModels: {\n /** List */\n nodes: LargeLanguageModel[];\n };\n }>(client, LARGE_LANGUAGE_MODELS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n largeLanguageModels.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return largeLanguageModels.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const PROMPT_THREADS = gql`\n query TranscendCliPromptThreads(\n $first: Int!\n $offset: Int!\n $filterBy: PromptThreadFiltersInput!\n ) {\n promptThreads(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n threadId\n slackMessageTs\n slackTeamId\n slackChannelId\n slackChannelName\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PROMPT_THREADS } from './gqls/promptThread.js';\n\nexport interface PromptThread {\n /** ID of prompts */\n id: string;\n /** Thread ID from API */\n threadId: string;\n /** Related slack message TS */\n slackMessageTs?: string;\n /** Related slack message team ID */\n slackTeamId?: string;\n /** Related slack channel ID */\n slackChannelId?: string;\n /** Related slack channel name */\n slackChannelName?: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all PromptThreads in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All PromptThreads in the organization\n */\nexport async function fetchAllPromptThreads(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** Thread IDs to filter on */\n threadIds?: string[];\n /** Slack message timestamps to filter on */\n slackMessageTs?: string[];\n };\n },\n): Promise<PromptThread[]> {\n const { logger, filterBy } = options;\n const promptThreads: PromptThread[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n promptThreads: { nodes },\n } = await makeGraphQLRequest<{\n /** PromptThreads */\n promptThreads: {\n /** List */\n nodes: PromptThread[];\n };\n }>(client, PROMPT_THREADS, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n promptThreads.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return promptThreads.sort((a, b) => a.threadId.localeCompare(b.threadId));\n}\n","import { PromptFilePurpose } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllAgentFiles, type AgentFile } from './fetchAllAgentFiles.js';\nimport { UPDATE_AGENT_FILES, CREATE_AGENT_FILE } from './gqls/agentFile.js';\n\nexport interface AgentFileInput {\n /** Name of the agent file */\n name: string;\n /** Description of the agent file */\n description?: string;\n /** File ID */\n fileId: string;\n /** File size */\n size: number;\n /** File purpose */\n purpose: PromptFilePurpose;\n}\n\n/**\n * Create a new agent file\n *\n * @param client - GraphQL client\n * @param agentFile - Input\n * @param options - Options\n * @returns Created agent file\n */\nexport async function createAgentFile(\n client: GraphQLClient,\n options: {\n /** Agent file to create */\n input: AgentFileInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<AgentFile, 'id' | 'name' | 'fileId'>> {\n const { input: agentFile, logger = NOOP_LOGGER } = options;\n const input = {\n name: agentFile.name,\n description: agentFile.description,\n fileId: agentFile.fileId,\n size: agentFile.size,\n purpose: agentFile.purpose,\n fileUploadedAt: new Date(),\n agentIds: [],\n // TODO: https://transcend.height.app/T-31994 - sync agents\n };\n\n const { createAgentFile } = await makeGraphQLRequest<{\n /** Create agent file mutation */\n createAgentFile: {\n /** Created agent file */\n agentFile: AgentFile;\n };\n }>(client, CREATE_AGENT_FILE, {\n variables: { input },\n logger,\n });\n return createAgentFile.agentFile;\n}\n\n/**\n * Update agent files\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateAgentFiles(\n client: GraphQLClient,\n options: {\n /** [AgentFileInput, agentFileId] list */\n input: [AgentFileInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: agentFiles, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_AGENT_FILES, {\n variables: {\n input: {\n agentFiles: agentFiles.map(([agentFile, id]) => ({\n id,\n name: agentFile.name,\n description: agentFile.description,\n fileId: agentFile.fileId,\n size: agentFile.size,\n purpose: agentFile.purpose,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the agent files\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncAgentFiles(\n client: GraphQLClient,\n inputs: AgentFileInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" agent files...`);\n\n let encounteredError = false;\n\n const existingAgentFiles = await fetchAllAgentFiles(client, { logger });\n\n const agentFileByName = keyBy(existingAgentFiles, 'name') as {\n [k in string]: Pick<AgentFile, 'id' | 'name' | 'fileId'>;\n };\n\n const newAgentFiles = inputs.filter((input) => !agentFileByName[input.name]);\n\n await mapSeries(newAgentFiles, async (agentFile) => {\n try {\n const newAgentFile = await createAgentFile(client, { input: agentFile, logger });\n agentFileByName[newAgentFile.name] = newAgentFile;\n logger.info(`Successfully synced agent file \"${agentFile.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync agent file \"${agentFile.name}\"! - ${(err as Error).message}`);\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" agent files!`);\n await updateAgentFiles(client, {\n input: inputs.map((input) => [input, agentFileByName[input.name]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" agent files!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" agent files! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport type { JSONSchema7 } from 'json-schema';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllAgentFunctions, type AgentFunction } from './fetchAllAgentFunctions.js';\nimport { UPDATE_AGENT_FUNCTIONS, CREATE_AGENT_FUNCTION } from './gqls/agentFunction.js';\n\nexport interface AgentFunctionInput {\n /** Name of the agent function */\n name: string;\n /** Description of the agent function */\n description: string;\n /** The JSON schema parameters (string or parsed) */\n parameters: JSONSchema7 | string;\n}\n\n/**\n * Create a new agent function\n *\n * @param client - GraphQL client\n * @param agentFunction - Input\n * @param options - Options\n * @returns Created agent function\n */\nexport async function createAgentFunction(\n client: GraphQLClient,\n options: {\n /** Agent function to create */\n input: AgentFunctionInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<AgentFunction, 'id' | 'name'>> {\n const { input: agentFunction, logger = NOOP_LOGGER } = options;\n const input = {\n name: agentFunction.name,\n description: agentFunction.description,\n parameters: agentFunction.parameters,\n agentIds: [],\n // TODO: https://transcend.height.app/T-31994 - sync agents\n };\n\n const { createAgentFunction } = await makeGraphQLRequest<{\n /** Create agent function mutation */\n createAgentFunction: {\n /** Created agent function */\n agentFunction: AgentFunction;\n };\n }>(client, CREATE_AGENT_FUNCTION, {\n variables: { input },\n logger,\n });\n return createAgentFunction.agentFunction;\n}\n\n/**\n * Update agent functions\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateAgentFunctions(\n client: GraphQLClient,\n options: {\n /** [AgentFunctionInput, agentFunctionId] list */\n input: [AgentFunctionInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: agentFunctions, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_AGENT_FUNCTIONS, {\n variables: {\n input: {\n agentFunctions: agentFunctions.map(([agentFunction, id]) => ({\n id,\n name: agentFunction.name,\n description: agentFunction.description,\n parameters: agentFunction.parameters,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the agent functions\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncAgentFunctions(\n client: GraphQLClient,\n inputs: AgentFunctionInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" agent functions...`);\n\n let encounteredError = false;\n\n const existingAgentFunctions = await fetchAllAgentFunctions(client, { logger });\n\n const agentFunctionByName = keyBy(existingAgentFunctions, 'name') as {\n [k in string]: Pick<AgentFunction, 'id' | 'name'>;\n };\n\n const newAgentFunctions = inputs.filter((input) => !agentFunctionByName[input.name]);\n\n await mapSeries(newAgentFunctions, async (agentFunction) => {\n try {\n const newAgentFunction = await createAgentFunction(client, { input: agentFunction, logger });\n agentFunctionByName[newAgentFunction.name] = newAgentFunction;\n logger.info(`Successfully synced agent function \"${agentFunction.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync agent function \"${agentFunction.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" agent functions!`);\n await updateAgentFunctions(client, {\n input: inputs.map((input) => [input, agentFunctionByName[input.name]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" agent functions!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" agent functions! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { LargeLanguageModelClient } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllAgents, type Agent } from './fetchAllAgents.js';\nimport { UPDATE_AGENTS, CREATE_AGENT } from './gqls/agent.js';\n\nexport interface AgentInput {\n /** Name of the agent */\n name: string;\n /** Description of the agent */\n description?: string;\n /** Whether the agent has code interpreter enabled */\n codeInterpreterEnabled?: boolean;\n /** Whether the agent has retrieval enabled */\n retrievalEnabled?: boolean;\n /** The prompt title */\n prompt?: string;\n /** Large language model config */\n 'large-language-model': {\n /** Name of model */\n name: string;\n /** Client */\n client: LargeLanguageModelClient;\n };\n}\n\n/**\n * Create a new agent\n *\n * @param client - GraphQL client\n * @param agent - Input\n * @param options - Options\n * @returns Created agent\n */\nexport async function createAgent(\n client: GraphQLClient,\n options: {\n /** Agent to create */\n input: AgentInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Agent, 'id' | 'name' | 'agentId'>> {\n const { input: agent, logger = NOOP_LOGGER } = options;\n const input = {\n name: agent.name,\n description: agent.description,\n codeInterpreterEnabled: agent.codeInterpreterEnabled,\n retrievalEnabled: agent.retrievalEnabled,\n promptTitle: agent.prompt,\n largeLanguageModelName: agent['large-language-model'].name,\n largeLanguageModelClient: agent['large-language-model'].client,\n // TODO: https://transcend.height.app/T-32760 - agentFunction, agentFile\n // TODO: https://transcend.height.app/T-31994 - owners and teams\n };\n\n const { createAgent } = await makeGraphQLRequest<{\n /** Create agent mutation */\n createAgent: {\n /** Created agent */\n agent: Agent;\n };\n }>(client, CREATE_AGENT, {\n variables: { input },\n logger,\n });\n return createAgent.agent;\n}\n\n/**\n * Update agents\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateAgents(\n client: GraphQLClient,\n options: {\n /** [AgentInput, agentId] list */\n input: [AgentInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: agents, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_AGENTS, {\n variables: {\n input: {\n agents: agents.map(([agent, id]) => ({\n id,\n name: agent.name,\n description: agent.description,\n codeInterpreterEnabled: agent.codeInterpreterEnabled,\n retrievalEnabled: agent.retrievalEnabled,\n // TODO: https://transcend.height.app/T-31995 - prompt, largeLanguageModel, agentFunction, agentFile\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the agents\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncAgents(\n client: GraphQLClient,\n inputs: AgentInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" agents...`);\n\n let encounteredError = false;\n\n const existingAgents = await fetchAllAgents(client, { logger });\n\n const agentByName = keyBy(existingAgents, 'name') as {\n [k in string]: Pick<Agent, 'id' | 'name' | 'agentId'>;\n };\n\n const newAgents = inputs.filter((input) => !agentByName[input.name]);\n\n await mapSeries(newAgents, async (agent) => {\n try {\n const newAgent = await createAgent(client, { input: agent, logger });\n agentByName[newAgent.name] = newAgent;\n logger.info(`Successfully synced agent \"${agent.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync agent \"${agent.name}\"! - ${(err as Error).message}`);\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" agents!`);\n await updateAgents(client, {\n input: inputs.map((input) => [input, agentByName[input.name]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" agents!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" agents! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: title, direction: ASC }\n// ]\nexport const GLOBAL_ACTION_ITEM_COLLECTIONS = gql`\n query TranscendCliGlobalActionItemCollectionss(\n $filterBy: GlobalActionItemCollectionFiltersInput!\n ) {\n globalActionItemCollections(filterBy: $filterBy) {\n nodes {\n id\n title\n description\n hidden\n productLine\n }\n }\n }\n`;\n\nexport const CREATE_ACTION_ITEM_COLLECTION = gql`\n mutation TranscendCliCreateActionItemCollection($input: CreateActionItemCollectionInput!) {\n createActionItemCollection(input: $input) {\n created {\n id\n title\n }\n }\n }\n`;\n\nexport const UPDATE_ACTION_ITEM_COLLECTION = gql`\n mutation TranscendCliUpdateActionItemCollection($input: UpdateActionItemCollectionInput!) {\n updateActionItemCollection(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { TranscendProduct } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { GLOBAL_ACTION_ITEM_COLLECTIONS } from './gqls/actionItemCollection.js';\n\nexport interface ActionItemCollection {\n /** ID of collection */\n id: string;\n /** Title of collection */\n title: string;\n /** Description of collection */\n description: string;\n /** Whether section is hidden */\n hidden: boolean;\n /** Which locations/products the action item shows up in */\n productLine: TranscendProduct;\n}\n\n/**\n * Fetch all action item collections in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All action item collections in the organization\n */\nexport async function fetchAllActionItemCollections(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: {\n /** Filter on location */\n location?: TranscendProduct;\n };\n } = {},\n): Promise<ActionItemCollection[]> {\n const { logger, filterBy = {} } = options;\n const {\n globalActionItemCollections: { nodes },\n } = await makeGraphQLRequest<{\n /** ActionItemCollections */\n globalActionItemCollections: {\n /** List */\n nodes: ActionItemCollection[];\n };\n }>(client, GLOBAL_ACTION_ITEM_COLLECTIONS, {\n variables: {\n filterBy: {\n ...filterBy,\n },\n },\n logger,\n });\n return nodes;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: title, direction: ASC }\n// ]\nexport const GLOBAL_ACTION_ITEMS = gql`\n query TranscendCliGlobalActionItems(\n $first: Int!\n $offset: Int!\n $filterBy: GlobalActionItemFiltersInput!\n ) {\n globalActionItems(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n ids\n count\n teams {\n id\n name\n }\n customerExperienceActionItemIds\n users {\n id\n email\n }\n collections {\n title\n id\n }\n dueDate\n priority\n titles\n resolved\n notes\n links\n type\n additionalContexts {\n iconOverride\n requestId\n dataSiloId\n requestType\n latestAirgapVersion\n parentTitle\n }\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n }\n }\n }\n`;\n\nexport const UPDATE_ACTION_ITEMS = gql`\n mutation TranscendCliUpdateActionItems($input: UpdateActionItemsInput!) {\n updateActionItems(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const CREATE_ACTION_ITEMS = gql`\n mutation TranscendCliCreateActionItems($input: [CreateActionItemsInput!]!) {\n createActionItems(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ActionItemCode, ActionItemPriorityOverride } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { GLOBAL_ACTION_ITEMS } from './gqls/actionItem.js';\n\nexport interface ActionItemRaw {\n /** ID of action item */\n ids: string[];\n /** Count of action items */\n count: number;\n /** Teams assigned to action items */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** Customer experience action item ID */\n customerExperienceActionItemIds: string[];\n /** Users assigned to the action item */\n users: {\n /** ID of user */\n id: string;\n /** User email */\n email: string;\n }[];\n /** Due date of action item */\n dueDate?: string;\n /** Priority of action item */\n priority?: ActionItemPriorityOverride;\n /** Titles of action items */\n titles: string[];\n /** Description of the action item */\n resolved: boolean;\n /** Notes */\n notes: string[];\n /** links */\n links: string[];\n /** Action item types */\n type: ActionItemCode;\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n /** Additional context related to action item */\n additionalContexts?: {\n /** Override of icon */\n iconOverride?: string;\n /** Request ID */\n requestId?: string;\n /** Data Silo ID */\n dataSiloId?: string;\n /** Request type */\n requestType?: string;\n /** Airgap version */\n latestAirgapVersion?: string;\n /** Parent title */\n parentTitle?: string;\n };\n /** Sections where action item is grouped under */\n collections: {\n /** ID of collection that action item belongs to */\n id: string;\n /** Title of collection */\n title: string;\n }[];\n}\n\nexport interface ActionItem extends Omit<ActionItemRaw, 'ids' | 'titles' | 'links' | 'notes'> {\n /** ID of action item */\n id: string;\n /** Title of action item */\n title: string;\n /** Notes */\n notes: string;\n /** Links */\n link: string;\n /** Sections where action item is grouped under */\n collections: {\n /** ID of collection that action item belongs to */\n id: string;\n /** Title of collection */\n title: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all action items in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All action items in the organization\n */\nexport async function fetchAllActionItems(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: {\n /** Names of the action items to filter for */\n priority?: ActionItemPriorityOverride[];\n /** Type of action item */\n type?: ActionItemCode[];\n /** Whether resolved or not */\n resolved?: boolean;\n /** Filter for action items due before this date */\n startDueDate?: Date;\n /** Filter for action items due after this date */\n endDueDate?: Date;\n };\n } = {},\n): Promise<ActionItem[]> {\n const { logger, filterBy = {} } = options;\n const actionItems: ActionItem[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n globalActionItems: { nodes },\n } = await makeGraphQLRequest<{\n /** ActionItems */\n globalActionItems: {\n /** List */\n nodes: ActionItemRaw[];\n };\n }>(client, GLOBAL_ACTION_ITEMS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n filterBy: {\n ...filterBy,\n ...(filterBy.startDueDate ? { startDueDate: filterBy.startDueDate.toISOString() } : {}),\n ...(filterBy.endDueDate ? { endDueDate: filterBy.endDueDate.toISOString() } : {}),\n },\n },\n logger,\n });\n actionItems.push(\n ...nodes.map((node) => ({\n ...node,\n id: node.ids[0]!,\n title: node.titles[0]!,\n notes: node.notes[0]!,\n link: node.links[0]!,\n })),\n );\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return actionItems;\n}\n","import { gql } from 'graphql-request';\n\nexport const ASSESSMENT_SECTION_FIELDS = `\n id\n title\n status\n index\n questions {\n id\n title\n index\n type\n subType\n placeholder\n description\n isRequired\n displayLogic\n riskLogic\n requireRiskEvaluation\n requireRiskMatrixEvaluation\n riskCategories {\n id\n title\n }\n riskFramework {\n id\n title\n description\n riskLevels {\n id\n title\n }\n riskCategories {\n id\n title\n }\n riskMatrixColumns {\n id\n title\n }\n riskMatrixRows {\n id\n title\n }\n riskMatrix {\n id\n title\n }\n creator {\n id\n email\n name\n }\n riskMatrixRowTitle\n riskMatrixColumnTitle\n }\n riskLevel {\n id\n title\n }\n reviewerRiskLevel {\n id\n title\n }\n riskLevelFromRiskMatrix {\n id\n title\n }\n answerOptions {\n id\n index\n value\n }\n selectedAnswers {\n ... on AssessmentAnswerInterface {\n id\n index\n value\n }\n }\n respondent {\n id\n email\n name\n }\n attributeKey {\n name\n }\n externalRespondentEmail\n comments {\n id\n content\n createdAt\n updatedAt\n author {\n id\n email\n name\n }\n }\n allowedMimeTypes\n updatedAt\n referenceId\n previousSubmissions {\n id\n updatedAt\n assessmentQuestionId\n answers {\n ... on AssessmentAnswerInterface {\n id\n index\n value\n }\n }\n }\n allowSelectOther\n syncModel\n syncColumn\n syncRowIds\n syncOverride\n }\n assignees {\n id\n email\n name\n }\n externalAssignees {\n id\n email\n }\n isReviewed\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: name, direction: ASC }\n// ]\nexport const ASSESSMENTS = gql`\n query TranscendCliAssessments(\n $first: Int!\n $offset: Int!\n $filterBy: AssessmentFormFiltersInput\n ) {\n assessmentForms(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n creator {\n id\n email\n name\n }\n lastEditor {\n id\n email\n name\n }\n title\n description\n status\n assignees {\n id\n email\n name\n }\n externalAssignees {\n id\n email\n }\n reviewers {\n id\n email\n name\n }\n isLocked\n isArchived\n isExternallyCreated\n dueDate\n createdAt\n updatedAt\n assignedAt\n submittedAt\n approvedAt\n rejectedAt\n titleIsInternal\n retentionSchedule {\n id\n type\n durationDays\n operation\n }\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n sections {\n ${ASSESSMENT_SECTION_FIELDS}\n }\n assessmentGroup {\n id\n title\n description\n }\n resources {\n resourceType\n ... on AttributeBusinessEntityResource {\n id\n title\n }\n ... on AttributeDataSiloResource {\n id\n title\n }\n ... on AttributeDataSubCategoryResource {\n id\n name\n category\n }\n ... on AttributeSubDataPointResource {\n id\n name\n }\n ... on AttributeProcessingPurposeSubCategoryResource {\n id\n name\n purpose\n }\n ... on AttributeRequestResource {\n id\n type\n }\n ... on AttributeVendorResource {\n id\n title\n }\n ... on AttributePromptResource {\n id\n title\n }\n ... on AttributePromptRunResource {\n id\n title\n }\n ... on AttributePromptGroupResource {\n id\n title\n }\n }\n syncedRows {\n resourceType\n ... on AttributeBusinessEntityResource {\n id\n title\n }\n ... on AttributeDataSiloResource {\n id\n title\n }\n ... on AttributeDataSubCategoryResource {\n id\n name\n category\n }\n ... on AttributeSubDataPointResource {\n id\n name\n }\n ... on AttributeProcessingPurposeSubCategoryResource {\n id\n name\n purpose\n }\n ... on AttributeVendorResource {\n id\n title\n }\n }\n }\n }\n }\n`;\n\nexport const IMPORT_ONE_TRUST_ASSESSMENT_FORMS = gql`\n mutation TranscendCliImportOneTrustAssessmentForms($input: ImportOnetrustAssessmentsInput!) {\n importOneTrustAssessmentForms(input: $input) {\n assessmentForms {\n id\n title\n }\n }\n }\n`;\n","import {\n AssessmentFormStatus,\n AssessmentQuestionSubType,\n AssessmentQuestionType,\n AssessmentSyncColumn,\n AssessmentSyncModel,\n AttributeSupportedResourceType,\n DataCategoryType,\n ProcessingPurpose,\n RetentionScheduleOperation,\n RetentionScheduleType,\n} from '@transcend-io/privacy-types';\n/* eslint-disable max-lines */\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ASSESSMENTS } from './gqls/assessment.js';\n\n/**\n * Represents an assessment with various properties and metadata.\n */\nexport interface Assessment {\n /** The ID of the assessment */\n id: string;\n /** The user who created the assessment */\n creator: UserPreview;\n /** The user who last edited the assessment */\n lastEditor: UserPreview;\n /** The title of the assessment */\n title: string;\n /** The description of the assessment */\n description: string;\n /** The current status of the assessment */\n status: AssessmentFormStatus;\n /** The users assigned to the assessment */\n assignees: UserPreview[];\n /** The external users assigned to the assessment */\n externalAssignees: ExternalUser[];\n /** The users who are reviewers of the assessment */\n reviewers: UserPreview[];\n /** Indicates if the assessment is locked */\n isLocked: boolean;\n /** Indicates if the assessment is archived */\n isArchived: boolean;\n /** Indicates if the assessment was created externally */\n isExternallyCreated: boolean;\n /** The due date of the assessment */\n dueDate: string;\n /** The date when the assessment was created */\n createdAt: string;\n /** The date when the assessment was last updated */\n updatedAt: string;\n /** The date when the assessment was assigned */\n assignedAt: string;\n /** The date when the assessment was submitted */\n submittedAt: string;\n /** The date when the assessment was approved */\n approvedAt: string;\n /** The date when the assessment was rejected */\n rejectedAt: string;\n /** Indicates if the title of the assessment is internal */\n titleIsInternal: boolean;\n /** The retention schedule of the assessment */\n retentionSchedule?: RetentionSchedule;\n /** The attribute values associated with the assessment */\n attributeValues: AttributeValue[];\n /** The sections of the assessment */\n sections: AssessmentSection[];\n /** The group to which the assessment belongs */\n assessmentGroup: AssessmentGroup;\n /** The resources associated with the assessment */\n resources: AssessmentResource[];\n /** The rows that are synced with the assessment */\n syncedRows: AssessmentResource[];\n}\n\nexport interface UserPreview {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n /** Name of user */\n name: string;\n}\n\nexport interface ExternalUser {\n /** ID of external user */\n id: string;\n /** Email of external user */\n email: string;\n}\n\nexport interface RetentionSchedule {\n /** ID of retention schedule */\n id: string;\n /** Type */\n type: RetentionScheduleType;\n /** Duration of retention schedule */\n durationDays: number;\n /** The operation to perform on the retention schedule */\n operation: RetentionScheduleOperation;\n}\n\ninterface AttributeValue {\n /** Name of attribute value */\n name: string;\n /** Key */\n attributeKey: {\n /** Name of key */\n name: string;\n };\n}\n\nexport interface AssessmentSection {\n /** ID of section */\n id: string;\n /** Title of section */\n title: string;\n /** Status of section */\n status: string;\n /** Index of section */\n index: number;\n /** Questions */\n questions: AssessmentQuestion[];\n /** Assignees */\n assignees: UserPreview[];\n /** External assignees */\n externalAssignees: ExternalUser[];\n /** Whether is reviewed */\n isReviewed: boolean;\n}\n\n/**\n * Represents a question in the assessment.\n */\nexport interface AssessmentQuestion {\n /**\n * Unique identifier for the question.\n */\n id: string;\n /** Title of the question */\n title: string;\n /** Index of the question in the assessment */\n index: number;\n /** Type of the question */\n type: AssessmentQuestionType;\n /** Subtype of the question */\n subType: AssessmentQuestionSubType;\n /** Placeholder text for the question */\n placeholder: string;\n /** Description of the question */\n description: string;\n /** Indicates if the question is required */\n isRequired: boolean;\n /** Logic for displaying the question */\n displayLogic: string;\n /** Logic for assessing risk related to the question */\n riskLogic: string[];\n /** Indicates if risk evaluation is required for the question */\n requireRiskEvaluation: boolean;\n /** Indicates if risk matrix evaluation is required for the question */\n requireRiskMatrixEvaluation: boolean;\n /** Categories of risk associated with the question */\n riskCategories: RiskCategory[];\n /** Framework used for risk assessment */\n riskFramework?: RiskFramework;\n /** Level of risk associated with the question */\n riskLevel?: RiskLevel;\n /** Risk level assigned by the reviewer */\n reviewerRiskLevel?: RiskLevel;\n /** Risk level derived from the risk matrix */\n riskLevelFromRiskMatrix?: RiskLevel;\n /** Options available for answering the question */\n answerOptions: AssessmentAnswerOption[];\n /** Answers selected for the question */\n selectedAnswers: AssessmentAnswer[];\n /** User who responded to the question */\n respondent: UserPreview;\n /** Key attribute associated with the question */\n attributeKey?: {\n /** Name of key */\n name: string;\n };\n /** Email of the external respondent */\n externalRespondentEmail?: string;\n /** Comments related to the question */\n comments: unknown[];\n /** Allowed MIME types for file uploads in the question */\n allowedMimeTypes: string[];\n /** Timestamp of the last update to the question */\n updatedAt: string;\n /** Reference identifier for the question */\n referenceId: string;\n /** Previous submissions related to the question */\n previousSubmissions: AssessmentPreviousSubmission[];\n /** Indicates if selecting \"Other\" is allowed for the question */\n allowSelectOther: boolean;\n /** Model used for synchronization */\n syncModel: AssessmentSyncModel;\n /** Column used for synchronization */\n syncColumn: AssessmentSyncColumn;\n /** Row IDs used for synchronization */\n syncRowIds: string[];\n /** Indicates if synchronization override is allowed */\n syncOverride: boolean;\n}\n\nexport interface RiskCategory {\n /** ID of category */\n id: string;\n /** Title of category */\n title: string;\n}\n\nexport interface RiskFramework {\n /** ID of framework */\n id: string;\n /** Title of framework */\n title: string;\n /** Description of framework */\n description: string;\n /** Risk levels */\n riskLevels: RiskLevel[];\n /** Risk categories */\n riskCategories: RiskCategory[];\n /** Risk matrix columns */\n riskMatrixColumns: RiskMatrixColumn[];\n /** Risk matrix rows */\n riskMatrixRows: RiskMatrixRow[];\n /** Risk matrix settings */\n riskMatrix: RiskMatrix[][];\n /** Creator of risk framework */\n creator?: UserPreview;\n /** Risk matrix row title */\n riskMatrixRowTitle: string;\n /** Risk matrix column title */\n riskMatrixColumnTitle: string;\n}\n\nexport interface RiskLevel {\n /** ID of risk level */\n id: string;\n /** Title of risk level */\n title: string;\n}\n\nexport interface RiskMatrix {\n /** ID of risk matrix */\n id: string;\n /** Title of risk matrix */\n title: string;\n}\n\nexport interface RiskMatrixColumn {\n /** ID of column */\n id: string;\n /** Title of column */\n title: string;\n}\n\nexport interface RiskMatrixRow {\n /** ID of row */\n id: string;\n /** Title of row */\n title: string;\n}\n\nexport interface AssessmentAnswerOption {\n /** ID of answer option */\n id: string;\n /** Index of answer option */\n index: number;\n /** Value of answer */\n value: string;\n}\n\nexport interface AssessmentAnswer {\n /** ID of answer */\n id: string;\n /** Index of answer */\n index: number;\n /** Value of answer */\n value: string;\n}\n\nexport interface AssessmentComment {\n /** ID of comment */\n id: string;\n /** Content of comment */\n content: string;\n /** Date comment made */\n createdAt: string;\n /** Date comment updated */\n updatedAt: string;\n /** Author of comment */\n author?: UserPreview;\n}\n\nexport interface AssessmentPreviousSubmission {\n /** Id of submission */\n id: string;\n /** Date updated */\n updatedAt: string;\n /** ID of question */\n assessmentQuestionId: string;\n /** Answers */\n answers: AssessmentAnswer[];\n}\n\nexport interface AssessmentGroup {\n /** ID of group */\n id: string;\n /** Title of group */\n title: string;\n /** Description of group */\n description: string;\n}\n\nexport interface AssessmentResource {\n /** Type of resource */\n resourceType: AttributeSupportedResourceType;\n /** ID of resource */\n id: string;\n /** Title of resource */\n title?: string;\n /** Name of resource */\n name?: string;\n /** Category of resource */\n category?: DataCategoryType;\n /** Purpose of resource */\n purpose?: ProcessingPurpose;\n /** Type of integration */\n type?: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all assessments in the organization\n *\n * @param client - GraphQL client\n * @returns All assessments in the organization\n */\nexport async function fetchAllAssessments(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Assessment[]> {\n const { logger } = options;\n const assessments: Assessment[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n assessmentForms: { nodes },\n } = await makeGraphQLRequest<{\n /** Forms */\n assessmentForms: {\n /** Nodes */\n nodes: Assessment[];\n };\n }>(client, ASSESSMENTS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n assessments.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return assessments.sort((a, b) => a.title.localeCompare(b.title));\n}\n/* eslint-enable max-lines */\n","import {\n AssessmentsDisplayLogicAction,\n ComparisonOperator,\n LogicOperator,\n} from '@transcend-io/privacy-types';\nimport { decodeCodec, valuesOf } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\n\n// This codec is for rules that logically require a list of values to compare against.\nexport const AssessmentRuleWithOperands = t.type({\n dependsOnQuestionReferenceId: t.string,\n comparisonOperator: t.union([\n t.literal(ComparisonOperator.IsEqualTo),\n t.literal(ComparisonOperator.IsNotEqualTo),\n t.literal(ComparisonOperator.IsOneOf),\n t.literal(ComparisonOperator.IsNotOneOf),\n t.literal(ComparisonOperator.Contains),\n t.literal(ComparisonOperator.DoesNotContain),\n ]),\n comparisonOperands: t.array(t.string),\n});\n\n// This codec is for the specific rule that does NOT require comparison operands.\nexport const AssessmentRuleWithoutOperands = t.type({\n dependsOnQuestionReferenceId: t.string,\n comparisonOperator: t.union([\n t.literal(ComparisonOperator.IsNotShown),\n t.literal(ComparisonOperator.IsShown),\n ]),\n});\n\n/**\n * The final, flexible codec that accepts EITHER a rule with operands OR a rule without them.\n */\nexport const AssessmentRule = t.union([AssessmentRuleWithOperands, AssessmentRuleWithoutOperands]);\n/** Type override */\nexport type AssessmentRule = t.TypeOf<typeof AssessmentRule>;\n\nexport interface AssessmentNestedRule {\n /** The operator to use when comparing the nested rules */\n logicOperator: LogicOperator;\n /** The rules to evaluate and be compared with to other using the LogicOperator */\n rules?: AssessmentRule[];\n /** The nested rules to add one more level of nesting to the rules. They are also compared to each other. */\n nestedRules?: AssessmentNestedRule[];\n}\n\nexport const AssessmentNestedRule: t.RecursiveType<t.Type<AssessmentNestedRule>> = t.recursion(\n 'AssessmentNestedRule',\n (self) =>\n t.intersection([\n t.type({\n /** The operator to use when comparing the nested rules */\n logicOperator: valuesOf(LogicOperator),\n }),\n t.partial({\n /** The rules to evaluate and be compared with to other using the LogicOperator */\n rules: t.array(AssessmentRule),\n /** The nested rules to add one more level of nesting to the rules. They are also compared to each other. */\n nestedRules: t.array(self),\n }),\n ]),\n);\n\nexport const AssessmentAction = t.partial({\n action: valuesOf(AssessmentsDisplayLogicAction),\n rule: AssessmentRule,\n nestedRule: AssessmentNestedRule,\n});\n\n/** Type override */\nexport type AssessmentAction = t.TypeOf<typeof AssessmentAction>;\n\n/**\n * Parse the assessment display logic\n *\n * @param displayLogic - Stringified rule\n * @returns The parsed assessment display logic\n */\nexport function parseAssessmentDisplayLogic(displayLogic: string): AssessmentAction {\n return decodeCodec(AssessmentAction, displayLogic);\n}\n","import { ComparisonOperator } from '@transcend-io/privacy-types';\nimport { decodeCodec, valuesOf } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\n\nexport const AssessmentRiskLogic = t.intersection([\n t.partial({\n riskAssignment: t.partial({\n riskLevelId: t.string,\n riskMatrixRowId: t.string,\n riskMatrixColumnId: t.string,\n }),\n }),\n t.type({\n comparisonOperands: t.array(t.string),\n comparisonOperator: valuesOf(ComparisonOperator),\n }),\n]);\n\n/** Type override */\nexport type AssessmentRiskLogic = t.TypeOf<typeof AssessmentRiskLogic>;\n\n/**\n * Parse the assessment risk logic\n *\n * @param riskLogic - Stringified rule\n * @returns The parsed assessment risk logic\n */\nexport function parseAssessmentRiskLogic(riskLogic: string): AssessmentRiskLogic {\n return decodeCodec(AssessmentRiskLogic, riskLogic);\n}\n","import { TranscendProduct } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n type ActionItemCollection,\n fetchAllActionItemCollections,\n} from './fetchAllActionItemCollections.js';\nimport {\n CREATE_ACTION_ITEM_COLLECTION,\n UPDATE_ACTION_ITEM_COLLECTION,\n} from './gqls/actionItemCollection.js';\n\nexport interface ActionItemCollectionInput {\n /** The display title of the collection */\n title: string;\n /** Locations where collection is shown */\n productLine: TranscendProduct;\n /** Description of collection */\n description?: string;\n /** Whether hidden */\n hidden?: boolean;\n}\n\n/**\n * Create a new action item collection\n *\n * @param client - GraphQL client\n * @param actionItemCollection - Input\n * @param options - Options\n * @returns Created action item collection\n */\nexport async function createActionItemCollection(\n client: GraphQLClient,\n options: {\n /** Action item collection to create */\n input: ActionItemCollectionInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<ActionItemCollection, 'id' | 'title'>> {\n const { input: actionItemCollection, logger = NOOP_LOGGER } = options;\n const input = {\n title: actionItemCollection.title,\n description: actionItemCollection.description || '',\n hidden: actionItemCollection.hidden || false,\n productLine: actionItemCollection.productLine,\n };\n\n const { createActionItemCollection } = await makeGraphQLRequest<{\n /** Create actionItemCollection mutation */\n createActionItemCollection: {\n /** Created actionItemCollection */\n created: ActionItemCollection;\n };\n }>(client, CREATE_ACTION_ITEM_COLLECTION, {\n variables: { input },\n logger,\n });\n return createActionItemCollection.created;\n}\n\n/**\n * Update an action item collection\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateActionItemCollection(\n client: GraphQLClient,\n options: {\n /** Action item collection input to update */\n input: ActionItemCollectionInput & {\n /** ID of action item collection to update */\n id: string;\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: actionItemCollection, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_ACTION_ITEM_COLLECTION, {\n variables: {\n input: {\n id: actionItemCollection.id,\n title: actionItemCollection.title,\n description: actionItemCollection.description,\n hidden: actionItemCollection.hidden,\n productLine: actionItemCollection.productLine,\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the action item collections\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncActionItemCollections(\n client: GraphQLClient,\n inputs: ActionItemCollectionInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${inputs.length}\" action item collections...`);\n\n const existingActionItemCollections = await fetchAllActionItemCollections(client, { logger });\n\n const collectionByTitle: { [k in string]: ActionItemCollection } = keyBy(\n existingActionItemCollections,\n 'title',\n );\n\n const newCollections = inputs.filter((input) => !collectionByTitle[input.title]);\n\n await mapSeries(newCollections, async (input) => {\n try {\n await createActionItemCollection(client, { input, logger });\n logger.info(`Successfully created action item collection \"${input.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to create action item collection \"${input.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n const actionItemsToUpdate = inputs\n .map((input) => [input, collectionByTitle[input.title]?.id])\n .filter((x): x is [ActionItemCollectionInput, string] => !!x[1]);\n await mapSeries(actionItemsToUpdate, async ([input, actionItemId]) => {\n try {\n await updateActionItemCollection(client, {\n input: { ...input, id: actionItemId },\n logger,\n });\n logger.info(`Successfully synced action item collection \"${input.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync action item collection \"${input.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n return !encounteredError;\n}\n","import { ActionItemCode, ActionItemPriorityOverride } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { uniq, keyBy, chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n type ActionItemCollection,\n fetchAllActionItemCollections,\n} from './fetchAllActionItemCollections.js';\nimport { fetchAllActionItems, type ActionItem } from './fetchAllActionItems.js';\nimport { UPDATE_ACTION_ITEMS, CREATE_ACTION_ITEMS } from './gqls/actionItem.js';\n\n/** Minimal attribute key shape needed for action item sync */\nexport interface ActionItemAttributeKey {\n /** ID of attribute */\n id: string;\n /** Name of attribute */\n name: string;\n}\n\nexport interface ActionItemInput {\n /** The display title of the action item */\n title: string;\n /** Action item type */\n type: ActionItemCode;\n /** The titles of the collections that the action item is grouped within */\n collections: string[];\n /** Priority of the action item */\n priority?: ActionItemPriorityOverride;\n /** Customer experience action item key */\n customerExperienceActionItemId?: string;\n /** Due date of the action item */\n dueDate?: string;\n /** Whether action item has been resolved */\n resolved?: boolean;\n /** Notes */\n notes?: string;\n /** Links to action items */\n link?: string;\n /** The email addresses of the employees assigned to the action item */\n users?: string[];\n /** The names of teams assigned to the action item */\n teams?: string[];\n /** Attribute value and its corresponding attribute key */\n attributes?: {\n /** Attribute key name */\n key: string;\n /** Attribute value names */\n values: string[];\n }[];\n}\n\n/**\n * Create new action items\n *\n * @param client - GraphQL client\n * @param actionItems - Action item inputs\n * @param actionItemCollectionByTitle - Action item collections indexed by title\n * @param options - Options\n */\nexport async function createActionItems(\n client: GraphQLClient,\n options: {\n /** Action items to create */\n input: ActionItemInput[];\n /** Logger instance */\n logger?: Logger;\n /** Action item collections indexed by title */\n actionItemCollectionByTitle: {\n [k in string]: ActionItemCollection;\n };\n // TODO: https://transcend.height.app/T-38961 - insert attributes\n /** Attribute keys indexed by name */\n attributeKeysByName?: {\n [k in string]: ActionItemAttributeKey;\n };\n },\n): Promise<void> {\n const { input: actionItems, logger = NOOP_LOGGER, actionItemCollectionByTitle } = options;\n // TODO: https://transcend.height.app/T-38961 - insert attributes\n // const getAttribute = (key: string): string => {\n // const existing = attributeKeysByName[key];\n // if (!existing) {\n // throw new Error(`Attribute key \"${key}\" does not exist!`);\n // }\n // return existing.id;\n // };\n const chunked = chunk(actionItems, 100);\n await mapSeries(chunked, async (chunkToUpload) => {\n await makeGraphQLRequest(client, CREATE_ACTION_ITEMS, {\n variables: {\n input: chunkToUpload.map((actionItem) => ({\n title: actionItem.title,\n type: actionItem.type,\n priorityOverride: actionItem.priority,\n dueDate: actionItem.dueDate,\n customerExperienceActionItemId: actionItem.customerExperienceActionItemId,\n resolved: actionItem.resolved,\n notes: actionItem.notes,\n link: actionItem.link,\n assigneesUserEmails: actionItem.users,\n assigneesTeamNames: actionItem.teams,\n ...(actionItem.attributes\n ? {\n // TODO: https://transcend.height.app/T-38961 - insert attributes\n // attributes: actionItem.attributes.map(({ key, values }) => ({\n // attributeKeyId: getAttribute(key),\n // attributeValueNames: values,\n // })),\n }\n : {}),\n collectionIds: actionItem.collections.map(\n (collectionTitle) => actionItemCollectionByTitle[collectionTitle]!.id,\n ),\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Update an action item\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateActionItem(\n client: GraphQLClient,\n options: {\n /** Action item input to update */\n input: ActionItemInput & {\n /** ID of action item to update */\n id: string;\n };\n /** Logger instance */\n logger?: Logger;\n /** Attribute keys indexed by name */\n attributeKeysByName?: {\n [k in string]: ActionItemAttributeKey;\n };\n },\n): Promise<void> {\n const { input: actionItem, logger = NOOP_LOGGER, attributeKeysByName = {} } = options;\n const getAttribute = (key: string): string => {\n const existing = attributeKeysByName[key];\n if (!existing) {\n throw new Error(`Attribute key \"${key}\" does not exist!`);\n }\n return existing.id;\n };\n await makeGraphQLRequest(client, UPDATE_ACTION_ITEMS, {\n variables: {\n input: {\n ids: [actionItem.id],\n title: actionItem.title,\n priorityOverride: actionItem.priority,\n dueDate: actionItem.dueDate,\n resolved: actionItem.resolved,\n customerExperienceActionItemId: actionItem.customerExperienceActionItemId,\n notes: actionItem.notes,\n link: actionItem.link,\n assigneesUserEmails: actionItem.users,\n assigneesTeamNames: actionItem.teams,\n ...(actionItem.attributes\n ? {\n attributes: actionItem.attributes.map(({ key, values }) => ({\n attributeKeyId: getAttribute(key),\n attributeValueNames: values,\n })),\n }\n : {}),\n },\n },\n logger,\n });\n}\n\n/**\n * Convert action item to a unique key\n *\n * @param actionItem - action item\n * @returns Unique key\n */\nfunction actionItemToUniqueCode({\n title,\n collections,\n}: Pick<ActionItem, 'title' | 'collections'>): string {\n return `${title}-${collections\n .map((c) => c.title)\n .sort()\n .join('-')}`;\n}\n\n/**\n * Convert action item input to a unique key\n *\n * @param actionItem - action item\n * @returns Unique key\n */\nfunction actionItemInputToUniqueCode({\n title,\n collections,\n}: Pick<ActionItemInput, 'title' | 'collections'>): string {\n return `${title}-${collections.sort().join('-')}`;\n}\n\n/**\n * Sync the action items\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncActionItems(\n client: GraphQLClient,\n inputs: ActionItemInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Pre-fetched attribute keys (pass result of fetchAllAttributes) */\n attributeKeys?: ActionItemAttributeKey[];\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER, attributeKeys = [] } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${inputs.length}\" actionItems...`);\n\n const [existingActionItems, existingActionItemCollections] = await Promise.all([\n fetchAllActionItems(client, { logger }),\n fetchAllActionItemCollections(client, { logger }),\n ]);\n\n const actionItemCollectionByTitle: { [k in string]: ActionItemCollection } = keyBy(\n existingActionItemCollections,\n 'title',\n );\n const actionItemByTitle = keyBy(existingActionItems, actionItemToUniqueCode) as {\n [k in string]: ActionItem;\n };\n const attributeKeysByName = keyBy(attributeKeys, 'name');\n const actionItemByCxId = keyBy(\n existingActionItems.filter((x) => !!x.customerExperienceActionItemIds),\n ({ customerExperienceActionItemIds }) => customerExperienceActionItemIds[0],\n ) as { [k in string]: ActionItem };\n\n const missingCollections = uniq(inputs.map((input) => input.collections).flat()).filter(\n (collectionTitle) => !actionItemCollectionByTitle[collectionTitle],\n );\n if (missingCollections.length > 0) {\n logger.error(\n `Missing action item collections: \"${missingCollections.join(\n '\", \"',\n )}\" - please create them first!`,\n );\n return false;\n }\n\n const newActionItems = inputs.filter(\n (input) =>\n !actionItemByTitle[actionItemInputToUniqueCode(input)] &&\n !actionItemByCxId[input.customerExperienceActionItemId!],\n );\n\n if (newActionItems.length > 0) {\n try {\n logger.info(`Creating \"${newActionItems.length}\" actionItems...`);\n await createActionItems(client, {\n input: newActionItems,\n logger,\n actionItemCollectionByTitle,\n attributeKeysByName,\n });\n logger.info(`Successfully created \"${newActionItems.length}\" actionItems!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create action items! - ${(err as Error).message}`);\n }\n }\n\n const actionItemsToUpdate = inputs\n .map((input) => [\n input,\n actionItemByTitle[actionItemInputToUniqueCode(input)]?.id ||\n actionItemByCxId[input.customerExperienceActionItemId!]?.id,\n ])\n .filter((x): x is [ActionItemInput, string] => !!x[1]);\n await mapSeries(actionItemsToUpdate, async ([input, actionItemId]) => {\n try {\n await updateActionItem(client, {\n input: { ...input, id: actionItemId },\n logger,\n attributeKeysByName,\n });\n logger.info(`Successfully synced action item \"${input.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync action item \"${input.title}\"! - ${(err as Error).message}`);\n }\n });\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const CODE_PACKAGES = gql`\n query TranscendCliCodePackages($first: Int!, $offset: Int!, $input: CodePackageFiltersInput) {\n codePackages(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n type\n relativePath\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n repository {\n id\n name\n }\n dataSilo {\n id\n title\n type\n }\n }\n }\n }\n`;\n\nexport const UPDATE_CODE_PACKAGES = gql`\n mutation TranscendCliUpdateCodePackages($input: UpdateCodePackagesInput!) {\n updateCodePackages(input: $input) {\n clientMutationId\n codePackages {\n id\n name\n description\n type\n relativePath\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n repository {\n id\n name\n }\n dataSilo {\n id\n title\n type\n }\n }\n }\n }\n`;\n\nexport const CREATE_CODE_PACKAGE = gql`\n mutation TranscendCliCreateCodePackage($input: CreateCodePackageInput!) {\n createCodePackage(input: $input) {\n clientMutationId\n codePackage {\n id\n name\n description\n type\n relativePath\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n repository {\n id\n name\n }\n dataSilo {\n id\n title\n type\n }\n }\n }\n }\n`;\n","import { CodePackageType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CODE_PACKAGES } from './gqls/codePackage.js';\n\nexport interface CodePackage {\n /** ID of code package */\n id: string;\n /** Name of code package */\n name: string;\n /** Description of code package */\n description: string;\n /** Type of code package */\n type: CodePackageType;\n /** Relative path to code package in repository */\n relativePath: string;\n /** The teams that manage the code package */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** The users that manage the code package */\n owners: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n }[];\n /** The repository where the code package belongs */\n repository: {\n /** ID of repository */\n id: string;\n /** Name of repository */\n name: string;\n };\n /** The data silo that the code package relates to */\n dataSilo?: {\n /** ID of repository */\n id: string;\n /** Title of repository */\n title: string;\n /** Type of data silo */\n type: string;\n };\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all code packages in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All code packages in the organization\n */\nexport async function fetchAllCodePackages(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<CodePackage[]> {\n const { logger } = options;\n const codePackages: CodePackage[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n codePackages: { nodes },\n } = await makeGraphQLRequest<{\n /** Code packages */\n codePackages: {\n /** List */\n nodes: CodePackage[];\n };\n }>(client, CODE_PACKAGES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n codePackages.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return codePackages.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const REPOSITORIES = gql`\n query TranscendCliRepositories($first: Int!, $offset: Int!, $input: RepositoryFiltersInput) {\n repositories(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n url\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const UPDATE_REPOSITORIES = gql`\n mutation TranscendCliUpdateRepositories($input: UpdateRepositoriesInput!) {\n updateRepositories(input: $input) {\n clientMutationId\n repositories {\n id\n name\n url\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const CREATE_REPOSITORY = gql`\n mutation TranscendCliCreateRepository($input: CreateRepositoryInput!) {\n createRepository(input: $input) {\n clientMutationId\n repository {\n id\n name\n url\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { REPOSITORIES } from './gqls/repository.js';\n\nexport interface Repository {\n /** ID of repository */\n id: string;\n /** Name of repository */\n name: string;\n /** Description of repository */\n description: string;\n /** URL of repo */\n url: string;\n /** The teams that manage the repository */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** The users that manage the repository */\n owners: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all repositories in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All repositories in the organization\n */\nexport async function fetchAllRepositories(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Repository[]> {\n const { logger } = options;\n const repositories: Repository[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n repositories: { nodes },\n } = await makeGraphQLRequest<{\n /** Repositories */\n repositories: {\n /** List */\n nodes: Repository[];\n };\n }>(client, REPOSITORIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n repositories.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return repositories.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const SOFTWARE_DEVELOPMENT_KITS = gql`\n query TranscendCliSoftwareDevelopmentKits(\n $first: Int!\n $offset: Int!\n $input: SoftwareDevelopmentKitFiltersInput\n ) {\n softwareDevelopmentKits(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n codePackageType\n documentationLinks\n repositoryUrl\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const UPDATE_SOFTWARE_DEVELOPMENT_KITS = gql`\n mutation TranscendCliUpdateSoftwareDevelopmentKits($input: UpdateSoftwareDevelopmentKitsInput!) {\n updateSoftwareDevelopmentKits(input: $input) {\n clientMutationId\n softwareDevelopmentKits {\n id\n name\n description\n codePackageType\n documentationLinks\n repositoryUrl\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const CREATE_SOFTWARE_DEVELOPMENT_KIT = gql`\n mutation TranscendCliCreateSoftwareDevelopmentKit($input: CreateSoftwareDevelopmentKitInput!) {\n createSoftwareDevelopmentKit(input: $input) {\n clientMutationId\n softwareDevelopmentKit {\n id\n name\n description\n codePackageType\n documentationLinks\n repositoryUrl\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n","import { CodePackageType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { SOFTWARE_DEVELOPMENT_KITS } from './gqls/softwareDevelopmentKit.js';\n\nexport interface SoftwareDevelopmentKit {\n /** ID of software development kit */\n id: string;\n /** Name of software development kit */\n name: string;\n /** Description of software development kit */\n description: string;\n /** Type of software development kit */\n codePackageType: CodePackageType;\n /** Related documentation */\n documentationLinks: string[];\n /** Link to git repository */\n repositoryUrl?: string;\n /** The teams that manage the software development kit */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** The users that manage the software development kit */\n owners: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all software development kits in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All software development kits in the organization\n */\nexport async function fetchAllSoftwareDevelopmentKits(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SoftwareDevelopmentKit[]> {\n const { logger } = options;\n const softwareDevelopmentKits: SoftwareDevelopmentKit[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n softwareDevelopmentKits: { nodes },\n } = await makeGraphQLRequest<{\n /** Software development kits */\n softwareDevelopmentKits: {\n /** List */\n nodes: SoftwareDevelopmentKit[];\n };\n }>(client, SOFTWARE_DEVELOPMENT_KITS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n softwareDevelopmentKits.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return softwareDevelopmentKits.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { mapSeries, map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllRepositories, type Repository } from './fetchAllRepositories.js';\nimport { UPDATE_REPOSITORIES, CREATE_REPOSITORY } from './gqls/repository.js';\n\nexport interface RepositoryInput {\n /** Title of repository */\n name: string;\n /** Description of the repository */\n description?: string;\n /** Github repository URL */\n url: string;\n}\n\nconst CHUNK_SIZE = 100;\n\n/**\n * Create a new repository\n *\n * @param client - GraphQL client\n * @param input - Repository input\n * @param options - Options\n * @returns Created repository\n */\nexport async function createRepository(\n client: GraphQLClient,\n options: {\n /** Repository to create */\n input: {\n /** Title of repository */\n name: string;\n /** Description of the repository */\n description?: string;\n /** Github repository */\n url: string;\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Repository> {\n const { input, logger = NOOP_LOGGER } = options;\n const {\n createRepository: { repository },\n } = await makeGraphQLRequest<{\n /** createRepository mutation */\n createRepository: {\n /** Software development kit */\n repository: Repository;\n };\n }>(client, CREATE_REPOSITORY, {\n variables: { input },\n logger,\n });\n logger.info(`Successfully created repository \"${input.name}\"!`);\n return repository;\n}\n\n/**\n * Update an existing repository\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Updated repositories\n */\nexport async function updateRepositories(\n client: GraphQLClient,\n options: {\n /** Repository inputs to update */\n input: {\n /** ID of repository */\n id: string;\n /** Title of repository */\n name?: string;\n /** Description of the repository */\n description?: string;\n /** Github repository */\n url?: string;\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n }[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Repository[]> {\n const { input: repositories, logger = NOOP_LOGGER } = options;\n const {\n updateRepositories: { repositories: updatedRepositories },\n } = await makeGraphQLRequest<{\n /** updateRepositories mutation */\n updateRepositories: {\n /** Software development kit */\n repositories: Repository[];\n };\n }>(client, UPDATE_REPOSITORIES, {\n variables: {\n input: {\n repositories,\n },\n },\n logger,\n });\n logger.info(`Successfully updated ${repositories.length} repositories!`);\n return updatedRepositories;\n}\n\n/**\n * Sync the repositories\n *\n * @param client - GraphQL client\n * @param repositories - Repositories\n * @param options - Options\n * @returns The repositories that were upserted and whether the sync was successful\n */\nexport async function syncRepositories(\n client: GraphQLClient,\n repositories: RepositoryInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Concurrency */\n concurrency?: number;\n } = {},\n): Promise<{\n /** The repositories that were upserted */\n repositories: Repository[];\n /** If successful */\n success: boolean;\n}> {\n const { logger = NOOP_LOGGER, concurrency = 20 } = options;\n let encounteredError = false;\n const repos: Repository[] = [];\n\n // Index existing repositories\n const existing = await fetchAllRepositories(client, { logger });\n const repositoryByName = keyBy(existing, 'name');\n\n // Determine which repositories are new vs existing\n const mapRepositoriesToExisting = repositories.map((repoInput) => [\n repoInput,\n repositoryByName[repoInput.name]?.id,\n ]);\n\n // Create the new repositories\n const newRepositories = mapRepositoriesToExisting\n .filter(([, existing]) => !existing)\n .map(([repoInput]) => repoInput as RepositoryInput);\n try {\n logger.info(`Creating \"${newRepositories.length}\" new repositories...`);\n await map(\n newRepositories,\n async (repo) => {\n const newRepo = await createRepository(client, { input: repo, logger });\n repos.push(newRepo);\n },\n {\n concurrency,\n },\n );\n logger.info(`Successfully synced ${newRepositories.length} repositories!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create repositories! - ${(err as Error).message}`);\n }\n\n // Update existing repositories\n const existingRepositories = mapRepositoriesToExisting.filter(\n (x): x is [RepositoryInput, string] => !!x[1],\n );\n const chunks = chunk(existingRepositories, CHUNK_SIZE);\n logger.info(`Updating \"${existingRepositories.length}\" repositories...`);\n\n await mapSeries(chunks, async (chunk) => {\n try {\n const updatedRepos = await updateRepositories(client, {\n input: chunk.map(([input, id]) => ({\n ...input,\n id,\n })),\n logger,\n });\n repos.push(...updatedRepos);\n logger.info(`Successfully updated \"${existingRepositories.length}\" repositories!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to update repositories! - ${(err as Error).message}`);\n }\n\n logger.info(`Synced \"${repositories.length}\" repositories!`);\n });\n\n // Return true upon success\n return {\n repositories: repos,\n success: !encounteredError,\n };\n}\n","import { CodePackageType } from '@transcend-io/privacy-types';\nimport { mapSeries, map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n fetchAllSoftwareDevelopmentKits,\n type SoftwareDevelopmentKit,\n} from './fetchAllSoftwareDevelopmentKits.js';\nimport {\n UPDATE_SOFTWARE_DEVELOPMENT_KITS,\n CREATE_SOFTWARE_DEVELOPMENT_KIT,\n} from './gqls/softwareDevelopmentKit.js';\n\nexport interface SoftwareDevelopmentKitInput {\n /** Title of software development kit */\n name: string;\n /** Code package type */\n codePackageType: CodePackageType;\n /** Description of the SDK */\n description?: string;\n /** Github repository URL */\n repositoryUrl?: string;\n /** Integration name */\n catalogIntegrationName?: string;\n /** Documentation links */\n documentationLinks?: string[];\n}\n\nconst CHUNK_SIZE = 100;\n\n/**\n * Create a new software development kit\n *\n * @param client - GraphQL client\n * @param input - Software development kit input\n * @param options - Options\n * @returns Created software development kit\n */\nexport async function createSoftwareDevelopmentKit(\n client: GraphQLClient,\n options: {\n /** Software development kit to create */\n input: {\n /** Title of software development kit */\n name: string;\n /** Code package type */\n codePackageType: CodePackageType;\n /** Description of the SDK */\n description?: string;\n /** Github repository */\n repositoryUrl?: string;\n /** Integration name */\n catalogIntegrationName?: string;\n /** Doc links */\n documentationLinks?: string[];\n /** Code package IDs */\n codePackageIds?: string[];\n /** Code package names */\n codePackageNames?: string[];\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<SoftwareDevelopmentKit> {\n const { input, logger = NOOP_LOGGER } = options;\n const {\n createSoftwareDevelopmentKit: { softwareDevelopmentKit },\n } = await makeGraphQLRequest<{\n /** createSoftwareDevelopmentKit mutation */\n createSoftwareDevelopmentKit: {\n /** Software development kit */\n softwareDevelopmentKit: SoftwareDevelopmentKit;\n };\n }>(client, CREATE_SOFTWARE_DEVELOPMENT_KIT, {\n variables: { input },\n logger,\n });\n logger.info(`Successfully created software development kit \"${input.name}\"!`);\n return softwareDevelopmentKit;\n}\n\n/**\n * Update an existing software development kit\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Updated software development kits\n */\nexport async function updateSoftwareDevelopmentKits(\n client: GraphQLClient,\n options: {\n /** Software development kit inputs to update */\n input: {\n /** ID of software development kit */\n id: string;\n /** Title of software development kit */\n name?: string;\n /** Description of the SDK */\n description?: string;\n /** Github repository */\n repositoryUrl?: string;\n /** Integration name */\n catalogIntegrationName?: string;\n /** Doc links */\n documentationLinks?: string[];\n /** Code package IDs */\n codePackageIds?: string[];\n /** Code package names */\n codePackageNames?: string[];\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n }[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<SoftwareDevelopmentKit[]> {\n const { input: sdks, logger = NOOP_LOGGER } = options;\n const {\n updateSoftwareDevelopmentKits: { softwareDevelopmentKits },\n } = await makeGraphQLRequest<{\n /** updateSoftwareDevelopmentKits mutation */\n updateSoftwareDevelopmentKits: {\n /** Software development kit */\n softwareDevelopmentKits: SoftwareDevelopmentKit[];\n };\n }>(client, UPDATE_SOFTWARE_DEVELOPMENT_KITS, {\n variables: {\n input: {\n softwareDevelopmentKits: sdks,\n },\n },\n logger,\n });\n logger.info(`Successfully updated ${sdks.length} software development kits!`);\n return softwareDevelopmentKits;\n}\n\n/**\n * Sync the software development kits\n *\n * @param client - GraphQL client\n * @param softwareDevelopmentKits - Software development kits\n * @param options - Options\n * @returns The software development kits that were upserted and whether the sync was successful\n */\nexport async function syncSoftwareDevelopmentKits(\n client: GraphQLClient,\n softwareDevelopmentKits: SoftwareDevelopmentKitInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Concurrency */\n concurrency?: number;\n } = {},\n): Promise<{\n /** The SDKs that were upserted */\n softwareDevelopmentKits: SoftwareDevelopmentKit[];\n /** If successful */\n success: boolean;\n}> {\n const { logger = NOOP_LOGGER, concurrency = 20 } = options;\n let encounteredError = false;\n const sdks: SoftwareDevelopmentKit[] = [];\n logger.info('Syncing software development kits...');\n\n // Index existing software development kits\n const existing = await fetchAllSoftwareDevelopmentKits(client, { logger });\n const softwareDevelopmentKitByTitle = keyBy(existing, ({ name, codePackageType }) =>\n JSON.stringify({ name, codePackageType }),\n );\n\n // Determine which software development kits are new vs existing\n const mapSoftwareDevelopmentKitsToExisting = softwareDevelopmentKits.map((sdkInput) => [\n sdkInput,\n softwareDevelopmentKitByTitle[\n JSON.stringify({\n name: sdkInput.name,\n codePackageType: sdkInput.codePackageType,\n })\n ]?.id,\n ]);\n\n // Create the new software development kits\n const newSoftwareDevelopmentKits = mapSoftwareDevelopmentKitsToExisting\n .filter(([, existing]) => !existing)\n .map(([sdkInput]) => sdkInput as SoftwareDevelopmentKitInput);\n try {\n logger.info(`Creating \"${newSoftwareDevelopmentKits.length}\" new software development kits...`);\n await map(\n newSoftwareDevelopmentKits,\n async (sdk) => {\n const newSdk = await createSoftwareDevelopmentKit(client, { input: sdk, logger });\n sdks.push(newSdk);\n },\n {\n concurrency,\n },\n );\n logger.info(\n `Successfully synced ${newSoftwareDevelopmentKits.length} software development kits!`,\n );\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create software development kits! - ${(err as Error).message}`);\n }\n\n // Update existing software development kits\n const existingSoftwareDevelopmentKits = mapSoftwareDevelopmentKitsToExisting.filter(\n (x): x is [SoftwareDevelopmentKitInput, string] => !!x[1],\n );\n const chunks = chunk(existingSoftwareDevelopmentKits, CHUNK_SIZE);\n logger.info(`Updating \"${existingSoftwareDevelopmentKits.length}\" software development kits...`);\n\n await mapSeries(chunks, async (chunk) => {\n try {\n const updatedSdks = await updateSoftwareDevelopmentKits(client, {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n input: chunk.map(([{ codePackageType, ...input }, id]) => ({\n ...input,\n id,\n })),\n logger,\n });\n sdks.push(...updatedSdks);\n logger.info(\n `Successfully updated \"${existingSoftwareDevelopmentKits.length}\" software development kits!`,\n );\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to update software development kits! - ${(err as Error).message}`);\n }\n\n logger.info(`Synced \"${softwareDevelopmentKits.length}\" software development kits!`);\n });\n\n // Return true upon success\n return {\n softwareDevelopmentKits: sdks,\n success: !encounteredError,\n };\n}\n","import type { CustomFunctionSignPayload } from './codeSigning.js';\nimport type { CustomFunctionConfigInput } from './syncCustomFunction.js';\n\n/**\n * Build the sign payload for a custom function config.\n *\n * @param input - The custom function config\n * @returns The plaintext sign payload\n */\nexport function buildCustomFunctionSignPayload(\n input: CustomFunctionConfigInput,\n): CustomFunctionSignPayload {\n return {\n code: input.code,\n context: {\n userDefinedEnv: input.env ?? {},\n allowedHosts: input.allowedHosts ?? [],\n ...(input.allowThirdPartyImports !== undefined\n ? { allowThirdPartyImports: input.allowThirdPartyImports }\n : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n },\n };\n}\n","import jwt from 'jsonwebtoken';\n\n/**\n * The execution context for a custom function's code.\n */\nexport interface CustomFunctionCodeContextInput {\n /** Environment variables made available to the function (values are encrypted at sign time) */\n userDefinedEnv: Record<string, string>;\n /** Hosts the function is allowed to make network requests to */\n allowedHosts: string[];\n /** Whether the function may import third party modules */\n allowThirdPartyImports?: boolean;\n /** Execution timeout in milliseconds (capped by the Sombra gateway configuration) */\n timeoutMs?: number;\n}\n\n/**\n * The plaintext payload sent to the Sombra customer-ingress\n * `/v1/custom/sign` endpoint.\n */\nexport interface CustomFunctionSignPayload {\n /** The plaintext TypeScript source code of the custom function */\n code: string;\n /** The execution context for the code */\n context: CustomFunctionCodeContextInput;\n}\n\n/**\n * Decode the payload of a JWT without verifying its signature.\n *\n * Custom function code and context JWTs are HMAC-signed by Sombra but their\n * payloads are readable base64url JSON. Since the JWTs are fetched over the\n * authenticated GraphQL API, decoding without verification is safe for the\n * purposes of change detection.\n *\n * @param token - The JWT string\n * @returns The decoded payload object, or undefined if the JWT is malformed\n */\nexport function decodeJwtPayload<T extends object>(token: string): T | undefined {\n return (jwt.decode(token, { json: true }) as T | null) ?? undefined;\n}\n\n/**\n * The decoded payload of a signed custom function code JWT.\n */\nexport interface SignedCustomFunctionCode {\n /** The base64-encoded source code */\n base64Code: string;\n}\n\n/**\n * The decoded payload of a signed custom function code context JWT. The\n * environment variable values are encrypted at sign time, so only their names\n * are comparable.\n */\nexport interface SignedCustomFunctionCodeContext {\n /** Hosts the function is allowed to make network requests to */\n allowedHosts?: string[];\n /** Whether the function may import third party modules */\n allowThirdPartyImports?: boolean;\n /** Execution timeout in milliseconds */\n timeoutMs?: number;\n /** Environment variables, with values encrypted by the Sombra gateway */\n userDefinedEncryptedEnv?: Record<string, unknown>;\n}\n\n/**\n * The result of comparing a local custom function definition against the\n * signed code and context currently stored in Transcend.\n */\nexport interface CustomFunctionDiff {\n /** Whether any comparable field differs */\n changed: boolean;\n /** Human-readable list of fields that differ */\n changedFields: string[];\n}\n\n/**\n * Compare a desired sign payload against the signed code/context JWTs of an\n * existing custom function version.\n *\n * Environment variable *values* are encrypted server-side and cannot be\n * compared — only their names are diffed. Callers should offer a force flag to\n * re-push when only env values change.\n *\n * @param desired - The desired code and context\n * @param existing - The existing signed JWTs from the API\n * @returns The diff result\n */\nexport function diffCustomFunctionCode(\n desired: CustomFunctionSignPayload,\n existing: {\n /** The signed code JWT of the existing version */\n signedCodeJwt: string;\n /** The signed code context JWT of the existing version */\n signedCodeContextJwt: string;\n },\n): CustomFunctionDiff {\n const changedFields: string[] = [];\n\n const codePayload = decodeJwtPayload<SignedCustomFunctionCode>(existing.signedCodeJwt);\n const existingCode =\n codePayload === undefined\n ? undefined\n : Buffer.from(codePayload.base64Code, 'base64').toString('utf-8');\n if (existingCode !== desired.code) {\n changedFields.push('code');\n }\n\n const contextPayload = decodeJwtPayload<SignedCustomFunctionCodeContext>(\n existing.signedCodeContextJwt,\n );\n const existingHosts = [...(contextPayload?.allowedHosts ?? [])].sort();\n const desiredHosts = [...desired.context.allowedHosts].sort();\n if (JSON.stringify(existingHosts) !== JSON.stringify(desiredHosts)) {\n changedFields.push('allowedHosts');\n }\n if (\n (contextPayload?.allowThirdPartyImports ?? false) !==\n (desired.context.allowThirdPartyImports ?? false)\n ) {\n changedFields.push('allowThirdPartyImports');\n }\n if (contextPayload?.timeoutMs !== desired.context.timeoutMs) {\n changedFields.push('timeoutMs');\n }\n const existingEnvNames = Object.keys(contextPayload?.userDefinedEncryptedEnv ?? {}).sort();\n const desiredEnvNames = Object.keys(desired.context.userDefinedEnv).sort();\n if (JSON.stringify(existingEnvNames) !== JSON.stringify(desiredEnvNames)) {\n changedFields.push('env');\n }\n\n return { changed: changedFields.length > 0, changedFields };\n}\n","import { gql } from 'graphql-request';\n\nexport const CUSTOM_FUNCTIONS = gql`\n query TranscendCliCustomFunctions($first: Int!, $offset: Int!, $text: String) {\n customFunctions(first: $first, offset: $offset, filterBy: { text: $text }) {\n nodes {\n id\n name\n description\n type\n lifecycleState\n sombraId\n dataSiloId\n signedCodeJwt\n signedCodeContextJwt\n hasPendingDraft\n activeVersion {\n id\n versionNumber\n lifecycleState\n signedCodeJwt\n }\n draftVersion {\n id\n versionNumber\n lifecycleState\n signedCodeJwt\n }\n }\n totalCount\n }\n }\n`;\n\nexport const CREATE_CUSTOM_FUNCTION = gql`\n mutation TranscendCliCreateCustomFunction($input: CreateCustomFunctionInput!) {\n createCustomFunction(input: $input) {\n customFunction {\n id\n name\n activeVersion {\n id\n versionNumber\n }\n draftVersion {\n id\n versionNumber\n }\n }\n success\n }\n }\n`;\n\nexport const UPDATE_STANDALONE_CUSTOM_FUNCTION = gql`\n mutation TranscendCliUpdateStandaloneCustomFunction(\n $input: UpdateStandaloneCustomFunctionInput!\n ) {\n updateStandaloneCustomFunction(input: $input) {\n customFunction {\n id\n name\n hasPendingDraft\n activeVersion {\n id\n versionNumber\n }\n draftVersion {\n id\n versionNumber\n }\n }\n success\n }\n }\n`;\n\nexport const CREATE_CUSTOM_FUNCTION_DATA_SILO = gql`\n mutation TranscendCliCreateCustomFunctionDataSilo($input: [CreateDataSilosInput!]!) {\n createDataSilos(input: $input) {\n dataSilos {\n id\n title\n }\n }\n }\n`;\n\nexport const DELETE_DATA_SILOS = gql`\n mutation TranscendCliDeleteDataSilos($input: DeleteDataSilosInput!) {\n deleteDataSilos(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const RUN_CUSTOM_FUNCTION = gql`\n mutation TranscendCliRunCustomFunction($input: RunCustomFunctionInput!) {\n runCustomFunction(input: $input) {\n result {\n exitCode\n error {\n message\n stack\n }\n logs {\n message\n file\n }\n profile {\n timeMs\n }\n }\n }\n }\n`;\n\nexport const PROMOTE_CUSTOM_FUNCTION_VERSION = gql`\n mutation TranscendCliPromoteCustomFunctionVersion($input: PromoteCustomFunctionVersionInput!) {\n promoteCustomFunctionVersion(input: $input) {\n customFunction {\n id\n activeVersion {\n id\n versionNumber\n }\n }\n dependencyWarnings {\n dependencyType\n dependencyTitle\n message\n }\n success\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { CREATE_CUSTOM_FUNCTION_DATA_SILO, DELETE_DATA_SILOS } from './gqls/index.js';\n\n/**\n * The integration/catalog name for custom function DSR integrations. Silos\n * created with this catalog get `customSiloConnectionStrategy =\n * CUSTOM_FUNCTION` and start `NOT_CONFIGURED` until a custom function is\n * attached — the same shell the Admin Dashboard creates for a new DSR\n * function.\n */\nexport const CUSTOM_FUNCTION_INTEGRATION_NAME = 'customFunction';\n\n/**\n * A newly created custom function data silo (DSR integration).\n */\nexport interface CustomFunctionDataSilo {\n /** Data silo ID */\n id: string;\n /** Data silo title */\n title: string;\n}\n\n/**\n * Create the data silo (DSR integration) backing a new DSR custom function.\n *\n * The silo is an inert `customFunction`-catalog shell: it has no function\n * attached yet and stays `NOT_CONFIGURED` until `createCustomFunction` links\n * one (which flips it to `Connected`). The `customFunction` catalog requires\n * a Sombra gateway on create — the function's code runs on that gateway.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param options - Options\n * @returns The created data silo\n */\nexport async function createCustomFunctionDataSilo(\n client: GraphQLClient,\n options: {\n /** Title of the integration (conventionally the custom function name) */\n title: string;\n /** The Sombra gateway the function belongs to */\n sombraId: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CustomFunctionDataSilo> {\n const { title, sombraId, logger = NOOP_LOGGER } = options;\n const {\n createDataSilos: { dataSilos },\n } = await makeGraphQLRequest<{\n /** Mutation response */\n createDataSilos: {\n /** The created data silos */\n dataSilos: CustomFunctionDataSilo[];\n };\n }>(client, CREATE_CUSTOM_FUNCTION_DATA_SILO, {\n variables: {\n input: [\n {\n name: CUSTOM_FUNCTION_INTEGRATION_NAME,\n title,\n sombraId,\n },\n ],\n },\n logger,\n });\n const [dataSilo] = dataSilos;\n if (!dataSilo) {\n throw new Error(`Failed to create a custom function data silo titled \"${title}\".`);\n }\n return dataSilo;\n}\n\n/**\n * Delete a data silo. Used to roll back a just-created custom function data\n * silo when the function's test run fails before anything was linked to it.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param dataSiloId - The data silo ID to delete\n * @param options - Options\n */\nexport async function deleteDataSilo(\n client: GraphQLClient,\n dataSiloId: string,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, DELETE_DATA_SILOS, {\n variables: {\n input: { ids: [dataSiloId] },\n },\n logger,\n });\n}\n","import type {\n CustomFunctionLifecycleState,\n CustomFunctionType,\n CustomFunctionVersionLifecycleState,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { CUSTOM_FUNCTIONS } from './gqls/index.js';\n\n/**\n * Preview of a custom function version.\n */\nexport interface CustomFunctionVersionPreview {\n /** Version ID */\n id: string;\n /** Monotonic version number (e.g. \"1.0\") */\n versionNumber: string;\n /** Lifecycle state of the version */\n lifecycleState: CustomFunctionVersionLifecycleState;\n /** Signed code JWT for this specific version's code */\n signedCodeJwt: string;\n}\n\n/**\n * A custom function as returned by the `customFunctions` query.\n */\nexport interface CustomFunction {\n /** Custom function ID */\n id: string;\n /** Display name */\n name: string;\n /** Description */\n description?: string | null;\n /** Product-facing type */\n type: CustomFunctionType;\n /** Lifecycle state */\n lifecycleState: CustomFunctionLifecycleState;\n /** ID of the Sombra gateway the function belongs to (signs its code and runs it) */\n sombraId?: string | null;\n /** ID of the data silo (DSR integration) the function is linked to, for DSR functions */\n dataSiloId?: string | null;\n /** Signed code JWT of the preferred (draft if pending, else active) version */\n signedCodeJwt: string;\n /** Signed code context JWT of the preferred version */\n signedCodeContextJwt: string;\n /** Whether a draft version is newer than the current active version */\n hasPendingDraft: boolean;\n /** Current active version metadata */\n activeVersion?: CustomFunctionVersionPreview | null;\n /** Pending draft version metadata */\n draftVersion?: CustomFunctionVersionPreview | null;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all custom functions in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All custom functions in the organization\n */\nexport async function fetchAllCustomFunctions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter by text match on name or description */\n text?: string;\n };\n } = {},\n): Promise<CustomFunction[]> {\n const { logger = NOOP_LOGGER, filterBy } = options;\n const { text } = filterBy ?? {};\n const customFunctions: CustomFunction[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n customFunctions: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n customFunctions: {\n /** List of matches */\n nodes: CustomFunction[];\n };\n }>(client, CUSTOM_FUNCTIONS, {\n variables: { first: PAGE_SIZE, offset, text },\n logger,\n });\n customFunctions.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return customFunctions.sort((a, b) => a.name.localeCompare(b.name));\n}\n","/**\n * Inject the resolved data silo into a DSR test payload.\n *\n * The backend's unsaved-DSR test path resolves the execution Sombra from\n * `extras.dataSilo.id`, so the payload must reference the function's actual\n * data silo — which the payload file cannot know for silos created during\n * the same push. The silo `id` is always overridden; other `extras.dataSilo`\n * fields from the payload file are preserved. The backend validates DSR\n * payloads against the full webhook notification codec, which requires\n * `title`, `description`, and `link` on the data silo — those are defaulted\n * when the payload file omits them.\n *\n * @param payload - The DSR test payload from the manifest\n * @param dataSilo - The resolved data silo\n * @returns The payload with `extras.dataSilo` pointing at the resolved silo\n */\nexport function injectDataSiloIntoDsrTestPayload(\n payload: object,\n dataSilo: {\n /** Data silo ID */\n id: string;\n /** Fallback title when the payload does not carry one */\n title: string;\n },\n): object {\n const record = payload as {\n /** DSR payload extras */\n extras?: Record<string, unknown>;\n };\n const extras = record.extras ?? {};\n const existingDataSilo = (extras['dataSilo'] ?? {}) as Record<string, unknown>;\n return {\n ...record,\n extras: {\n ...extras,\n dataSilo: {\n title: dataSilo.title,\n description: '',\n link: '',\n ...existingDataSilo,\n id: dataSilo.id,\n },\n },\n };\n}\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { ORGANIZATION } from '../api/gqls/organization.js';\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { CustomFunction } from './fetchAllCustomFunctions.js';\nimport type { CustomFunctionConfigInput } from './syncCustomFunction.js';\n\n/**\n * Resolve which Sombra gateway a custom function's code must be signed\n * against.\n *\n * Each custom function belongs to a single gateway, whose keys sign the code\n * and encrypt the env values — signing against any other gateway would\n * produce JWTs that fail verification at execution time. The gateway is\n * resolved as: the config's `sombraId`, else the existing function's\n * gateway, else the caller's default (e.g. a CLI flag), else `undefined`\n * meaning the organization's primary Sombra.\n *\n * A config that pins a different gateway than the existing function is an\n * error — the gateway of an existing function cannot be changed by a push.\n *\n * @param input - The custom function config\n * @param existing - The matching existing custom function, when there is one\n * @param defaultSombraId - Fallback gateway ID when neither the config nor the existing function specify one\n * @returns The Sombra gateway ID to sign against, or undefined for the primary gateway\n */\nexport function resolveEffectiveSombraId(\n input: Pick<CustomFunctionConfigInput, 'name' | 'sombraId'>,\n existing: Pick<CustomFunction, 'id' | 'sombraId'> | undefined,\n defaultSombraId?: string,\n): string | undefined {\n if (input.sombraId && existing?.sombraId && input.sombraId !== existing.sombraId) {\n throw new Error(\n `Custom function \"${input.name}\" specifies sombra-id \"${input.sombraId}\" but the ` +\n `existing function (id: ${existing.id}) belongs to Sombra gateway \"${existing.sombraId}\". ` +\n 'A push cannot move a custom function between gateways — fix the sombra-id in the ' +\n 'manifest, or remove it to keep the existing gateway.',\n );\n }\n return input.sombraId ?? existing?.sombraId ?? defaultSombraId;\n}\n\n/**\n * Resolve the organization's primary Sombra gateway ID.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param logger - Logger instance\n * @returns The primary Sombra ID\n */\nexport async function resolvePrimarySombraId(\n client: GraphQLClient,\n logger: Logger = NOOP_LOGGER,\n): Promise<string> {\n const { organization } = await makeGraphQLRequest<{\n /** Organization query response */\n organization: {\n /** Primary Sombra gateway */\n sombra: {\n /** Sombra ID */\n id: string;\n } | null;\n };\n }>(client, ORGANIZATION, { logger });\n if (!organization.sombra?.id) {\n throw new Error(\n 'Could not resolve the primary Sombra gateway of the organization, which is ' +\n 'required to create a custom function. Specify a sombra-id on the manifest ' +\n 'entry instead.',\n );\n }\n return organization.sombra.id;\n}\n","import type { CustomFunction } from './fetchAllCustomFunctions.js';\nimport type { CustomFunctionConfigInput } from './syncCustomFunction.js';\n\n/**\n * Resolve which existing custom function a config refers to.\n *\n * When the config has an `id`, it must match an existing function. Otherwise\n * the function is matched by exact name; an ambiguous name (multiple existing\n * functions with the same name) is an error that the caller should resolve by\n * adding an `id` to the config.\n *\n * @param existing - All existing custom functions in the organization\n * @param input - The custom function config\n * @returns The matching custom function, or undefined when it should be created\n */\nexport function resolveExistingCustomFunction(\n existing: CustomFunction[],\n input: Pick<CustomFunctionConfigInput, 'id' | 'name'>,\n): CustomFunction | undefined {\n if (input.id) {\n const match = existing.find(({ id }) => id === input.id);\n if (!match) {\n throw new Error(\n `Custom function \"${input.name}\" specifies id \"${input.id}\" but no custom function ` +\n 'with that ID exists in the organization. Remove the id to create a new function, ' +\n 'or fix the ID.',\n );\n }\n return match;\n }\n\n const matches = existing.filter(({ name }) => name === input.name);\n if (matches.length > 1) {\n throw new Error(\n `Multiple custom functions are named \"${input.name}\" ` +\n `(ids: ${matches.map(({ id }) => id).join(', ')}). ` +\n 'Add an `id` field to this manifest entry to disambiguate which one to update.',\n );\n }\n return matches[0];\n}\n","import type { CustomFunctionPayloadType, CustomFunctionType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { RUN_CUSTOM_FUNCTION } from './gqls/index.js';\n\n/**\n * A log line emitted while executing a custom function.\n */\nexport interface CustomFunctionExecutionLog {\n /** The logged message */\n message: string;\n /** The stream/file the message was written to (e.g. stdout, stderr) */\n file: string;\n}\n\n/**\n * The error a custom function execution failed with, when it failed.\n */\nexport interface CustomFunctionExecutionError {\n /** Error message */\n message: string;\n /** Stack trace, when available */\n stack?: string | null;\n}\n\n/**\n * The result of executing a custom function via the `runCustomFunction`\n * mutation.\n */\nexport interface CustomFunctionExecutionResult {\n /** Process exit code (negative values are internal \"success with warnings\" codes) */\n exitCode: number;\n /** The error the execution failed with, when it failed */\n error?: CustomFunctionExecutionError | null;\n /** Log lines emitted during execution */\n logs: CustomFunctionExecutionLog[];\n /** Execution profile */\n profile: {\n /** Wall-clock execution time in milliseconds */\n timeMs: number;\n };\n}\n\n/**\n * The outcome of test-running a custom function.\n */\nexport interface CustomFunctionTestRunResult {\n /** Whether the test passed (no error and a non-failure exit code) */\n passed: boolean;\n /** The raw execution result (error, logs, exit code, timing) */\n result: CustomFunctionExecutionResult;\n}\n\n/**\n * A single test payload to run a custom function with before pushing.\n */\nexport interface CustomFunctionTestPayload {\n /** The JSON test payload */\n payload: object;\n /**\n * Which export the payload invokes for DSR functions (`DATA_POINT` →\n * `default`, `REQUEST_ENRICHER` → `enricher`). Defaults to DATA_POINT.\n * GENERAL functions are always run as Maestro payloads and ignore this\n */\n payloadType?: CustomFunctionPayloadType;\n}\n\n/**\n * Whether an execution result counts as a passing test.\n *\n * Mirrors the Admin Dashboard's function editor: a run passes when it\n * produced no error and exited with a code <= 0 (negative codes are internal\n * success-with-metadata codes).\n *\n * @param result - The execution result\n * @returns True when the run passed\n */\nexport function didCustomFunctionTestPass(\n result: Pick<CustomFunctionExecutionResult, 'error' | 'exitCode'>,\n): boolean {\n return !result.error && result.exitCode <= 0;\n}\n\n/**\n * Test-run custom function code that has been signed but not yet saved.\n *\n * Sends the pre-signed code/context JWT pair to the `runCustomFunction`\n * mutation as a test run (`isCustomFunctionTestRun: true`). The backend\n * verifies JWT provenance against the target Sombra gateway, executes the\n * code on that gateway, and returns the execution result — nothing is\n * persisted.\n *\n * - GENERAL functions run against `sombraId` (or the organization's primary\n * Sombra) with the payload validated as a Maestro payload.\n * - DSR functions run against the Sombra of the data silo referenced by\n * `extras.dataSilo.id` in the payload; `payloadType` selects which export\n * is invoked (`DATA_POINT` → `default`, `REQUEST_ENRICHER` → `enricher`).\n *\n * Requires backend support for pre-signed JWTs on `runCustomFunction`; older\n * backends reject the JWT fields with a friendly upgrade error.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param options - Options\n * @returns The execution result plus a `passed` boolean\n */\nexport async function runCustomFunctionTest(\n client: GraphQLClient,\n options: {\n /** Product-facing type of the custom function under test */\n type: CustomFunctionType;\n /** Pre-signed code JWT from the Sombra customer-ingress `/v1/custom/sign` route */\n signedCodeJwt: string;\n /** Pre-signed code context JWT from the same sign call */\n signedCodeContextJwt: string;\n /** The JSON test payload to invoke the function with (base64-encoded on the wire) */\n payload: object;\n /** Dedicated Sombra gateway ID (GENERAL only; DSR derives it from the payload's data silo) */\n sombraId?: string;\n /**\n * Which export to invoke for DSR functions (`DATA_POINT` → `default`,\n * `REQUEST_ENRICHER` → `enricher`). Defaults to DATA_POINT. GENERAL\n * functions are always run as Maestro payloads and ignore this setting\n */\n payloadType?: CustomFunctionPayloadType;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CustomFunctionTestRunResult> {\n const {\n type,\n signedCodeJwt,\n signedCodeContextJwt,\n payload,\n sombraId,\n payloadType,\n logger = NOOP_LOGGER,\n } = options;\n\n let response: {\n /** Mutation response */\n runCustomFunction: {\n /** The execution result */\n result: CustomFunctionExecutionResult;\n };\n };\n try {\n response = await makeGraphQLRequest<typeof response>(client, RUN_CUSTOM_FUNCTION, {\n variables: {\n input: {\n type,\n isCustomFunctionTestRun: true,\n payload: Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64'),\n signedCodeJwt,\n signedCodeContextJwt,\n ...(sombraId !== undefined ? { sombraId } : {}),\n ...(payloadType !== undefined ? { payloadType } : {}),\n },\n },\n logger,\n });\n } catch (err) {\n const message = (err as Error).message ?? '';\n // Older backends don't know the pre-signed JWT fields on RunCustomFunctionInput\n if (\n /signedCodeJwt|signedCodeContextJwt/.test(message) &&\n /is not defined|Unknown argument|got invalid value/i.test(message)\n ) {\n throw new Error(\n 'The Transcend backend does not support test-running custom functions from ' +\n 'pre-signed JWTs yet. Re-run with tests skipped, or contact Transcend support. ' +\n `Underlying error: ${message}`,\n );\n }\n throw err;\n }\n\n const { result } = response.runCustomFunction;\n return {\n passed: didCustomFunctionTestPass(result),\n result,\n };\n}\n","import type { Got } from 'got';\n\nimport type { CustomFunctionSignPayload } from './codeSigning.js';\n\n/**\n * The signed JWT pair returned by Sombra's customer-ingress\n * `/v1/custom/sign` endpoint.\n */\nexport interface SignedCustomFunctionJwts {\n /** HMAC-signed JWT wrapping the function code */\n signedCodeJwt: string;\n /** HMAC-signed JWT wrapping the execution context (env values encrypted at sign time) */\n signedCodeContextJwt: string;\n}\n\n/**\n * Sign custom function code against the Sombra customer-ingress\n * `/v1/custom/sign` route.\n *\n * The route is authenticated by the sombra instance's bearer headers (the\n * Transcend API key, plus the Sombra internal key when self-hosting — see\n * `createSombraGotInstance`). Code and env values travel to the customer's\n * own Sombra gateway over TLS and never reach Transcend's backend in\n * plaintext.\n *\n * @param sombra - Got instance authenticated against the Sombra customer ingress\n * @param payload - The plaintext code and execution context to sign\n * @param options - Options\n * @returns The signed code and context JWTs to store via the GraphQL API\n */\nexport async function signCustomFunctionCode(\n sombra: Got,\n payload: CustomFunctionSignPayload,\n options: {\n /** Existing custom function ID, forwarded for Sombra log context */\n customFunctionId?: string;\n } = {},\n): Promise<SignedCustomFunctionJwts> {\n const { customFunctionId } = options;\n try {\n return await sombra\n .post('v1/custom/sign', {\n json: {\n code: payload.code,\n context: payload.context,\n ...(customFunctionId ? { customFunction: { id: customFunctionId } } : {}),\n },\n })\n .json<SignedCustomFunctionJwts>();\n } catch (err) {\n const statusCode = (err as { response?: { statusCode?: number } })?.response?.statusCode;\n if (statusCode === 404) {\n throw new Error(\n 'The Sombra gateway does not support the /v1/custom/sign route. ' +\n 'Upgrade your Sombra gateway to a version that supports custom function code signing.',\n );\n }\n throw err;\n }\n}\n","import { CustomFunctionType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { buildCustomFunctionSignPayload } from './buildCustomFunctionSignPayload.js';\nimport { diffCustomFunctionCode } from './codeSigning.js';\nimport { createCustomFunctionDataSilo, deleteDataSilo } from './customFunctionDataSilo.js';\nimport type { CustomFunction } from './fetchAllCustomFunctions.js';\nimport {\n CREATE_CUSTOM_FUNCTION,\n PROMOTE_CUSTOM_FUNCTION_VERSION,\n UPDATE_STANDALONE_CUSTOM_FUNCTION,\n} from './gqls/index.js';\nimport { injectDataSiloIntoDsrTestPayload } from './injectDataSiloIntoDsrTestPayload.js';\nimport { resolveEffectiveSombraId, resolvePrimarySombraId } from './resolveEffectiveSombraId.js';\nimport { resolveExistingCustomFunction } from './resolveExistingCustomFunction.js';\nimport {\n runCustomFunctionTest,\n type CustomFunctionTestPayload,\n type CustomFunctionTestRunResult,\n} from './runCustomFunctionTest.js';\nimport { signCustomFunctionCode } from './signCustomFunctionCode.js';\n\n/**\n * The desired state of a custom function to sync to Transcend.\n */\nexport interface CustomFunctionConfigInput {\n /**\n * Custom function ID to update. When set, this is the sync key and the\n * function must already exist. When omitted, the function is matched by\n * exact name — which fails if multiple functions share the name.\n */\n id?: string;\n /** Display name of the custom function (the sync key when no `id` is set) */\n name: string;\n /** The plaintext TypeScript source code */\n code: string;\n /** Description of the custom function */\n description?: string;\n /** Product-facing type. Defaults to GENERAL */\n type?: CustomFunctionType;\n /** Data silo ID the function is attached to (required for DSR functions) */\n dataSiloId?: string;\n /** Dedicated Sombra gateway ID (defaults to the organization's primary Sombra) */\n sombraId?: string;\n /** Hosts the function is allowed to make network requests to */\n allowedHosts?: string[];\n /** Whether the function may import third party modules */\n allowThirdPartyImports?: boolean;\n /** Execution timeout in milliseconds */\n timeoutMs?: number;\n /** Environment variables to expose to the function */\n env?: Record<string, string>;\n}\n\n/**\n * The outcome of syncing a single custom function.\n */\nexport type CustomFunctionSyncOutcome =\n /** A new custom function was created */\n | 'created'\n /** A new revision was pushed to an existing custom function */\n | 'updated'\n /** Only metadata (name/description) changed; updated without a new code revision */\n | 'metadata-updated'\n /** No changes detected; nothing was pushed */\n | 'skipped'\n /** The test run failed; nothing was pushed */\n | 'test-failed'\n /** Dry run: a new custom function would be created */\n | 'would-create'\n /** Dry run: a new revision would be pushed */\n | 'would-update';\n\n/**\n * The result of running one test payload during a sync, tagged with the\n * export the payload invoked.\n */\nexport interface CustomFunctionSyncTestResult extends CustomFunctionTestRunResult {\n /** Which export the payload invoked (DSR functions; DATA_POINT when omitted) */\n payloadType?: CustomFunctionTestPayload['payloadType'];\n}\n\n/**\n * The result of syncing a single custom function.\n */\nexport interface CustomFunctionSyncResult {\n /** The sync outcome */\n outcome: CustomFunctionSyncOutcome;\n /** The custom function ID (when it exists or was created) */\n customFunctionId?: string;\n /** The version number that is now active or drafted (when a revision was pushed) */\n versionNumber?: string;\n /** Fields that changed, driving the update (empty on create/skip) */\n changedFields: string[];\n /** Whether the pushed revision was promoted to active */\n promoted: boolean;\n /** The test-run results (one per payload), when the code was tested */\n testResults?: CustomFunctionSyncTestResult[];\n /** The data silo (DSR integration) the function is linked to, for DSR functions */\n dataSiloId?: string;\n /**\n * Whether a new data silo (DSR integration) was created during this sync.\n * On a `test-failed` outcome this means the auto-created silo was rolled\n * back (deleted) — `dataSiloId` is absent in that case.\n */\n createdDataSilo?: boolean;\n}\n\n/**\n * Sync a custom function definition (metadata + code revision) to Transcend.\n *\n * - When no custom function with the given name exists, one is created.\n * - A new DSR function without a `dataSiloId` also gets its DSR integration\n * created: a `customFunction`-catalog data silo shell is created first (the\n * backend needs it to exist for the test run), the code is tested against\n * it, and on a passing test the function is created and linked. A failing\n * test rolls the silo back (deletes it).\n * - When one exists and the code/context changed, a new draft revision is\n * created and (unless `promote` is false) promoted to active.\n * - When only metadata changed (description, or name for id-pinned entries),\n * the function record is updated in place — no signing, no test runs, and\n * no new code revision.\n * - When nothing changed, the function is skipped (unless `force` is set).\n *\n * Code is signed against the Sombra customer-ingress `/v1/custom/sign` route\n * before being saved via the GraphQL API, so plaintext code and env values\n * never reach Transcend's backend.\n *\n * Environment variable values are encrypted server-side and cannot be diffed;\n * use `force` to re-push when only env values changed.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param options - Options\n * @returns The sync result\n */\nexport async function syncCustomFunction(\n client: GraphQLClient,\n options: {\n /** The desired custom function state */\n input: CustomFunctionConfigInput;\n /**\n * Got instance authenticated against the customer ingress of the Sombra\n * gateway the function belongs to (see `resolveEffectiveSombraId`).\n * Required unless `dryRun` is set\n */\n sombra?: Got;\n /** Default Sombra gateway ID when neither the config nor the existing function specify one */\n defaultSombraId?: string;\n /** All existing custom functions in the organization (see `fetchAllCustomFunctions`) */\n existing: CustomFunction[];\n /** Whether to promote new revisions to active. Defaults to true */\n promote?: boolean;\n /** When true, report what would happen without mutating anything */\n dryRun?: boolean;\n /** When true, push a new revision even if no changes were detected */\n force?: boolean;\n /**\n * JSON test payloads to run the freshly signed code with before pushing.\n * Every payload is run via the `runCustomFunction` mutation (DSR\n * functions can cover both the default and enricher exports via\n * `payloadType`) and all must pass — any failure rejects the push\n * (outcome `test-failed`). Skipped on dry runs (nothing is signed).\n */\n testPayloads?: CustomFunctionTestPayload[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CustomFunctionSyncResult> {\n const {\n input,\n sombra,\n defaultSombraId,\n existing: allExisting,\n promote = true,\n dryRun = false,\n force = false,\n testPayloads,\n logger = NOOP_LOGGER,\n } = options;\n const type: CustomFunctionType = input.type ?? CustomFunctionType.General;\n\n const existing = resolveExistingCustomFunction(allExisting, input);\n // Validates config-vs-existing gateway mismatches, including on dry runs\n const effectiveSombraId = resolveEffectiveSombraId(input, existing, defaultSombraId);\n const signPayload = buildCustomFunctionSignPayload(input);\n\n // Diff against the existing preferred (draft if pending, else active) version\n let changedFields: string[] = [];\n let metadataOnly = false;\n if (existing) {\n const diff = diffCustomFunctionCode(signPayload, {\n signedCodeJwt: existing.signedCodeJwt,\n signedCodeContextJwt: existing.signedCodeContextJwt,\n });\n changedFields = diff.changedFields;\n if (!diff.changed && !force) {\n // Code and context are identical — check the plaintext metadata on the\n // function record. A rename can only surface for id-pinned entries\n // (name is the sync key otherwise), and a manifest without a\n // description leaves the existing one unmanaged.\n const metadataChanges: string[] = [];\n if (input.name !== existing.name) {\n metadataChanges.push('name');\n }\n if (input.description !== undefined && input.description !== (existing.description ?? '')) {\n metadataChanges.push('description');\n }\n if (metadataChanges.length === 0) {\n logger.info(`No changes detected for custom function \"${input.name}\" — skipping.`);\n return {\n outcome: 'skipped',\n customFunctionId: existing.id,\n changedFields: [],\n promoted: false,\n ...(existing.dataSiloId ? { dataSiloId: existing.dataSiloId } : {}),\n };\n }\n metadataOnly = true;\n changedFields = metadataChanges;\n }\n }\n\n if (dryRun) {\n return {\n outcome: existing ? 'would-update' : 'would-create',\n ...(existing ? { customFunctionId: existing.id } : {}),\n changedFields,\n promoted: false,\n };\n }\n\n // Metadata-only changes update the function record in place: no signing,\n // no test runs, no new code revision — the active version keeps running\n // byte-identical code.\n if (existing && metadataOnly) {\n logger.info(\n `Updating metadata (${changedFields.join(', ')}) for custom function ` +\n `\"${input.name}\" — code is unchanged, no new revision.`,\n );\n await makeGraphQLRequest(client, UPDATE_STANDALONE_CUSTOM_FUNCTION, {\n variables: {\n input: {\n id: existing.id,\n name: input.name,\n ...(input.description !== undefined ? { description: input.description } : {}),\n },\n },\n logger,\n });\n return {\n outcome: 'metadata-updated',\n customFunctionId: existing.id,\n changedFields,\n promoted: false,\n ...(existing.dataSiloId ? { dataSiloId: existing.dataSiloId } : {}),\n };\n }\n\n if (!sombra) {\n throw new Error('A Sombra customer-ingress client is required to push custom function code.');\n }\n\n // Sign the code and context against the Sombra gateway; the resulting JWTs\n // are stored via the GraphQL API\n const { signedCodeJwt, signedCodeContextJwt } = await signCustomFunctionCode(\n sombra,\n signPayload,\n { customFunctionId: existing?.id },\n );\n\n // Resolve the data silo (DSR integration) backing a DSR function: the\n // config's, else the existing function's. A brand-new DSR function without\n // one gets its integration created here — an inert `customFunction`-catalog\n // shell that stays NOT_CONFIGURED until the function is linked to it. It\n // must exist before the test run, because the backend resolves the\n // execution Sombra from the payload's `extras.dataSilo.id`.\n let dataSiloId = input.dataSiloId ?? existing?.dataSiloId ?? undefined;\n let createdDataSilo = false;\n if (type === CustomFunctionType.Dsr && !existing && dataSiloId === undefined) {\n const siloSombraId = effectiveSombraId ?? (await resolvePrimarySombraId(client, logger));\n logger.info(`Creating DSR integration (data silo) for custom function \"${input.name}\"...`);\n const dataSilo = await createCustomFunctionDataSilo(client, {\n title: input.name,\n sombraId: siloSombraId,\n logger,\n });\n dataSiloId = dataSilo.id;\n createdDataSilo = true;\n }\n\n // Test the freshly signed code before pushing anything. Every payload runs\n // (so one CI run reports every failing export) and all must pass — any\n // failure rejects the push so a broken revision never reaches (or is\n // promoted on) the function.\n let testResults: CustomFunctionSyncTestResult[] | undefined;\n if (testPayloads !== undefined && testPayloads.length > 0) {\n logger.info(\n `Testing custom function \"${input.name}\" before push ` +\n `(${testPayloads.length} payload${testPayloads.length === 1 ? '' : 's'})...`,\n );\n testResults = [];\n for (const { payload, payloadType } of testPayloads) {\n const run: CustomFunctionTestRunResult = await runCustomFunctionTest(client, {\n type,\n signedCodeJwt,\n signedCodeContextJwt,\n // DSR payloads must reference the function's actual data silo — the\n // backend derives the execution gateway from `extras.dataSilo.id`\n payload:\n type === CustomFunctionType.Dsr && dataSiloId !== undefined\n ? injectDataSiloIntoDsrTestPayload(payload, {\n id: dataSiloId,\n title: input.name,\n })\n : payload,\n // GENERAL runs on the function's gateway\n ...(type === CustomFunctionType.General && effectiveSombraId !== undefined\n ? { sombraId: effectiveSombraId }\n : {}),\n ...(type === CustomFunctionType.Dsr && payloadType !== undefined ? { payloadType } : {}),\n logger,\n });\n testResults.push({ ...run, ...(payloadType !== undefined ? { payloadType } : {}) });\n }\n if (testResults.some(({ passed }) => !passed)) {\n // Roll back the integration created for this function — nothing was\n // linked to it yet, so a failed test leaves no trace behind\n if (createdDataSilo && dataSiloId !== undefined) {\n logger.info(\n `Rolling back DSR integration (data silo ${dataSiloId}) for \"${input.name}\" — test failed.`,\n );\n await deleteDataSilo(client, dataSiloId, { logger });\n dataSiloId = undefined;\n }\n return {\n outcome: 'test-failed',\n ...(existing ? { customFunctionId: existing.id } : {}),\n changedFields,\n promoted: false,\n testResults,\n ...(createdDataSilo ? { createdDataSilo } : {}),\n };\n }\n }\n\n // Create fresh\n if (!existing) {\n // The backend requires an explicit gateway for GENERAL functions (and\n // rejects one for DSR functions, whose data silo dictates the gateway)\n const createSombraId =\n type === CustomFunctionType.General\n ? (effectiveSombraId ?? (await resolvePrimarySombraId(client, logger)))\n : undefined;\n // DSR functions are always created active — the backend rejects setActive\n if (type === CustomFunctionType.Dsr && !promote) {\n logger.warn(\n `DSR custom functions are always created active — \"${input.name}\" ` +\n 'will be created promoted despite promote being disabled.',\n );\n }\n let response: {\n /** Mutation response */\n createCustomFunction: {\n /** The created custom function */\n customFunction: {\n /** Custom function ID */\n id: string;\n /** Active version, when created active */\n activeVersion?: {\n /** Version ID */\n id: string;\n /** Version number */\n versionNumber: string;\n } | null;\n /** Draft version, when created inactive */\n draftVersion?: {\n /** Version ID */\n id: string;\n /** Version number */\n versionNumber: string;\n } | null;\n };\n };\n };\n try {\n response = await makeGraphQLRequest<typeof response>(client, CREATE_CUSTOM_FUNCTION, {\n variables: {\n input: {\n type,\n ...(createSombraId !== undefined ? { sombraId: createSombraId } : {}),\n ...(type === CustomFunctionType.Dsr ? { dataSiloId } : {}),\n name: input.name,\n ...(input.description !== undefined ? { description: input.description } : {}),\n ...(type === CustomFunctionType.General ? { setActive: promote } : {}),\n signedCodeJwt,\n signedCodeContextJwt,\n },\n },\n logger,\n });\n } catch (err) {\n // Roll back the integration created for this function — nothing was\n // linked to it yet, so a failed create must not strand an orphaned silo\n if (createdDataSilo && dataSiloId !== undefined) {\n logger.warn(\n `Rolling back DSR integration (data silo ${dataSiloId}) for ` +\n `\"${input.name}\" — creating the custom function failed.`,\n );\n await deleteDataSilo(client, dataSiloId, { logger });\n }\n throw err;\n }\n const { customFunction } = response.createCustomFunction;\n const version = customFunction.activeVersion ?? customFunction.draftVersion;\n return {\n outcome: 'created',\n customFunctionId: customFunction.id,\n ...(version ? { versionNumber: version.versionNumber } : {}),\n changedFields,\n promoted: type === CustomFunctionType.Dsr ? true : promote,\n ...(testResults ? { testResults } : {}),\n ...(dataSiloId !== undefined ? { dataSiloId } : {}),\n ...(createdDataSilo ? { createdDataSilo } : {}),\n };\n }\n\n // Push a new draft revision to the existing function\n const {\n updateStandaloneCustomFunction: { customFunction: updated },\n } = await makeGraphQLRequest<{\n /** Mutation response */\n updateStandaloneCustomFunction: {\n /** The updated custom function */\n customFunction: {\n /** Custom function ID */\n id: string;\n /** The new draft version */\n draftVersion?: {\n /** Version ID */\n id: string;\n /** Version number */\n versionNumber: string;\n } | null;\n };\n };\n }>(client, UPDATE_STANDALONE_CUSTOM_FUNCTION, {\n variables: {\n input: {\n id: existing.id,\n name: input.name,\n ...(input.description !== undefined ? { description: input.description } : {}),\n signedCodeJwt,\n signedCodeContextJwt,\n },\n },\n logger,\n });\n\n const draft = updated.draftVersion;\n if (!draft) {\n throw new Error(\n `Expected a draft version to be created for custom function \"${input.name}\" but none was returned.`,\n );\n }\n\n if (promote) {\n await makeGraphQLRequest(client, PROMOTE_CUSTOM_FUNCTION_VERSION, {\n variables: {\n input: {\n customFunctionId: updated.id,\n versionId: draft.id,\n },\n },\n logger,\n });\n }\n\n return {\n outcome: 'updated',\n customFunctionId: updated.id,\n versionNumber: draft.versionNumber,\n changedFields,\n promoted: promote,\n ...(testResults ? { testResults } : {}),\n ...(dataSiloId !== undefined ? { dataSiloId } : {}),\n };\n}\n","import { describePackageName } from '@transcend-io/utils';\n\nexport interface MonorepoPackageDefinition {\n directory: string;\n displayName: string;\n packageName: string;\n}\n\nexport function createMonorepoPackageDefinition(\n name: string,\n directory: string,\n): MonorepoPackageDefinition {\n const packageNameParts = describePackageName(name);\n\n return {\n directory,\n displayName: packageNameParts.displayName,\n packageName: `@transcend-io/${packageNameParts.slug}`,\n };\n}\n\nexport * from './gqls/index.js';\nexport * from './api/index.js';\nexport * from './data-inventory/index.js';\nexport * from './preference-management/index.js';\nexport * from './administration/index.js';\nexport * from './consent/index.js';\nexport * from './dsr-automation/index.js';\nexport * from './ai/index.js';\nexport * from './assessments/index.js';\nexport * from './code-intelligence/index.js';\nexport * from './custom-functions/index.js';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PURPOSES","PAGE_SIZE","PURPOSES","PAGE_SIZE","MAX_BATCH_SIZE","pmap","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","MAX_PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","MAX_PAGE_SIZE","MAX_PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","CHUNK_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","PAGE_SIZE","CHUNK_SIZE"],"sources":["../src/gqls/shared.ts","../src/api/buildTranscendGraphQLClient.ts","../src/api/makeGraphQLRequest.ts","../src/api/gqls/organization.ts","../src/api/createSombraGotInstance.ts","../src/api/withTransientRetry.ts","../src/data-inventory/gqls/businessEntity.ts","../src/data-inventory/fetchAllBusinessEntities.ts","../src/data-inventory/gqls/dataCategory.ts","../src/data-inventory/fetchAllDataCategories.ts","../src/data-inventory/gqls/dataPoint.ts","../src/data-inventory/fetchAllDataPoints.ts","../src/data-inventory/gqls/dataSilo.ts","../src/data-inventory/fetchAllDataSilos.ts","../src/data-inventory/gqls/identifier.ts","../src/data-inventory/fetchAllIdentifiers.ts","../src/data-inventory/gqls/processingActivity.ts","../src/data-inventory/fetchAllProcessingActivities.ts","../src/data-inventory/gqls/vendor.ts","../src/data-inventory/fetchAllVendors.ts","../src/data-inventory/fetchEnrichedDataSilos.ts","../src/data-inventory/syncBusinessEntities.ts","../src/data-inventory/syncDataCategories.ts","../src/data-inventory/syncDataSiloDependencies.ts","../src/data-inventory/syncProcessingActivities.ts","../src/data-inventory/syncVendors.ts","../src/preference-management/gqls/purpose.ts","../src/preference-management/fetchAllPurposes.ts","../src/preference-management/gqls/preferenceTopic.ts","../src/preference-management/fetchAllPreferenceTopics.ts","../src/preference-management/fetchAllPurposesAndPreferences.ts","../src/preference-management/gqls/preferenceAccessTokens.ts","../src/preference-management/createPreferenceAccessTokens.ts","../src/preference-management/types.ts","../src/preference-management/codecs.ts","../src/preference-management/getPreferenceMetadataFromRow.ts","../src/preference-management/getPreferenceIdentifiersFromRow.ts","../src/preference-management/getUniquePreferenceIdentifierNamesFromRow.ts","../src/preference-management/getPreferenceUpdatesFromRow.ts","../src/preference-management/checkIfPendingPreferenceUpdatesAreNoOp.ts","../src/preference-management/checkIfPendingPreferenceUpdatesCauseConflict.ts","../src/preference-management/transformPreferenceRecordToCsv.ts","../src/preference-management/buildConsentChunks.ts","../src/preference-management/pickConsentChunkMode.ts","../src/preference-management/getComparisonTimeForRecord.ts","../src/preference-management/iterateConsentPages.ts","../src/preference-management/consentWindowHasAny.ts","../src/preference-management/discoverConsentWindow.ts","../src/preference-management/fetchConsentPreferences.ts","../src/preference-management/getPreferencesForIdentifiers.ts","../src/preference-management/fetchConsentPreferencesChunked.ts","../src/preference-management/loadReferenceData.ts","../src/preference-management/gqls/preferenceOptionValues.ts","../src/preference-management/fetchAllPreferenceOptionValues.ts","../src/preference-management/syncPreferenceOptionValues.ts","../src/preference-management/syncPurposes.ts","../src/preference-management/syncPreferenceTopics.ts","../src/administration/gqls/attribute.ts","../src/administration/fetchAllAttributes.ts","../src/administration/gqls/message.ts","../src/administration/fetchAllMessages.ts","../src/administration/gqls/team.ts","../src/administration/fetchAllTeams.ts","../src/administration/fetchParentOrganizationTeams.ts","../src/administration/gqls/user.ts","../src/administration/fetchAllUsers.ts","../src/administration/gqls/apiKey.ts","../src/administration/fetchApiKeys.ts","../src/administration/formatRegions.ts","../src/administration/gqls/auth.ts","../src/administration/loginUser.ts","../src/administration/manageApiKeys.ts","../src/administration/setResourceAttributes.ts","../src/administration/syncAttribute.ts","../src/administration/syncIntlMessages.ts","../src/administration/syncTeams.ts","../src/administration/gqls/attributeKey.ts","../src/consent/createTranscendConsentGotInstance.ts","../src/consent/gqls/consentManager.ts","../src/consent/deployConsentManager.ts","../src/consent/fetchConsentManagerId.ts","../src/consent/gqls/shared.ts","../src/consent/gqls/cookies.ts","../src/consent/fetchAllCookies.ts","../src/consent/gqls/dataFlows.ts","../src/consent/fetchAllDataFlows.ts","../src/consent/gqls/privacyCenter.ts","../src/consent/fetchPrivacyCenterId.ts","../src/consent/gqls/policy.ts","../src/consent/fetchAllPolicies.ts","../src/consent/fetchAllPrivacyCenters.ts","../src/consent/gqls/processingPurpose.ts","../src/consent/fetchAllProcessingPurposes.ts","../src/consent/fetchConsentManager.ts","../src/consent/gqls/consentManagerMetrics.ts","../src/consent/fetchConsentManagerAnalyticsData.ts","../src/consent/gqls/experiences.ts","../src/consent/fetchConsentManagerExperiences.ts","../src/consent/fetchConsentManagerTheme.ts","../src/consent/fetchConsentThemes.ts","../src/consent/fetchConsentVariants.ts","../src/consent/gqls/airgapBundleAnalytics.ts","../src/consent/fetchAirgapBundleAnalytics.ts","../src/consent/gqls/purposes.ts","../src/consent/gqls/partitions.ts","../src/consent/gqls/stats.ts","../src/consent/gqls/consentWorkflowTrigger.ts","../src/consent/resolveDisplayedChildOrganizationIds.ts","../src/consent/syncConsentUi.ts","../src/consent/syncPartitions.ts","../src/consent/syncConsentManager.ts","../src/consent/syncCookies.ts","../src/consent/syncDataFlows.ts","../src/consent/syncPolicies.ts","../src/consent/syncPrivacyCenterFooterLinks.ts","../src/consent/syncPrivacyCenter.ts","../src/consent/fetchAllConsentWorkflowTriggers.ts","../src/dsr-automation/gqls/action.ts","../src/dsr-automation/fetchAllActions.ts","../src/dsr-automation/gqls/workflowConfig.ts","../src/dsr-automation/fetchAllWorkflowConfigs.ts","../src/dsr-automation/gqls/dataSubject.ts","../src/dsr-automation/fetchDataSubjects.ts","../src/consent/syncConsentWorkflowTriggers.ts","../src/consent/syncProcessingPurposes.ts","../src/dsr-automation/gqls/requestEnricher.ts","../src/dsr-automation/fetchAllRequestEnrichers.ts","../src/dsr-automation/gqls/sombraVersion.ts","../src/dsr-automation/fetchAllRequestIdentifiers.ts","../src/dsr-automation/gqls/requestIdentifier.ts","../src/dsr-automation/fetchAllRequestIdentifierMetadata.ts","../src/dsr-automation/gqls/dsrIdentifier.ts","../src/dsr-automation/fetchIdentifiers.ts","../src/dsr-automation/retryRequestEnricher.ts","../src/dsr-automation/syncDataSubject.ts","../src/dsr-automation/gqls/enricher.ts","../src/dsr-automation/syncEnrichers.ts","../src/dsr-automation/syncIdentifier.ts","../src/dsr-automation/gqls/siloDiscovery.ts","../src/dsr-automation/fetchActiveSiloDiscoPlugin.ts","../src/dsr-automation/fetchAllAttributeKeys.ts","../src/dsr-automation/gqls/siloDiscoveryResult.ts","../src/dsr-automation/fetchAllSiloDiscoveryResults.ts","../src/dsr-automation/gqls/catalog.ts","../src/dsr-automation/fetchCatalogs.ts","../src/dsr-automation/gqls/requestDataSilo.ts","../src/dsr-automation/fetchRequestDataSilo.ts","../src/dsr-automation/gqls/requestFile.ts","../src/dsr-automation/fetchRequestFilesForRequest.ts","../src/dsr-automation/formatAttributeValues.ts","../src/dsr-automation/syncAction.ts","../src/dsr-automation/gqls/template.ts","../src/dsr-automation/syncTemplates.ts","../src/dsr-automation/resolveWorkflowConfigMatch.ts","../src/dsr-automation/syncWorkflowConfigs.ts","../src/dsr-automation/uploadSiloDiscoveryResults.ts","../src/ai/gqls/promptRun.ts","../src/ai/addMessagesToPromptRun.ts","../src/ai/gqls/agentFile.ts","../src/ai/fetchAllAgentFiles.ts","../src/ai/gqls/agentFunction.ts","../src/ai/fetchAllAgentFunctions.ts","../src/ai/gqls/agent.ts","../src/ai/fetchAllAgents.ts","../src/ai/gqls/largeLanguageModel.ts","../src/ai/fetchLargeLanguageModels.ts","../src/ai/gqls/promptThread.ts","../src/ai/fetchPromptThreads.ts","../src/ai/syncAgentFiles.ts","../src/ai/syncAgentFunctions.ts","../src/ai/syncAgents.ts","../src/assessments/gqls/actionItemCollection.ts","../src/assessments/fetchAllActionItemCollections.ts","../src/assessments/gqls/actionItem.ts","../src/assessments/fetchAllActionItems.ts","../src/assessments/gqls/assessment.ts","../src/assessments/fetchAllAssessments.ts","../src/assessments/parseAssessmentDisplayLogic.ts","../src/assessments/parseAssessmentRiskLogic.ts","../src/assessments/syncActionItemCollections.ts","../src/assessments/syncActionItems.ts","../src/code-intelligence/gqls/codePackage.ts","../src/code-intelligence/fetchAllCodePackages.ts","../src/code-intelligence/gqls/repository.ts","../src/code-intelligence/fetchAllRepositories.ts","../src/code-intelligence/gqls/softwareDevelopmentKit.ts","../src/code-intelligence/fetchAllSoftwareDevelopmentKits.ts","../src/code-intelligence/syncRepositories.ts","../src/code-intelligence/syncSoftwareDevelopmentKits.ts","../src/custom-functions/buildCustomFunctionSignPayload.ts","../src/custom-functions/codeSigning.ts","../src/custom-functions/gqls/customFunction.ts","../src/custom-functions/customFunctionDataSilo.ts","../src/custom-functions/fetchAllCustomFunctions.ts","../src/custom-functions/injectDataSiloIntoDsrTestPayload.ts","../src/custom-functions/resolveEffectiveSombraId.ts","../src/custom-functions/resolveExistingCustomFunction.ts","../src/custom-functions/runCustomFunctionTest.ts","../src/custom-functions/signCustomFunctionCode.ts","../src/custom-functions/syncCustomFunction.ts","../src/index.ts"],"sourcesContent":["/**\n * Shared GQL field selection constants and types used across\n * multiple SDK domains (consent, data-inventory, etc.).\n */\n\n/** Owner fields shared across queries */\nexport const OWNER_FIELDS = `\n id\n name\n email\n`;\n\n/** Team fields shared across queries */\nexport const TEAM_FIELDS = `\n id\n name\n`;\n\n/** Attribute value fields (with nested key) shared across queries */\nexport const ATTRIBUTE_VALUE_FIELDS = `\n id\n name\n attributeKey {\n id\n name\n }\n`;\n\n/** Owner assigned to an entity */\nexport interface TranscendOwnerGql {\n /** Owner ID */\n id: string;\n /** Owner display name */\n name: string;\n /** Owner email */\n email: string;\n}\n\n/** Team assigned to an entity */\nexport interface TranscendTeamGql {\n /** Team ID */\n id: string;\n /** Team name */\n name: string;\n}\n\n/** Attribute value on an entity */\nexport interface TranscendAttributeValueGql {\n /** Attribute value ID */\n id: string;\n /** Attribute value name */\n name: string;\n /** Attribute key */\n attributeKey: {\n /** Attribute key ID */\n id: string;\n /** Attribute key name */\n name: string;\n };\n}\n","import { GraphQLClient } from 'graphql-request';\n\n/**\n * Create a GraphQL client\n *\n * @param transcendUrl - Transcend API URL\n * @param headers - Request headers to include in each request\n * @param version - Optional version string to include in request headers\n * @returns GraphQL client\n */\nexport function buildTranscendGraphQLClientGeneric(\n transcendUrl: string,\n headers: Record<string, string>,\n version?: string,\n): GraphQLClient {\n return new GraphQLClient(`${transcendUrl}/graphql`, {\n headers: {\n ...headers,\n ...(version ? { version } : {}),\n },\n });\n}\n\n/**\n * Create a GraphQL client capable of submitting requests with an API key\n *\n * @param transcendUrl - Transcend API URL\n * @param auth - API key to authenticate to API\n * @param version - Optional version string to include in request headers\n * @returns GraphQL client\n */\nexport function buildTranscendGraphQLClient(\n transcendUrl: string,\n auth: string,\n version?: string,\n): GraphQLClient {\n return buildTranscendGraphQLClientGeneric(\n transcendUrl,\n { Authorization: `Bearer ${auth}` },\n version,\n );\n}\n","import { sleepPromise, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient, RequestDocument, Variables } from 'graphql-request';\n\n/** Logger that silently discards all messages */\nexport const NOOP_LOGGER: Logger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n debug: () => {},\n};\n\nconst DEFAULT_MAX_RETRIES = 4;\n\nconst KNOWN_ERRORS = [\n 'syntax error',\n 'got invalid value',\n 'Client error',\n 'cannot affect row a second time',\n 'GRAPHQL_VALIDATION_FAILED',\n // Backend io-ts payload validation failures are deterministic — retrying\n // just repeats a (potentially enormous) codec error message\n 'Failed to decode codec',\n];\n\n/**\n * Make a GraphQL request with retries\n *\n * @param client - GraphQL client\n * @param document - GraphQL document\n * @param options - Options including logger, variables, headers, and retry config\n * @returns Response\n */\nexport async function makeGraphQLRequest<T, V extends Variables = Variables>(\n client: GraphQLClient,\n document: RequestDocument,\n options: {\n /** GraphQL variables */\n variables?: V;\n /** Logger for retry/error messages */\n logger?: Logger;\n /** Additional request headers */\n requestHeaders?: Record<string, string> | string[][] | Headers;\n /** Max number of retry attempts (default 4) */\n maxRetries?: number;\n },\n): Promise<T> {\n const {\n variables,\n logger = NOOP_LOGGER,\n requestHeaders,\n maxRetries = DEFAULT_MAX_RETRIES,\n } = options;\n\n let retryCount = 0;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n const result = await client.request(document, variables, requestHeaders);\n return result as T;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (err: any) {\n if (err.message?.includes('API key is invalid')) {\n throw new Error(\n 'API key is invalid. ' +\n 'Please ensure that the key provided has the proper scope and is not expired, ' +\n 'and that the transcendUrl corresponds to the correct backend for your organization.',\n );\n }\n\n if (KNOWN_ERRORS.some((msg) => err.message?.includes(msg))) {\n throw err;\n }\n\n if (err.message?.startsWith('Client error: Too many requests')) {\n const rateLimitResetAt = err.response?.headers?.get('x-ratelimit-reset');\n const sleepTime = rateLimitResetAt\n ? new Date(rateLimitResetAt).getTime() - new Date().getTime() + 100\n : 1000 * 10;\n logger.warn(`DETECTED RATE LIMIT: ${err.message}. Sleeping for ${sleepTime}ms`);\n await sleepPromise(sleepTime);\n }\n\n if (retryCount >= maxRetries) {\n throw err;\n }\n retryCount += 1;\n logger.warn(`Retrying failed request (${retryCount} / ${maxRetries}): ${err.message}`);\n }\n }\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const ORGANIZATION: DocumentNode = parse(gql`\n query TranscendCliOrganization {\n organization {\n sombra {\n id\n customerUrl\n }\n sombras {\n id\n customerUrl\n }\n }\n }\n`);\n","import type { Logger } from '@transcend-io/utils';\nimport got, { type Got } from 'got';\n\nimport { buildTranscendGraphQLClient } from './buildTranscendGraphQLClient.js';\nimport { ORGANIZATION } from './gqls/organization.js';\nimport { makeGraphQLRequest, NOOP_LOGGER } from './makeGraphQLRequest.js';\n\n/** Preview of a Sombra gateway in the organization */\ninterface SombraPreview {\n /** Sombra gateway ID */\n id: string;\n /** Customer-ingress URL */\n customerUrl: string | null;\n}\n\nconst UNCONFIGURED_CUSTOMER_URLS = [\n 'https://sombra-reverse-tunnel.transcend.io',\n 'https://sombra-reverse-tunnel.us.transcend.io',\n];\n\n/**\n * Instantiate an instance of got that is capable of making requests\n * to a sombra gateway.\n *\n * @param transcendUrl - URL of Transcend API\n * @param transcendApiKey - Transcend API key\n * @param options - Additional options\n * @returns The instance of got that is capable of making requests to the customer ingress\n */\nexport async function createSombraGotInstance(\n transcendUrl: string,\n transcendApiKey: string,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Sombra API key */\n sombraApiKey?: string;\n /** Override Sombra URL (replaces process.env.SOMBRA_URL lookup) */\n sombraUrl?: string;\n /**\n * Connect to a specific Sombra gateway by ID instead of the\n * organization's primary gateway\n */\n sombraId?: string;\n } = {},\n): Promise<Got> {\n const { logger = NOOP_LOGGER, sombraApiKey, sombraUrl, sombraId } = options;\n\n const client = buildTranscendGraphQLClient(transcendUrl, transcendApiKey);\n const { organization } = await makeGraphQLRequest<{\n /** Organization */\n organization: {\n /** Primary Sombra */\n sombra: SombraPreview;\n /** All Sombra gateways in the organization */\n sombras: SombraPreview[];\n };\n }>(client, ORGANIZATION, { logger });\n\n let customerUrl: string | null;\n if (sombraId) {\n const target = organization.sombras.find(({ id }) => id === sombraId);\n if (!target) {\n throw new Error(`Could not find a Sombra gateway with ID: \"${sombraId}\"`);\n }\n customerUrl = target.customerUrl;\n } else {\n customerUrl = organization.sombra.customerUrl;\n }\n\n const sombraToUse = sombraUrl || customerUrl;\n\n if (!sombraUrl && (!customerUrl || UNCONFIGURED_CUSTOMER_URLS.includes(customerUrl))) {\n throw new Error(\n `It looks like the customer ingress URL of your Sombra gateway${\n sombraId ? ` \"${sombraId}\"` : ''\n } has not been set up. ` +\n 'Please follow the instructions here to configure networking for Sombra: ' +\n 'https://docs.transcend.io/docs/articles/sombra/deploying/customizing-sombra/networking',\n );\n }\n logger.info(`Using sombra: ${sombraToUse}`);\n\n return (got as unknown as Got).extend({\n prefixUrl: sombraToUse as string,\n headers: {\n Authorization: `Bearer ${transcendApiKey}`,\n ...(sombraApiKey ? { 'X-Sombra-Authorization': `Bearer ${sombraApiKey}` } : {}),\n },\n });\n}\n","import { extractErrorMessage, sleepPromise, type Logger } from '@transcend-io/utils';\n\nimport { NOOP_LOGGER } from './makeGraphQLRequest.js';\n\n/**\n * Transient network / platform errors that merit a retry.\n *\n * We keep this list short and specific to avoid masking real failures (e.g.\n * bad payloads, auth errors, or validation errors). Matches the behaviour that\n * was previously scoped to preference-management operations.\n */\nexport const RETRY_TRANSIENT_MSGS: string[] = [\n 'ENOTFOUND',\n 'ECONNRESET',\n 'ETIMEDOUT',\n 'EAI_AGAIN',\n 'EPIPE',\n // Gateway / upstream 5xx surfaced in messages; we match the bare reason\n // phrase rather than `502 Bad Gateway` because `got`'s default message\n // shape is `Response code 502 (Bad Gateway)` (parenthesised) so a strict\n // substring against the numeric form would miss real failures.\n 'bad gateway',\n 'service unavailable',\n 'gateway time-out',\n 'gateway timeout',\n '429',\n 'rate limit exceeded',\n 'too many requests',\n 'task timed out after',\n 'unknown request error',\n 'socket hang up',\n].map((s) => s.toLowerCase());\n\n/**\n * Transient HTTP status codes that merit a retry. Covers rate limiting and\n * the typical gateway / upstream classes of 5xx errors surfaced by our\n * reverse tunnel and load balancers.\n */\nconst RETRY_TRANSIENT_STATUS_CODES: readonly number[] = [408, 425, 429, 500, 502, 503, 504];\n\n/**\n * Options for running an operation with transient-error retries.\n */\nexport type RetryOptions = {\n /** Logger used to emit retry breadcrumbs */\n logger?: Logger;\n /** Max attempts including the first try (default 12) */\n maxAttempts?: number;\n /** Initial backoff in ms; doubled on each attempt with added jitter (default 250) */\n baseDelayMs?: number;\n /** Optional custom predicate to decide if an error is retryable */\n isRetryable?: (err: unknown, message: string) => boolean;\n /** Optional hook called once per retry attempt (before the sleep) */\n onRetry?: (attempt: number, err: unknown, message: string) => void;\n};\n\n/**\n * Default retry predicate. Matches either a known transient error message\n * substring or a known transient HTTP status code on `got`-style errors.\n *\n * @param err - Thrown error\n * @param message - Extracted human-readable message\n * @returns True if the error should be retried\n */\nexport function isTransientError(err: unknown, message: string): boolean {\n const lower = message.toLowerCase();\n if (RETRY_TRANSIENT_MSGS.some((m) => lower.includes(m))) {\n return true;\n }\n\n // `got` HTTPError puts the status code on `err.response.statusCode`; other\n // clients use `err.status` or `err.statusCode`.\n const maybe = err as {\n /** `got` HTTPError shape */\n response?: {\n /** HTTP status code returned by the server */\n statusCode?: number;\n };\n /** Axios / fetch-style shape */\n status?: number;\n /** Node http error shape */\n statusCode?: number;\n };\n const statusCode = maybe?.response?.statusCode ?? maybe?.status ?? maybe?.statusCode;\n return typeof statusCode === 'number' && RETRY_TRANSIENT_STATUS_CODES.includes(statusCode);\n}\n\n/**\n * Run an async function with standardized retry behaviour for transient\n * network / gateway errors (502/503/504/429, ECONNRESET, ETIMEDOUT, etc.).\n *\n * Applies exponential backoff with jitter, and only retries on known-transient\n * errors to avoid masking real client-side failures.\n *\n * @param name - Short name of the operation, used in logs and the final error message\n * @param fn - Function to run; called once per attempt\n * @param options - Retry options\n * @returns Result of the function on the first successful attempt\n */\nexport async function withTransientRetry<T>(\n name: string,\n fn: () => Promise<T>,\n {\n logger = NOOP_LOGGER,\n maxAttempts = 12,\n baseDelayMs = 250,\n isRetryable = isTransientError,\n onRetry,\n }: RetryOptions = {},\n): Promise<T> {\n let attempt = 0;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n attempt += 1;\n try {\n return await fn();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (err: any) {\n const msg: string = extractErrorMessage(err);\n const willRetry = attempt < maxAttempts && isRetryable(err, msg);\n if (!willRetry) {\n throw new Error(`${name} failed after ${attempt} attempt(s): ${msg}`);\n }\n onRetry?.(attempt, err, msg);\n\n const backoff = baseDelayMs * 2 ** (attempt - 1);\n const jitter = Math.floor(Math.random() * baseDelayMs);\n const delay = backoff + jitter;\n logger.warn(`[retry] attempt ${attempt}/${maxAttempts - 1}; backing off ${delay}ms: ${msg}`);\n await sleepPromise(delay);\n }\n }\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const BUSINESS_ENTITIES = gql`\n query TranscendCliBusinessEntities($first: Int!, $offset: Int!) {\n businessEntities(\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n useMaster: false\n ) {\n nodes {\n id\n title\n description\n dataProtectionOfficerName\n dataProtectionOfficerEmail\n address\n headquarterCountry\n headquarterSubDivision\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n }\n }\n }\n`;\n\nexport const CREATE_BUSINESS_ENTITY = gql`\n mutation TranscendCliCreateBusinessEntity($input: CreateBusinessEntityInput!) {\n createBusinessEntity(input: $input) {\n businessEntity {\n id\n title\n description\n dataProtectionOfficerName\n dataProtectionOfficerEmail\n address\n headquarterCountry\n headquarterSubDivision\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n }\n }\n }\n`;\n\nexport const UPDATE_BUSINESS_ENTITIES = gql`\n mutation TranscendCliUpdateBusinessEntities($input: [UpdateBusinessEntityInput!]!) {\n updateBusinessEntities(input: { businessEntities: $input }) {\n clientMutationId\n }\n }\n`;\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { BUSINESS_ENTITIES } from './gqls/businessEntity.js';\n\nexport interface BusinessEntity {\n /** ID of business entity */\n id: string;\n /** Title of business entity */\n title: string;\n /** Description of business entity */\n description?: string;\n /** Data protection officer name */\n dataProtectionOfficerName?: string;\n /** Data protection officer email */\n dataProtectionOfficerEmail?: string;\n /** Address of business entity */\n address?: string;\n /** Headquarters of business entity */\n headquarterCountry?: IsoCountryCode;\n /** Subdivision of business entity */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Attributes */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key */\n attributeKey: {\n /** Name of attribute key */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all businessEntities in the organization\n *\n * @param client - GraphQL client\n * @returns All businessEntities in the organization\n */\nexport async function fetchAllBusinessEntities(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<BusinessEntity[]> {\n const { logger } = options;\n const businessEntities: BusinessEntity[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n businessEntities: { nodes },\n } = await makeGraphQLRequest<{\n /** Business entities */\n businessEntities: {\n /** List */\n nodes: BusinessEntity[];\n };\n }>(client, BUSINESS_ENTITIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n businessEntities.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return businessEntities.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: name, direction: ASC }\n// ]\nexport const DATA_SUB_CATEGORIES = gql`\n query TranscendCliDataSubCategories($first: Int!, $offset: Int!) {\n dataSubCategories(first: $first, offset: $offset, isExportCsv: true, useMaster: false) {\n nodes {\n id\n name\n category\n description\n regex\n teams {\n name\n }\n owners {\n email\n }\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_DATA_SUB_CATEGORY = gql`\n mutation TranscendCliCreateDataSubCategory($input: CreateDataInventorySubCategoryInput!) {\n createDataSubCategory(input: $input) {\n dataSubCategory {\n id\n name\n category\n }\n }\n }\n`;\n\nexport const UPDATE_DATA_SUB_CATEGORIES = gql`\n mutation TranscendCliUpdateDataSubCategories($input: UpdateDataSubCategoriesInput!) {\n updateDataSubCategories(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { DataCategoryType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DATA_SUB_CATEGORIES } from './gqls/dataCategory.js';\n\nexport interface DataSubCategory {\n /** ID of data category */\n id: string;\n /** Name of data category */\n name: string;\n /** Type of data category */\n category: DataCategoryType;\n /** Description of data category */\n description?: string;\n /** Regex for data category */\n regex?: string;\n /** Assigned teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Assigned owners */\n owners: {\n /** Email */\n email: string;\n }[];\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all dataSubCategories in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All dataSubCategories in the organization\n */\nexport async function fetchAllDataCategories(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<DataSubCategory[]> {\n const { logger } = options;\n const dataSubCategories: DataSubCategory[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n dataSubCategories: { nodes },\n } = await makeGraphQLRequest<{\n /** DataCategories */\n dataSubCategories: {\n /** List */\n nodes: DataSubCategory[];\n };\n }>(client, DATA_SUB_CATEGORIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n dataSubCategories.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return dataSubCategories.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_POINTS = gql`\n query TranscendCliDataPoints($filterBy: DataPointFiltersInput, $first: Int!, $offset: Int!) {\n dataPoints(\n filterBy: $filterBy\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n title {\n defaultMessage\n }\n description {\n defaultMessage\n }\n owners {\n email\n }\n teams {\n name\n }\n name\n path\n actionSettings {\n type\n active\n }\n dataCollection {\n title {\n defaultMessage\n }\n }\n dbIntegrationQueries {\n query\n suggestedQuery\n requestType\n }\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_POINT_COUNT = gql`\n query TranscendCliDataPointCount($filterBy: DataPointFiltersInput) {\n dataPoints(filterBy: $filterBy, useMaster: false) {\n totalCount\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - add orderBy\n// isExportCsv: true\nexport const SUB_DATA_POINTS = gql`\n query TranscendCliSubDataPoints(\n $filterBy: SubDataPointFiltersInput\n $first: Int!\n $offset: Int!\n ) {\n subDataPoints(filterBy: $filterBy, first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n name\n description\n purposes {\n name\n purpose\n }\n categories {\n name\n category\n }\n accessRequestVisibilityEnabled\n erasureRequestRedactionEnabled\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const SUB_DATA_POINTS_COUNT = gql`\n query TranscendCliSubDataPointsCount($filterBy: SubDataPointFiltersInput) {\n subDataPoints(filterBy: $filterBy, useMaster: false) {\n totalCount\n }\n }\n`;\n\nexport const SUB_DATA_POINTS_WITH_GUESSES = gql`\n query TranscendCliSubDataPointGuesses(\n $filterBy: SubDataPointFiltersInput\n $first: Int!\n $offset: Int!\n ) {\n subDataPoints(filterBy: $filterBy, first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n name\n description\n purposes {\n name\n purpose\n }\n categories {\n name\n category\n }\n pendingCategoryGuesses {\n category {\n name\n category\n }\n status\n confidence\n classifierVersion\n }\n accessRequestVisibilityEnabled\n erasureRequestRedactionEnabled\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const UPDATE_OR_CREATE_DATA_POINT = gql`\n mutation TranscendCliUpdateOrCreateDataPoint(\n $dataSiloId: ID!\n $name: String!\n $path: [String!]\n $title: String\n $description: String\n $ownerIds: [ID!]\n $ownerEmails: [String!]\n $teamNames: [String!]\n $teamIds: [ID!]\n $dataCollectionTag: String\n $querySuggestions: [DbIntegrationQuerySuggestionInput!]\n $enabledActions: [RequestActionObjectResolver!]\n $subDataPoints: [DataPointSubDataPointInput!]\n ) {\n updateOrCreateDataPoint(\n input: {\n dataSiloId: $dataSiloId\n name: $name\n path: $path\n title: $title\n teamNames: $teamNames\n ownerEmails: $ownerEmails\n dataCollectionTag: $dataCollectionTag\n description: $description\n ownerIds: $ownerIds\n teamIds: $teamIds\n querySuggestions: $querySuggestions\n enabledActions: $enabledActions\n subDataPoints: $subDataPoints\n }\n ) {\n dataPoint {\n id\n name\n }\n }\n }\n`;\n\nexport const DATAPOINT_EXPORT = gql`\n query TranscendCliDataPointCsvExport($filterBy: DataPointFiltersInput, $first: Int!) {\n dataPoints(filterBy: $filterBy, first: $first, useMaster: false) {\n nodes {\n id\n title {\n defaultMessage\n }\n description {\n defaultMessage\n }\n owners {\n email\n }\n teams {\n name\n }\n name\n path\n }\n }\n }\n`;\n","import {\n ConfidenceLabel,\n DataCategoryType,\n ProcessingPurpose,\n RequestActionObjectResolver,\n SubDataPointDataSubCategoryGuessStatus,\n} from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { sortBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSiloAttributeValue } from './fetchAllDataSilos.js';\nimport { DATA_POINTS, SUB_DATA_POINTS, SUB_DATA_POINTS_WITH_GUESSES } from './gqls/dataPoint.js';\n\nexport interface SubDataPointCategory {\n /** Sub-category name */\n name: string;\n /** Top-level category */\n category: DataCategoryType;\n}\n\nexport interface SubDataPointPurpose {\n /** Sub-purpose name */\n name: string;\n /** Top-level purpose */\n purpose: ProcessingPurpose;\n}\n\nexport interface SubDataPoint {\n /** Name (or key) of the subdatapoint */\n name: string;\n /** The description */\n description?: string;\n /** Personal data category */\n categories: SubDataPointCategory[];\n /** The processing purpose for this sub datapoint */\n purposes: SubDataPointPurpose[];\n /**\n * When true, this subdatapoint should be revealed in a data access request.\n * When false, this field should be redacted\n */\n accessRequestVisibilityEnabled: boolean;\n /**\n * When true, this subdatapoint should be redacted during an erasure request.\n * There normally is a choice of enabling hard deletion or redaction at the\n * datapoint level, but if redaction is enabled, this column can be used\n * to define which fields should be redacted.\n */\n erasureRequestRedactionEnabled: boolean;\n /** Attribute attached to subdatapoint */\n attributeValues: DataSiloAttributeValue[];\n /** Data category guesses that are output by the classifier */\n pendingCategoryGuesses?: {\n /** Data category being guessed */\n category: SubDataPointCategory;\n /** Status of guess */\n status: SubDataPointDataSubCategoryGuessStatus;\n /** Confidence level of guess */\n confidence: number;\n /** Confidence label */\n confidenceLabel: ConfidenceLabel;\n /** classifier version that produced the guess */\n classifierVersion: number;\n }[];\n}\n\nexport interface DataPoint {\n /** ID of dataPoint */\n id: string;\n /** Title of dataPoint */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** The path to this data point */\n path: string[];\n /** Description */\n description: {\n /** Default message */\n defaultMessage: string;\n };\n /** Name */\n name: string;\n /** Global actions */\n actionSettings: {\n /** Action type */\n type: RequestActionObjectResolver;\n /** Is enabled */\n active: boolean;\n }[];\n /** Data collection tag for privacy request download zip labeling */\n dataCollection?: {\n /** Title of data collection */\n title: {\n /** Default message (since message can be translated) */\n defaultMessage: string;\n };\n };\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n /** Owners of the datapoint */\n owners: {\n /** Email address of the owner */\n email: string;\n }[];\n /** Teams that own the datapoint */\n teams: {\n /** Name of the team */\n name: string;\n }[];\n /** Database integration queries */\n dbIntegrationQueries: {\n /** Approved query */\n query: string | null;\n /** Suggested query */\n suggestedQuery: string | null;\n /** Request action */\n requestType: RequestActionObjectResolver;\n }[];\n}\n\nexport interface DataPointWithSubDataPoint extends DataPoint {\n /** The associated subdatapoints */\n subDataPoints: SubDataPoint[];\n}\n\n/**\n * Helper to fetch all subdatapoints for a given datapoint\n *\n * @param client - The GraphQL client\n * @param dataPointId - The datapoint ID\n * @param options - Options\n * @returns The list of subdatapoints\n */\nexport async function fetchAllSubDataPoints(\n client: GraphQLClient,\n options: {\n /** Filter criteria */\n filterBy: {\n /** Datapoint ID to fetch sub-datapoints for */\n dataPointId: string;\n };\n /** Enable debug logging */\n debug?: boolean;\n /** Page size for paginated fetching */\n pageSize: number;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<SubDataPoint[]> {\n const {\n filterBy: { dataPointId },\n debug,\n includeGuessedCategories,\n pageSize,\n logger = NOOP_LOGGER,\n } = options;\n const subDataPoints: SubDataPoint[] = [];\n\n let offset = 0;\n\n let shouldContinue = false;\n do {\n try {\n if (debug) {\n logger.info(`Pulling in subdatapoints for offset ${offset}`);\n }\n const {\n subDataPoints: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n subDataPoints: {\n /** List of matches */\n nodes: SubDataPoint[];\n };\n }>(client, includeGuessedCategories ? SUB_DATA_POINTS_WITH_GUESSES : SUB_DATA_POINTS, {\n variables: {\n first: pageSize,\n filterBy: {\n dataPoints: [dataPointId],\n },\n offset,\n },\n logger,\n });\n\n subDataPoints.push(...nodes);\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n\n if (debug) {\n logger.info(`Pulled in subdatapoints for offset ${offset} for dataPointId=${dataPointId}`);\n }\n } catch (err) {\n logger.error(\n `An error fetching subdatapoints for offset ${offset} for dataPointId=${dataPointId}`,\n );\n throw err;\n }\n } while (shouldContinue);\n return sortBy(subDataPoints, 'name');\n}\n\n/**\n * Fetch all datapoints for a data silo\n *\n * @param client - GraphQL client\n * @param dataSiloId - Data silo ID\n * @param options - Options\n * @returns List of datapoints\n */\nexport async function fetchAllDataPoints(\n client: GraphQLClient,\n options: {\n /** Filter criteria */\n filterBy: {\n /** Data silo ID to fetch datapoints for */\n dataSiloId: string;\n };\n /** Enable debug logging */\n debug?: boolean;\n /** Page size for paginated fetching */\n pageSize: number;\n /** Skip fetching of subdatapoints */\n skipSubDatapoints?: boolean;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<DataPointWithSubDataPoint[]> {\n const {\n filterBy: { dataSiloId },\n debug,\n pageSize,\n skipSubDatapoints,\n includeGuessedCategories,\n logger = NOOP_LOGGER,\n } = options;\n const dataPoints: DataPointWithSubDataPoint[] = [];\n\n // TODO: https://transcend.height.app/T-40481 - add cursor pagination\n let offset = 0;\n\n let shouldContinue = false;\n do {\n if (debug) {\n logger.info(`Fetching datapoints with offset: ${offset}`);\n }\n\n const {\n dataPoints: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n dataPoints: {\n /** List of matches */\n nodes: DataPoint[];\n };\n }>(client, DATA_POINTS, {\n variables: {\n first: pageSize,\n filterBy: {\n dataSilos: [dataSiloId],\n },\n offset,\n },\n logger,\n });\n\n if (debug) {\n logger.info(`Fetched ${nodes.length} datapoints at offset: ${offset}`);\n }\n\n if (!skipSubDatapoints) {\n await map(\n nodes,\n async (node) => {\n try {\n if (debug) {\n logger.info(`Fetching subdatapoints for ${node.name} for datapoint offset ${offset}`);\n }\n\n const subDataPoints = await fetchAllSubDataPoints(client, {\n filterBy: { dataPointId: node.id },\n pageSize: 1000,\n debug,\n includeGuessedCategories,\n logger,\n });\n dataPoints.push({\n ...node,\n subDataPoints: subDataPoints.sort((a, b) => a.name.localeCompare(b.name)),\n });\n\n if (debug) {\n logger.info(`Successfully fetched subdatapoints for ${node.name}`);\n }\n } catch (err) {\n logger.error(\n `An error fetching subdatapoints for ${node.name} datapoint offset ${offset}`,\n );\n throw err;\n }\n },\n {\n concurrency: 5,\n },\n );\n\n if (debug) {\n logger.info(`Fetched all subdatapoints for page of datapoints at offset: ${offset}`);\n }\n }\n\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n } while (shouldContinue);\n return dataPoints.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_SILOS = gql`\n query TranscendCliDataSilos($filterBy: DataSiloFiltersInput!, $first: Int!, $offset: Int!) {\n dataSilos(\n filterBy: $filterBy\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n useMaster: false\n ) {\n nodes {\n id\n title\n link\n type\n catalog {\n hasAvcFunctionality\n }\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_SILO_EXPORT = gql`\n query TranscendCliDataSiloExport($filterBy: DataSiloFiltersInput!, $first: Int!) {\n dataSilos(filterBy: $filterBy, first: $first, useMaster: false) {\n nodes {\n id\n title\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_SILOS_ENRICHED = gql`\n query TranscendCliDataSilosEnriched(\n $filterBy: DataSiloFiltersInput!\n $first: Int!\n $offset: Int!\n ) {\n dataSilos(\n filterBy: $filterBy\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n useMaster: false\n ) {\n nodes {\n id\n title\n description\n type\n outerType\n link\n country\n countrySubDivision\n url\n notifyEmailAddress\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n apiKeys {\n title\n }\n subjectBlocklist {\n type\n }\n identifiers {\n name\n isConnected\n }\n dependentDataSilos {\n title\n }\n dependedOnDataSilosPerWorkflow {\n workflowConfigId\n workflowInternalName\n dependedOnDataSilos {\n title\n }\n }\n owners {\n email\n }\n teams {\n id\n name\n }\n catalog {\n hasAvcFunctionality\n }\n isLive\n promptAVendorEmailSendFrequency\n promptAVendorEmailSendType\n promptAVendorEmailIncludeIdentifiersAttachment\n promptAVendorEmailCompletionLinkType\n manualWorkRetryFrequency\n discoveredBy {\n title\n }\n businessEntities {\n title\n }\n }\n }\n }\n`;\n\n/**\n * Singular data silo fetch — `sombra` is available on `DataSilo` but not on\n * the `DataSiloBulkPreview` nodes returned by the paginated `dataSilos` query.\n */\nexport const DATA_SILO_SOMBRA = gql`\n query TranscendCliDataSiloSombra($id: String!) {\n dataSilo(id: $id) {\n id\n sombra {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_DATA_SILOS = gql`\n mutation TranscendCliUpdateDataSilo($input: UpdateDataSilosInput!) {\n updateDataSilos(input: $input) {\n clientMutationId\n dataSilos {\n id\n title\n }\n }\n }\n`;\n\nexport const CREATE_DATA_SILOS = gql`\n mutation TranscendCliCreateDataSilo($input: [CreateDataSilosInput!]!) {\n createDataSilos(input: $input) {\n dataSilos {\n id\n title\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSiloAttributeValue } from '../dsr-automation/formatAttributeValues.js';\nimport { DATA_SILOS } from './gqls/dataSilo.js';\n\nexport type { DataSiloAttributeValue };\n\nexport interface DataSilo {\n /** ID of dataSilo */\n id: string;\n /** Title of dataSilo */\n title: string;\n /** Type of silo */\n type: string;\n /** The link to the data silo */\n link: string;\n /** Attribute labels */\n attributeValues: DataSiloAttributeValue[];\n /** description */\n description: string;\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n}\n\n/**\n * Fetch all dataSilos in the organization\n *\n * @param client - GraphQL client\n * @param options - Filter and pagination options\n * @returns All dataSilos in the organization\n */\nexport async function fetchAllDataSilos<TDataSilo extends DataSilo>(\n client: GraphQLClient,\n options: {\n /** Page size to fetch data silos in */\n pageSize: number;\n /** Filter by title */\n titles?: string[];\n /** Filter by IDs */\n ids?: string[];\n /** Set of integration names to fetch */\n integrationNames?: string[];\n /** GQL query override for data silos */\n gql?: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<TDataSilo[]> {\n const {\n titles,\n pageSize,\n ids = [],\n gql = DATA_SILOS,\n integrationNames = [],\n logger = NOOP_LOGGER,\n } = options;\n\n logger.info(`Fetching ${ids.length === 0 ? 'all' : ids.length} Data Silos...`);\n\n const dataSilos: TDataSilo[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n dataSilos: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n dataSilos: {\n /** List of matches */\n nodes: TDataSilo[];\n };\n }>(client, gql, {\n variables: {\n filterBy: {\n ids: ids.length > 0 ? ids : undefined,\n type: integrationNames.length > 0 ? integrationNames : undefined,\n titles,\n },\n first: pageSize,\n offset,\n },\n logger,\n });\n dataSilos.push(...nodes);\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n } while (shouldContinue);\n\n logger.info(\n `Found a total of ${dataSilos.length} data silo${\n ids.length > 0 ? ` matching IDs ${ids.join(',')}` : ''\n }s${\n integrationNames.length > 0 ? ` matching integrationNames ${integrationNames.join(',')}` : ''\n }`,\n );\n\n return dataSilos.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const IDENTIFIERS: DocumentNode = parse(gql`\n query TranscendCliIdentifiers($first: Int!, $offset: Int!) {\n identifiers(\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n type\n regex\n selectOptions\n privacyCenterVisibility\n dataSubjects {\n type\n }\n isRequiredInForm\n placeholder\n displayTitle {\n defaultMessage\n }\n displayDescription {\n defaultMessage\n }\n displayOrder\n isUniqueOnPreferenceStore\n }\n }\n }\n`);\n","import { IdentifierType, RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { IDENTIFIERS } from './gqls/identifier.js';\n\nexport interface Identifier {\n /** ID of identifier */\n id: string;\n /** Name of identifier */\n name: string;\n /** The type of identifier */\n type: IdentifierType;\n /** Regular expression */\n regex: string;\n /** The set of options that the identifier supports */\n selectOptions: string[];\n /** Whether identifier is enabled on privacy center */\n privacyCenterVisibility: RequestAction[];\n /** Enabled data subjects that are exposed this identifier on the privacy center */\n dataSubjects: {\n /** type of data subjects */\n type: string;\n }[];\n /** Whether identifier is a required field in privacy center form */\n isRequiredInForm: boolean;\n /** Identifier placeholder text */\n placeholder: string;\n /** Display title for identifier */\n displayTitle: {\n /** Default message */\n defaultMessage: string;\n };\n /** Display description for identifier */\n displayDescription: {\n /** Default */\n defaultMessage: string;\n };\n /** Display order */\n displayOrder: number;\n /** Does this identifier uniquely identify a consent record */\n isUniqueOnPreferenceStore: boolean;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all identifiers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All identifiers in the organization\n */\nexport async function fetchAllIdentifiers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Identifier[]> {\n const { logger } = options;\n const identifiers: Identifier[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n identifiers: { nodes },\n } = await makeGraphQLRequest<{\n /** Identifiers */\n identifiers: {\n /** List */\n nodes: Identifier[];\n };\n }>(client, IDENTIFIERS, {\n logger,\n variables: { first: PAGE_SIZE, offset },\n });\n identifiers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return identifiers.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const PROCESSING_ACTIVITIES = gql`\n query TranscendCliProcessingActivities($first: Int!, $offset: Int!) {\n processingActivities(first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n title\n description\n securityMeasureDetails\n controllerships\n storageRegions {\n countrySubDivision\n country\n }\n transferRegions {\n countrySubDivision\n country\n }\n retentionType\n retentionPeriod\n dataProtectionImpactAssessmentLink\n dataProtectionImpactAssessmentStatus\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n dataSilos {\n title\n }\n dataSubjects {\n type\n }\n teams {\n name\n }\n owners {\n email\n }\n processingPurposeSubCategories {\n name\n purpose\n }\n dataSubCategories {\n name\n category\n }\n saaSCategories {\n title\n }\n }\n }\n }\n`;\n\nexport const CREATE_PROCESSING_ACTIVITY = gql`\n mutation TranscendCliCreateProcessingActivity($input: CreateProcessingActivityInput!) {\n createProcessingActivity(input: $input) {\n processingActivity {\n id\n title\n }\n }\n }\n`;\n\nexport const UPDATE_PROCESSING_ACTIVITIES = gql`\n mutation TranscendCliUpdateProcessingActivities($input: UpdateProcessingActivitiesInput!) {\n updateProcessingActivities(input: $input) {\n clientMutationId\n }\n }\n`;\n","import type {\n ProcessingPurpose,\n DataCategoryType,\n DataProtectionImpactAssessmentStatus,\n Controllership,\n RetentionType,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport type { Region } from '../administration/formatRegions.js';\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PROCESSING_ACTIVITIES } from './gqls/processingActivity.js';\n\nexport interface ProcessingActivity {\n /** ID of processing activity */\n id: string;\n /** Title of processing activity */\n title: string;\n /** Description of processing activity */\n description: string;\n /** Security measure details */\n securityMeasureDetails?: string;\n /** Controllerships */\n controllerships: Controllership[];\n /** Storage regions */\n storageRegions: Region[];\n /** Transfer regions */\n transferRegions: Region[];\n /** Retention type */\n retentionType: RetentionType;\n /** Retention period in days */\n retentionPeriod?: number;\n /** Data protection impact assessment link */\n dataProtectionImpactAssessmentLink?: string;\n /** Data protection impact assessment status */\n dataProtectionImpactAssessmentStatus: DataProtectionImpactAssessmentStatus;\n /** Attribute values */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key */\n attributeKey: {\n /** Name of attribute key */\n name: string;\n };\n }[];\n /** Data silos */\n dataSilos: {\n /** Data silo title */\n title: string;\n }[];\n /** Data subjects */\n dataSubjects: {\n /** Data subject type */\n type: string;\n }[];\n /** Teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Owners */\n owners: {\n /** Owner email */\n email: string;\n }[];\n /** Processing purpose sub categories */\n processingPurposeSubCategories: {\n /** Processing purpose sub category name */\n name: string;\n /** Processing purpose */\n purpose: ProcessingPurpose;\n }[];\n /** Data sub categories */\n dataSubCategories: {\n /** Data sub category name */\n name: string;\n /** Data category */\n category: DataCategoryType;\n }[];\n /** SaaS categories */\n saaSCategories: {\n /** Title */\n title: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all processingActivities in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All processingActivities in the organization\n */\nexport async function fetchAllProcessingActivities(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ProcessingActivity[]> {\n const { logger } = options;\n const processingActivities: ProcessingActivity[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n processingActivities: { nodes },\n } = await makeGraphQLRequest<{\n /** Processing activities */\n processingActivities: {\n /** List */\n nodes: ProcessingActivity[];\n };\n }>(client, PROCESSING_ACTIVITIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n processingActivities.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return processingActivities.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import { gql } from 'graphql-request';\n\nexport const VENDORS = gql`\n query TranscendCliVendors($first: Int!, $offset: Int!) {\n vendors(\n first: $first\n offset: $offset\n useMaster: false\n isExportCsv: true\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n ) {\n nodes {\n id\n title\n description\n dataProcessingAgreementLink\n contactName\n contactEmail\n contactPhone\n address\n headquarterCountry\n headquarterSubDivision\n websiteUrl\n businessEntity {\n title\n }\n teams {\n name\n }\n owners {\n email\n }\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_VENDOR = gql`\n mutation TranscendCliCreateVendor($input: CreateVendorInput!) {\n createVendor(input: $input) {\n vendor {\n id\n title\n }\n }\n }\n`;\n\nexport const UPDATE_VENDORS = gql`\n mutation TranscendCliUpdateVendor($input: UpdateVendorsInput!) {\n updateVendors(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { VENDORS } from './gqls/vendor.js';\n\nexport interface Vendor {\n /** ID of vendor */\n id: string;\n /** Title of vendor */\n title: string;\n /** Description of vendor */\n description: string;\n /** DPA link */\n dataProcessingAgreementLink?: string;\n /** Contract email */\n contactName?: string;\n /** Contract phone */\n contactPhone?: string;\n /** Address */\n address?: string;\n /** Headquarters country */\n headquarterCountry?: IsoCountryCode;\n /** Headquarters subdivision */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Website URL */\n websiteUrl?: string;\n /** Business entity */\n businessEntity?: {\n /** Business entity title */\n title: string;\n };\n /** Assigned teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Assigned owners */\n owners: {\n /** Email */\n email: string;\n }[];\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all vendors in the organization\n *\n * @param client - GraphQL client\n * @returns All vendors in the organization\n */\nexport async function fetchAllVendors(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Vendor[]> {\n const { logger } = options;\n const vendors: Vendor[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n vendors: { nodes },\n } = await makeGraphQLRequest<{\n /** Vendors */\n vendors: {\n /** List */\n nodes: Vendor[];\n };\n }>(client, VENDORS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n vendors.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return vendors.sort((a, b) => a.title.localeCompare(b.title));\n}\n","import {\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n PromptAVendorEmailCompletionLinkType,\n PromptAVendorEmailSendType,\n} from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataPoints, type DataPointWithSubDataPoint } from './fetchAllDataPoints.js';\nimport { fetchAllDataSilos, type DataSiloAttributeValue } from './fetchAllDataSilos.js';\nimport { DATA_SILO_SOMBRA, DATA_SILOS_ENRICHED } from './gqls/dataSilo.js';\n\nexport interface DataSiloEnriched {\n /** ID of dataSilo */\n id: string;\n /** Title of dataSilo */\n title: string;\n /** Type of silo */\n type: string;\n /** Link to silo */\n link: string;\n /** Outer type of silo */\n outerType: string;\n /** Description of data silo */\n description: string;\n /** Webhook URL */\n url?: string;\n /** Email address of user to notify for prompt a person use case */\n notifyEmailAddress?: string;\n /** Associated API keys */\n apiKeys: {\n /** Title */\n title: string;\n }[];\n /** Data subject block list */\n subjectBlocklist: {\n /** Type of data subject */\n type: string;\n }[];\n /** Identifiers */\n identifiers: {\n /** Name of identifier */\n name: string;\n /** True if identifier is wired */\n isConnected: boolean;\n }[];\n /** Dependent data silos that apply to every erasure workflow */\n dependentDataSilos: {\n /** Title of silo */\n title: string;\n }[];\n /**\n * Dependent data silos for the erasure workflows that override the global configuration.\n * An entry with an empty list is a workflow that runs this data silo with no dependencies.\n */\n dependedOnDataSilosPerWorkflow: {\n /** ID of the workflow config the override applies to */\n workflowConfigId: string;\n /** Internal name of the workflow config; used as the transcend.yml workflow key */\n workflowInternalName: string;\n /** Silos depended on within that workflow */\n dependedOnDataSilos: {\n /** Title of silo */\n title: string;\n }[];\n }[];\n /** Silo owners */\n owners: {\n /** Email owners */\n email: string;\n }[];\n /** The teams assigned to this data silo */\n teams: {\n /** Name of the team assigned to this data silo */\n name: string;\n }[];\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n /** Silo is live */\n isLive: boolean;\n /** Hosting country of data silo */\n country?: IsoCountryCode;\n /** Hosting subdivision data silo */\n countrySubDivision?: IsoCountrySubdivisionCode;\n /**\n * The frequency with which we should be sending emails for this data silo, in milliseconds.\n */\n promptAVendorEmailSendFrequency: number;\n /**\n * The type of emails to send for this data silo, i.e. send an email for each DSR, across all open DSRs,\n * or per profile in a DSR.\n */\n promptAVendorEmailSendType: PromptAVendorEmailSendType;\n /**\n * Indicates whether prompt-a-vendor emails should include a list of identifiers\n * in addition to a link to the bulk processing UI.\n */\n promptAVendorEmailIncludeIdentifiersAttachment: boolean;\n /**\n * Indicates what kind of link to generate as part of the emails sent out for this Prompt-a-Vendor silo.\n */\n promptAVendorEmailCompletionLinkType: PromptAVendorEmailCompletionLinkType;\n /**\n * The frequency with which we should retry sending emails for this data silo, in milliseconds.\n * Needs to be a string because the number can be larger than the MAX_INT\n */\n manualWorkRetryFrequency: string;\n /** Attribute values tagged to data silo */\n attributeValues: DataSiloAttributeValue[];\n /**\n * The data silos that discovered this particular data silo\n */\n discoveredBy: {\n /** Title of data silo */\n title: string;\n }[];\n /**\n * The business entities assigned directly to this data silo\n */\n businessEntities: {\n /** Title of business entity */\n title: string;\n }[];\n /**\n * Configured Sombra instance that processes this data silo\n */\n sombra?: {\n /** Sombra instance ID */\n id: string;\n } | null;\n}\n\n/**\n * Fetch all dataSilos with additional metadata\n *\n * @param client - GraphQL client\n * @param options - Filter options\n * @returns All dataSilos in the organization\n */\nexport async function fetchEnrichedDataSilos(\n client: GraphQLClient,\n options: {\n /** Page size */\n pageSize: number;\n /** Filter by IDs */\n ids?: string[];\n /** Enable debug logs */\n debug?: boolean;\n /** Filter by title */\n titles?: string[];\n /** Integration names */\n integrationNames?: string[];\n /** Skip fetching of datapoints */\n skipDatapoints?: boolean;\n /** Skip fetching of subdatapoints */\n skipSubDatapoints?: boolean;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<[DataSiloEnriched, DataPointWithSubDataPoint[]][]> {\n const {\n ids,\n pageSize,\n titles,\n debug,\n skipDatapoints,\n skipSubDatapoints,\n includeGuessedCategories,\n integrationNames,\n logger = NOOP_LOGGER,\n } = options;\n\n const dataSilos: [DataSiloEnriched, DataPointWithSubDataPoint[]][] = [];\n\n const silos = await fetchAllDataSilos<DataSiloEnriched>(client, {\n titles,\n ids,\n integrationNames,\n pageSize,\n gql: DATA_SILOS_ENRICHED,\n logger,\n });\n\n // `sombra` is only on singular `DataSilo`, not `DataSiloBulkPreview`.\n await mapSeries(silos, async (silo) => {\n const {\n dataSilo: { sombra },\n } = await makeGraphQLRequest<{\n /** Singular data silo */\n dataSilo: {\n /** Data silo ID */\n id: string;\n /** Configured Sombra instance */\n sombra?: {\n /** Sombra instance ID */\n id: string;\n } | null;\n };\n }>(client, DATA_SILO_SOMBRA, {\n variables: { id: silo.id },\n logger,\n });\n silo.sombra = sombra;\n });\n\n if (!skipDatapoints) {\n await mapSeries(silos, async (silo, index) => {\n logger.info(`[${index + 1}/${silos.length}] Fetching data silo - ${silo.title}`);\n\n const dataPoints = await fetchAllDataPoints(client, {\n filterBy: { dataSiloId: silo.id },\n debug,\n pageSize,\n skipSubDatapoints,\n includeGuessedCategories,\n logger,\n });\n\n if (debug) {\n logger.info(\n `[${index + 1}/${silos.length}] Successfully fetched datapoint for - ${silo.title}`,\n );\n }\n\n dataSilos.push([silo, dataPoints]);\n });\n } else {\n silos.forEach((silo) => {\n dataSilos.push([silo, []]);\n });\n }\n\n logger.info(`Successfully fetched all ${silos.length} data silo configurations`);\n\n return dataSilos;\n}\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllBusinessEntities, type BusinessEntity } from './fetchAllBusinessEntities.js';\nimport { UPDATE_BUSINESS_ENTITIES, CREATE_BUSINESS_ENTITY } from './gqls/businessEntity.js';\n\nexport interface BusinessEntityInput {\n /** Display title of the business entity */\n title: string;\n /** Description of the business entity */\n description?: string;\n /** Physical address */\n address?: string;\n /** Headquarters country */\n headquarterCountry?: IsoCountryCode;\n /** Headquarters country subdivision */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Name of the data protection officer */\n dataProtectionOfficerName?: string;\n /** Email of the data protection officer */\n dataProtectionOfficerEmail?: string;\n /** Custom attribute values to assign */\n attributes?: {\n /** Attribute key name */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n /** Team names to assign */\n teams?: string[];\n /** Owner email addresses to assign */\n owners?: string[];\n}\n\n/**\n * Input to create a new business entity\n *\n * @param client - GraphQL client\n * @param businessEntity - Input\n * @returns Created business entity\n */\nexport async function createBusinessEntity(\n client: GraphQLClient,\n options: {\n /** Business entity to create */\n input: BusinessEntityInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<BusinessEntity> {\n const { input: businessEntity, logger = NOOP_LOGGER } = options;\n const input = {\n title: businessEntity.title,\n description: businessEntity.description,\n address: businessEntity.address,\n headquarterCountry: businessEntity.headquarterCountry,\n headquarterSubDivision: businessEntity.headquarterSubDivision,\n dataProtectionOfficerName: businessEntity.dataProtectionOfficerName,\n dataProtectionOfficerEmail: businessEntity.dataProtectionOfficerEmail,\n attributes: businessEntity.attributes,\n teamNames: businessEntity.teams,\n ownerEmails: businessEntity.owners,\n };\n\n const { createBusinessEntity } = await makeGraphQLRequest<{\n /** Create business entity mutation */\n createBusinessEntity: {\n /** Created business entity */\n businessEntity: BusinessEntity;\n };\n }>(client, CREATE_BUSINESS_ENTITY, {\n variables: { input },\n logger,\n });\n return createBusinessEntity.businessEntity;\n}\n\n/**\n * Input to update business entities\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateBusinessEntities(\n client: GraphQLClient,\n options: {\n /** [BusinessEntityInput, businessEntityId] list */\n input: [BusinessEntityInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: businessEntities, logger = NOOP_LOGGER } = options;\n const chunkedUpdates = chunk(businessEntities, 100);\n await mapSeries(chunkedUpdates, async (chunked) => {\n await makeGraphQLRequest(client, UPDATE_BUSINESS_ENTITIES, {\n variables: {\n input: chunked.map(([businessEntity, id]) => ({\n id,\n title: businessEntity.title,\n description: businessEntity.description,\n address: businessEntity.address,\n headquarterCountry: businessEntity.headquarterCountry,\n headquarterSubDivision: businessEntity.headquarterSubDivision,\n dataProtectionOfficerName: businessEntity.dataProtectionOfficerName,\n dataProtectionOfficerEmail: businessEntity.dataProtectionOfficerEmail,\n attributes: businessEntity.attributes,\n teamNames: businessEntity.teams,\n ownerEmails: businessEntity.owners,\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the data inventory business entities\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncBusinessEntities(\n client: GraphQLClient,\n inputs: BusinessEntityInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" business entities...`);\n\n let encounteredError = false;\n\n // Fetch existing\n const existingBusinessEntities = await fetchAllBusinessEntities(client, { logger });\n\n // Look up by title\n const businessEntityByTitle = keyBy(existingBusinessEntities, 'title');\n\n // Create new business entities\n const newBusinessEntities = inputs.filter((input) => !businessEntityByTitle[input.title]);\n\n // Create new business entities\n await mapSeries(newBusinessEntities, async (businessEntity) => {\n try {\n const newBusinessEntity = await createBusinessEntity(client, {\n input: businessEntity,\n logger,\n });\n businessEntityByTitle[newBusinessEntity.title] = newBusinessEntity;\n logger.info(`Successfully synced business entity \"${businessEntity.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync business entity \"${businessEntity.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n // Update all business entities\n try {\n logger.info(`Updating \"${inputs.length}\" business entities!`);\n await updateBusinessEntities(client, {\n input: inputs.map((input) => [input, businessEntityByTitle[input.title]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" business entities!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync \"${inputs.length}\" business entities ! - ${(err as Error).message}`,\n );\n }\n\n return !encounteredError;\n}\n","import { DataCategoryType } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataCategories, type DataSubCategory } from './fetchAllDataCategories.js';\nimport { UPDATE_DATA_SUB_CATEGORIES, CREATE_DATA_SUB_CATEGORY } from './gqls/dataCategory.js';\n\nexport interface DataCategoryInput {\n /** Name of data category */\n name: string;\n /** Type of data category */\n category: DataCategoryType;\n /** Description of data category */\n description?: string;\n /** Regex for data category */\n regex?: string;\n /** Owner email addresses to assign */\n owners?: string[];\n /** Team names to assign */\n teams?: string[];\n /** Attribute value and its corresponding attribute key */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\n/**\n * Create a new data category\n *\n * @param client - GraphQL client\n * @param dataCategory - Input\n * @param options - Options\n * @returns Created data category\n */\nexport async function createDataCategory(\n client: GraphQLClient,\n options: {\n /** Data category to create */\n input: DataCategoryInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<DataSubCategory, 'id' | 'name' | 'category'>> {\n const { input: dataCategory, logger = NOOP_LOGGER } = options;\n const input = {\n name: dataCategory.name,\n category: dataCategory.category,\n description: dataCategory.description,\n // TODO: https://transcend.height.app/T-31994 - add attributes, teams, owners\n };\n\n const { createDataCategory } = await makeGraphQLRequest<{\n /** Create data category mutation */\n createDataCategory: {\n /** Created data category */\n dataCategory: DataSubCategory;\n };\n }>(client, CREATE_DATA_SUB_CATEGORY, {\n variables: { input },\n logger,\n });\n return createDataCategory.dataCategory;\n}\n\n/**\n * Update data categories\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateDataCategories(\n client: GraphQLClient,\n options: {\n /** [DataCategoryInput, dataCategoryId] list */\n input: [DataCategoryInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataCategories, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_DATA_SUB_CATEGORIES, {\n variables: {\n input: {\n dataSubCategories: dataCategories.map(([dataCategory, id]) => ({\n id,\n description: dataCategory.description,\n // TODO: https://transcend.height.app/T-31994 - add teams, owners\n attributes: dataCategory.attributes,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory data categories\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncDataCategories(\n client: GraphQLClient,\n inputs: DataCategoryInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" data categories...`);\n\n let encounteredError = false;\n\n // Fetch existing\n const existingDataCategories = await fetchAllDataCategories(client, {\n logger,\n });\n\n // Look up by name\n const dataCategoryByName: {\n [k in string]: Pick<DataSubCategory, 'id' | 'name' | 'category'>;\n } = keyBy(existingDataCategories, ({ name, category }) => `${name}:${category}`);\n\n // Create new data categories\n const newDataCategories = inputs.filter(\n (input) => !dataCategoryByName[`${input.name}:${input.category}`],\n );\n\n await mapSeries(newDataCategories, async (dataCategory) => {\n try {\n const newDataCategory = await createDataCategory(client, { input: dataCategory, logger });\n dataCategoryByName[`${newDataCategory.name}:${newDataCategory.category}`] = newDataCategory;\n logger.info(`Successfully synced data category \"${dataCategory.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync data category \"${dataCategory.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n // Update all data categories\n try {\n logger.info(`Updating \"${inputs.length}\" data categories!`);\n await updateDataCategories(client, {\n input: inputs.map((input) => [\n input,\n dataCategoryByName[`${input.name}:${input.category}`]!.id,\n ]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" data categories!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" data categories ! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSilo } from './fetchAllDataSilos.js';\nimport { UPDATE_DATA_SILOS } from './gqls/dataSilo.js';\n\nconst BATCH_SILOS_LIMIT = 20;\n\n/**\n * A set of data silos that another data silo depends on during an erasure request,\n * either globally or scoped to a single erasure workflow.\n */\nexport interface DependedOnDataSiloInput {\n /** IDs of the data silos depended on */\n ids?: string[];\n /** Titles of the data silos depended on */\n titles?: string[];\n /** ID of the workflow config this override applies to. Omit for the global configuration */\n workflowConfigId?: string;\n /** Internal name of the workflow config this override applies to. Omit for the global configuration */\n workflowConfigInternalName?: string;\n /** Clear the workflow-scoped override so the data silo falls back to the global configuration */\n resetToGlobal?: boolean;\n}\n\n/**\n * Sync data silo dependencies\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns True upon success\n */\nexport async function syncDataSiloDependencies(\n client: GraphQLClient,\n options: {\n /** Pairs of [data silo ID, dependency entries] */\n input: [string, DependedOnDataSiloInput[]][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<boolean> {\n const { input: dependencyUpdates, logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${dependencyUpdates.length}\" data silo dependencies...`);\n\n const chunkedUpdates = chunk(dependencyUpdates, BATCH_SILOS_LIMIT);\n await mapSeries(chunkedUpdates, async (dependencyUpdateChunk, ind) => {\n logger.info(\n `[Batch ${ind}/${dependencyUpdateChunk.length}] Updating \"${dependencyUpdateChunk.length}\" data silos...`,\n );\n try {\n await makeGraphQLRequest<{\n /** Mutation result */\n updateDataSilos: {\n /** New data silos */\n dataSilos: Pick<DataSilo, 'id' | 'title'>[];\n };\n }>(client, UPDATE_DATA_SILOS, {\n variables: {\n input: {\n dataSilos: dependencyUpdateChunk.map(([id, dependedOnDataSilos]) => ({\n id,\n dependedOnDataSilos,\n })),\n },\n },\n logger,\n });\n logger.info(\n `[Batch ${ind + 1}/${dependencyUpdateChunk.length}] ` +\n `Synced \"${dependencyUpdateChunk.length}\" data silos!`,\n );\n } catch (err) {\n encounteredError = true;\n logger.error(\n `[Batch ${ind + 1}/${dependencyUpdateChunk.length}] ` +\n `Failed to update \"${dependencyUpdateChunk.length}\" silos! - ${(err as Error).message}`,\n );\n }\n });\n return !encounteredError;\n}\n","import type {\n ProcessingPurpose,\n DataCategoryType,\n DataProtectionImpactAssessmentStatus,\n Controllership,\n RetentionType,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n} from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n fetchAllProcessingActivities,\n type ProcessingActivity,\n} from './fetchAllProcessingActivities.js';\nimport {\n UPDATE_PROCESSING_ACTIVITIES,\n CREATE_PROCESSING_ACTIVITY,\n} from './gqls/processingActivity.js';\n\nexport interface ProcessingActivityInput {\n /** The title of the processing activity */\n title: string;\n /** Description of the processing activity */\n description?: string;\n /** Security measure details */\n securityMeasureDetails?: string;\n /** Controllerships */\n controllerships?: Controllership[];\n /** Storage regions */\n storageRegions?: {\n /** The country */\n country?: IsoCountryCode;\n /** The country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n }[];\n /** Transfer regions */\n transferRegions?: {\n /** The country */\n country?: IsoCountryCode;\n /** The country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n }[];\n /** Retention type */\n retentionType?: RetentionType;\n /** Retention period in days */\n retentionPeriod?: number;\n /** Data protection impact assessment link */\n dataProtectionImpactAssessmentLink?: string;\n /** Data protection impact assessment status */\n dataProtectionImpactAssessmentStatus?: DataProtectionImpactAssessmentStatus;\n /** Attribute value and its corresponding attribute key */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n /** Data silo titles */\n dataSiloTitles?: string[];\n /** Data subject types */\n dataSubjectTypes?: string[];\n /** Team names */\n teamNames?: string[];\n /** Owner emails */\n ownerEmails?: string[];\n /** Processing sub purposes */\n processingSubPurposes?: {\n /** The parent purpose */\n purpose: ProcessingPurpose;\n /** User-defined name for this processing purpose sub category */\n name?: string;\n }[];\n /** Data sub categories */\n dataSubCategories?: {\n /** The parent category */\n category: DataCategoryType;\n /** User-defined name for this sub category */\n name?: string;\n }[];\n /** SaaS category titles */\n saaSCategories?: string[];\n}\n\n/**\n * Create a new processing activity, setting only title and description\n *\n * @param client - GraphQL client\n * @param processingActivity - Input\n * @param options - Options\n * @returns Created processingActivity\n */\nasync function createProcessingActivity(\n client: GraphQLClient,\n processingActivity: ProcessingActivityInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Pick<ProcessingActivity, 'id' | 'title'>> {\n const { logger = NOOP_LOGGER } = options;\n const input = {\n title: processingActivity.title,\n description: processingActivity.description,\n };\n\n const { createProcessingActivity } = await makeGraphQLRequest<{\n /** Create processingActivity mutation */\n createProcessingActivity: {\n /** Created processingActivity */\n processingActivity: ProcessingActivity;\n };\n }>(client, CREATE_PROCESSING_ACTIVITY, {\n variables: { input },\n logger,\n });\n return createProcessingActivity.processingActivity;\n}\n\n/**\n * Update a list of processing activities.\n *\n * @param client - GraphQL client\n * @param processingActivityIdPairs - [ProcessingActivityInput, processingActivityId] list\n * @param options - Options\n */\nasync function updateProcessingActivities(\n client: GraphQLClient,\n processingActivityIdPairs: [ProcessingActivityInput, string][],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n const invalidProcessingActivityTitles = processingActivityIdPairs\n .filter(([, id]) => id === undefined)\n .map(([{ title }]) => title);\n if (invalidProcessingActivityTitles.length > 0) {\n throw new Error(\n `The following ${\n invalidProcessingActivityTitles.length\n } processing activities do not exist and thus can't be updated: \"${invalidProcessingActivityTitles.join(\n '\", \"',\n )}\"`,\n );\n }\n await makeGraphQLRequest(client, UPDATE_PROCESSING_ACTIVITIES, {\n variables: {\n input: {\n processingActivities: processingActivityIdPairs.map(\n ([\n { processingSubPurposes, dataSubCategories, saaSCategories, ...processingActivity },\n id,\n ]) => ({\n dataSubCategoryInputs: dataSubCategories?.map(({ category, name }) => ({\n category,\n name: name ?? '',\n })),\n processingPurposeSubCategoryInputs: processingSubPurposes?.map(({ purpose, name }) => ({\n purpose,\n name: name ?? 'Other',\n })),\n saaSCategoryTitles: saaSCategories,\n ...processingActivity,\n id,\n }),\n ),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory processing activities\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncProcessingActivities(\n client: GraphQLClient,\n inputs: ProcessingActivityInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" processing activities...`);\n const existingProcessingActivities = await fetchAllProcessingActivities(client, { logger });\n\n // Look up by title\n const processingActivityByTitle: Record<string, Pick<ProcessingActivity, 'id' | 'title'>> = keyBy(\n existingProcessingActivities,\n 'title',\n );\n\n // Create new processingActivities\n const newProcessingActivities = inputs.filter((input) => !processingActivityByTitle[input.title]);\n if (newProcessingActivities.length > 0) {\n logger.info(`Creating \"${newProcessingActivities.length}\" new processing activities...`);\n }\n await mapSeries(newProcessingActivities, async (processingActivity) => {\n try {\n const newProcessingActivity = await createProcessingActivity(client, processingActivity, {\n logger,\n });\n processingActivityByTitle[newProcessingActivity.title] = newProcessingActivity;\n logger.info(`Successfully created processing activity \"${processingActivity.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to create processing activity \"${processingActivity.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n // Update all processing activities\n try {\n logger.info(`Updating \"${inputs.length}\" processing activities!`);\n await updateProcessingActivities(\n client,\n inputs\n .map((input) => [input, processingActivityByTitle[input.title]?.id] as const)\n .filter((x): x is [ProcessingActivityInput, string] => !!x[1]),\n { logger },\n );\n logger.info(`Successfully synced \"${inputs.length}\" processingActivities!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync \"${inputs.length}\" processingActivities! - ${(err as Error).message}`,\n );\n }\n\n return !encounteredError;\n}\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllVendors, type Vendor } from './fetchAllVendors.js';\nimport { UPDATE_VENDORS, CREATE_VENDOR } from './gqls/vendor.js';\n\nexport interface VendorInput {\n /** Display title of the vendor */\n title: string;\n /** Description of the vendor */\n description?: string;\n /** URL to the data processing agreement */\n dataProcessingAgreementLink?: string;\n /** Name of the primary contact */\n contactName?: string;\n /** Phone number of the primary contact */\n contactPhone?: string;\n /** Physical address */\n address?: string;\n /** Headquarters country */\n headquarterCountry?: IsoCountryCode;\n /** Headquarters country subdivision */\n headquarterSubDivision?: IsoCountrySubdivisionCode;\n /** Vendor website URL */\n websiteUrl?: string;\n /** Title of the associated business entity */\n businessEntity?: string;\n /** Owner email addresses to assign */\n owners?: string[];\n /** Team names to assign */\n teams?: string[];\n /** Custom attribute values to assign */\n attributes?: {\n /** Attribute key name */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\n/**\n * Input to create a new vendor\n *\n * @param client - GraphQL client\n * @param vendor - Input\n * @returns Created vendor\n */\nexport async function createVendor(\n client: GraphQLClient,\n options: {\n /** Vendor to create */\n input: VendorInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Vendor, 'id' | 'title'>> {\n const { input: vendor, logger = NOOP_LOGGER } = options;\n const input = {\n title: vendor.title,\n description: vendor.description,\n address: vendor.address,\n headquarterCountry: vendor.headquarterCountry,\n headquarterSubDivision: vendor.headquarterSubDivision,\n dataProcessingAgreementLink: vendor.dataProcessingAgreementLink,\n contactName: vendor.contactName,\n contactPhone: vendor.contactPhone,\n websiteUrl: vendor.websiteUrl,\n // TODO: https://transcend.height.app/T-31994 - add attributes, teams, owners\n };\n\n const { createVendor } = await makeGraphQLRequest<{\n /** Create vendor mutation */\n createVendor: {\n /** Created vendor */\n vendor: Vendor;\n };\n }>(client, CREATE_VENDOR, {\n variables: { input },\n logger,\n });\n return createVendor.vendor;\n}\n\n/**\n * Input to update vendors\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateVendors(\n client: GraphQLClient,\n options: {\n /** [VendorInput, vendorId] list */\n input: [VendorInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: vendors, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_VENDORS, {\n variables: {\n input: {\n vendors: vendors.map(([vendor, id]) => ({\n id,\n title: vendor.title,\n description: vendor.description,\n address: vendor.address,\n headquarterCountry: vendor.headquarterCountry,\n headquarterSubDivision: vendor.headquarterSubDivision,\n dataProcessingAgreementLink: vendor.dataProcessingAgreementLink,\n contactName: vendor.contactName,\n contactPhone: vendor.contactPhone,\n websiteUrl: vendor.websiteUrl,\n // TODO: https://transcend.height.app/T-31994 - add teams, owners\n attributes: vendor.attributes,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory vendors\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncVendors(\n client: GraphQLClient,\n inputs: VendorInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" vendors...`);\n\n let encounteredError = false;\n\n // Fetch existing\n const existingVendors = await fetchAllVendors(client, { logger });\n\n // Look up by title\n const vendorByTitle: { [k in string]: Pick<Vendor, 'id' | 'title'> } = keyBy(\n existingVendors,\n 'title',\n );\n\n // Create new vendors\n const newVendors = inputs.filter((input) => !vendorByTitle[input.title]);\n\n // Create new vendors\n await mapSeries(newVendors, async (vendor) => {\n try {\n const newVendor = await createVendor(client, { input: vendor, logger });\n vendorByTitle[newVendor.title] = newVendor;\n logger.info(`Successfully synced vendor \"${vendor.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync vendor \"${vendor.title}\"! - ${(err as Error).message}`);\n }\n });\n\n // Update all vendors\n try {\n logger.info(`Updating \"${inputs.length}\" vendors!`);\n await updateVendors(client, {\n input: inputs.map((input) => [input, vendorByTitle[input.title]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" vendors!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" vendors ! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const PURPOSES: DocumentNode = parse(gql`\n query TranscendCliPurposes(\n $first: Int!\n $offset: Int!\n $filterBy: TrackingPurposeFiltersInput\n $input: TrackingPurposeInput!\n ) {\n purposes(first: $first, offset: $offset, filterBy: $filterBy, input: $input) {\n nodes {\n id\n name\n description\n defaultConsent\n trackingType\n configurable\n essential\n showInConsentManager\n isActive\n displayOrder\n optOutSignals\n deletedAt\n authLevel\n showInPrivacyCenter\n title\n }\n }\n }\n`);\n\nexport const CREATE_PURPOSE: DocumentNode = parse(gql`\n mutation TranscendCliCreatePurpose($input: CreatePurposeInput!) {\n createPurpose(input: $input) {\n purpose {\n id\n trackingType\n }\n }\n }\n`);\n\nexport const UPDATE_PURPOSE: DocumentNode = parse(gql`\n mutation TranscendCliUpdatePurpose($input: UpdatePurposeInput!) {\n updatePurpose(input: $input) {\n purpose {\n id\n trackingType\n }\n }\n }\n`);\n","import { UserPrivacySignalEnum } from '@transcend-io/airgap.js-types';\nimport { DefaultConsentOption, PreferenceStoreAuthLevel } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PURPOSES } from './gqls/purpose.js';\n\nexport interface Purpose {\n /** ID of purpose */\n id: string;\n /** Name of purpose */\n name: string;\n /** Description of purpose */\n description: string;\n /** Default consent status */\n defaultConsent: DefaultConsentOption;\n /** Slug of purpose */\n trackingType: string;\n /** Whether the purpose is configurable */\n configurable: boolean;\n /** Whether the purpose is essential */\n essential: boolean;\n /** Whether to show the purpose in the consent manager */\n showInConsentManager: boolean;\n /** Whether the purpose is active */\n isActive: boolean;\n /** Display order of the purpose */\n displayOrder: number;\n /** Opt-out signals for the purpose */\n optOutSignals: UserPrivacySignalEnum[];\n /** Whether the purpose is deleted */\n deletedAt?: string;\n /** Authorization level required for the purpose */\n authLevel: PreferenceStoreAuthLevel;\n /** Whether to show the purpose in the privacy center */\n showInPrivacyCenter: boolean;\n /** Title of the purpose */\n title: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all purposes in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All purposes in the organization\n */\nexport async function fetchAllPurposes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Whether to include deleted purposes */\n includeDeleted?: boolean;\n };\n } = {},\n): Promise<Purpose[]> {\n const { logger, filterBy: { includeDeleted = false } = {} } = options;\n const purposes: Purpose[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n purposes: { nodes },\n } = await makeGraphQLRequest<{\n /** Purposes */\n purposes: {\n /** List */\n nodes: Purpose[];\n };\n }>(client, PURPOSES, {\n logger,\n variables: { first: PAGE_SIZE, offset, input: { includeDeleted } },\n });\n purposes.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return purposes.sort((a, b) => a.trackingType.localeCompare(b.trackingType));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const PREFERENCE_TOPICS: DocumentNode = parse(gql`\n query TranscendCliPreferenceTopics(\n $first: Int!\n $offset: Int!\n $filterBy: PreferenceTopicFilterInput\n ) {\n preferenceTopics(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n slug\n type\n color\n title {\n id\n defaultMessage\n }\n showInPrivacyCenter\n displayDescription {\n id\n defaultMessage\n }\n defaultConfiguration\n preferenceOptionValues {\n slug\n title {\n id\n defaultMessage\n }\n }\n purpose {\n trackingType\n }\n }\n }\n }\n`);\n\nexport const CREATE_OR_UPDATE_PREFERENCE_TOPIC: DocumentNode = parse(gql`\n mutation TranscendCliCreateOrUpdatePreferenceTopic($input: CreateOrUpdatePreferenceTopicInput!) {\n createOrUpdatePreferenceTopic(input: $input) {\n preferenceTopic {\n id\n slug\n }\n }\n }\n`);\n","import { PreferenceTopicType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PREFERENCE_TOPICS } from './gqls/preferenceTopic.js';\n\nexport interface PreferenceTopic {\n /** ID of preference topic */\n id: string;\n /** Slug of preference topic */\n slug: string;\n /** Title of topic */\n title: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n /** Whether to show in privacy center */\n showInPrivacyCenter: boolean;\n /** Description to display in privacy center */\n displayDescription: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n /** Type of preference topic */\n type: PreferenceTopicType;\n /** Default configuration */\n defaultConfiguration: string;\n /** Display color for the preference topic */\n color?: string;\n /** Option values */\n preferenceOptionValues: {\n /** Slug of value */\n slug: string;\n /** Title of value */\n title: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n }[];\n /** Related purpose */\n purpose: {\n /** Slug */\n trackingType: string;\n };\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all preference topics in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All preference topics in the organization\n */\nexport async function fetchAllPreferenceTopics(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PreferenceTopic[]> {\n const { logger } = options;\n const preferenceTopics: PreferenceTopic[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n preferenceTopics: { nodes },\n } = await makeGraphQLRequest<{\n /** Preference topics */\n preferenceTopics: {\n /** List */\n nodes: PreferenceTopic[];\n };\n }>(client, PREFERENCE_TOPICS, {\n logger,\n variables: { first: PAGE_SIZE, offset },\n });\n preferenceTopics.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return preferenceTopics.sort((a, b) =>\n `${a.slug}:${a.purpose.trackingType}`.localeCompare(`${b.slug}:${b.purpose.trackingType}`),\n );\n}\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { type PreferenceTopic, fetchAllPreferenceTopics } from './fetchAllPreferenceTopics.js';\nimport { type Purpose, fetchAllPurposes } from './fetchAllPurposes.js';\n\nexport interface PurposeWithPreferences extends Purpose {\n /** Topics */\n topics: PreferenceTopic[];\n}\n\n/**\n * Fetch all purposes and preferences\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of purposes with their preference topics\n */\nexport async function fetchAllPurposesAndPreferences(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PurposeWithPreferences[]> {\n const [purposes, topics] = await Promise.all([\n fetchAllPurposes(client, options),\n fetchAllPreferenceTopics(client, options),\n ]);\n\n return purposes.map((purpose) => ({\n ...purpose,\n topics: topics.filter((topic) => topic.purpose.trackingType === purpose.trackingType),\n }));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const CREATE_PREFERENCE_ACCESS_TOKENS: DocumentNode = parse(gql`\n mutation TranscendCliCreatePreferenceAccessTokens($input: CreatePrivacyCenterAccessTokensInput!) {\n createPrivacyCenterAccessTokens(input: $input) {\n nodes {\n token\n }\n }\n }\n`);\n","import type { SombraStandardScope } from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CREATE_PREFERENCE_ACCESS_TOKENS } from './gqls/preferenceAccessTokens.js';\n\nexport interface PreferenceAccessTokenInput {\n /** Slug of data subject to authenticate as */\n subjectType: string;\n /** Scopes to grant */\n scopes: SombraStandardScope[];\n /** Expiration time in seconds */\n expiresIn?: number;\n /** Email address of user */\n email: string;\n /** Core identifier for the user */\n coreIdentifier?: string;\n}\n\nexport interface PreferenceAccessTokenInputWithIndex extends PreferenceAccessTokenInput {\n /** Index of the input record */\n index?: number;\n}\n\nconst MAX_BATCH_SIZE = 50;\n\n/**\n * Create preference access tokens for a single page of identifiers.\n *\n * @param client - GraphQL client\n * @param records - Inputs to sign\n * @param logger - Logger\n * @returns list of access tokens\n */\nasync function createPreferenceAccessTokensPage(\n client: GraphQLClient,\n records: PreferenceAccessTokenInput[],\n logger?: Logger,\n): Promise<string[]> {\n const {\n createPrivacyCenterAccessTokens: { nodes },\n } = await makeGraphQLRequest<{\n /** createPrivacyCenterAccessTokens mutation */\n createPrivacyCenterAccessTokens: {\n /** Nodes */\n nodes: {\n /** Token */\n token: string;\n }[];\n };\n }>(client, CREATE_PREFERENCE_ACCESS_TOKENS, {\n logger,\n variables: { input: { records } },\n });\n return nodes.map((node) => node.token);\n}\n\n/**\n * Create preference access tokens for the given identifiers.\n *\n * @see https://docs.transcend.io/docs/articles/preference-management/access-links\n * @param client - GraphQL client\n * @param options - Options\n * @returns list of access tokens/input identifiers\n */\nexport async function createPreferenceAccessTokens(\n client: GraphQLClient,\n options: {\n /** Records to create tokens for */\n records: PreferenceAccessTokenInputWithIndex[];\n /** Logger instance */\n logger?: Logger;\n /** Optional progress emitter */\n emitProgress?: (progress: number) => void;\n /** Number of concurrent requests to make (default: 10) */\n concurrency?: number;\n },\n): Promise<\n {\n /** Identifier for the record */\n input: PreferenceAccessTokenInputWithIndex;\n /** Access token */\n accessToken: string;\n }[]\n> {\n const { records, logger, emitProgress, concurrency = 10 } = options;\n\n let completed = 0;\n emitProgress?.(0);\n\n const results: {\n /** Identifier for the record */\n input: PreferenceAccessTokenInput;\n /** Access token */\n accessToken: string;\n }[] = [];\n\n await map(\n chunk(records, MAX_BATCH_SIZE),\n async (chunkedRecords) => {\n const tokens = await createPreferenceAccessTokensPage(\n client,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n chunkedRecords.map(({ index, ...rest }) => rest),\n logger,\n );\n const mappedResults = tokens.map((token, idx) => ({\n input: chunkedRecords[idx]!,\n accessToken: token,\n }));\n results.push(...mappedResults);\n completed += chunkedRecords.length;\n emitProgress?.(completed);\n },\n { concurrency },\n );\n\n return results;\n}\n","import { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport * as t from 'io-ts';\n\n/**\n * New response codec for the query endpoint\n */\nexport const ConsentPreferenceResponse = t.intersection([\n t.type({\n nodes: t.array(PreferenceQueryResponseItem),\n }),\n t.partial({\n /** Cursor for next page (opaque) */\n cursor: t.string,\n }),\n]);\n\n/**\n * Type override\n */\nexport type ConsentPreferenceResponse = t.TypeOf<typeof ConsentPreferenceResponse>;\n\n/** Identifier filter (new shape) */\nexport type PreferenceIdentifier = {\n /** e.g., \"email\", \"phone\" */\n name: string;\n /** identifier value */\n value: string;\n};\n\n/** Filter shape for the new query endpoint */\nexport type PreferencesQueryFilter = {\n /** Identifiers to filter by */\n identifiers?: PreferenceIdentifier[];\n /** Consent collection time */\n timestampBefore?: string;\n /** Consent collection time */\n timestampAfter?: string;\n /** System updatedAt time */\n system?: {\n /** Updated before this time */\n updatedBefore?: string;\n /** Updated after this time */\n updatedAfter?: string;\n };\n};\n\n/** Which dimension we chunk on */\nexport type ChunkMode = 'timestamp' | 'updated';\n","import { UserPrivacySignalEnum } from '@transcend-io/airgap.js-types';\nimport {\n PreferenceQueryResponseItem,\n PreferenceStoreAuthLevel,\n PreferenceStoreIdentifier,\n PreferenceTopicType,\n PreferenceUpdateItem,\n} from '@transcend-io/privacy-types';\nimport { valuesOf } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\n\nexport const PurposeRowMapping = t.type({\n /**\n * The slug or trackingType of the purpose to map to\n *\n * e.g. `Marketing`\n */\n purpose: t.string,\n /**\n * If the column maps to a preference instead of a purpose\n * this is the slug of the purpose.\n *\n * null value indicates that this column maps to the true/false\n * value of the purpose\n */\n preference: t.union([t.string, t.null]),\n /**\n * The mapping between each row value and purpose/preference value.\n *\n * e.g. for a boolean preference or purpose\n * {\n * 'true': true,\n * 'false': false,\n * '': true,\n * }\n *\n * or for a single or multi select preference\n * {\n * '': true,\n * 'value1': 'Value1',\n * 'value2': 'Value2',\n * }\n */\n valueMapping: t.record(t.string, t.union([t.string, t.boolean, t.null, t.undefined])),\n});\n\n/** Override type */\nexport type PurposeRowMapping = t.TypeOf<typeof PurposeRowMapping>;\n\n/**\n * Mapping of column name to purpose row mapping.\n * This is used to map each column in the CSV to the relevant purpose and preference definitions in\n * transcend.\n */\nexport const ColumnPurposeMap = t.record(t.string, PurposeRowMapping);\n\n/** Override type */\nexport type ColumnPurposeMap = t.TypeOf<typeof ColumnPurposeMap>;\n\nexport const IdentifierMetadataForPreference = t.type({\n /** The identifier name */\n name: t.string,\n /** Is unique on preference store */\n isUniqueOnPreferenceStore: t.boolean,\n});\n\n/** Override type */\nexport type IdentifierMetadataForPreference = t.TypeOf<typeof IdentifierMetadataForPreference>;\n\n/**\n * Mapping of identifier name to the column name in the CSV file.\n * This is used to map each identifier name to the column in the CSV file.\n */\nexport const ColumnIdentifierMap = t.record(t.string, IdentifierMetadataForPreference);\n\n/** Override type */\nexport type ColumnIdentifierMap = t.TypeOf<typeof ColumnIdentifierMap>;\n\n/** Mapping of a CSV column to a metadata key in the preference store. */\nexport const MetadataMapping = t.type({\n /** The metadata key name in the preference store */\n key: t.string,\n});\n\n/** Override type */\nexport type MetadataMapping = t.TypeOf<typeof MetadataMapping>;\n\n/** Record mapping CSV column names to metadata keys. */\nexport const ColumnMetadataMap = t.record(t.string, MetadataMapping);\n\n/** Override type */\nexport type ColumnMetadataMap = t.TypeOf<typeof ColumnMetadataMap>;\n\nexport const FileMetadataState = t.intersection([\n t.type({\n /**\n * Definition of how to map each column in the CSV to\n * the relevant purpose and preference definitions in transcend\n */\n columnToPurposeName: t.record(t.string, PurposeRowMapping),\n /** Last time the file was last parsed at */\n lastFetchedAt: t.string,\n /**\n * Mapping of userId to the rows in the file that need to be uploaded\n * These uploads are overwriting non-existent preferences and are safe\n */\n pendingSafeUpdates: t.record(t.string, t.record(t.string, t.string)),\n /**\n * Mapping of userId to the rows in the file that need to be uploaded\n * these records have conflicts with existing consent preferences\n */\n pendingConflictUpdates: t.record(\n t.string,\n t.type({\n record: PreferenceQueryResponseItem,\n row: t.record(t.string, t.string),\n }),\n ),\n /**\n * Mapping of userId to the rows in the file that can be skipped because\n * their preferences are already in the store\n */\n skippedUpdates: t.record(t.string, t.record(t.string, t.string)),\n }),\n t.partial({\n /** Determine which column name in file maps to consent record identifier to upload on */\n identifierColumn: t.string,\n /** Determine which column name in file maps to the timestamp */\n timestampColum: t.string,\n }),\n]);\n\n/** Override type */\nexport type FileMetadataState = t.TypeOf<typeof FileMetadataState>;\n\n/**\n * Schema-only state for a preference CSV file format.\n *\n * Unlike FileMetadataState this does NOT embed upload receipts — it only\n * describes how columns map to identifiers, purposes, timestamps, and metadata.\n */\nexport const FileFormatState = t.intersection([\n t.type({\n /** Maps each CSV column to its purpose/preference definition in Transcend */\n columnToPurposeName: ColumnPurposeMap,\n /** ISO 8601 timestamp of when this config was last generated or refreshed */\n lastFetchedAt: t.string,\n /** Maps each CSV column to the identifier it represents (e.g. email, userId) */\n columnToIdentifier: ColumnIdentifierMap,\n }),\n t.partial({\n /** CSV column whose values contain the consent timestamp */\n timestampColumn: t.string,\n /** Maps CSV columns to metadata keys stored alongside the preference record */\n columnToMetadata: ColumnMetadataMap,\n /** CSV columns that should be skipped during upload */\n columnsToIgnore: t.array(t.string),\n }),\n]);\n\n/** Override type */\nexport type FileFormatState = t.TypeOf<typeof FileFormatState>;\n\n/**\n * This is the type of the receipts that are stored in the file\n * that is used to track the state of the upload process.\n * It is used to resume the upload process from where it left off.\n * It is used to persist the state of the upload process across multiple runs.\n */\nexport const PreferenceUpdateMap = t.record(\n t.string,\n // This can either be true to indicate the record is pending\n // or it can be an object showing the object\n // We only return a fixed number of results to avoid\n // making the JSON file too large\n t.union([t.boolean, PreferenceUpdateItem]),\n);\n\n/** Override type */\nexport type PreferenceUpdateMap = t.TypeOf<typeof PreferenceUpdateMap>;\n\n/**\n * This is the type of the pending updates that are safe to run without\n * conflicts with existing consent preferences.\n *\n * Key is primaryKey of the record in the file.\n * The value is the row in the file that is safe to upload.\n */\nexport const PendingSafePreferenceUpdates = t.record(\n t.string,\n // This can either be true to indicate the record is safe\n // or it can be an object showing the object\n // We only return a fixed number of results to avoid\n // making the JSON file too large\n t.union([t.boolean, t.record(t.string, t.string)]),\n);\n\n/** Override type */\nexport type PendingSafePreferenceUpdates = t.TypeOf<typeof PendingSafePreferenceUpdates>;\n\n/**\n * These are the updates that failed to be uploaded to the API.\n */\nexport const FailingPreferenceUpdates = t.record(\n t.string,\n t.type({\n /** Time upload ran at */\n uploadedAt: t.string,\n /** Attempts to upload that resulted in an error */\n error: t.string,\n /** The update body */\n update: PreferenceUpdateItem,\n }),\n);\n\n/** Override type */\nexport type FailingPreferenceUpdates = t.TypeOf<typeof FailingPreferenceUpdates>;\n\n/**\n * This is the type of the pending updates that are in conflict with existing consent preferences.\n *\n * Key is primaryKey of the record in the file.\n * The value is the row in the file that is pending upload.\n */\nexport const PendingWithConflictPreferenceUpdates = t.record(\n t.string,\n // We always return the conflicts for investigation\n t.type({\n /** Record to be inserted to transcend v1/preferences API */\n record: PreferenceQueryResponseItem,\n /** The row in the file that is pending upload */\n row: t.record(t.string, t.string),\n }),\n);\n\n/** Override type */\nexport type PendingWithConflictPreferenceUpdates = t.TypeOf<\n typeof PendingWithConflictPreferenceUpdates\n>;\n\n/**\n * The set of preference updates that are skipped\n * Key is primaryKey and value is the row in the CSV\n * that is skipped.\n *\n * This is usually because the preferences are already in the store\n * or there are duplicate rows in the CSV file that are identical.\n */\nexport const SkippedPreferenceUpdates = t.record(t.string, t.record(t.string, t.string));\n\n/** Override type */\nexport type SkippedPreferenceUpdates = t.TypeOf<typeof SkippedPreferenceUpdates>;\n\n/** Persist this data between runs of the script */\nexport const PreferenceState = t.type({\n /**\n * Store a cache of previous files read in\n */\n fileMetadata: t.record(t.string, FileMetadataState),\n /**\n * The set of successful uploads to Transcend\n * Mapping from userId to the upload metadata\n */\n failingUpdates: t.record(\n t.string,\n t.type({\n /** Time upload ran at */\n uploadedAt: t.string,\n /** Attempts to upload that resulted in an error */\n error: t.string,\n /** The update body */\n update: PreferenceUpdateItem,\n }),\n ),\n /**\n * The set of pending uploads to Transcend\n * Mapping from userId to the upload metadata\n */\n pendingUpdates: t.record(t.string, PreferenceUpdateItem),\n});\n\n/** Override type */\nexport type PreferenceState = t.TypeOf<typeof PreferenceState>;\n\nexport const RequestUploadReceipts = t.type({\n /** ISO 8601 timestamp of when the receipt file was last written */\n lastFetchedAt: t.string,\n /** Updates that can be applied without conflicting with existing preferences */\n pendingSafeUpdates: PendingSafePreferenceUpdates,\n /** Updates that conflict with existing preference values and need review */\n pendingConflictUpdates: PendingWithConflictPreferenceUpdates,\n /** Rows skipped because their preferences already match the store */\n skippedUpdates: SkippedPreferenceUpdates,\n /** Updates that were attempted but failed with an API error */\n failingUpdates: FailingPreferenceUpdates,\n /** Updates still queued to be sent to the API */\n pendingUpdates: PreferenceUpdateMap,\n /** Updates that have been successfully written to the preference store */\n successfulUpdates: PreferenceUpdateMap,\n});\n\n/** Override type */\nexport type RequestUploadReceipts = t.TypeOf<typeof RequestUploadReceipts>;\n\nexport const DeletePreferenceRecordsInput = t.type({\n /** Array of consent preference records to delete */\n records: t.array(\n t.type({\n /** The anchor identifier to locate the consent record */\n anchorIdentifier: PreferenceStoreIdentifier,\n /** The ISO 8601 timestamp of when the deletion is requested */\n timestamp: t.string,\n }),\n ),\n});\n\n/** Override type */\nexport type DeletePreferenceRecordsInput = t.TypeOf<typeof DeletePreferenceRecordsInput>;\n\nexport const DeletePreferenceRecordsResponse = t.intersection([\n t.type({\n /** Array of results for each preference record deletion */\n records: t.array(\n t.intersection([\n t.type({\n /** Whether the deletion was successful */\n success: t.boolean,\n }),\n t.partial({\n /** An error message if the deletion failed */\n errorMessage: t.string,\n }),\n ]),\n ),\n /** The list of failed deletions with their respective errors */\n failures: t.array(\n t.type({\n /** The index of the failed update in the original request */\n index: t.number,\n /** The error message associated with the failure */\n error: t.string,\n }),\n ),\n }),\n t.partial({\n /** Any general errors that occurred during the operation */\n errors: t.array(t.string),\n }),\n]);\n\n/** Override type */\nexport type DeletePreferenceRecordsResponse = t.TypeOf<typeof DeletePreferenceRecordsResponse>;\n\n/** CLI CSV Row for deleting preference records */\nexport const DeletePreferenceRecordCliCsvRow = t.type({\n /** The name of the identifier type (e.g., email, userId) */\n name: t.string,\n /** The value of the identifier */\n value: t.string,\n});\n\n/** Override type */\nexport type DeletePreferenceRecordCliCsvRow = t.TypeOf<typeof DeletePreferenceRecordCliCsvRow>;\n\n/** Preference option value input for sync */\nexport const PreferenceOptionValueInput = t.type({\n /** Title of option value */\n title: t.string,\n /** API slug */\n slug: t.string,\n});\n\n/** Override type */\nexport type PreferenceOptionValueInput = t.TypeOf<typeof PreferenceOptionValueInput>;\n\n/** Preference topic input for sync */\nexport const PreferenceTopicSyncInput = t.intersection([\n t.type({\n /** Purpose slug (trackingType) this topic belongs to */\n 'tracking-type': t.string,\n /** The type of the preference topic */\n type: valuesOf(PreferenceTopicType),\n /** The title of the preference topic */\n title: t.string,\n /** The description of the preference topic */\n description: t.string,\n /** API slug for the preference topic */\n slug: t.string,\n }),\n t.partial({\n /** Default configuration value */\n 'default-configuration': t.string,\n /** Whether the preference topic is shown in the privacy center */\n 'show-in-privacy-center': t.boolean,\n /** The option values when the type is single or multi select */\n options: t.array(PreferenceOptionValueInput),\n }),\n]);\n\n/** Override type */\nexport type PreferenceTopicSyncInput = t.TypeOf<typeof PreferenceTopicSyncInput>;\n\n/** Consent purpose input for sync */\nexport const PurposeInput = t.intersection([\n t.type({\n /** Purpose slug (immutable key used to match existing purposes) */\n trackingType: t.string,\n /** Display name of the purpose */\n name: t.string,\n }),\n t.partial({\n /** Title shown in Consent Management and Privacy Center UIs */\n title: t.string,\n /** Description of the purpose */\n description: t.string,\n /** Whether the purpose is active */\n 'is-active': t.boolean,\n /** Whether the purpose is configurable */\n configurable: t.boolean,\n /** Display order of the purpose in the privacy center */\n 'display-order': t.number,\n /** Whether the purpose is shown in the privacy center */\n 'show-in-privacy-center': t.boolean,\n /** Whether the purpose is shown in the consent manager */\n 'show-in-consent-manager': t.boolean,\n /** Authentication level required for the purpose */\n 'auth-level': valuesOf(PreferenceStoreAuthLevel),\n /** Opt-out signals that instantly opt out of this purpose */\n 'opt-out-signals': t.array(valuesOf(UserPrivacySignalEnum)),\n }),\n]);\n\n/** Override type */\nexport type PurposeInput = t.TypeOf<typeof PurposeInput>;\n","import type { ColumnMetadataMap } from './codecs.js';\n\n/**\n * Extract metadata values from a CSV row based on the column-to-metadata mapping.\n *\n * @param options - Options for extracting metadata\n * @returns Array of metadata key-value pairs for the preference store API\n */\nexport function getPreferenceMetadataFromRow({\n row,\n columnToMetadata,\n}: {\n /** The CSV row as a record of column name to value */\n row: Record<string, string>;\n /** Mapping from CSV column name to metadata key */\n columnToMetadata: ColumnMetadataMap;\n}): Array<{\n /** Metadata key name */ key: string;\n /** Metadata value from the CSV row */\n value: string;\n}> {\n return Object.entries(columnToMetadata)\n .map(([columnName, { key }]) => {\n const value = row[columnName];\n // Skip if no value in the row or empty string\n if (value === undefined || value === '') {\n return null;\n }\n return { key, value };\n })\n .filter(\n (\n x,\n ): x is {\n /** Metadata key name */ key: string;\n /** Metadata value from the CSV row */\n value: string;\n } => x !== null,\n );\n}\n","import type { PreferenceStoreIdentifier } from '@transcend-io/privacy-types';\n\nimport type { FileFormatState } from './codecs.js';\n\n/**\n * Extract preference store identifiers from a CSV row based on the column-to-identifier mapping.\n *\n * @param options - Options\n * @returns Array of identifiers for the preference store API\n */\nexport function getPreferenceIdentifiersFromRow({\n row,\n columnToIdentifier,\n}: {\n /** The current row from CSV file */\n row: Record<string, string>;\n /** The current file metadata state */\n columnToIdentifier: FileFormatState['columnToIdentifier'];\n}): PreferenceStoreIdentifier[] {\n const identifiers = Object.entries(columnToIdentifier)\n .filter(([col]) => !!row[col])\n .map(([col, identifierMapping]) => ({\n name: identifierMapping.name,\n value: row[col]!,\n }));\n return identifiers.sort(\n (a, b) =>\n (a.name === 'email' ? -1 : 0) - (b.name === 'email' ? -1 : 0) ||\n a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }),\n );\n}\n","import type { FileFormatState, IdentifierMetadataForPreference } from './codecs.js';\n\n/**\n * Helper function to get unique identifier name present in a row\n *\n * @param options - Options\n * @param options.row - The current row from CSV file\n * @param options.columnToIdentifier - The column to identifier mapping metadata\n * @returns The unique identifier names present in the row\n */\nexport function getUniquePreferenceIdentifierNamesFromRow({\n row,\n columnToIdentifier,\n}: {\n /** The current row from CSV file */\n row: Record<string, string>;\n /** The current file metadata state */\n columnToIdentifier: FileFormatState['columnToIdentifier'];\n}): (IdentifierMetadataForPreference & {\n /** Column name */\n columnName: string;\n /** Value of the identifier in the row */\n value: string;\n})[] {\n return Object.entries(columnToIdentifier)\n .sort(\n ([, a], [, b]) =>\n (a.name === 'email' ? -1 : 0) - (b.name === 'email' ? -1 : 0) ||\n a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }),\n )\n .filter(([col]) => row[col] && columnToIdentifier[col]!.isUniqueOnPreferenceStore)\n .map(([col, identifier]) => ({\n ...identifier,\n columnName: col,\n value: row[col]!,\n }));\n}\n","import { PreferenceStorePurposeResponse, PreferenceTopicType } from '@transcend-io/privacy-types';\nimport { apply } from '@transcend-io/type-utils';\nimport { splitCsvToList } from '@transcend-io/utils';\n\nimport { PurposeRowMapping } from './codecs.js';\nimport type { PreferenceTopic } from './fetchAllPreferenceTopics.js';\n\n/**\n * Parse an arbitrary object to the Transcend PUT /v1/preference update shape\n * by using a mapping of column names to purpose/preference slugs.\n *\n * `columnToPurposeName` looks like:\n * {\n * 'my_purpose': { purpose: 'Marketing', preference: null, valueMapping: { 'true': true, 'false': false } },\n * 'has_topic_1': { purpose: 'Marketing', preference: 'BooleanPreference1', valueMapping: { 'true': true, 'false': false } },\n * 'has_topic_2': { purpose: 'Marketing', preference: 'SingleSelectPreference', valueMapping: { 'Option 1': 'Value1', 'Option 2': 'Value2' } }\n * }\n *\n * `row` looks like:\n * {\n * 'my_purpose': 'true',\n * 'has_topic_1': 'true',\n * 'has_topic_2': 'Option 1'\n * }\n *\n * OMISSION RULE:\n * - If `valueMapping[row[columnName]]`\n * returns `undefined` or `null`, we **omit** that column entirely (do not set purpose enabled, do not push a preference).\n * - For MultiSelect, **each token** is treated independently: tokens that map to `undefined|null` are skipped;\n * if all tokens are skipped, nothing is pushed.\n * - We still validate **types** for mapped values (e.g., boolean must map to boolean, select must map to string, etc.).\n *\n * NOTE:\n * - Final shape must have `enabled` for every purpose touched (enforced by `apply` below). If you omit all top-level purpose mappings,\n * but emit preferences, this will throw at the end. This preserves the existing “enabled required” contract.\n *\n * @param options - Options\n * @returns The parsed row\n */\nexport function getPreferenceUpdatesFromRow({\n row,\n columnToPurposeName,\n purposeSlugs,\n preferenceTopics,\n}: {\n /** Row to parse */\n row: Record<string, string>;\n /** Mapping from column name to parser config */\n columnToPurposeName: Record<string, PurposeRowMapping>;\n /** The set of allowed purpose slugs */\n purposeSlugs: string[];\n /** The preference topics */\n preferenceTopics: PreferenceTopic[];\n}): {\n [k in string]: Omit<PreferenceStorePurposeResponse, 'purpose'>;\n} {\n // Create a result object to store the parsed preferences\n const result: {\n [k in string]: Partial<PreferenceStorePurposeResponse>;\n } = {};\n\n // Iterate over each column and map to the purpose or preference\n Object.entries(columnToPurposeName).forEach(\n ([columnName, { purpose, preference, valueMapping }]) => {\n // Ensure the purpose is valid\n if (!purposeSlugs.includes(purpose)) {\n throw new Error(`Invalid purpose slug: ${purpose}, expected: ${purposeSlugs.join(', ')}`);\n }\n\n // The raw value from the CSV row for this column\n const rawValue = row[columnName] ?? '';\n\n // Check if parsing a preference or just the top level purpose\n if (preference) {\n const preferenceTopic = preferenceTopics.find(\n (x) => x.slug === preference && x.purpose.trackingType === purpose,\n );\n if (!preferenceTopic) {\n const allowedTopics = preferenceTopics\n .filter((x) => x.purpose.trackingType === purpose)\n .map((x) => x.slug);\n throw new Error(\n `Invalid preference slug: ${preference} for purpose: ${purpose}. ` +\n `Allowed preference slugs for purpose are: ${allowedTopics.join(',')}`,\n );\n }\n\n // Ensure destination array\n if (!result[purpose]) {\n result[purpose] = {\n preferences: [],\n };\n }\n if (!result[purpose].preferences) {\n result[purpose].preferences = [];\n }\n\n // handle each type of preference\n switch (preferenceTopic.type) {\n case PreferenceTopicType.Boolean: {\n const mappedValue = valueMapping[rawValue];\n // Throw error on missing mapping\n if (mappedValue === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for value \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=${preference})`,\n );\n }\n\n // Purposefully missing mapping\n if (mappedValue === null || mappedValue === undefined) {\n return;\n }\n\n // Ensure boolean\n if (typeof mappedValue !== 'boolean') {\n throw new Error(\n `Invalid value for boolean preference: ${preference}, expected boolean, got: ${rawValue}`,\n );\n }\n result[purpose].preferences!.push({\n topic: preference,\n choice: { booleanValue: mappedValue },\n });\n break;\n }\n\n case PreferenceTopicType.Select: {\n const mappedValue = valueMapping[rawValue];\n // Throw error on missing mapping\n if (mappedValue === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for value \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=${preference})`,\n );\n }\n\n // Omit if null\n if (mappedValue === null || mappedValue === undefined) {\n return;\n }\n\n // Ensure string\n if (typeof mappedValue !== 'string') {\n throw new Error(\n `Invalid value for select preference: ${preference}, expected string, got: ${rawValue}`,\n );\n }\n const trimmed = mappedValue.trim() || null;\n\n if (\n trimmed &&\n !preferenceTopic.preferenceOptionValues.map(({ slug }) => slug).includes(trimmed)\n ) {\n throw new Error(\n `Invalid value for select preference: ${preference}, expected one of: ` +\n `${preferenceTopic.preferenceOptionValues\n .map(({ slug }) => slug)\n .join(', ')}, got: ${rawValue}`,\n );\n }\n\n result[purpose].preferences!.push({\n topic: preference,\n choice: { selectValue: trimmed },\n });\n break;\n }\n\n case PreferenceTopicType.MultiSelect: {\n if (typeof rawValue !== 'string') {\n throw new Error(\n `Invalid value for multi select preference: ${preference}, expected string, got: ${rawValue}`,\n );\n }\n\n // IMPORTANT: Do NOT rely on valueMapping[rawValue] for CSV.\n // Split and map per token with the new rule.\n const selectValues = splitCsvToList(rawValue)\n .map((token) => {\n const tokenMapped = valueMapping[token];\n // Throw error on missing mapping\n if (tokenMapped === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for multi select token \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=${preference})`,\n );\n }\n\n // Omit if null\n if (tokenMapped === null || tokenMapped === undefined) {\n return null;\n }\n\n // Ensure string\n if (typeof tokenMapped !== 'string') {\n throw new Error(\n `Invalid value for multi select preference: ${preference}, ` +\n `expected one of: ${preferenceTopic.preferenceOptionValues\n .map(({ slug }) => slug)\n .join(', ')}, got: ${token}`,\n );\n }\n return tokenMapped;\n })\n .filter((x): x is string => x !== null)\n .sort((a, b) => a.localeCompare(b));\n\n // Only push if at least one mapped token survived\n if (selectValues.length > 0) {\n result[purpose].preferences!.push({\n topic: preference,\n choice: { selectValues },\n });\n }\n break;\n }\n\n default:\n throw new Error(`Unknown preference type: ${preferenceTopic.type}`);\n }\n } else {\n // Top-level purpose (no preference)\n const mappedValue = valueMapping[rawValue];\n if (mappedValue === undefined && rawValue !== '') {\n throw new Error(\n `No preference mapping found for value \"${rawValue}\" in column ` +\n `\"${columnName}\" (purpose=${purpose}, preference=∅) ${JSON.stringify(row)}`,\n );\n }\n if (mappedValue === null) {\n return; // Omit if null\n }\n\n if (!result[purpose]) {\n // Top-level purpose: set enabled strictly from mapped boolean\n result[purpose] = { enabled: mappedValue === true };\n } else {\n // Preserve preferences; update enabled\n result[purpose].enabled = mappedValue === true;\n }\n }\n },\n );\n\n // Ensure that enabled is provided for any purpose that appears.\n // (This preserves the prior contract and existing tests.)\n return apply(result, (x, purposeName) => {\n if (typeof x.enabled !== 'boolean') {\n throw new Error(`No mapping provided for purpose.enabled=true/false value: ${purposeName}`);\n }\n return {\n ...x,\n enabled: x.enabled!,\n };\n });\n}\n","import {\n PreferenceQueryResponseItem,\n PreferenceStorePurposeResponse,\n PreferenceTopicType,\n} from '@transcend-io/privacy-types';\n\nimport type { PreferenceTopic } from './fetchAllPreferenceTopics.js';\n\n/**\n * Check if the pending set of updates are exactly the same as the current consent record.\n *\n * @param options - Options\n * @returns Whether the pending updates already exist in the preference store\n */\nexport function checkIfPendingPreferenceUpdatesAreNoOp({\n currentConsentRecord,\n pendingUpdates,\n preferenceTopics,\n}: {\n /** The current consent record */\n currentConsentRecord: PreferenceQueryResponseItem;\n /** The pending updates */\n pendingUpdates: {\n [purposeName in string]: Omit<PreferenceStorePurposeResponse, 'purpose'>;\n };\n /** The preference topic configurations */\n preferenceTopics: PreferenceTopic[];\n}): boolean {\n // Check each update\n return Object.entries(pendingUpdates).every(([purposeName, { preferences = [], enabled }]) => {\n // Ensure the purpose exists\n const currentPurpose = currentConsentRecord.purposes.find(\n (existingPurpose) => existingPurpose.purpose === purposeName,\n );\n\n // Ensure purpose.enabled is in sync\n // Also false if the purpose does not exist\n const enabledIsInSync = !!currentPurpose && currentPurpose.enabled === enabled;\n if (!enabledIsInSync) {\n return false;\n }\n\n // Compare the preferences are in sync\n return preferences.every(\n ({ topic, choice }) =>\n // ensure preferences exist on record\n currentPurpose.preferences &&\n currentPurpose.preferences.find((existingPreference) => {\n // find matching topic\n if (existingPreference.topic !== topic) {\n return false;\n }\n\n // Determine type of preference topic\n const preferenceTopic = preferenceTopics.find(\n (x) => x.slug === topic && x.purpose.trackingType === purposeName,\n );\n if (!preferenceTopic) {\n throw new Error(`Could not find preference topic for ${topic}`);\n }\n\n // Handle comparison based on type\n switch (preferenceTopic.type) {\n case PreferenceTopicType.Boolean:\n return existingPreference.choice.booleanValue === choice.booleanValue;\n case PreferenceTopicType.Select:\n return existingPreference.choice.selectValue === choice.selectValue;\n case PreferenceTopicType.MultiSelect:\n // eslint-disable-next-line no-case-declarations\n const sortedCurrentValues = (existingPreference.choice.selectValues || []).sort();\n // eslint-disable-next-line no-case-declarations\n const sortedNewValues = (choice.selectValues || []).sort();\n return (\n sortedCurrentValues.length === sortedNewValues.length &&\n sortedCurrentValues.every((x, i) => x === sortedNewValues[i])\n );\n default:\n throw new Error(`Unknown preference topic type: ${preferenceTopic.type}`);\n }\n }),\n );\n });\n}\n","import {\n PreferenceQueryResponseItem,\n PreferenceStorePurposeResponse,\n PreferenceTopicType,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\n\nimport type { PreferenceTopic } from './fetchAllPreferenceTopics.js';\n\n/**\n * Check if the pending set of updates will result in a change of\n * value to an existing purpose or preference in the preference store.\n *\n * @param options - Options\n * @returns True if conflict, false if no conflict and just adding new data for first time\n */\nexport function checkIfPendingPreferenceUpdatesCauseConflict({\n currentConsentRecord,\n pendingUpdates,\n preferenceTopics,\n logger,\n}: {\n /** The current consent record */\n currentConsentRecord: PreferenceQueryResponseItem;\n /** The pending updates */\n pendingUpdates: {\n [purposeName in string]: Omit<PreferenceStorePurposeResponse, 'purpose'>;\n };\n /** The preference topic configurations */\n preferenceTopics: PreferenceTopic[];\n /** Optional logger — when provided, conflicts are logged */\n logger?: Logger;\n}): boolean {\n // Check if any update has conflict\n return !!Object.entries(pendingUpdates).find(([purposeName, { preferences = [], enabled }]) => {\n // Ensure the purpose exists\n const currentPurpose = currentConsentRecord.purposes.find(\n (existingPurpose) => existingPurpose.purpose === purposeName,\n );\n\n // If no purpose exists, then it is not a conflict\n if (!currentPurpose) {\n logger?.warn(\n `No existing purpose found for ${purposeName} in consent record for ${currentConsentRecord.userId}.`,\n );\n return false;\n }\n\n // If purpose.enabled value is off, this is a conflict\n if (currentPurpose.enabled !== enabled) {\n logger?.warn(\n `Purpose ${purposeName} enabled value conflict for user ${currentConsentRecord.userId}. ` +\n `Pending Value: ${enabled}, Current Value: ${currentPurpose.enabled}`,\n );\n return true;\n }\n\n // Check if any preferences are out of sync\n return !!preferences.find(({ topic, choice }) => {\n // find matching topic\n const currentPreference = (currentPurpose.preferences || []).find(\n (existingPreference) => existingPreference.topic === topic,\n );\n\n // if no topic exists, no conflict\n if (!currentPreference) {\n logger?.warn(\n `No existing preference found for topic ${topic} in purpose ` +\n `${purposeName} for user ${currentConsentRecord.userId}.`,\n );\n return false;\n }\n\n // Determine type of preference topic\n const preferenceTopic = preferenceTopics.find(\n (x) => x.slug === topic && x.purpose.trackingType === purposeName,\n );\n if (!preferenceTopic) {\n throw new Error(`Could not find preference topic for ${topic}`);\n }\n\n // Handle comparison based on type\n let boolMatch: boolean;\n let selectMatch: boolean;\n switch (preferenceTopic.type) {\n case PreferenceTopicType.Boolean:\n boolMatch = currentPreference.choice.booleanValue !== choice.booleanValue;\n logger?.warn(\n `Preference topic ${topic} boolean value conflict for user ` +\n `${currentConsentRecord.userId}. Expected: ${choice.booleanValue}, ` +\n `Found: ${currentPreference.choice.booleanValue}`,\n );\n return boolMatch;\n case PreferenceTopicType.Select:\n selectMatch = currentPreference.choice.selectValue !== choice.selectValue;\n logger?.warn(\n `Preference topic ${topic} select value conflict for user ` +\n `${currentConsentRecord.userId}. Expected: ${choice.selectValue}, ` +\n `Found: ${currentPreference.choice.selectValue}`,\n );\n return selectMatch;\n case PreferenceTopicType.MultiSelect:\n // eslint-disable-next-line no-case-declarations\n const sortedCurrentValues = (currentPreference.choice.selectValues || []).sort();\n // eslint-disable-next-line no-case-declarations\n const sortedNewValues = (choice.selectValues || []).sort();\n selectMatch =\n sortedCurrentValues.length !== sortedNewValues.length ||\n !sortedCurrentValues.every((x, i) => x === sortedNewValues[i]);\n logger?.warn(\n `Preference topic ${topic} multi-select value conflict for user ` +\n `${currentConsentRecord.userId}. Expected: ${sortedNewValues.join(\n ', ',\n )}, Found: ${sortedCurrentValues.join(', ')}`,\n );\n return selectMatch;\n default:\n throw new Error(`Unknown preference topic type: ${preferenceTopic.type}`);\n }\n });\n });\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\n\n/**\n * Transforms the output of the consent preferences query into a CSV-friendly format.\n *\n * @param input - The input object containing consent preferences data.\n * @param exportIdentifiersWithDelimiter - Delimiter to use when combining multiple identifier values.\n * @returns A record representing the transformed CSV output.\n */\nexport function transformPreferenceRecordToCsv(\n {\n identifiers = [],\n purposes = [],\n metadata = [],\n consentManagement = {},\n system = {\n decryptionStatus: 'DECRYPTED',\n },\n // keep other top-level fields as-is (e.g., partition, timestamp, metadataTimestamp)\n ...topLevel\n }: PreferenceQueryResponseItem,\n exportIdentifiersWithDelimiter: string,\n): Record<string, unknown> {\n // Start with: all other top-level fields + spread system and consentManagement\n const out: Record<string, unknown> = {\n ...topLevel,\n ...system,\n ...consentManagement,\n };\n\n // ── identifiers: each identifier.name -> CSV of values\n if (Array.isArray(identifiers)) {\n const byName = new Map<string, Set<string>>();\n for (const { name, value } of identifiers) {\n if (!byName.has(name)) byName.set(name, new Set());\n if (value) byName.get(name)!.add(value);\n }\n for (const [name, set] of byName.entries()) {\n out[name] = Array.from(set).join(exportIdentifiersWithDelimiter);\n }\n }\n\n // ── metadata: serialize as JSON\n if (Array.isArray(metadata)) {\n out.metadata = JSON.stringify(\n metadata.reduce(\n (acc, { key, value }) => {\n acc[key] = value;\n return acc;\n },\n {} as Record<string, string>,\n ),\n );\n }\n\n // ── purposes:\n // - purpose.slug column => true/false (enabled)\n // - for each preference: purpose.slug_preference.slug => bool | single | CSV (multi)\n if (Array.isArray(purposes)) {\n for (const { purpose, preferences, enabled } of purposes) {\n out[purpose] = Boolean(enabled);\n\n // nested preferences\n if (Array.isArray(preferences)) {\n for (const { topic, choice } of preferences) {\n const col = `${purpose}_${topic}`;\n\n let val: unknown = null;\n\n if (typeof choice.booleanValue === 'boolean') {\n val = choice.booleanValue;\n } else if (choice.selectValue) {\n val = choice.selectValue;\n } else if (Array.isArray(choice.selectValues)) {\n const vs = choice.selectValues.filter((v) => v.length > 0);\n val = vs.join(',');\n } else {\n // no pref value present -> null\n val = null;\n }\n\n out[col] = val;\n }\n }\n }\n }\n\n return out;\n}\n","import { FIVE_MIN_MS } from '@transcend-io/utils';\n\nimport type { ChunkMode, PreferencesQueryFilter } from './types.js';\n\n/**\n * Build chunk windows by splitting [lower, upperExclusive) into up to `maxChunks`\n * equal-sized ranges, with a minimum chunk span of 5 minutes. Boundaries are snapped\n * once at the start to the nearest 5-minute boundary for stability.\n *\n * Each returned window is already \"half-open\" for an *inclusive* backend:\n * we subtract 1ms from the exclusive end so adjacent chunks do not overlap.\n *\n * Example (timestamp mode): [10:00, 12:00) → { after=10:00:00.000Z, before=11:59:59.999Z }\n *\n * @param mode - 'timestamp' or 'updated'\n * @param lower - Lower bound (inclusive)\n * @param upperExclusive - Upper bound (exclusive)\n * @param maxChunks - Maximum number of chunks to create\n * @returns Array of chunked preference query filters\n */\nexport function buildConsentChunks(\n mode: ChunkMode,\n lower: Date,\n upperExclusive: Date,\n maxChunks = 5000,\n): Array<PreferencesQueryFilter> {\n const totalMs = Math.max(0, upperExclusive.getTime() - lower.getTime());\n if (totalMs === 0) return [];\n\n // Snap only the starting boundary to the nearest 5-minute boundary.\n // We avoid re-snapping every step to prevent cumulative drift.\n const seriesStart = new Date(Math.floor(lower.getTime() / FIVE_MIN_MS) * FIVE_MIN_MS);\n\n // Compute base chunk size (ceil to ensure ≤ maxChunks), enforced ≥ 5m.\n const rawChunkMs = Math.ceil(totalMs / Math.max(1, maxChunks));\n const chunkMs = Math.max(FIVE_MIN_MS, rawChunkMs);\n\n // Number of chunks needed to cover [seriesStart, upperExclusive)\n const count = Math.ceil((upperExclusive.getTime() - seriesStart.getTime()) / chunkMs);\n\n const chunks: PreferencesQueryFilter[] = [];\n\n for (let i = 0; i < count; i += 1) {\n const startMs = seriesStart.getTime() + i * chunkMs;\n const endExclusiveMs = Math.min(upperExclusive.getTime(), startMs + chunkMs);\n\n // Convert exclusive end to inclusive end for an inclusive backend: -1ms.\n const endInclusiveMs = endExclusiveMs - 1;\n\n // Guard: in degenerate cases (shouldn’t happen with the math above), clamp.\n const safeEndMs = Math.max(startMs, endInclusiveMs);\n\n const afterIso = new Date(startMs).toISOString();\n const beforeIso = new Date(safeEndMs).toISOString();\n\n if (mode === 'timestamp') {\n chunks.push({\n timestampAfter: afterIso,\n timestampBefore: beforeIso,\n });\n } else {\n chunks.push({\n system: {\n updatedAfter: afterIso,\n updatedBefore: beforeIso,\n },\n });\n }\n }\n\n return chunks;\n}\n","import type { ChunkMode, PreferencesQueryFilter } from './types.js';\n\n/**\n * Decide which dimension to chunk on: 'timestamp' if timestamps provided, otherwise 'updated'\n *\n * @param filterBy - Filter to examine\n * @returns Chosen chunk mode\n */\nexport function pickConsentChunkMode(filterBy: PreferencesQueryFilter): ChunkMode {\n const hasTimestamp = !!filterBy.timestampAfter || !!filterBy.timestampBefore;\n return hasTimestamp ? 'timestamp' : 'updated';\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\n\nimport type { ChunkMode } from './types.js';\n\n/**\n * Get the comparison instant for sorting based on the chosen dimension.\n *\n * @param mode - Chunking mode\n * @param item - Preference item\n * @returns date\n */\nexport function getComparisonTimeForRecord(\n mode: ChunkMode,\n item: PreferenceQueryResponseItem,\n): Date {\n if (mode === 'timestamp') {\n return new Date(item.timestamp);\n }\n // mode === 'updated'\n return item.system?.updatedAt ? new Date(item.system.updatedAt) : new Date();\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse, type PreferencesQueryFilter } from './types.js';\n\n/**\n * Async generator over pages for a given filter\n *\n * @param sombra - Sombra Got instance\n * @param partition - Partition key\n * @param filter - Query filter\n * @param pageSize - Number of items per page\n * @param logger - Logger for retries\n * @yields Pages of PreferenceQueryResponseItem\n */\nexport async function* iterateConsentPages(\n sombra: Got,\n partition: string,\n filter: PreferencesQueryFilter,\n pageSize: number,\n logger: Logger,\n): AsyncGenerator<PreferenceQueryResponseItem[], void, void> {\n let cursor: string | undefined;\n\n while (true) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const body: any = { limit: pageSize };\n if (filter && Object.keys(filter).length) body.filter = filter;\n if (cursor) body.cursor = cursor;\n\n const resp = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partition}/query`, {\n json: body,\n })\n .json(),\n {\n logger,\n onRetry: (attempt, _error, message) => {\n logger.warn(`Retry attempt ${attempt} for iterateConsentPages due to error: ${message}`);\n },\n },\n );\n\n const { nodes, cursor: nextCursor } = decodeCodec(ConsentPreferenceResponse, resp);\n if (!nodes?.length) break;\n\n yield nodes;\n\n if (!nextCursor) break;\n cursor = nextCursor;\n }\n}\n","import { decodeCodec } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse, type PreferencesQueryFilter, type ChunkMode } from './types.js';\n\n/**\n * Probe window: does it contain any records? Uses the given mode.\n *\n * @param sombra - Sombra\n * @param options - Options\n * @returns True if any records exist in the given window\n */\nexport async function consentWindowHasAny(\n sombra: Got,\n {\n partition,\n mode,\n baseFilter,\n afterISO,\n beforeISO,\n logger = NOOP_LOGGER,\n }: {\n /** Partition */\n partition: string;\n /** Chunking mode */\n mode: ChunkMode;\n /** Base filter */\n baseFilter: PreferencesQueryFilter;\n /** After ISO date */\n afterISO: string;\n /** Before ISO date */\n beforeISO: string;\n logger?: Logger;\n },\n): Promise<boolean> {\n const filter: PreferencesQueryFilter =\n mode === 'timestamp'\n ? {\n ...baseFilter,\n timestampAfter: afterISO,\n timestampBefore: beforeISO,\n system: baseFilter.system,\n }\n : {\n ...baseFilter,\n timestampAfter: undefined,\n timestampBefore: undefined,\n system: {\n ...baseFilter.system,\n updatedAfter: afterISO,\n updatedBefore: beforeISO,\n },\n };\n const resp = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partition}/query`, {\n json: { limit: 1, filter },\n })\n .json(),\n {\n logger,\n onRetry: (attempt, error, message) => {\n logger.warn(`Retry attempt ${attempt} for consentWindowHasAny due to error: ${message}`);\n },\n },\n );\n\n const { nodes } = decodeCodec(ConsentPreferenceResponse, resp);\n return Array.isArray(nodes) && nodes.length > 0;\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { startOfUtcDay, DAY_MS, type Logger } from '@transcend-io/utils';\n/* eslint-disable max-lines */\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { getComparisonTimeForRecord } from './getComparisonTimeForRecord.js';\nimport { iterateConsentPages } from './iterateConsentPages.js';\nimport { pickConsentChunkMode } from './pickConsentChunkMode.js';\nimport { type ChunkMode, type PreferencesQueryFilter } from './types.js';\n\n/**\n * Get after/before bounds from filter for the given mode\n *\n * @param mode - Chunking mode\n * @param filterBy - Filter to examine\n * @returns after/before dates\n */\nexport function getBoundsFromConsentFilter(\n mode: ChunkMode,\n filterBy: PreferencesQueryFilter,\n): {\n /** After date */\n after?: Date;\n /** Before date */\n before?: Date;\n} {\n if (mode === 'timestamp') {\n return {\n after: filterBy.timestampAfter ? new Date(filterBy.timestampAfter) : undefined,\n before: filterBy.timestampBefore ? new Date(filterBy.timestampBefore) : undefined,\n };\n }\n const u = filterBy.system ?? {};\n return {\n after: u.updatedAfter ? new Date(u.updatedAfter) : undefined,\n before: u.updatedBefore ? new Date(u.updatedBefore) : undefined,\n };\n}\n\n/**\n * Merge base filter with a \"before\" bound (without mixing dimensions).\n *\n * @param mode - Chunking mode\n * @param base - Base filter to augment\n * @param beforeISO - ISO timestamp to apply as the exclusive *Before bound for the chosen dimension\n * @returns New filter with the appropriate *Before constraint applied\n */\nfunction withBeforeBound(\n mode: ChunkMode,\n base: PreferencesQueryFilter,\n beforeISO?: string,\n): PreferencesQueryFilter {\n if (mode === 'timestamp') {\n return {\n ...base,\n timestampBefore: beforeISO ?? base.timestampBefore,\n };\n }\n return {\n ...base,\n system: {\n ...base.system,\n ...(beforeISO ? { updatedBefore: beforeISO } : {}),\n },\n // ensure we don't mix dimensions\n timestampAfter: undefined,\n timestampBefore: undefined,\n };\n}\n\n/**\n * Fetch a single record (or null) with the given filter.\n *\n * @param sombra - Got instance configured for Sombra API\n * @param partition - Preference Store partition id\n * @param filter - Query filter to use (page size internally forced to 1)\n * @param logger - Logger\n * @returns The first record or null if none\n */\nasync function fetchOne(\n sombra: Got,\n partition: string,\n filter: PreferencesQueryFilter,\n logger: Logger,\n): Promise<PreferenceQueryResponseItem | null> {\n logger.info(`Single-record probe with filter: ${JSON.stringify(filter)}`);\n const it = iterateConsentPages(sombra, partition, filter, /* pageSize */ 1, logger);\n const res = await it.next();\n if (res.done || !res.value || res.value.length === 0) {\n logger.info('Probe result: no record');\n return null;\n }\n const item = res.value[0]!;\n logger.info(\n `Probe result: found record at ${getComparisonTimeForRecord(\n pickConsentChunkMode(filter),\n item,\n ).toISOString()}`,\n );\n return item;\n}\n\n/**\n * Robust earliest-day search (UTC):\n * 1) Anchor at the newest record (single-record probe).\n * 2) Exponential “jump back” using seeds (1d, 7d, 30d) then doubling (60d, 120d, 240d, …)\n * to cross into an empty region and establish a lower empty bound.\n * 3) **Exponential forward-from-empty**: gallop forward from the empty bound toward the last-found\n * to land close to the frontier quickly.\n * 4) Tighten with a short binary search on time using single-record probes.\n *\n * (Implementation note: preserves the public signature and docs while improving efficiency.)\n *\n * @param sombra - Sombra\n * @param opts - Options\n * @returns Earliest day with data (UTC start-of-day)\n */\nexport async function findEarliestDayWithData(\n sombra: Got,\n opts: {\n /** Partition */\n partition: string;\n /** Chunking mode */\n mode: ChunkMode;\n /** Base filter */\n baseFilter: PreferencesQueryFilter;\n /** Optional safety cap in days to avoid unbounded lookback (default ~10 years) */\n maxLookbackDays?: number;\n /** Logger */\n logger?: Logger;\n },\n): Promise<Date> {\n const { partition, mode, baseFilter, maxLookbackDays = 3650, logger = NOOP_LOGGER } = opts;\n\n // 1) Find newest record (anchors our backtracking).\n const newest = await fetchOne(sombra, partition, withBeforeBound(mode, baseFilter), logger);\n if (!newest) {\n logger.info('No records found; defaulting earliest day to today.');\n return startOfUtcDay(new Date());\n }\n const newestInstant = getComparisonTimeForRecord(mode, newest);\n logger.info(`Newest instant: ${newestInstant.toISOString()}`);\n\n // 2) Exponential jump back to find an empty region.\n const seedSteps = [1, 7, 30] as const; // days\n let stepDaysIdx = 0;\n let stepMs = seedSteps[0]! * DAY_MS;\n\n let lastFoundInstant = newestInstant; // last instant we *could* find a record before\n let emptyBeforeInstant: Date | null = null; // first bound that yielded no results\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const probeBound =\n stepDaysIdx < seedSteps.length\n ? new Date(newestInstant.getTime() - seedSteps[stepDaysIdx]! * DAY_MS)\n : new Date(newestInstant.getTime() - stepMs);\n\n // stop if we exceeded lookback cap\n const daysSince =\n (startOfUtcDay(new Date()).getTime() - startOfUtcDay(probeBound).getTime()) / DAY_MS;\n if (daysSince > maxLookbackDays) {\n logger.warn(\n `Exponential jump exceeded maxLookbackDays=${maxLookbackDays}. Using current bounds.`,\n );\n emptyBeforeInstant = probeBound;\n break;\n }\n\n logger.info(\n `Probing before=${probeBound.toISOString()} (jump step ${\n stepDaysIdx < seedSteps.length\n ? `${seedSteps[stepDaysIdx]!}d`\n : `${Math.round(stepMs / DAY_MS)}d`\n })…`,\n );\n\n const hit = await fetchOne(\n sombra,\n partition,\n withBeforeBound(mode, baseFilter, probeBound.toISOString()),\n logger,\n );\n\n if (hit) {\n lastFoundInstant = getComparisonTimeForRecord(mode, hit);\n logger.info(\n `Found older record at ${lastFoundInstant.toISOString()} — continue jumping back.`,\n );\n // advance step\n if (stepDaysIdx < seedSteps.length - 1) {\n stepDaysIdx += 1;\n stepMs = seedSteps[stepDaysIdx]! * DAY_MS;\n } else if (stepDaysIdx === seedSteps.length - 1) {\n stepDaysIdx += 1; // switch to doubling mode\n stepMs = seedSteps[seedSteps.length - 1]! * 2 * DAY_MS; // start at 60d\n } else {\n stepMs *= 2;\n }\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // crossed into an empty zone — remember this bound\n emptyBeforeInstant = probeBound;\n logger.info(`No record before ${probeBound.toISOString()} — established empty lower bound.`);\n break;\n }\n\n // Guard: if for some reason empty bound wasn't set, synthesize one “just before” lastFound.\n if (!emptyBeforeInstant) {\n emptyBeforeInstant = new Date(lastFoundInstant.getTime() - DAY_MS);\n }\n\n // 3) Exponential forward-from-empty toward the found frontier.\n // This “gallop” reduces the span dramatically before binary search.\n // We keep moving the empty bound forward with exponentially growing steps\n // until we get a hit; then we shrink onto that hit instant.\n let lo = emptyBeforeInstant; // known EMPTY (no data before this bound)\n let hi = lastFoundInstant; // known FOUND (there is data before this instant)\n let fwdStep = Math.max(DAY_MS, Math.floor((hi.getTime() - lo.getTime()) / 64)); // start small-ish\n logger.info(\n `Exponential forward-from-empty start: empty=${lo.toISOString()} found=${hi.toISOString()} step=${Math.round(\n fwdStep / DAY_MS,\n )}d`,\n );\n\n // Do a few gallop iterations (bounded so we don't loop forever if distribution is dense)\n for (let i = 0; i < 8; i += 1) {\n const probe = new Date(lo.getTime() + fwdStep);\n if (probe.getTime() >= hi.getTime()) break;\n\n logger.info(`Forward gallop probe before=${probe.toISOString()}…`);\n const hit = await fetchOne(\n sombra,\n partition,\n withBeforeBound(mode, baseFilter, probe.toISOString()),\n logger,\n );\n\n if (hit) {\n // We crossed into data — tighten hi to the actual hit instant.\n hi = getComparisonTimeForRecord(mode, hit);\n logger.info(`Gallop hit at ${hi.toISOString()} — tightening found bound. Next step halves.`);\n fwdStep = Math.max(DAY_MS, Math.floor(fwdStep / 2));\n } else {\n // Still empty up to probe — advance lo and double the step.\n lo.setTime(probe.getTime());\n logger.info(`Gallop miss — advancing empty bound to ${lo.toISOString()}. Next step doubles.`);\n fwdStep = Math.min(hi.getTime() - lo.getTime(), fwdStep * 2);\n if (fwdStep < DAY_MS) fwdStep = DAY_MS;\n }\n\n if (hi.getTime() - lo.getTime() <= DAY_MS) break;\n }\n\n // 4) Finish with a short binary search between [lo (empty), hi (found)].\n while (hi.getTime() - lo.getTime() > DAY_MS) {\n const mid = new Date(lo.getTime() + Math.floor((hi.getTime() - lo.getTime()) / 2));\n logger.info(`Binary probe before=${mid.toISOString()}…`);\n\n const hit = await fetchOne(\n sombra,\n partition,\n withBeforeBound(mode, baseFilter, mid.toISOString()),\n logger,\n );\n\n if (hit) {\n const when = getComparisonTimeForRecord(mode, hit);\n logger.info(`Binary probe found record at ${when.toISOString()}.`);\n hi = when; // there is data before mid -> earliest could be even earlier\n } else {\n logger.info('Binary probe found no record.');\n lo = mid; // still empty -> move low up\n }\n }\n\n const earliestDay = startOfUtcDay(hi);\n logger.info(\n `Earliest day (UTC) resolved to ${earliestDay.toISOString()} (instant ≈ ${hi.toISOString()}).`,\n );\n return earliestDay;\n}\n\n/**\n * Find latest day with data using exponential growth forward from earliest (UTC day math).\n *\n * (Implementation note: per your request, we now fetch a single newest record to infer the latest day.)\n *\n * @param sombra - Sombra\n * @param opts - Options\n * @returns Latest day with data\n */\nexport async function findLatestDayWithData(\n sombra: Got,\n opts: {\n /** Partition */\n partition: string;\n /** Chunking mode */\n mode: ChunkMode;\n /** Base filter */\n baseFilter: PreferencesQueryFilter;\n /** Earliest date */\n earliest: Date; // inclusive day start\n /** Logger */\n logger?: Logger;\n },\n): Promise<Date> {\n const { partition, mode, baseFilter, logger = NOOP_LOGGER } = opts;\n\n logger.info('Latest-day discovery: probing newest record…');\n const latest = await fetchOne(sombra, partition, withBeforeBound(mode, baseFilter), logger);\n if (!latest) {\n logger.info('No records found at all; defaulting latest day to today.');\n return startOfUtcDay(new Date());\n }\n\n const when = getComparisonTimeForRecord(mode, latest);\n logger.info(`Newest record instant is ${when.toISOString()}.`);\n\n const latestDay = startOfUtcDay(when);\n logger.info(\n `Latest day (UTC) resolved to ${latestDay.toISOString()} from instant ${when.toISOString()}.`,\n );\n\n return latestDay;\n}\n/* eslint-enable max-lines */\n","import { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse, type PreferencesQueryFilter } from './types.js';\n\n/**\n * Fetch consent preferences for the managed consent database (new query endpoint)\n *\n * Uses POST /v1/preferences/{partition}/query with cursor pagination.\n *\n * If `onItems` is provided, this streams pages to the callback and does not\n * accumulate results in memory. If omitted, the function returns all items.\n *\n * @param sombra - Sombra instance (must include auth headers)\n * @param options - Query options\n * @returns All nodes (only when onItems is not provided)\n */\nexport async function fetchConsentPreferences(\n sombra: Got,\n {\n partition,\n filterBy = {},\n limit = 50,\n onItems,\n logger = NOOP_LOGGER,\n }: {\n /** Partition key to fetch (moved to URL path on new endpoint) */\n partition: string;\n /** Query filter (wrapped under \"filter\" in request body) */\n filterBy?: PreferencesQueryFilter;\n /** Number of users per page (1–50 per API spec) */\n limit?: number;\n /** Optional streaming sink; if provided, pages are not accumulated */\n onItems?: (items: PreferenceQueryResponseItem[]) => Promise<void> | void;\n logger?: Logger;\n },\n): Promise<PreferenceQueryResponseItem[]> {\n const collected: PreferenceQueryResponseItem[] = [];\n\n // Cursor-based pagination per new endpoint\n let cursor: string | undefined;\n\n // Build the filter payload, omitting empty filter\n const hasFilter =\n filterBy &&\n (Object.keys(filterBy).length > 0 ||\n (filterBy.system && Object.keys(filterBy.system).length > 0));\n\n // Enforce API max (defensive; backend also validates)\n const pageSize = Math.max(1, Math.min(50, limit ?? 50));\n\n // Keep fetching until no cursor is returned\n // (The API returns an opaque cursor string for the next page)\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const body: {\n /** Filter by user identifiers (new shape) */\n filter?: PreferencesQueryFilter;\n /** Cursor for pagination */\n cursor?: string;\n /** Number of records per page */\n limit: number;\n } = { limit: pageSize };\n\n if (hasFilter) {\n body.filter = filterBy;\n }\n if (cursor) {\n body.cursor = cursor;\n }\n\n const response = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partition}/query`, {\n json: body,\n })\n .json(),\n {\n logger,\n onRetry: (attempt, _error, message) => {\n logger.warn(\n `Retry attempt ${attempt} for fetchConsentPreferences due to error: ${message}`,\n );\n },\n },\n );\n\n const { nodes, cursor: nextCursor } = decodeCodec(ConsentPreferenceResponse, response);\n\n if (!nodes || nodes.length === 0) {\n break;\n }\n\n if (onItems) {\n await onItems(nodes);\n } else {\n collected.push(...nodes);\n }\n\n if (!nextCursor) {\n break;\n }\n cursor = nextCursor;\n }\n\n return onItems ? [] : collected;\n}\n","import { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { map, type Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\nimport { chunk } from 'lodash-es';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { ConsentPreferenceResponse } from './types.js';\n\n/**\n * Grab the current consent preference values for a list of identifiers\n *\n * @param sombra - Backend to make API call to\n * @param options - Options\n * @returns Plaintext context information\n */\nexport async function getPreferencesForIdentifiers(\n sombra: Got,\n {\n identifiers,\n partitionKey,\n skipLogging = false,\n concurrency = 40,\n logger = NOOP_LOGGER,\n onProgress,\n }: {\n /** The list of identifiers to look up */\n identifiers: {\n /** The value of the identifier */\n value: string;\n }[];\n /** The partition key to look up */\n partitionKey: string;\n /** Whether to skip logging */\n skipLogging?: boolean;\n /** Concurrency for requests (default 40) */\n concurrency?: number;\n /** Logger */\n logger?: Logger;\n /** Optional progress callback (completed count, total identifiers) */\n onProgress?: (completed: number, total: number) => void;\n },\n): Promise<PreferenceQueryResponseItem[]> {\n const results: PreferenceQueryResponseItem[] = [];\n const groupedIdentifiers = chunk(identifiers, 100);\n\n const t0 = new Date().getTime();\n\n let total = 0;\n await map(\n groupedIdentifiers,\n async (group) => {\n const rawResult = await withTransientRetry(\n 'Preference Query',\n () =>\n sombra\n .post(`v1/preferences/${partitionKey}/query`, {\n json: {\n filter: { identifiers: group },\n limit: group.length,\n },\n })\n .json(),\n {\n logger,\n onRetry: (attempt, _err, msg) => {\n logger.warn(\n `[RETRY] group size=${group.length} partition=${partitionKey} attempt=${attempt}: ${msg}`,\n );\n },\n },\n );\n\n const result = decodeCodec(ConsentPreferenceResponse, rawResult);\n results.push(...result.nodes);\n total += group.length;\n onProgress?.(total, identifiers.length);\n },\n {\n concurrency,\n },\n );\n\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n if (!skipLogging) {\n logger.info(`Completed download in \"${totalTime / 1000}\" seconds.`);\n }\n\n return results;\n}\n","import type { PreferenceQueryResponseItem } from '@transcend-io/privacy-types';\nimport { addDaysUtc, clampPageSize, map as pmap, type Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\n\nimport { NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { buildConsentChunks } from './buildConsentChunks.js';\nimport {\n findEarliestDayWithData,\n findLatestDayWithData,\n getBoundsFromConsentFilter,\n} from './discoverConsentWindow.js';\nimport { iterateConsentPages } from './iterateConsentPages.js';\nimport { pickConsentChunkMode } from './pickConsentChunkMode.js';\nimport { type PreferencesQueryFilter, type ChunkMode } from './types.js';\n\n/**\n * Merge baseFilter with a window filter, taking care not to mix timestamp/updated fields improperly.\n *\n * @param mode - The chunking mode\n * @param base - The base filter\n * @param window - The per-chunk window filter\n * @returns merged filter\n */\nfunction mergeFilter(\n mode: ChunkMode,\n base: PreferencesQueryFilter,\n window: PreferencesQueryFilter,\n): PreferencesQueryFilter {\n if (mode === 'timestamp') {\n return {\n ...base,\n timestampAfter: window.timestampAfter ?? base.timestampAfter,\n timestampBefore: window.timestampBefore ?? base.timestampBefore,\n // ensure we don't pass `system.*` when chunking by timestamp\n system: undefined,\n };\n }\n // mode === 'updated'\n return {\n ...base,\n system: {\n ...base.system,\n ...(window.system?.updatedAfter ? { updatedAfter: window.system.updatedAfter } : {}),\n ...(window.system?.updatedBefore ? { updatedBefore: window.system.updatedBefore } : {}),\n },\n // Ensure we don't mix dimensions\n timestampAfter: undefined,\n timestampBefore: undefined,\n };\n}\n\n/**\n * High-level chunked fetch with optional progress callback.\n *\n * If an `onItems` callback is provided, pages are streamed to the callback\n * as they are fetched (no accumulation in memory). If no callback is provided,\n * the function returns all items (legacy behavior).\n *\n * @param sombra - Got instance\n * @param options - Options\n * @returns preference items (only if onItems is not provided)\n */\nexport async function fetchConsentPreferencesChunked(\n sombra: Got,\n {\n partition,\n filterBy = {},\n limit = 50,\n windowConcurrency = 25,\n maxChunks = 5000,\n maxLookbackDays = 3650,\n onItems,\n logger = NOOP_LOGGER,\n onProgress,\n }: {\n /** Partition */\n partition: string;\n /** Filter by preferences */\n filterBy?: PreferencesQueryFilter;\n /** Limit number of results (page size) */\n limit?: number;\n /** Window concurrency */\n windowConcurrency?: number;\n /** Max chunks */\n maxChunks?: number; // up to N chunks; min 1 hour per chunk\n /** Max lookback days for discovering bounds */\n maxLookbackDays?: number;\n /** Optional streaming sink; if provided, items are not accumulated */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onItems?: (items: PreferenceQueryResponseItem[]) => Promise<any> | any;\n /** Logger */\n logger?: Logger;\n /** Optional progress: completed chunks, total chunks, records fetched so far */\n onProgress?: (completed: number, total: number, fetched: number) => void;\n },\n): Promise<PreferenceQueryResponseItem[]> {\n const mode: ChunkMode = pickConsentChunkMode(filterBy);\n logger.info(\n `Fetching consent preferences in chunks by ${\n mode === 'timestamp' ? 'timestamp' : 'system.updatedAt'\n }...`,\n );\n\n // Resolve / discover bounds (UTC)\n let { after, before } = getBoundsFromConsentFilter(mode, filterBy);\n logger.info(\n `Initial bounds: after=${after?.toISOString() ?? 'undefined'} before=${\n before?.toISOString() ?? 'undefined'\n }`,\n );\n\n if (!after || !before) {\n if (!after) {\n logger.info(`Discovering earliest day with data for partition ${partition}...`);\n after = await findEarliestDayWithData(sombra, {\n partition,\n mode,\n baseFilter: filterBy,\n maxLookbackDays,\n logger,\n });\n logger.info(`Discovered earliest day with data: ${after.toISOString()}`);\n }\n if (!before) {\n logger.info(`Discovering latest day with data for partition ${partition}...`);\n const latestDay = await findLatestDayWithData(sombra, {\n partition,\n mode,\n baseFilter: filterBy,\n earliest: after,\n logger,\n });\n // Exclusive upper bound = latest day start + 1 day (UTC)\n before = addDaysUtc(latestDay, 1);\n logger.info(`Discovered latest day with data: ${latestDay.toISOString()}`);\n }\n }\n\n logger.info(`Final bounds (UTC): after=${after.toISOString()} before=${before.toISOString()}`);\n\n // Build up to `maxChunks` chunks, min 1 hour each\n const chunks = buildConsentChunks(mode, after, before, maxChunks);\n\n logger.info(\n `Fetching consent preferences from partition ${partition} in ${chunks.length} chunks...`,\n );\n\n let completed = 0; // finished chunks (out-of-order)\n let fetched = 0; // raw records counter\n\n const t0 = Date.now();\n const pageSize = clampPageSize(limit);\n\n // If we are streaming, do not accumulate everything in memory.\n const out: PreferenceQueryResponseItem[] = [];\n\n await pmap(\n chunks.map((windowFilter, idx) => ({ windowFilter, idx })),\n async ({ windowFilter }) => {\n const filter = mergeFilter(mode, filterBy, windowFilter);\n\n // Stream this chunk page-by-page\n for await (const page of iterateConsentPages(sombra, partition, filter, pageSize, logger)) {\n fetched += page.length;\n onProgress?.(completed, chunks.length, fetched);\n\n if (onItems) {\n await onItems(page);\n } else {\n out.push(...page);\n }\n }\n\n completed += 1;\n onProgress?.(completed, chunks.length, fetched);\n },\n { concurrency: Math.max(1, windowConcurrency) },\n );\n\n onProgress?.(completed, chunks.length, fetched);\n\n logger.info(\n `Fetched ${fetched} consent preference records from partition ${partition} in ${\n (Date.now() - t0) / 1000\n }s.`,\n );\n\n return onItems ? [] : out;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { fetchAllIdentifiers, type Identifier } from '../data-inventory/fetchAllIdentifiers.js';\nimport { fetchAllPreferenceTopics, type PreferenceTopic } from './fetchAllPreferenceTopics.js';\nimport { fetchAllPurposes, type Purpose } from './fetchAllPurposes.js';\n\nexport interface PreferenceUploadReferenceData {\n /** List of purposes in the organization */\n purposes: Purpose[];\n /** List of preference topics in the organization */\n preferenceTopics: PreferenceTopic[];\n /** List of identifiers in the organization */\n identifiers: Identifier[];\n}\n\n/**\n * Load all required reference data for an upload run.\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Reference data arrays\n */\nexport async function loadReferenceData(\n client: GraphQLClient,\n { logger }: { logger: Logger },\n): Promise<PreferenceUploadReferenceData> {\n const [purposes, preferenceTopics, identifiers] = await Promise.all([\n fetchAllPurposes(client, { logger }),\n fetchAllPreferenceTopics(client, { logger }),\n fetchAllIdentifiers(client, { logger }),\n ]);\n return { purposes, preferenceTopics, identifiers };\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const PREFERENCE_OPTION_VALUES: DocumentNode = parse(gql`\n query TranscendCliPreferenceOptionValues($first: Int!, $offset: Int!) {\n preferenceOptionValues(first: $first, offset: $offset) {\n nodes {\n id\n title {\n id\n defaultMessage\n }\n slug\n }\n }\n }\n`);\n\nexport const CREATE_OR_UPDATE_PREFERENCE_OPTION_VALUES: DocumentNode = parse(gql`\n mutation TranscendCliCreateOrUpdatePreferenceOptionValues(\n $input: CreateOrUpdatePreferenceOptionValuesInput!\n ) {\n createOrUpdatePreferenceOptionValues(input: $input) {\n preferenceOptionValues {\n id\n slug\n }\n }\n }\n`);\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PREFERENCE_OPTION_VALUES } from './gqls/preferenceOptionValues.js';\n\nexport interface PreferenceOptionValue {\n /** ID of preference option value */\n id: string;\n /** Slug of preference option value */\n slug: string;\n /** Title of preference option value */\n title: {\n /** ID */\n id: string;\n /** Default message */\n defaultMessage: string;\n };\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all preference option values in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All preference option values in the organization\n */\nexport async function fetchAllPreferenceOptionValues(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PreferenceOptionValue[]> {\n const { logger } = options;\n const preferenceOptionValues: PreferenceOptionValue[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n preferenceOptionValues: { nodes },\n } = await makeGraphQLRequest<{\n /** Preference option values */\n preferenceOptionValues: {\n /** List */\n nodes: PreferenceOptionValue[];\n };\n }>(client, PREFERENCE_OPTION_VALUES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n preferenceOptionValues.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return preferenceOptionValues.sort((a, b) => a.slug.localeCompare(b.slug));\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { type PreferenceOptionValueInput } from './codecs.js';\nimport {\n fetchAllPreferenceOptionValues,\n type PreferenceOptionValue,\n} from './fetchAllPreferenceOptionValues.js';\nimport { CREATE_OR_UPDATE_PREFERENCE_OPTION_VALUES } from './gqls/preferenceOptionValues.js';\n\nconst MAX_BATCH_SIZE = 50;\n\n/**\n * Create or update preference option values\n *\n * @param client - GraphQL client\n * @param optionValues - Preference option values paired with existing IDs\n * @param options - Options\n * @returns Created/updated preference option values\n */\nexport async function createOrUpdatePreferenceOptionValues(\n client: GraphQLClient,\n optionValues: [PreferenceOptionValueInput, string | undefined][],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PreferenceOptionValue[]> {\n const { logger } = options;\n const results: PreferenceOptionValue[] = [];\n\n await mapSeries(chunk(optionValues, MAX_BATCH_SIZE), async (batch) => {\n const result = await makeGraphQLRequest<{\n /** createOrUpdatePreferenceOptionValues mutation */\n createOrUpdatePreferenceOptionValues: {\n /** Preference option values */\n preferenceOptionValues: PreferenceOptionValue[];\n };\n }>(client, CREATE_OR_UPDATE_PREFERENCE_OPTION_VALUES, {\n variables: {\n input: {\n preferenceOptionValues: batch.map(([optionValue, id]) =>\n id\n ? { id, title: optionValue.title }\n : { slug: optionValue.slug, title: optionValue.title },\n ),\n },\n },\n logger,\n });\n results.push(...result.createOrUpdatePreferenceOptionValues.preferenceOptionValues);\n });\n\n return results;\n}\n\n/**\n * Sync the preference option values\n *\n * @param client - GraphQL client\n * @param optionValues - Preference option values\n * @param options - Options\n * @returns True if run without error\n */\nexport async function syncPreferenceOptionValues(\n client: GraphQLClient,\n optionValues: PreferenceOptionValueInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger } = options;\n logger?.info(`Syncing \"${optionValues.length}\" preference option values...`);\n\n const existing = await fetchAllPreferenceOptionValues(client, { logger });\n const optionValueBySlug = keyBy(existing, 'slug');\n\n const changedOptionValues = optionValues.filter((optionValueInput) => {\n const found = optionValueBySlug[optionValueInput.slug];\n return !found || found.title.defaultMessage !== optionValueInput.title;\n });\n\n if (changedOptionValues.length === 0) {\n logger?.info(`Skipping \"${optionValues.length}\" unchanged preference option values`);\n return true;\n }\n\n try {\n await createOrUpdatePreferenceOptionValues(\n client,\n changedOptionValues.map((optionValueInput) => [\n optionValueInput,\n optionValueBySlug[optionValueInput.slug]?.id,\n ]),\n { logger },\n );\n logger?.info(\n `Successfully synced \"${changedOptionValues.length}\" preference option values (${optionValues.length - changedOptionValues.length} unchanged skipped)!`,\n );\n return true;\n } catch (err) {\n logger?.error(`Failed to sync preference option values! - ${(err as Error).message}`);\n return false;\n }\n}\n","import { KnownDefaultPurpose, type UserPrivacySignalEnum } from '@transcend-io/airgap.js-types';\nimport { type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { type PurposeInput } from './codecs.js';\nimport { fetchAllPurposes, type Purpose } from './fetchAllPurposes.js';\nimport { CREATE_PURPOSE, UPDATE_PURPOSE } from './gqls/purpose.js';\n\nexport type { PurposeInput } from './codecs.js';\n\nexport interface SyncPurposesResult {\n /** Whether every purpose synced without error */\n success: boolean;\n /** Map from purpose trackingType to its ID (used to sync nested preference topics) */\n purposeIdByTrackingType: Record<string, string>;\n}\n\nconst DEFAULT_PURPOSE_TRACKING_TYPES: ReadonlySet<string> = new Set(\n Object.values(KnownDefaultPurpose),\n);\n\n/**\n * Whether a purpose tracking type is a built-in default purpose.\n *\n * @param trackingType - Purpose tracking type slug\n * @returns True when the purpose is a built-in default\n */\nfunction isDefaultPurposeTrackingType(trackingType: string): boolean {\n return DEFAULT_PURPOSE_TRACKING_TYPES.has(trackingType);\n}\n\n/**\n * Compare opt-out signal lists regardless of order.\n *\n * @param left - First signal list\n * @param right - Second signal list\n * @returns True when both lists contain the same signals\n */\nfunction optOutSignalsMatch(\n left: UserPrivacySignalEnum[] | undefined,\n right: UserPrivacySignalEnum[],\n): boolean {\n const normalizedLeft = [...(left ?? [])].sort();\n const normalizedRight = [...right].sort();\n return (\n normalizedLeft.length === normalizedRight.length &&\n normalizedLeft.every((signal, index) => signal === normalizedRight[index])\n );\n}\n\n/**\n * Build updatePurpose input with only changed fields.\n * Built-in default purposes cannot sync `configurable` or `showInConsentManager`.\n *\n * @param found - Existing purpose from the API\n * @param input - Purpose input from YAML\n * @param options - Options\n * @returns Mutation input and any warnings for omitted default-purpose fields\n */\nfunction buildUpdatePurposeInput(\n found: Purpose,\n input: PurposeInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): { input: Record<string, unknown>; isEmpty: boolean } {\n const { logger } = options;\n const isDefault = isDefaultPurposeTrackingType(input.trackingType);\n const updateInput: Record<string, unknown> = { id: found.id };\n\n if (input.name !== found.name) {\n updateInput.name = input.name;\n }\n if (input.title !== found.title) {\n updateInput.title = input.title;\n }\n if (input.description !== found.description) {\n updateInput.description = input.description;\n }\n if (input['is-active'] !== found.isActive) {\n updateInput.isActive = input['is-active'];\n }\n if (input['display-order'] !== found.displayOrder) {\n updateInput.displayOrder = input['display-order'];\n }\n if (input['show-in-privacy-center'] !== found.showInPrivacyCenter) {\n updateInput.showInPrivacyCenter = input['show-in-privacy-center'];\n }\n if (input['auth-level'] !== found.authLevel) {\n updateInput.authLevel = input['auth-level'];\n }\n if (!optOutSignalsMatch(input['opt-out-signals'], found.optOutSignals)) {\n updateInput.optOutSignals = input['opt-out-signals'];\n }\n\n if (isDefault) {\n if (input.configurable !== found.configurable) {\n logger?.warn(\n `Cannot sync \"configurable\" for built-in purpose \"${input.trackingType}\" via API — ` +\n 'update this field in the Admin Dashboard instead.',\n );\n }\n if (input['show-in-consent-manager'] !== found.showInConsentManager) {\n logger?.warn(\n `Cannot sync \"show-in-consent-manager\" for built-in purpose \"${input.trackingType}\" via API — ` +\n 'update this field in the Admin Dashboard instead.',\n );\n }\n } else {\n if (input.configurable !== found.configurable) {\n updateInput.configurable = input.configurable;\n }\n if (input['show-in-consent-manager'] !== found.showInConsentManager) {\n updateInput.showInConsentManager = input['show-in-consent-manager'];\n }\n }\n\n const isEmpty = Object.keys(updateInput).length === 1;\n return { input: updateInput, isEmpty };\n}\n\n/**\n * Sync tracking purposes to Transcend, matching existing purposes by trackingType.\n * Creates purposes that do not yet exist and updates the rest.\n *\n * @param client - GraphQL client\n * @param purposes - Purpose inputs from YAML\n * @param options - Options\n * @returns Whether the sync succeeded and a trackingType -> purpose ID map\n */\nexport async function syncPurposes(\n client: GraphQLClient,\n purposes: PurposeInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SyncPurposesResult> {\n const { logger } = options;\n logger?.info(`Syncing \"${purposes.length}\" purposes...`);\n\n const existing = await fetchAllPurposes(client, { logger });\n const existingByTrackingType = keyBy(existing, 'trackingType');\n const purposeIdByTrackingType: Record<string, string> = Object.fromEntries(\n existing.map(({ trackingType, id }) => [trackingType, id]),\n );\n\n let success = true;\n for (const purpose of purposes) {\n const found = existingByTrackingType[purpose.trackingType];\n try {\n if (found) {\n const { input: updateInput, isEmpty } = buildUpdatePurposeInput(found, purpose, {\n logger,\n });\n if (isEmpty) {\n logger?.info(\n `Skipping purpose \"${purpose.trackingType}\" — only blocked fields differ for built-in purpose`,\n );\n purposeIdByTrackingType[purpose.trackingType] = found.id;\n continue;\n }\n\n await makeGraphQLRequest(client, UPDATE_PURPOSE, {\n variables: { input: updateInput },\n logger,\n });\n purposeIdByTrackingType[purpose.trackingType] = found.id;\n } else {\n const {\n createPurpose: { purpose: created },\n } = await makeGraphQLRequest<{\n /** createPurpose mutation */\n createPurpose: {\n /** Created purpose */\n purpose: {\n /** ID */\n id: string;\n /** Slug */\n trackingType: string;\n };\n };\n }>(client, CREATE_PURPOSE, {\n variables: {\n input: {\n name: purpose.name,\n description: purpose.description ?? '',\n trackingType: purpose.trackingType,\n isActive: purpose['is-active'],\n configurable: purpose.configurable,\n displayOrder: purpose['display-order'],\n showInPrivacyCenter: purpose['show-in-privacy-center'],\n showInConsentManager: purpose['show-in-consent-manager'],\n authLevel: purpose['auth-level'],\n optOutSignals: purpose['opt-out-signals'],\n },\n },\n logger,\n });\n purposeIdByTrackingType[purpose.trackingType] = created.id;\n\n // `title` can only be set via updatePurpose (not createPurpose)\n if (purpose.title !== undefined) {\n await makeGraphQLRequest(client, UPDATE_PURPOSE, {\n variables: { input: { id: created.id, title: purpose.title } },\n logger,\n });\n }\n }\n } catch (err) {\n success = false;\n logger?.error(\n `Failed to sync purpose \"${purpose.trackingType}\"! - ${(err as Error).message}`,\n );\n }\n }\n\n if (success) {\n logger?.info(`Successfully synced \"${purposes.length}\" purposes!`);\n }\n return { success, purposeIdByTrackingType };\n}\n","import { PreferenceTopicType } from '@transcend-io/privacy-types';\nimport { type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy, uniqBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { type PreferenceTopicSyncInput } from './codecs.js';\nimport { fetchAllPreferenceOptionValues } from './fetchAllPreferenceOptionValues.js';\nimport { fetchAllPreferenceTopics } from './fetchAllPreferenceTopics.js';\nimport { fetchAllPurposes } from './fetchAllPurposes.js';\nimport { CREATE_OR_UPDATE_PREFERENCE_TOPIC } from './gqls/preferenceTopic.js';\nimport { createOrUpdatePreferenceOptionValues } from './syncPreferenceOptionValues.js';\n\nexport type { PreferenceTopicSyncInput } from './codecs.js';\n\n/**\n * Build GraphQL input for creating a preference topic.\n *\n * @param topic - Topic input from YAML\n * @param purposeId - Resolved purpose ID\n * @param preferenceOptionValueIds - Linked option value IDs (omit for BOOLEAN)\n * @returns Mutation input with only the fields allowed on create\n */\nfunction buildCreatePreferenceTopicInput(\n topic: PreferenceTopicSyncInput,\n purposeId: string,\n preferenceOptionValueIds: string[],\n): Record<string, unknown> {\n const input: Record<string, unknown> = {\n title: topic.title,\n slug: topic.slug,\n type: topic.type,\n displayDescription: topic.description,\n purposeId,\n };\n if (topic['show-in-privacy-center'] !== undefined) {\n input.showInPrivacyCenter = topic['show-in-privacy-center'];\n }\n if (topic['default-configuration'] !== undefined) {\n input.defaultConfiguration = topic['default-configuration'];\n }\n if (topic.type !== PreferenceTopicType.Boolean && preferenceOptionValueIds.length > 0) {\n input.preferenceOptionValueIds = preferenceOptionValueIds;\n }\n return input;\n}\n\n/**\n * Build GraphQL input for updating an existing preference topic.\n * Slug, type, and purposeId must be omitted — the backend rejects them on update.\n *\n * @param existingId - ID of the existing topic\n * @param topic - Topic input from YAML\n * @param preferenceOptionValueIds - Linked option value IDs (omit for BOOLEAN)\n * @returns Mutation input with only the fields allowed on update\n */\nfunction buildUpdatePreferenceTopicInput(\n existingId: string,\n topic: PreferenceTopicSyncInput,\n preferenceOptionValueIds: string[],\n): Record<string, unknown> {\n const input: Record<string, unknown> = {\n id: existingId,\n title: topic.title,\n displayDescription: topic.description,\n };\n if (topic['show-in-privacy-center'] !== undefined) {\n input.showInPrivacyCenter = topic['show-in-privacy-center'];\n }\n if (topic['default-configuration'] !== undefined) {\n input.defaultConfiguration = topic['default-configuration'];\n }\n if (topic.type !== PreferenceTopicType.Boolean && preferenceOptionValueIds.length > 0) {\n input.preferenceOptionValueIds = preferenceOptionValueIds;\n }\n return input;\n}\n\n/**\n * Sync preference topics to Transcend, matching existing topics by (trackingType, slug).\n * Inline option values are upserted (by slug) so their IDs can be linked to the topic.\n *\n * @param client - GraphQL client\n * @param topics - Preference topic inputs, each tagged with its purpose trackingType\n * @param options - Options\n * @returns True if every topic synced without error\n */\nexport async function syncPreferenceTopics(\n client: GraphQLClient,\n topics: PreferenceTopicSyncInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Pre-resolved trackingType -> purpose ID map (avoids re-fetching purposes) */\n purposeIdByTrackingType?: Record<string, string>;\n } = {},\n): Promise<boolean> {\n const { logger } = options;\n logger?.info(`Syncing \"${topics.length}\" preference topics...`);\n\n // Resolve purpose IDs (reuse the map from syncPurposes when provided)\n let { purposeIdByTrackingType } = options;\n if (!purposeIdByTrackingType) {\n const purposes = await fetchAllPurposes(client, { logger });\n purposeIdByTrackingType = Object.fromEntries(\n purposes.map(({ trackingType, id }) => [trackingType, id]),\n );\n }\n\n // Upsert inline option values so their IDs can be linked to topics\n const existingOptionValues = await fetchAllPreferenceOptionValues(client, { logger });\n const optionIdBySlug: Record<string, string> = Object.fromEntries(\n existingOptionValues.map(({ slug, id }) => [slug, id]),\n );\n const inlineOptions = uniqBy(\n topics\n .filter((topic) => topic.type !== PreferenceTopicType.Boolean)\n .flatMap((topic) => topic.options ?? []),\n 'slug',\n ).filter((optionValue) => {\n const found = existingOptionValues.find(({ slug }) => slug === optionValue.slug);\n return !found || found.title.defaultMessage !== optionValue.title;\n });\n if (inlineOptions.length > 0) {\n try {\n const upserted = await createOrUpdatePreferenceOptionValues(\n client,\n inlineOptions.map((optionValue) => [optionValue, optionIdBySlug[optionValue.slug]]),\n { logger },\n );\n upserted.forEach(({ slug, id }) => {\n optionIdBySlug[slug] = id;\n });\n } catch (err) {\n logger?.error(`Failed to sync inline preference option values! - ${(err as Error).message}`);\n return false;\n }\n }\n\n // Existing topics for matching updates\n const existingTopics = await fetchAllPreferenceTopics(client, { logger });\n const topicBySlugKey = keyBy(\n existingTopics,\n ({ purpose, slug }) => `${purpose.trackingType}:${slug}`,\n );\n\n let success = true;\n for (const topic of topics) {\n const trackingType = topic['tracking-type'];\n const purposeId = purposeIdByTrackingType[trackingType];\n if (!purposeId) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - unknown purpose \"${trackingType}\"`,\n );\n continue;\n }\n\n if (topic.type === PreferenceTopicType.Boolean && (topic.options ?? []).length > 0) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - BOOLEAN preference topics cannot have options in YAML (the backend auto-creates True/False options)`,\n );\n continue;\n }\n\n const existing = topicBySlugKey[`${trackingType}:${topic.slug}`];\n\n if (existing && existing.type !== topic.type) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - Cannot change preference topic type from \"${existing.type}\" to \"${topic.type}\" for an existing topic`,\n );\n continue;\n }\n\n const preferenceOptionValueIds =\n topic.type === PreferenceTopicType.Boolean\n ? []\n : (topic.options ?? [])\n .map((optionValue) => optionIdBySlug[optionValue.slug])\n .filter((id): id is string => Boolean(id));\n\n try {\n const input = existing\n ? buildUpdatePreferenceTopicInput(existing.id, topic, preferenceOptionValueIds)\n : buildCreatePreferenceTopicInput(topic, purposeId, preferenceOptionValueIds);\n\n await makeGraphQLRequest(client, CREATE_OR_UPDATE_PREFERENCE_TOPIC, {\n variables: { input },\n logger,\n });\n } catch (err) {\n success = false;\n logger?.error(\n `Failed to sync preference topic \"${topic.title}\"! - ${(err as Error).message}`,\n );\n }\n }\n\n if (success) {\n logger?.info(`Successfully synced \"${topics.length}\" preference topics!`);\n }\n return success;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const ATTRIBUTES = gql`\n query TranscendCliAttributes($first: Int!, $offset: Int!) {\n attributeKeys(first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n isCustom\n description\n enabledOn\n name\n type\n }\n }\n }\n`;\n\nexport const CREATE_ATTRIBUTE_VALUES = gql`\n mutation TranscendCliCreateAttributeValues($input: [CreateAttributeValuesInput!]!) {\n createAttributeValues(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_ATTRIBUTE_VALUES = gql`\n mutation TranscendCliUpdateAttributeValues($input: [UpdateAttributeValueInput!]!) {\n updateAttributeValues(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const DELETE_ATTRIBUTE_VALUE = gql`\n mutation TranscendCliDeleteAttributeValue($id: ID!) {\n deleteAttributeValue(id: $id) {\n clientMutationId\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const ATTRIBUTE_VALUES = gql`\n query TranscendCliAttributeValues($first: Int!, $offset: Int!, $attributeKeyId: ID!) {\n attributeValues(\n first: $first\n offset: $offset\n useMaster: false\n filterBy: { attributeKeys: [$attributeKeyId] }\n ) {\n nodes {\n id\n name\n description\n color\n }\n }\n }\n`;\n\nexport const CREATE_ATTRIBUTE = gql`\n mutation TranscendCliCreateAttribute(\n $name: String!\n $type: AttributeKeyType!\n $description: String\n $enabledOn: [AttributeSupportedResourceType!]\n ) {\n createAttributeKey(\n input: { name: $name, type: $type, description: $description, enabledOn: $enabledOn }\n ) {\n clientMutationId\n attributeKey {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_ATTRIBUTE = gql`\n mutation TranscendCliCreateAttribute(\n $attributeKeyId: ID!\n $description: String\n $enabledOn: [AttributeSupportedResourceType!]\n ) {\n updateAttributeKey(\n input: { id: $attributeKeyId, description: $description, enabledOn: $enabledOn }\n ) {\n clientMutationId\n attributeKey {\n id\n }\n }\n }\n`;\n\nexport const SET_RESOURCE_ATTRIBUTES = gql`\n mutation TranscendCliSetResourceAttributes($input: SetResourceAttributesInput!) {\n setResourceAttributes(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { AttributeKeyType, AttributeSupportedResourceType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ATTRIBUTES, ATTRIBUTE_VALUES } from './gqls/attribute.js';\n\nexport interface AttributeValue {\n /** Attribute ID */\n id: string;\n /** Attribute name */\n name: string;\n /** Attribute description */\n description: string;\n /** Color of attribute value */\n color: string;\n}\n\nexport interface Attribute {\n /** ID of attribute */\n id: string;\n /** Name of attribute */\n name: string;\n /** if custom attribute */\n isCustom: boolean;\n /** Description */\n description: string;\n /** Type of attribute */\n type: AttributeKeyType;\n /** Values */\n values: AttributeValue[];\n /** The different fields that the attribute is enabled on */\n enabledOn: AttributeSupportedResourceType[];\n}\n\nconst PAGE_SIZE = 100;\n\n/**\n * Fetch all attribute values for an attribute key\n *\n * @param client - GraphQL client\n * @param attributeKeyId - Attribute keyID\n * @returns A map from apiKey title to Identifier\n */\nexport async function fetchAllAttributeValues(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** Attribute key ID */\n attributeKeyId: string;\n };\n },\n): Promise<AttributeValue[]> {\n const {\n logger,\n filterBy: { attributeKeyId },\n } = options;\n const attributeValues: AttributeValue[] = [];\n let offset = 0;\n\n // Paginate\n let shouldContinue = false;\n do {\n const {\n attributeValues: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n attributeValues: {\n /** List of matches */\n nodes: AttributeValue[];\n };\n }>(client, ATTRIBUTE_VALUES, {\n variables: { first: PAGE_SIZE, offset, attributeKeyId },\n logger,\n });\n attributeValues.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return attributeValues.sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport const SYNC_ATTRIBUTE_TYPES = [AttributeKeyType.MultiSelect, AttributeKeyType.SingleSelect];\n\n/**\n * Fetch all attributes in an organization\n *\n * @param client - GraphQL client\n * @returns A map from apiKey title to Identifier\n */\nexport async function fetchAllAttributes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Attribute[]> {\n const { logger } = options;\n const attributes: Attribute[] = [];\n let offset = 0;\n\n // Paginate\n let shouldContinue = false;\n do {\n const {\n attributeKeys: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n attributeKeys: {\n /** List of matches */\n nodes: Attribute[];\n };\n }>(client, ATTRIBUTES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n attributes.push(\n ...(await Promise.all(\n nodes.map(async (node) => ({\n ...node,\n values: SYNC_ATTRIBUTE_TYPES.includes(node.type)\n ? await fetchAllAttributeValues(client, {\n logger,\n filterBy: { attributeKeyId: node.id },\n })\n : [],\n })),\n )),\n );\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return attributes.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const MESSAGES = gql`\n query TranscendCliFetchMessage {\n translatedMessages {\n id\n defaultMessage\n description\n targetReactIntlId\n translations {\n locale\n value\n }\n }\n }\n`;\n\nexport const UPDATE_INTL_MESSAGES = gql`\n mutation TranscendCliUpdateIntlMessages($messages: [MessageInput!]!) {\n updateIntlMessages(input: { messages: $messages, skipPublish: true }) {\n clientMutationId\n }\n }\n`;\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { MESSAGES } from './gqls/message.js';\n\nexport interface Message {\n /** ID of message */\n id: string;\n /** Default message */\n defaultMessage: string;\n /** Description */\n description: string;\n /** React Intl ID */\n targetReactIntlId: string | null;\n /** Disabled locales */\n translations: {\n /** Locale */\n locale: LocaleValue;\n /** Value */\n value: string;\n }[];\n}\n\n/**\n * Fetch all messages in the organization\n *\n * @param client - GraphQL client\n * @returns All messages in the organization\n */\nexport async function fetchAllMessages(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Message[]> {\n const { logger } = options;\n const { translatedMessages } = await makeGraphQLRequest<{\n /** Messages */\n translatedMessages: Message[];\n }>(client, MESSAGES, { logger });\n return translatedMessages;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const TEAMS = gql`\n query TranscendCliTeams($first: Int!, $offset: Int!, $input: TeamFiltersInput) {\n teams(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n ssoDepartment\n ssoGroup\n ssoTitle\n users {\n id\n email\n name\n }\n scopes {\n id\n name\n title\n }\n parentTeam {\n id\n name\n }\n }\n }\n }\n`;\n\nexport const PARENT_ORGANIZATION_TEAMS = gql`\n query TranscendCliParentOrganizationTeams(\n $first: Int!\n $offset: Int!\n $filterBy: ParentTeamFiltersInput\n ) {\n parentOrganizationTeams(\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n }\n totalCount\n }\n }\n`;\n\nexport const CREATE_TEAM = gql`\n mutation TranscendCliCreateTeam($input: TeamInput!) {\n createTeam(input: $input) {\n team {\n id\n name\n }\n }\n }\n`;\n\nexport const UPDATE_TEAM = gql`\n mutation TranscendCliUpdateTeam($input: UpdateTeamInput!) {\n updateTeam(input: $input) {\n team {\n id\n name\n }\n }\n }\n`;\n","import { ScopeName } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { TEAMS } from './gqls/team.js';\n\nexport interface Team {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n /** Description of team */\n description: string;\n /** SSO department for automated provisioning */\n ssoDepartment?: string;\n /** SSO group name for automated provisioning */\n ssoGroup?: string;\n /** SSO title mapping for automated provisioning */\n ssoTitle?: string;\n /** List of users on the team */\n users: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n /** Name of user */\n name: string;\n }[];\n /** List of scopes on the team */\n scopes: {\n /** ID of scope */\n id: string;\n /** Name of scope */\n name: ScopeName;\n /** Title of scope */\n title: string;\n }[];\n /** Linked parent organization team */\n parentTeam?: {\n /** ID of parent organization team */\n id: string;\n /** Name of parent organization team */\n name: string;\n };\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all teams in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All teams in the organization\n */\nexport async function fetchAllTeams(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Team[]> {\n const { logger } = options;\n const teams: Team[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n teams: { nodes },\n } = await makeGraphQLRequest<{\n /** Teams */\n teams: {\n /** List */\n nodes: Team[];\n };\n }>(client, TEAMS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n teams.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return teams.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PARENT_ORGANIZATION_TEAMS } from './gqls/team.js';\n\nexport interface ParentOrganizationTeam {\n /** ID of parent organization team */\n id: string;\n /** Display name of parent organization team */\n name: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch teams from the parent organization that can be linked to child-org teams\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Parent organization teams\n */\nexport async function fetchParentOrganizationTeams(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Partial match on team name or description */\n text?: string;\n /** Filter by team IDs */\n ids?: string[];\n };\n } = {},\n): Promise<ParentOrganizationTeam[]> {\n const { logger, filterBy } = options;\n const teams: ParentOrganizationTeam[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n parentOrganizationTeams: { nodes },\n } = await makeGraphQLRequest<{\n /** Parent organization teams */\n parentOrganizationTeams: {\n /** List */\n nodes: ParentOrganizationTeam[];\n };\n }>(client, PARENT_ORGANIZATION_TEAMS, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n teams.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return teams.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Resolve parent organization team names to IDs\n *\n * @param client - GraphQL client\n * @param parentTeamNames - Parent organization team names to resolve\n * @param options - Options\n * @returns Map from parent team name to ID\n */\nexport async function resolveParentTeamIdsByName(\n client: GraphQLClient,\n parentTeamNames: string[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<{ [name in string]: string }> {\n const { logger } = options;\n const uniqueNames = [...new Set(parentTeamNames)];\n const parentTeamIdsByName: { [name in string]: string } = {};\n\n for (const name of uniqueNames) {\n const teams = await fetchParentOrganizationTeams(client, {\n logger,\n filterBy: { text: name },\n });\n const match = teams.find((team) => team.name === name);\n if (!match) {\n throw new Error(`Failed to find parent organization team with name: ${name}`);\n }\n parentTeamIdsByName[name] = match.id;\n }\n\n return parentTeamIdsByName;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const USERS = gql`\n query TranscendCliUsers($first: Int!, $offset: Int!, $input: UserFiltersInput) {\n users(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n email\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { USERS } from './gqls/user.js';\n\nexport interface User {\n /** ID of user */\n id: string;\n /** Name of user */\n name: string;\n /** Email of user */\n email: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all users in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All users in the organization\n */\nexport async function fetchAllUsers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<User[]> {\n const { logger } = options;\n const users: User[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n users: { nodes },\n } = await makeGraphQLRequest<{\n /** Users */\n users: {\n /** List */\n nodes: User[];\n };\n }>(client, USERS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n users.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return users.sort((a, b) => a.email.localeCompare(b.email));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const API_KEYS = gql`\n query TranscendCliApiKeys($first: Int!, $offset: Int!, $titles: [String!]) {\n apiKeys(\n first: $first\n offset: $offset\n filterBy: { titles: $titles }\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n ) {\n nodes {\n id\n title\n }\n }\n }\n`;\n\nexport const CREATE_API_KEY = gql`\n mutation TranscendCliCreateApiKey($input: ApiKeyInput!) {\n createApiKey(input: $input) {\n apiKey {\n id\n apiKey\n title\n }\n }\n }\n`;\n\nexport const DELETE_API_KEY = gql`\n mutation TranscendCliDeleteApiKey($id: ID!) {\n deleteApiKey(id: $id) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, uniq, difference } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { API_KEYS } from './gqls/apiKey.js';\n\nexport interface ApiKey {\n /** ID of API key */\n id: string;\n /** Title of API key */\n title: string;\n}\n\nexport interface FetchApiKeysInput {\n /** API key definitions */\n 'api-keys'?: {\n /** Title of API key */\n title: string;\n }[];\n /** Data silo definitions that may reference API keys */\n 'data-silos'?: {\n /** Title of the API key to use */\n 'api-key-title'?: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\nconst ADMIN_LINK = 'https://app.transcend.io/infrastructure/api-keys';\n\n/**\n * Fetch all API keys in an organization\n *\n * @param client - Client\n * @param options - Options\n * @returns API keys\n */\nexport async function fetchAllApiKeys(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter on titles */\n titles?: string[];\n };\n } = {},\n): Promise<ApiKey[]> {\n const { logger = NOOP_LOGGER, filterBy: { titles } = {} } = options;\n const apiKeys: ApiKey[] = [];\n let offset = 0;\n\n // Paginate\n let shouldContinue = false;\n do {\n const {\n apiKeys: { nodes },\n } = await makeGraphQLRequest<{\n /** API keys */\n apiKeys: {\n /** List */\n nodes: ApiKey[];\n };\n }>(client, API_KEYS, {\n variables: { first: PAGE_SIZE, offset, titles },\n logger,\n });\n apiKeys.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n return apiKeys.sort((a, b) => a.title.localeCompare(b.title));\n}\n\n/**\n * Fetch all apiKeys and if any are found in the config that are\n * missing, create those apiKeys.\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns A map from apiKey title to Identifier\n */\nexport async function fetchApiKeys(\n client: GraphQLClient,\n options: {\n /** API key input configuration */\n apiKeyInputs?: FetchApiKeysInput;\n /** When true, fetch all API keys */\n fetchAll?: boolean;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<{ [k in string]: ApiKey }> {\n const {\n apiKeyInputs: { 'api-keys': apiKeyDefs = [], 'data-silos': dataSilos = [] } = {},\n fetchAll = false,\n logger = NOOP_LOGGER,\n } = options;\n logger.info(`Fetching ${fetchAll ? 'all' : apiKeyDefs.length} API keys...`);\n const titles = apiKeyDefs.map(({ title }) => title);\n const expectedApiKeyTitles = uniq(\n dataSilos.map((silo) => silo['api-key-title']).filter((x): x is string => !!x),\n );\n const allTitlesExpected = [...expectedApiKeyTitles, ...titles];\n const apiKeys = await fetchAllApiKeys(client, {\n logger,\n filterBy: {\n titles: fetchAll ? undefined : [...expectedApiKeyTitles, ...titles],\n },\n });\n\n // Create a map\n const apiKeysByTitle = keyBy(apiKeys, 'title');\n\n // Determine expected set of apiKeys expected\n const missingApiKeys = difference(\n allTitlesExpected,\n apiKeys.map(({ title }) => title),\n );\n\n // If there are missing apiKeys, throw an error\n if (missingApiKeys.length > 0) {\n logger.error(\n `Failed to find API keys \"${missingApiKeys.join(\n '\", \"',\n )}\"! Make sure these API keys are created at: ${ADMIN_LINK}`,\n );\n process.exit(1);\n }\n return apiKeysByTitle;\n}\n","import { IsoCountrySubdivisionCode, IsoCountryCode } from '@transcend-io/privacy-types';\n\nexport interface RegionInput {\n /** The country */\n country?: IsoCountryCode;\n /** The country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n}\n\n// Country subdivision is nullable in DB\nexport interface Region {\n /** Country */\n country: IsoCountryCode;\n /** Sub division (may be null in DB) */\n countrySubDivision?: IsoCountrySubdivisionCode | null;\n}\n\n/**\n * Format regions list to remove null country subdivisions\n *\n * @param vals - Regions\n * @returns formatted regions\n */\nexport function formatRegions(vals: Region[]): RegionInput[] {\n return vals.map(({ country, countrySubDivision }) => ({\n country,\n ...(countrySubDivision ? { countrySubDivision } : {}),\n }));\n}\n","import { gql } from 'graphql-request';\n\nexport const DETERMINE_LOGIN_METHOD = gql`\n mutation TranscendCliDetermineLoginMethod($email: String!) {\n determineLoginMethod(input: { email: $email }) {\n loginMethod {\n email\n sombraPublicKey\n }\n }\n }\n`;\n\nexport const LOGIN = gql`\n mutation TranscendCliLogin($email: String!, $password: String!, $publicKey: String!) {\n login(input: { email: $email, password: $password }, publicKey: $publicKey) {\n user {\n roles {\n id\n organization {\n name\n id\n uri\n parentOrganizationId\n }\n }\n }\n }\n }\n`;\n\nexport const ASSUME_ROLE = gql`\n mutation TranscendCliAssumeRole($id: ID!, $publicKey: String!) {\n assumeRole(id: $id, publicKey: $publicKey) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DETERMINE_LOGIN_METHOD, ASSUME_ROLE, LOGIN } from './gqls/auth.js';\n\nexport interface OrganizationPreview {\n /** Name of organization */\n name: string;\n /** Id of organization */\n id: string;\n /** uri of organization */\n uri: string;\n /** ID of parent organization */\n parentOrganizationId?: string;\n}\n\nexport interface UserRole {\n /** ID of role */\n id: string;\n /** Related organization */\n organization: OrganizationPreview;\n}\n\n/**\n * Log in as a user\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Cookie and roles\n */\nexport async function loginUser(\n client: GraphQLClient,\n options: {\n /** Email of user */\n email: string;\n /** Password of user */\n password: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<{\n /** Cookie to be used to make subsequent requests */\n loginCookie: string;\n /** Roles of the user */\n roles: UserRole[];\n}> {\n const { email, password, logger } = options;\n\n const {\n determineLoginMethod: { loginMethod },\n } = await makeGraphQLRequest<{\n /** Determine login method */\n determineLoginMethod: {\n /** Login method info */\n loginMethod: {\n /** Email being logged in */\n email: string;\n /** Sombra public key */\n sombraPublicKey: string;\n };\n };\n }>(client, DETERMINE_LOGIN_METHOD, {\n variables: { email },\n logger,\n });\n\n const res = await client.rawRequest<{\n /** Login */\n login: {\n /** User */\n user: {\n /** Roles of user */\n roles: UserRole[];\n };\n };\n }>(LOGIN, {\n email,\n password,\n publicKey: loginMethod.sombraPublicKey,\n });\n const {\n login: { user },\n } = res.data;\n\n // Get login cookie from response\n const loginCookie = res.headers.get('set-cookie');\n if (!loginCookie || !loginCookie.includes('laravel')) {\n throw new Error('Failed to get login cookie in response');\n }\n\n return {\n roles: user.roles,\n loginCookie,\n };\n}\n\n/**\n * Assume role for user into another organization\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function assumeRole(\n client: GraphQLClient,\n options: {\n /** Email of user */\n email: string;\n /** Role of user assuming into */\n roleId: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { email, roleId, logger } = options;\n\n const {\n determineLoginMethod: { loginMethod },\n } = await makeGraphQLRequest<{\n /** Determine login method */\n determineLoginMethod: {\n /** Login method info */\n loginMethod: {\n /** Email being logged in */\n email: string;\n /** Sombra public key */\n sombraPublicKey: string;\n };\n };\n }>(client, DETERMINE_LOGIN_METHOD, {\n variables: { email, userId: roleId },\n logger,\n });\n\n await client.rawRequest<{\n /** Assume role */\n assumeRole: {\n /** Mutation ID */\n clientMutationId: string;\n };\n }>(ASSUME_ROLE, {\n id: roleId,\n publicKey: loginMethod.sombraPublicKey,\n });\n}\n","import { ScopeName } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CREATE_API_KEY, DELETE_API_KEY } from './gqls/apiKey.js';\n\nexport interface CreatedApiKey {\n /** ID of API key */\n id: string;\n /** Actual API key */\n apiKey: string;\n /** Title of the API key */\n title: string;\n}\n\n/**\n * Create an API key\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns The API key\n */\nexport async function createApiKey(\n client: GraphQLClient,\n options: {\n /** Input for creating an API key */\n input: {\n /** Title of API key */\n title: string;\n /** Scopes for API key */\n scopes: ScopeName[];\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CreatedApiKey> {\n const {\n input: { title, scopes },\n logger,\n } = options;\n const {\n createApiKey: { apiKey },\n } = await makeGraphQLRequest<{\n /** Create API key */\n createApiKey: {\n /** API key */\n apiKey: CreatedApiKey;\n };\n }>(client, CREATE_API_KEY, { variables: { input: { title, scopes } }, logger });\n\n return apiKey;\n}\n\n/**\n * Delete an API key\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function deleteApiKey(\n client: GraphQLClient,\n options: {\n /** API key ID to delete */\n id: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { id, logger } = options;\n await makeGraphQLRequest(client, DELETE_API_KEY, { variables: { id }, logger });\n}\n","import { AttributeSupportedResourceType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { SET_RESOURCE_ATTRIBUTES } from './gqls/attribute.js';\n\ninterface SetResourceAttributesInput {\n /** ID of resource */\n resourceId: string;\n /** Type of resource */\n resourceType: AttributeSupportedResourceType;\n /** Attribute key ID */\n attributeKeyId: string;\n /** Attribute values by ID */\n attributeValueIds?: string[];\n /** Attribute values by name */\n attributeValueNames?: string[];\n}\n\n/**\n * Set attribute values on a particular resource\n *\n * @param client - GraphQL client\n * @param input - Input\n */\nexport async function setResourceAttributes(\n client: GraphQLClient,\n input: SetResourceAttributesInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger } = options;\n await makeGraphQLRequest(client, SET_RESOURCE_ATTRIBUTES, {\n variables: { input },\n logger,\n });\n}\n","import { AttributeKeyType, AttributeSupportedResourceType } from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, difference, groupBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { type Attribute } from './fetchAllAttributes.js';\nimport {\n CREATE_ATTRIBUTE,\n CREATE_ATTRIBUTE_VALUES,\n DELETE_ATTRIBUTE_VALUE,\n UPDATE_ATTRIBUTE,\n UPDATE_ATTRIBUTE_VALUES,\n} from './gqls/attribute.js';\n\nexport interface AttributeValueInput {\n /** Name of attribute value */\n name: string;\n /** Description */\n description?: string;\n /** Color */\n color?: string;\n}\n\nexport interface AttributeInput {\n /** Name of attribute */\n name: string;\n /** Type of attribute */\n type: AttributeKeyType;\n /** Description of attribute */\n description?: string;\n /** Resource types that the attribute is enabled on */\n resources?: AttributeSupportedResourceType[];\n /** Values of attribute */\n values?: AttributeValueInput[];\n}\n\n/**\n * Sync attribute\n *\n * @param client - GraphQL client\n * @param attribute - The attribute input\n * @param options - Options\n */\nexport async function syncAttribute(\n client: GraphQLClient,\n attribute: AttributeInput,\n {\n existingAttribute,\n deleteExtraAttributeValues,\n logger = NOOP_LOGGER,\n }: {\n /** The existing attribute configuration if it exists */\n existingAttribute?: Attribute;\n /** When true, delete extra attributes not specified in the list of values */\n deleteExtraAttributeValues?: boolean;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n // attribute key input\n const input = {\n name: attribute.name,\n enabledOn: attribute.resources,\n };\n\n // create or update attribute key\n let attributeKeyId: string;\n if (!existingAttribute) {\n const {\n createAttributeKey: { attributeKey },\n } = await makeGraphQLRequest<{\n /** Create attribute key response */\n createAttributeKey: {\n /** Attribute key */\n attributeKey: {\n /** ID */\n id: string;\n };\n };\n }>(client, CREATE_ATTRIBUTE, {\n variables: {\n type: attribute.type,\n description: attribute.description,\n ...input,\n },\n logger,\n });\n attributeKeyId = attributeKey.id;\n } else {\n await makeGraphQLRequest(client, UPDATE_ATTRIBUTE, {\n variables: {\n attributeKeyId: existingAttribute.id,\n description: existingAttribute.isCustom ? attribute.description : undefined,\n ...input,\n },\n logger,\n });\n attributeKeyId = existingAttribute.id;\n }\n\n // upsert attribute values\n const existingAttributeMap = keyBy(existingAttribute?.values || [], 'name');\n const { existingValues = [], newValues = [] } = groupBy(attribute.values || [], (field) =>\n existingAttributeMap[field.name] ? 'existingValues' : 'newValues',\n );\n const removedValues = difference(\n (existingAttribute?.values || []).map(({ name }) => name),\n (attribute.values || []).map(({ name }) => name),\n );\n\n // Create new attribute values\n if (newValues.length > 0) {\n await makeGraphQLRequest(client, CREATE_ATTRIBUTE_VALUES, {\n variables: {\n input: newValues.map(({ name, ...rest }) => ({\n name,\n attributeKeyId,\n ...rest,\n })),\n },\n logger,\n });\n logger.info(`Created ${newValues.length} attribute values`);\n }\n\n // Update existing attribute values\n if (existingValues.length > 0) {\n await makeGraphQLRequest(client, UPDATE_ATTRIBUTE_VALUES, {\n variables: {\n input: existingValues.map(({ name, ...rest }) => ({\n id: existingAttributeMap[name]!.id,\n name,\n description: existingAttributeMap[name]!.description,\n color: existingAttributeMap[name]!.color,\n ...rest,\n attributeKeyId,\n })),\n },\n logger,\n });\n logger.info(`Updated ${existingValues.length} attribute values`);\n }\n\n // Delete removed attribute values\n if (removedValues.length > 0 && deleteExtraAttributeValues) {\n await map(\n removedValues,\n async (value) => {\n await makeGraphQLRequest(client, DELETE_ATTRIBUTE_VALUE, {\n variables: { id: existingAttributeMap[value]!.id },\n logger,\n });\n },\n {\n concurrency: 10,\n },\n );\n logger.info(`Deleted ${removedValues.length} attribute values`);\n }\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { UPDATE_INTL_MESSAGES } from './gqls/message.js';\n\nexport interface IntlMessageInput {\n /** React Intl message ID */\n id: string;\n /** Target React Intl ID to map this message to */\n targetReactIntlId?: string;\n /** Description of the message for translators */\n description?: string;\n /** Default English message content */\n defaultMessage?: string;\n /** Map from locale code to translated string */\n translations?: Record<string, string>;\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Update or create intl messages\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateIntlMessages(\n client: GraphQLClient,\n options: {\n /** List of message inputs */\n input: IntlMessageInput[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: messages, logger = NOOP_LOGGER } = options;\n // Batch update messages\n await mapSeries(chunk(messages, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_INTL_MESSAGES, {\n variables: {\n messages: page.map((message) => ({\n ...(message.id.includes('.') ? {} : { id: message.id }),\n defaultMessage: message.defaultMessage,\n targetReactIntlId: message.targetReactIntlId,\n translations: !message.translations\n ? undefined\n : Object.entries(message.translations).map(([locale, value]) => ({\n locale,\n value,\n })),\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the set of messages from the YML interface into the product\n *\n * @param client - GraphQL client\n * @param messages - messages to sync\n * @returns True upon success, false upon failure\n */\nexport async function syncIntlMessages(\n client: GraphQLClient,\n messages: IntlMessageInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${messages.length}\" messages...`);\n\n // Ensure no duplicates are being uploaded\n const notUnique = messages.filter(\n (message) => messages.filter((pol) => message.id === pol.id).length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload messages as there were non-unique entries found: ${notUnique\n .map(({ id }) => id)\n .join(',')}`,\n );\n }\n\n try {\n logger.info(`Upserting \"${messages.length}\" new messages...`);\n await updateIntlMessages(client, { input: messages, logger });\n logger.info(`Successfully synced ${messages.length} messages!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create messages! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { ScopeName } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllTeams, type Team } from './fetchAllTeams.js';\nimport { resolveParentTeamIdsByName } from './fetchParentOrganizationTeams.js';\nimport { UPDATE_TEAM, CREATE_TEAM } from './gqls/team.js';\n\nexport interface TeamInput {\n /** The display name of the team */\n name: string;\n /** Team description */\n description: string;\n /** SSO department for automated provisioning */\n 'sso-department'?: string;\n /** SSO group name for automated provisioning */\n 'sso-group'?: string;\n /** SSO title mapping for automated provisioning */\n 'sso-title'?: string;\n /** Parent organization team name to link this team to */\n 'parent-team-name'?: string;\n /** List of user emails on the team */\n users?: string[];\n /** List of scopes that the team should have */\n scopes?: ScopeName[];\n}\n\n/**\n * Map team input to GraphQL mutation input\n *\n * @param team - Team input\n * @param parentTeamIdsByName - Resolved parent organization team IDs\n * @returns GraphQL team mutation input\n */\nfunction toTeamMutationInput(\n team: TeamInput,\n parentTeamIdsByName: { [name in string]: string },\n): {\n /** The display name of the team */\n name: string;\n /** Team description */\n description: string;\n /** SSO title mapping for automated provisioning */\n ssoTitle?: string;\n /** SSO department for automated provisioning */\n ssoDepartment?: string;\n /** SSO group name for automated provisioning */\n ssoGroup?: string;\n /** List of scopes that the team should have */\n scopes?: ScopeName[];\n /** List of user emails on the team */\n userEmails?: string[];\n /** Parent organization team ID to link this team to */\n parentTeam?: string;\n} {\n const parentTeamName = team['parent-team-name'];\n const parentTeam = parentTeamName ? parentTeamIdsByName[parentTeamName] : undefined;\n\n return {\n name: team.name,\n description: team.description,\n ssoTitle: team['sso-title'],\n ssoDepartment: team['sso-department'],\n ssoGroup: team['sso-group'],\n scopes: team.scopes,\n userEmails: team.users,\n ...(parentTeam ? { parentTeam } : {}),\n };\n}\n\n/**\n * Input to create a new team\n *\n * @param client - GraphQL client\n * @param team - Input\n * @param options - Options\n * @returns Created team\n */\nexport async function createTeam(\n client: GraphQLClient,\n options: {\n /** Team to create */\n input: TeamInput;\n /** Resolved parent organization team IDs */\n parentTeamIdsByName?: { [name in string]: string };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Team, 'id' | 'name'>> {\n const { input: team, parentTeamIdsByName = {}, logger = NOOP_LOGGER } = options;\n const input = toTeamMutationInput(team, parentTeamIdsByName);\n\n const { createTeam } = await makeGraphQLRequest<{\n /** Create team mutation */\n createTeam: {\n /** Created team */\n team: Team;\n };\n }>(client, CREATE_TEAM, {\n variables: { input },\n logger,\n });\n return createTeam.team;\n}\n\n/**\n * Input to update teams\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Updated team\n */\nexport async function updateTeam(\n client: GraphQLClient,\n options: {\n /** Team input to update */\n input: TeamInput & {\n /** ID of team to update */\n id: string;\n };\n /** Resolved parent organization team IDs */\n parentTeamIdsByName?: { [name in string]: string };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Team, 'id' | 'name'>> {\n const { input: team, parentTeamIdsByName = {}, logger = NOOP_LOGGER } = options;\n const teamId = team.id;\n const { updateTeam } = await makeGraphQLRequest<{\n /** Update team mutation */\n updateTeam: {\n /** Updated team */\n team: Team;\n };\n }>(client, UPDATE_TEAM, {\n variables: {\n input: {\n id: teamId,\n ...toTeamMutationInput(team, parentTeamIdsByName),\n },\n },\n logger,\n });\n return updateTeam.team;\n}\n\n/**\n * Sync the teams\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncTeams(\n client: GraphQLClient,\n inputs: TeamInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n // Fetch existing\n logger.info(`Syncing \"${inputs.length}\" teams...`);\n\n let encounteredError = false;\n\n const parentTeamNames = inputs\n .map((input) => input['parent-team-name'])\n .filter((name): name is string => !!name);\n let parentTeamIdsByName: { [name in string]: string } = {};\n if (parentTeamNames.length > 0) {\n try {\n parentTeamIdsByName = await resolveParentTeamIdsByName(client, parentTeamNames, { logger });\n } catch (err) {\n logger.error(`Failed to resolve parent organization teams - ${(err as Error).message}`);\n return false;\n }\n }\n\n // Fetch existing\n const existingTeams = await fetchAllTeams(client, { logger });\n\n // Look up by name\n const teamsByName: { [k in string]: Pick<Team, 'id' | 'name'> } = keyBy(existingTeams, 'name');\n\n // Create new teams\n const newTeams = inputs.filter((input) => !teamsByName[input.name]);\n const updatedTeams = inputs.filter((input) => !!teamsByName[input.name]);\n\n // Create new teams\n await mapSeries(newTeams, async (team) => {\n try {\n const newTeam = await createTeam(client, { input: team, parentTeamIdsByName, logger });\n teamsByName[newTeam.name] = newTeam;\n logger.info(`Successfully created team \"${team.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync team \"${team.name}\"! - ${(err as Error).message}`);\n }\n });\n\n // Update all teams\n await mapSeries(updatedTeams, async (input) => {\n try {\n const newTeam = await updateTeam(client, {\n input: { ...input, id: teamsByName[input.name]!.id },\n parentTeamIdsByName,\n logger,\n });\n teamsByName[newTeam.name] = newTeam;\n logger.info(`Successfully updated team \"${input.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync team \"${input.name}\"! - ${(err as Error).message}`);\n }\n });\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\nexport const ATTRIBUTE_KEYS_REQUESTS = gql`\n query TranscendCliAttributeKeys($first: Int!, $offset: Int!) {\n attributeKeys(\n filterBy: { enabledOn: [request] }\n first: $first\n useMaster: false\n offset: $offset\n ) {\n nodes {\n id\n name\n type\n }\n }\n }\n`;\n","import got, { type Got } from 'got';\n\n/**\n * Instantiate an instance of got that is capable of making requests\n * to a sombra gateway.\n *\n * @param transcendUrl - URL of Transcend API\n * @returns The instance of got that is capable of making requests to the customer ingress\n */\nexport function createTranscendConsentGotInstance(transcendUrl: string): Got {\n return (got as unknown as Got).extend({\n prefixUrl: transcendUrl,\n });\n}\n","import { gql } from 'graphql-request';\n\n// ---- FETCH_CONSENT_MANAGER_ID query ----\n\n/** Full response from FETCH_CONSENT_MANAGER_ID */\nexport interface TranscendCliFetchConsentManagerIdResponse {\n /** Consent manager wrapper */\n consentManager: {\n /** Consent manager object */\n consentManager: {\n /** Consent manager / airgap bundle ID */\n id: string;\n };\n };\n}\n\nexport const FETCH_CONSENT_MANAGER_ID = gql`\n query TranscendCliFetchConsentManagerId {\n consentManager {\n consentManager {\n id\n }\n }\n }\n`;\n\n// ---- FETCH_CONSENT_MANAGER query ----\n\n/** Consent manager configuration */\nexport interface TranscendConsentManagerConfigGql {\n /** Configured domains */\n domains: string[];\n /** Consent precedence setting */\n consentPrecedence: string;\n /** Unknown request policy */\n unknownRequestPolicy: string;\n /** Unknown cookie policy */\n unknownCookiePolicy: string;\n /** Sync endpoint URL */\n syncEndpoint: string;\n /** Telemetry partitioning strategy */\n telemetryPartitioning: string;\n /** Signed IAB agreement status */\n signedIabAgreement: string;\n /** Sync groups setting */\n syncGroups: string;\n /** Partition parameter */\n partition: string;\n}\n\n/** Consent manager node */\nexport interface TranscendConsentManagerGql {\n /** Consent manager / airgap bundle ID */\n id: string;\n /** Production bundle URL */\n bundleURL: string;\n /** Test bundle URL */\n testBundleURL: string;\n /** Configuration settings */\n configuration: TranscendConsentManagerConfigGql;\n /** Partition info */\n partition?: {\n /** Partition value */\n partition: string;\n };\n}\n\n/** Full response from FETCH_CONSENT_MANAGER */\nexport interface TranscendCliFetchConsentManagerResponse {\n /** Consent manager wrapper */\n consentManager: {\n /** Consent manager object */\n consentManager: TranscendConsentManagerGql;\n };\n}\n\nexport const FETCH_CONSENT_MANAGER = gql`\n query TranscendCliFetchConsentManager {\n consentManager {\n consentManager {\n id\n bundleURL\n testBundleURL\n configuration {\n domains\n consentPrecedence\n unknownRequestPolicy\n unknownCookiePolicy\n syncEndpoint\n telemetryPartitioning\n signedIabAgreement\n syncGroups\n partition\n }\n partition {\n partition\n }\n }\n }\n }\n`;\n\n// ---- FETCH_CONSENT_MANAGER_THEME query ----\n\n/** Consent manager theme fields */\nexport interface TranscendConsentManagerThemeGql {\n /** Primary color */\n primaryColor: string;\n /** Font color */\n fontColor: string;\n /** Privacy policy URL */\n privacyPolicy?: string;\n /** Auto-prompt setting */\n prompt: number;\n}\n\n/** Full response from FETCH_CONSENT_MANAGER_THEME */\nexport interface TranscendCliFetchConsentManagerThemeResponse {\n /** Theme wrapper */\n consentManagerTheme: {\n /** Theme object */\n theme: TranscendConsentManagerThemeGql;\n };\n}\n\nexport const FETCH_CONSENT_MANAGER_THEME = gql`\n query TranscendCliFetchConsentManagerTheme($airgapBundleId: ID!) {\n consentManagerTheme(input: { airgapBundleId: $airgapBundleId }) {\n theme {\n primaryColor\n fontColor\n privacyPolicy\n prompt\n }\n }\n }\n`;\n\n// ---- Consent Manager mutations ----\n\n/** Shared response type for mutations that return only clientMutationId */\nexport interface TranscendClientMutationIdResponse {\n /** Mutation result keyed by mutation name */\n [key: string]: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const CREATE_CONSENT_MANAGER = gql`\n mutation TranscendCliCreateConsentManager($privacyCenterId: ID!) {\n createConsentManager(input: { privacyCenterId: $privacyCenterId }) {\n consentManager {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_VERSION = gql`\n mutation TranscendCliUpdateConsentManager($airgapBundleId: ID!, $version: String!) {\n updateConsentManager(id: $airgapBundleId, input: { version: $version }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_TO_LATEST = gql`\n mutation TranscendCliUpdateConsentManagerToLatest(\n $airgapBundleId: ID!\n $bundleType: ConsentBundleType!\n ) {\n updateConsentManagerToLatestVersion(id: $airgapBundleId, input: { bundleType: $bundleType }) {\n clientMutationId\n }\n }\n`;\n\nexport const DEPLOY_CONSENT_MANAGER = gql`\n mutation TranscendCliDeployConsentManager($airgapBundleId: ID!, $bundleType: ConsentBundleType!) {\n deployConsentManagerBundle(id: $airgapBundleId, input: { bundleType: $bundleType }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_DOMAINS = gql`\n mutation TranscendCliUpdateConsentManagerDomains($airgapBundleId: ID!, $domains: [String!]!) {\n updateConsentManagerDomains(input: { id: $airgapBundleId, domains: $domains }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_PARTITION = gql`\n mutation TranscendCliUpdateConsentManagerPartition($airgapBundleId: ID!, $partitionId: ID!) {\n updateConsentManagerPartition(input: { id: $airgapBundleId, partitionId: $partitionId }) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_LOAD_OPTIONS = gql`\n mutation TranscendCliUpdateLoadOptions($input: UpdateLoadOptionsInput!) {\n updateLoadOptions(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_UNKNOWN_REQUEST_POLICY = gql`\n mutation TranscendCliToggleUnknownRequestPolicy($input: ToggleUnknownRequestPolicyInput!) {\n toggleUnknownRequestPolicy(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_UNKNOWN_COOKIE_POLICY = gql`\n mutation TranscendCliToggleUnknownCookiePolicy($input: ToggleUnknownCookiePolicyInput!) {\n toggleUnknownCookiePolicy(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_TELEMETRY_PARTITION_STRATEGY = gql`\n mutation TranscendCliToggleTelemetryPartitionStrategy(\n $input: ToggleTelemetryPartitionStrategyInput!\n ) {\n toggleTelemetryPartitioning(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const TOGGLE_CONSENT_PRECEDENCE = gql`\n mutation TranscendCliToggleConsentPrecedence($input: ToggleConsentPrecedenceInput!) {\n toggleConsentPrecedence(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_CONSENT_MANAGER_THEME = gql`\n mutation TranscendCliUpdateConsentManagerTheme($input: UpdateConsentManagerThemeInput!) {\n updateConsentManagerTheme(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const FETCH_CONSENT_UI_VARIANTS = gql`\n query TranscendCliConsentUiVariants($airgapBundleId: ID!, $first: Int!, $offset: Int!) {\n consentUiVariants(input: { airgapBundleId: $airgapBundleId }, first: $first, offset: $offset) {\n totalCount\n nodes {\n id\n name\n slug\n description\n status\n locales\n configuration {\n firstLayerConfig {\n showCloseButton\n alphaMask\n footer {\n showTranscendBadge\n showGpcDetected\n showLocalePicker\n }\n links\n buttons {\n type\n action\n }\n showHeader\n }\n secondLayerConfig {\n showCloseButton\n alphaMask\n footer {\n showTranscendBadge\n showGpcDetected\n showLocalePicker\n }\n links\n buttons {\n type\n action\n }\n showBulkActions\n closeAfterBulkAction\n showPurposeDescriptions\n showToggleText\n showToggleIcon\n }\n defaultLocale\n }\n theme {\n id\n slug\n }\n userFlow\n }\n }\n }\n`;\n\nexport const FETCH_CONSENT_UI_THEMES = gql`\n query TranscendCliConsentUiThemes($airgapBundleId: ID!, $first: Int!, $offset: Int!) {\n consentUiThemes(input: { airgapBundleId: $airgapBundleId }, first: $first, offset: $offset) {\n totalCount\n nodes {\n id\n name\n slug\n configuration {\n buttonThemes {\n backgroundColor\n backgroundHoverColor\n textColor\n textHoverColor\n borderRadius\n borderColor\n borderHoverColor\n linkColor\n linkHoverColor\n linkUnderline\n }\n firstLayer {\n alwaysShowScrollbar\n background {\n backgroundColor\n }\n buttons\n closeButton {\n backgroundColor\n backgroundHoverColor\n borderRadius\n borderColor\n borderHoverColor\n iconColor\n iconHoverColor\n }\n container {\n backgroundColor\n borderRadius\n borderColor\n }\n contentFlow\n contentLayout {\n breakpoint {\n value\n unit\n }\n maxWidths {\n flow\n width\n }\n fullWidth\n }\n cookieAndPrivacyPolicy {\n linkColor\n linkUnderline\n }\n poweredByTranscend {\n linkColor\n }\n description {\n textColor\n linkColor\n linkUnderline\n }\n footer {\n backgroundColor\n localePickerColor\n }\n header {\n textColor\n logoPosition\n logoImageUrl\n }\n horizontalAlign\n verticalAlign\n }\n secondLayer {\n alwaysOnText {\n textColor\n }\n alwaysShowScrollbar\n background {\n backgroundColor\n }\n bulkActionButtons\n buttons\n cardTitle {\n textColor\n }\n caretIcon {\n iconColor\n }\n closeButton {\n backgroundColor\n backgroundHoverColor\n borderColor\n borderHoverColor\n borderRadius\n iconColor\n iconHoverColor\n }\n container {\n backgroundColor\n borderRadius\n borderColor\n }\n cookieAndPrivacyPolicy {\n linkColor\n linkUnderline\n }\n poweredByTranscend {\n linkColor\n }\n description {\n textColor\n linkColor\n linkUnderline\n }\n footer {\n backgroundColor\n localePickerColor\n }\n header {\n logoImageUrl\n logoPosition\n textColor\n }\n horizontalAlign\n lockToEdges\n maxWidth\n modalSubtitle {\n textColor\n }\n modalTitle {\n textColor\n }\n purposeCard {\n backgroundColor\n borderRadius\n borderColor\n }\n purposeDescription {\n textColor\n linkColor\n linkUnderline\n }\n purposeListTitle {\n textColor\n }\n shrinkToFullWidth\n toggle {\n textColor\n iconColor\n enabledColor\n disabledColor\n knobColor\n }\n }\n }\n }\n }\n }\n`;\n\nexport const CREATE_CONSENT_UI_VARIANT = gql`\n mutation TranscendCliCreateConsentUiVariant($input: CreateConsentUiVariantInput!) {\n createConsentUiVariant(input: $input) {\n consentUiVariant {\n id\n }\n }\n }\n`;\n\nexport const UPDATE_CONSENT_UI_VARIANT = gql`\n mutation TranscendCliUpdateConsentUiVariant($input: UpdateConsentUiVariantInput!) {\n updateConsentUiVariant(input: $input) {\n consentUiVariant {\n id\n }\n }\n }\n`;\n\nexport const CREATE_CONSENT_UI_THEME = gql`\n mutation TranscendCliCreateConsentUiTheme($input: CreateConsentUiThemeInput!) {\n createConsentUiTheme(input: $input) {\n consentUiTheme {\n id\n name\n }\n }\n }\n`;\n\nexport const UPDATE_CONSENT_UI_THEME = gql`\n mutation TranscendCliUpdateConsentUiTheme($input: UpdateConsentUiThemeInput!) {\n updateConsentUiTheme(input: $input) {\n consentUiTheme {\n id\n }\n }\n }\n`;\n","import { ConsentBundleType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DEPLOY_CONSENT_MANAGER, UPDATE_CONSENT_MANAGER_TO_LATEST } from './gqls/consentManager.js';\n\n/**\n * Deploy the Consent Manager\n *\n * @param client - GraphQL client\n * @param input - Deploy input\n * @param options - Options\n */\nexport async function deployConsentManager(\n client: GraphQLClient,\n input: {\n /** ID of Consent Manager */\n id: string;\n /** Type of bundle */\n bundleType: ConsentBundleType;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n await makeGraphQLRequest(client, DEPLOY_CONSENT_MANAGER, {\n variables: { airgapBundleId: input.id, bundleType: input.bundleType },\n logger: options.logger,\n });\n}\n\n/**\n * Update the Consent Manager to the latest airgap.js version\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateConsentManagerToLatest(\n client: GraphQLClient,\n options: {\n /** Consent manager to update */\n input: {\n /** ID of Consent Manager */\n id: string;\n /** Type of bundle */\n bundleType: ConsentBundleType;\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: consentManager, logger } = options;\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_TO_LATEST, {\n variables: { airgapBundleId: consentManager.id, bundleType: consentManager.bundleType },\n logger,\n });\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { FETCH_CONSENT_MANAGER_ID } from './gqls/consentManager.js';\n\n/**\n * Fetch consent manager ID\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager ID in organization\n */\nexport async function fetchConsentManagerId(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Max number of requests to send */\n maxRequests?: number;\n } = {},\n): Promise<string> {\n const {\n consentManager: { consentManager },\n } = await makeGraphQLRequest<{\n /** Consent manager query */\n consentManager: {\n /** Consent manager object */\n consentManager: {\n /** ID of bundle */\n id: string;\n };\n };\n }>(client, FETCH_CONSENT_MANAGER_ID, {\n logger: options.logger,\n maxRetries: options.maxRequests,\n });\n return consentManager.id;\n}\n","/** Service associated with a cookie or data flow */\nexport interface TranscendCookieServiceGql {\n /** Service ID */\n id: string;\n /** Human-readable service title */\n title: string;\n /** Integration slug */\n integrationName: string;\n}\n\n/** Service fields shared by cookies and data flows */\nexport const SERVICE_FIELDS = `\n id\n title\n integrationName\n`;\n\n/** Tracking purpose assigned to a cookie or data flow */\nexport interface TranscendTrackingPurposeGql {\n /** Purpose ID */\n id: string;\n /** Purpose display name */\n name: string;\n /** Purpose slug (e.g. \"Advertising\", \"Analytics\") */\n trackingType: string;\n}\n\n/** Tracking purpose fields */\nexport const TRACKING_PURPOSE_FIELDS = `\n id\n name\n trackingType\n`;\n","import type { ConsentTrackerSource, ConsentTrackerStatus } from '@transcend-io/privacy-types';\nimport { gql } from 'graphql-request';\n\nimport {\n ATTRIBUTE_VALUE_FIELDS,\n OWNER_FIELDS,\n TEAM_FIELDS,\n type TranscendAttributeValueGql,\n type TranscendOwnerGql,\n type TranscendTeamGql,\n} from '../../gqls/shared.js';\nimport {\n SERVICE_FIELDS,\n TRACKING_PURPOSE_FIELDS,\n type TranscendCookieServiceGql,\n type TranscendTrackingPurposeGql,\n} from './shared.js';\n\n// ---- Shared cookie types ----\n\n/** Cookie domain with occurrence count */\nexport interface TranscendCookieDomainGql {\n /** Domain ID */\n id: string;\n /** Domain name */\n domain: string;\n /** Number of occurrences on this domain */\n occurrences: number;\n}\n\n/** Cookie node returned by COOKIES query */\nexport interface TranscendCookieGql {\n /** Cookie ID */\n id: string;\n /** Cookie name */\n name: string;\n /** Whether this is a regex pattern */\n isRegex: boolean;\n /** Description */\n description?: string;\n /** Tracking purpose slugs (string array) */\n trackingPurposes: string[];\n /** Resolved tracking purposes */\n purposes: TranscendTrackingPurposeGql[];\n /** Frequency of occurrence (0-1) */\n frequency: number;\n /** Associated service */\n service?: TranscendCookieServiceGql;\n /** Whether marked as junk */\n isJunk: boolean;\n /** How the tracker was discovered */\n source: ConsentTrackerSource;\n /** Triage status */\n status: ConsentTrackerStatus;\n /** Assigned owners */\n owners: TranscendOwnerGql[];\n /** Assigned teams */\n teams: TranscendTeamGql[];\n /** Custom attribute values */\n attributeValues: TranscendAttributeValueGql[];\n /** Creation timestamp */\n createdAt: string;\n /** Last updated timestamp */\n updatedAt: string;\n /** Last seen by telemetry */\n lastDiscoveredAt?: string;\n /** Domains this cookie appears on */\n domains: TranscendCookieDomainGql[];\n /** Total telemetry occurrences */\n occurrences: number;\n /** Number of sites seen on (all time) */\n consentSiteCountAllTime: number;\n /** Number of sites seen on (last week) */\n consentSiteCountLastWeek: number;\n}\n\n/** Input for updating or creating a cookie */\nexport interface TranscendUpdateCookieInputGql {\n /** Cookie name (used as identifier for upsert) */\n name: string;\n /** Tracking purpose slugs */\n trackingPurposes?: string[];\n /** Purpose IDs to assign */\n purposeIds?: string[];\n /** Description */\n description?: string;\n /** Service integration name */\n service?: string;\n /** Mark as junk */\n isJunk?: boolean;\n /** Triage status */\n status?: ConsentTrackerStatus;\n /** Whether this is a regex pattern */\n isRegex?: boolean;\n /** How the tracker was discovered */\n source?: ConsentTrackerSource;\n}\n\n// ---- COOKIES query ----\n\n/** Full response from the COOKIES query */\nexport interface TranscendCliCookiesResponse {\n /** Cookies result */\n cookies: {\n /** List of cookie nodes */\n nodes: TranscendCookieGql[];\n /** Total count of matching cookies */\n totalCount: number;\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const COOKIES = gql`\n query TranscendCliCookies(\n $input: AirgapBundleInput!\n $first: Int!\n $offset: Int!\n $filterBy: CookiesFiltersInput\n $orderBy: [CookieOrder!]\n ) {\n cookies(\n input: $input\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: $orderBy\n useMaster: false\n ) {\n totalCount\n nodes {\n id\n name\n isRegex\n description\n trackingPurposes\n purposes { ${TRACKING_PURPOSE_FIELDS} }\n frequency\n service { ${SERVICE_FIELDS} }\n isJunk\n source\n status\n owners { ${OWNER_FIELDS} }\n teams { ${TEAM_FIELDS} }\n attributeValues { ${ATTRIBUTE_VALUE_FIELDS} }\n createdAt\n updatedAt\n lastDiscoveredAt\n domains {\n id\n domain\n occurrences\n }\n occurrences\n consentSiteCountAllTime\n consentSiteCountLastWeek\n }\n }\n }\n`;\n\n// ---- UPDATE_OR_CREATE_COOKIES mutation ----\n\n/** Full response from the UPDATE_OR_CREATE_COOKIES mutation */\nexport interface TranscendCliUpdateOrCreateCookiesResponse {\n /** Mutation result */\n updateOrCreateCookies: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const UPDATE_OR_CREATE_COOKIES = gql`\n mutation TranscendCliUpdateOrCreateCookies(\n $cookies: [UpdateOrCreateCookieInput!]!\n $airgapBundleId: ID!\n ) {\n updateOrCreateCookies(input: { airgapBundleId: $airgapBundleId, cookies: $cookies }) {\n clientMutationId\n }\n }\n`;\n\n// ---- DELETE_COOKIES mutation ----\n\n/** Full response from the DELETE_COOKIES mutation */\nexport interface TranscendCliDeleteCookiesResponse {\n /** Mutation result */\n deleteCookies: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const DELETE_COOKIES = gql`\n mutation TranscendCliDeleteCookies($input: DeleteCookiesInput!) {\n deleteCookies(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ConsentTrackerStatus, OrderDirection } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport {\n COOKIES,\n type TranscendCookieGql,\n type TranscendCliCookiesResponse,\n} from './gqls/cookies.js';\n\n/** Sort order for cookie queries */\nexport interface CookieOrder {\n /** Field to sort by */\n field: string;\n /** Sort direction */\n direction: OrderDirection;\n}\n\nconst PAGE_SIZE = 20;\n\nconst DEFAULT_COOKIE_ORDER: CookieOrder[] = [\n { field: 'createdAt', direction: OrderDirection.Asc },\n { field: 'name', direction: OrderDirection.Asc },\n];\n\n/**\n * Fetch all Cookies in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All Cookies in the organization\n */\nexport async function fetchAllCookies(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** The status to fetch */\n status?: ConsentTrackerStatus;\n };\n /** Sort ordering (defaults to createdAt ASC, name ASC) */\n orderBy?: CookieOrder[];\n } = {},\n): Promise<TranscendCookieGql[]> {\n const {\n logger,\n filterBy: { status = ConsentTrackerStatus.Live } = {},\n orderBy = DEFAULT_COOKIE_ORDER,\n } = options;\n const cookies: TranscendCookieGql[] = [];\n let offset = 0;\n\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n let shouldContinue = false;\n do {\n const {\n cookies: { nodes },\n } = await makeGraphQLRequest<TranscendCliCookiesResponse>(client, COOKIES, {\n variables: {\n input: { airgapBundleId },\n first: PAGE_SIZE,\n offset,\n filterBy: { status },\n orderBy,\n },\n logger,\n });\n cookies.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return cookies.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import type {\n ConsentTrackerSource,\n ConsentTrackerStatus,\n DataFlowScope,\n} from '@transcend-io/privacy-types';\nimport { gql } from 'graphql-request';\n\nimport {\n ATTRIBUTE_VALUE_FIELDS,\n OWNER_FIELDS,\n TEAM_FIELDS,\n type TranscendAttributeValueGql,\n type TranscendOwnerGql,\n type TranscendTeamGql,\n} from '../../gqls/shared.js';\nimport {\n SERVICE_FIELDS,\n TRACKING_PURPOSE_FIELDS,\n type TranscendCookieServiceGql,\n type TranscendTrackingPurposeGql,\n} from './shared.js';\n\n// ---- Shared data flow types ----\n\n/** Data flow node returned by DATA_FLOWS query and UPDATE_DATA_FLOWS mutation */\nexport interface TranscendDataFlowGql {\n /** Data flow ID */\n id: string;\n /** URL/host value */\n value: string;\n /** Scope type (HOST, PATH, REGEX, etc.) */\n type: DataFlowScope;\n /** Description */\n description?: string;\n /** Tracking purpose slugs */\n trackingType: string[];\n /** Resolved tracking purposes */\n purposes: TranscendTrackingPurposeGql[];\n /** Frequency of occurrence (0-1) */\n frequency: number;\n /** Associated service */\n service?: TranscendCookieServiceGql;\n /** Whether marked as junk */\n isJunk: boolean;\n /** How the tracker was discovered */\n source: ConsentTrackerSource;\n /** Triage status */\n status: ConsentTrackerStatus;\n /** Assigned owners */\n owners: TranscendOwnerGql[];\n /** Assigned teams */\n teams: TranscendTeamGql[];\n /** Custom attribute values */\n attributeValues: TranscendAttributeValueGql[];\n /** Creation timestamp */\n createdAt: string;\n /** Last updated timestamp */\n updatedAt: string;\n /** Last seen by telemetry */\n lastDiscoveredAt?: string;\n /** Total telemetry occurrences */\n occurrences: number;\n /** Number of sites seen on (all time) */\n consentSiteCountAllTime: number;\n /** Number of sites seen on (last week) */\n consentSiteCountLastWeek: number;\n}\n\n/** Input for updating a data flow */\nexport interface TranscendUpdateDataFlowInputGql {\n /** Data flow ID */\n id: string;\n /** URL/host value */\n value?: string;\n /** Scope type */\n type?: DataFlowScope;\n /** Tracking purpose slugs */\n trackingType?: string[];\n /** Purpose IDs to assign */\n purposeIds?: string[];\n /** Description */\n description?: string;\n /** Service integration name */\n service?: string;\n /** Mark as junk */\n isJunk?: boolean;\n /** Triage status */\n status?: ConsentTrackerStatus;\n}\n\n// ---- Shared field selection for mutation responses ----\n\nconst DATA_FLOW_RESPONSE_FIELDS = `\n id\n value\n type\n description\n trackingType\n purposes { ${TRACKING_PURPOSE_FIELDS} }\n frequency\n service { ${SERVICE_FIELDS} }\n isJunk\n source\n status\n createdAt\n updatedAt\n lastDiscoveredAt\n occurrences\n consentSiteCountAllTime\n consentSiteCountLastWeek\n`;\n\n// ---- CREATE_DATA_FLOWS mutation ----\n\n/** Full response from the CREATE_DATA_FLOWS mutation */\nexport interface TranscendCliCreateDataFlowsResponse {\n /** Mutation result */\n createDataFlows: {\n /** Created data flows (IDs only) */\n dataFlows: {\n /** Data flow ID */\n id: string;\n }[];\n };\n}\n\nexport const CREATE_DATA_FLOWS = gql`\n mutation TranscendCliCreateDataFlows(\n $dataFlows: [DataFlowInput!]!\n $airgapBundleId: ID!\n $classifyService: Boolean\n ) {\n createDataFlows(\n input: {\n airgapBundleId: $airgapBundleId\n dataFlows: $dataFlows\n classifyService: $classifyService\n }\n ) {\n dataFlows {\n id\n }\n }\n }\n`;\n\n// ---- UPDATE_DATA_FLOWS mutation ----\n\n/** Full response from the UPDATE_DATA_FLOWS mutation */\nexport interface TranscendCliUpdateDataFlowsResponse {\n /** Mutation result */\n updateDataFlows: {\n /** Updated data flows with full fields */\n dataFlows: TranscendDataFlowGql[];\n };\n}\n\nexport const UPDATE_DATA_FLOWS = gql`\n mutation TranscendCliUpdateDataFlows(\n $airgapBundleId: ID!\n $dataFlows: [UpdateDataFlowInput!]!\n $classifyService: Boolean\n ) {\n updateDataFlows(\n input: {\n airgapBundleId: $airgapBundleId\n dataFlows: $dataFlows\n classifyService: $classifyService\n }\n ) {\n dataFlows { ${DATA_FLOW_RESPONSE_FIELDS} }\n }\n }\n`;\n\n// ---- DATA_FLOWS query ----\n\n/** Full response from the DATA_FLOWS query */\nexport interface TranscendCliDataFlowsResponse {\n /** Data flows result */\n dataFlows: {\n /** List of data flow nodes */\n nodes: TranscendDataFlowGql[];\n /** Total count of matching data flows */\n totalCount: number;\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const DATA_FLOWS = gql`\n query TranscendCliDataFlows(\n $input: AirgapBundleInput!\n $first: Int!\n $offset: Int!\n $filterBy: DataFlowsFiltersInput\n $orderBy: [DataFlowOrder!]\n ) {\n dataFlows(\n input: $input\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: $orderBy\n useMaster: false\n ) {\n totalCount\n nodes {\n id\n value\n type\n description\n trackingType\n purposes { ${TRACKING_PURPOSE_FIELDS} }\n frequency\n service { ${SERVICE_FIELDS} }\n isJunk\n source\n status\n owners { ${OWNER_FIELDS} }\n teams { ${TEAM_FIELDS} }\n attributeValues { ${ATTRIBUTE_VALUE_FIELDS} }\n createdAt\n updatedAt\n lastDiscoveredAt\n occurrences\n consentSiteCountAllTime\n consentSiteCountLastWeek\n }\n }\n }\n`;\n\n// ---- DELETE_DATA_FLOWS mutation ----\n\n/** Full response from the DELETE_DATA_FLOWS mutation */\nexport interface TranscendCliDeleteDataFlowsResponse {\n /** Mutation result */\n deleteDataFlows: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const DELETE_DATA_FLOWS = gql`\n mutation TranscendCliDeleteDataFlows($input: DeleteDataFlowsInput!) {\n deleteDataFlows(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ConsentTrackerStatus, OrderDirection } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport {\n DATA_FLOWS,\n type TranscendDataFlowGql,\n type TranscendCliDataFlowsResponse,\n} from './gqls/dataFlows.js';\n\n/** Sort order for data flow queries */\nexport interface DataFlowOrder {\n /** Field to sort by */\n field: string;\n /** Sort direction */\n direction: OrderDirection;\n}\n\nconst PAGE_SIZE = 20;\n\nconst DEFAULT_DATA_FLOW_ORDER: DataFlowOrder[] = [\n { field: 'createdAt', direction: OrderDirection.Asc },\n { field: 'value', direction: OrderDirection.Asc },\n];\n\n/**\n * Fetch all DataFlows in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All DataFlows in the organization\n */\nexport async function fetchAllDataFlows(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** The status to fetch */\n status?: ConsentTrackerStatus;\n };\n /** Sort ordering (defaults to createdAt ASC, value ASC) */\n orderBy?: DataFlowOrder[];\n } = {},\n): Promise<TranscendDataFlowGql[]> {\n const {\n logger,\n filterBy: { status = ConsentTrackerStatus.Live } = {},\n orderBy = DEFAULT_DATA_FLOW_ORDER,\n } = options;\n const dataFlows: TranscendDataFlowGql[] = [];\n let offset = 0;\n\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n let shouldContinue = false;\n do {\n const {\n dataFlows: { nodes },\n } = await makeGraphQLRequest<TranscendCliDataFlowsResponse>(client, DATA_FLOWS, {\n variables: {\n input: { airgapBundleId },\n first: PAGE_SIZE,\n offset,\n filterBy: {\n status,\n ...(status === ConsentTrackerStatus.NeedsReview ? { showZeroActivity: true } : {}),\n },\n orderBy,\n },\n logger,\n });\n dataFlows.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return dataFlows.sort((a, b) => a.value.localeCompare(b.value));\n}\n","import { gql } from 'graphql-request';\n\nexport const FETCH_PRIVACY_CENTER_ID = gql`\n query TranscendCliFetchPrivacyCenterId($url: String!) {\n privacyCenter(lookup: { url: $url }) {\n id\n }\n }\n`;\n\nexport const DEPLOYED_PRIVACY_CENTER_URL = gql`\n query TranscendCliDeployedPrivacyCenterUrl {\n organization {\n deployedPrivacyCenterUrl\n }\n }\n`;\n\nexport const PRIVACY_CENTER = gql`\n query TranscendCliFetchPrivacyCenters($url: String!) {\n privacyCenter(lookup: { url: $url }) {\n id\n url\n isDisabled\n showPrivacyRequestButton\n showPolicies\n showTrackingTechnologies\n showCookies\n showDataFlows\n showConsentManager\n showManageYourPrivacy\n showMarketingPreferences\n locales\n defaultLocale\n preferBrowserDefaultLocale\n supportEmail\n replyToEmail\n useNoReplyEmailAddress\n useCustomEmailDomain\n transformAccessReportJsonToCsv\n home {\n defaultMessage\n }\n expandSideMenuByDefault\n workflowsCustomFieldsRequired\n footerLayout\n themeStr\n childOrganizations {\n id\n uri\n name\n }\n footerLinks {\n id\n displayOrder\n title {\n defaultMessage\n }\n url {\n defaultMessage\n }\n icon {\n id\n src\n key\n size\n mimetype\n }\n iconOnly\n }\n }\n }\n`;\n\nexport const UPDATE_PRIVACY_CENTER = gql`\n mutation TranscendCliUpdatePrivacyCenter($input: UpdatePrivacyCenterInput!) {\n updatePrivacyCenter(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_PRIVACY_CENTER_FOOTER_LINKS = gql`\n mutation TranscendCliUpdatePrivacyCenterFooterLinks(\n $input: UpdatePrivacyCenterFooterLinksInput!\n ) {\n updatePrivacyCenterFooterLinks(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const DELETE_PRIVACY_CENTER_FOOTER_LINKS = gql`\n mutation TranscendCliDeletePrivacyCenterFooterLinks(\n $input: DeletePrivacyCenterFooterLinksInput!\n ) {\n deletePrivacyCenterFooterLinks(input: $input) {\n clientMutationId\n success\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { DEPLOYED_PRIVACY_CENTER_URL, FETCH_PRIVACY_CENTER_ID } from './gqls/privacyCenter.js';\n\n/**\n * Fetch default privacy center URL\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Privacy center URL in organization\n */\nexport async function fetchPrivacyCenterUrl(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<string> {\n const { organization } = await makeGraphQLRequest<{\n /** Organization */\n organization: {\n /** URL */\n deployedPrivacyCenterUrl: string;\n };\n }>(client, DEPLOYED_PRIVACY_CENTER_URL, { logger: options.logger });\n return organization.deployedPrivacyCenterUrl;\n}\n\n/**\n * Fetch privacy center ID\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Privacy center ID in organization\n */\nexport async function fetchPrivacyCenterId(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** URL to look up */\n url?: string;\n };\n } = {},\n): Promise<string> {\n const { logger, filterBy: { url } = {} } = options;\n let urlToUse = url;\n if (!urlToUse) {\n urlToUse = await fetchPrivacyCenterUrl(client, { logger });\n }\n const { privacyCenter } = await makeGraphQLRequest<{\n /** Privacy center query */\n privacyCenter: {\n /** ID of bundle */\n id: string;\n };\n }>(client, FETCH_PRIVACY_CENTER_ID, {\n variables: { url: urlToUse },\n logger,\n });\n return privacyCenter.id;\n}\n","import { gql } from 'graphql-request';\n\nexport const POLICIES = gql`\n query TranscendCliFetchPolicies($url: String!) {\n privacyCenterPolicies(lookup: { url: $url }) {\n id\n title {\n defaultMessage\n }\n type\n disableEffectiveOn\n disabledLocales\n versions {\n effectiveOn\n content {\n defaultMessage\n }\n }\n }\n }\n`;\n\nexport const UPDATE_POLICIES = gql`\n mutation TranscendCliUpdatePolicies($policies: [PolicyInput!]!, $privacyCenterId: ID!) {\n updatePolicies(\n input: { privacyCenterId: $privacyCenterId, policies: $policies, skipPublish: true }\n ) {\n clientMutationId\n }\n }\n`;\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type { PolicyType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchPrivacyCenterUrl } from './fetchPrivacyCenterId.js';\nimport { POLICIES } from './gqls/policy.js';\n\nexport interface Policy {\n /** ID of policy */\n id: string;\n /** Title of policy */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** Policy type */\n type: PolicyType;\n /** Disabled locales */\n disabledLocales: LocaleValue[];\n /** Versions */\n versions: {\n /** Message content */\n content: {\n /** Default message */\n defaultMessage: string;\n };\n }[];\n}\n\n/**\n * Fetch all policies in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All policies in the organization\n */\nexport async function fetchAllPolicies(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Policy[]> {\n const { logger } = options;\n const deployedPrivacyCenterUrl = await fetchPrivacyCenterUrl(client, { logger });\n const { privacyCenterPolicies } = await makeGraphQLRequest<{\n /** Policies */\n privacyCenterPolicies: Policy[];\n }>(client, POLICIES, {\n variables: { url: deployedPrivacyCenterUrl },\n logger,\n });\n\n return privacyCenterPolicies.sort((a, b) =>\n a.title.defaultMessage.localeCompare(b.title.defaultMessage),\n );\n}\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type {\n PrivacyCenterFooterLayout,\n PrivacyCenterThemePartial,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchPrivacyCenterUrl } from './fetchPrivacyCenterId.js';\nimport { PRIVACY_CENTER } from './gqls/privacyCenter.js';\n\nexport interface PrivacyCenterChildOrganization {\n /** Organization ID */\n id: string;\n /** Organization URI */\n uri: string;\n /** Organization name */\n name: string;\n}\n\nexport interface PrivacyCenterFooterLinkIcon {\n /** Asset file ID */\n id: string;\n /** Public URL where the asset can be downloaded */\n src: string;\n /** Storage key for the asset */\n key: string;\n /** File size in bytes */\n size: number;\n /** MIME type of the asset */\n mimetype: string;\n}\n\nexport interface PrivacyCenterFooterLink {\n /** Footer link ID */\n id: string;\n /** Display order */\n displayOrder: number;\n /** Link title */\n title: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Link URL (default locale) */\n url: string;\n /** Optional icon image for the footer link */\n icon?: PrivacyCenterFooterLinkIcon;\n /**\n * When true with an icon set, render as icon-only (title is used for\n * accessibility). When false with an icon set, render icon beside the\n * visible label.\n */\n iconOnly: boolean;\n}\n\nexport interface PrivacyCenter {\n /** ID of the privacy center */\n id: string;\n /** The URL of the privacy center */\n url: string;\n /** Whether or not the entire privacy center is enabled or disabled */\n isDisabled: boolean;\n /** Whether or not to show the privacy requests button */\n showPrivacyRequestButton: boolean;\n /** Whether or not to show the policies page */\n showPolicies: boolean;\n /** Whether or not to show the tracking technologies page */\n showTrackingTechnologies: boolean;\n /** Whether or not to show the cookies on the tracking technologies page */\n showCookies: boolean;\n /** Whether or not to show the data flows on the tracking technologies page */\n showDataFlows: boolean;\n /** Whether or not to show the consent manager opt out options on the tracking technologies page */\n showConsentManager: boolean;\n /** Whether or not to show the manage your privacy page */\n showManageYourPrivacy: boolean;\n /** Whether or not to show the marketing preferences page */\n showMarketingPreferences: boolean;\n /** What languages are supported for the privacy center */\n locales: LocaleValue[];\n /** The default locale for the privacy center */\n defaultLocale: LocaleValue;\n /** Whether or not to prefer the browser default locale */\n preferBrowserDefaultLocale: boolean;\n /** The email addresses of the employees within your company that are the go-to individuals for managing this privacy center */\n supportEmail: string;\n /** The email addresses of the employees within your company that are the go-to individuals for managing this privacy center */\n replyToEmail: string;\n /** Whether or not to send emails from a no reply email */\n useNoReplyEmailAddress: boolean;\n /** Whether or not to use a custom email domain */\n useCustomEmailDomain: boolean;\n /** Whether or not to transcend access requests from JSON to CSV */\n transformAccessReportJsonToCsv: boolean;\n /**\n * Privacy Center \"home\" / back-to-site link URL shown in the privacy center\n * header\n */\n home: string;\n /** Whether the side menu is expanded by default on the privacy center */\n expandSideMenuByDefault: boolean;\n /** Whether custom fields are required on privacy center workflows */\n workflowsCustomFieldsRequired: boolean;\n /** Footer layout */\n footerLayout: PrivacyCenterFooterLayout;\n /** Child organizations displayed on this privacy center */\n childOrganizations: PrivacyCenterChildOrganization[];\n /** Footer links displayed on this privacy center */\n footerLinks: PrivacyCenterFooterLink[];\n /** The theme object of colors to display on the privacy center */\n theme: PrivacyCenterThemePartial;\n}\n\n/**\n * Fetch all privacy centers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All privacy centers in the organization\n */\nexport async function fetchAllPrivacyCenters(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<PrivacyCenter[]> {\n const { logger } = options;\n const deployedPrivacyCenterUrl = await fetchPrivacyCenterUrl(client, { logger });\n const {\n privacyCenter: { themeStr, footerLinks, home, ...rest },\n } = await makeGraphQLRequest<{\n /** Privacy centers */\n privacyCenter: Omit<PrivacyCenter, 'theme' | 'footerLinks' | 'home'> & {\n /** Theme string */\n themeStr: string;\n /** Home / back-to-site link as returned by GraphQL */\n home: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Footer links as returned by GraphQL */\n footerLinks: {\n /** Footer link ID */\n id: string;\n /** Display order */\n displayOrder: number;\n /** Link title */\n title: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Link URL */\n url: {\n /** Default locale message */\n defaultMessage: string;\n };\n /** Optional icon image for the footer link */\n icon: PrivacyCenterFooterLinkIcon | null;\n /** Whether the link renders as icon-only */\n iconOnly: boolean;\n }[];\n };\n }>(client, PRIVACY_CENTER, {\n variables: { url: deployedPrivacyCenterUrl },\n logger,\n });\n\n return [\n {\n ...rest,\n home: home.defaultMessage,\n footerLinks: footerLinks.map((link) => ({\n id: link.id,\n displayOrder: link.displayOrder,\n title: link.title,\n url: link.url.defaultMessage,\n ...(link.icon ? { icon: link.icon } : {}),\n iconOnly: link.iconOnly,\n })),\n theme: JSON.parse(themeStr),\n },\n ];\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: name, direction: ASC }\n// ]\nexport const PROCESSING_PURPOSE_SUB_CATEGORIES = gql`\n query TranscendCliProcessingPurposeSubCategories($first: Int!, $offset: Int!) {\n processingPurposeSubCategories(\n first: $first\n offset: $offset\n isExportCsv: true\n useMaster: false\n ) {\n nodes {\n id\n name\n purpose\n description\n teams {\n name\n }\n owners {\n email\n }\n attributeValues {\n attributeKey {\n name\n }\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_PROCESSING_PURPOSE_SUB_CATEGORY = gql`\n mutation TranscendCliCreateProcessingPurposeSubCategory(\n $input: CreateProcessingPurposeCategoryInput!\n ) {\n createProcessingPurposeSubCategory(input: $input) {\n processingPurposeSubCategory {\n id\n name\n purpose\n }\n }\n }\n`;\n\nexport const UPDATE_PROCESSING_PURPOSE_SUB_CATEGORIES = gql`\n mutation TranscendCliUpdateProcessingPurposeSubCategories(\n $input: UpdateProcessingPurposeSubCategoriesInput!\n ) {\n updateProcessingPurposeSubCategories(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ProcessingPurpose } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PROCESSING_PURPOSE_SUB_CATEGORIES } from './gqls/processingPurpose.js';\n\nexport interface ProcessingPurposeSubCategory {\n /** ID of processing purpose */\n id: string;\n /** Name of processing purpose */\n name: string;\n /** Type of processing purpose */\n purpose: ProcessingPurpose;\n /** Description of processing purpose */\n description?: string;\n /** Assigned teams */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Assigned owners */\n owners: {\n /** Email */\n email: string;\n }[];\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all processingPurposeSubCategories in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All processingPurposeSubCategories in the organization\n */\nexport async function fetchAllProcessingPurposes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ProcessingPurposeSubCategory[]> {\n const processingPurposeSubCategories: ProcessingPurposeSubCategory[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n processingPurposeSubCategories: { nodes },\n } = await makeGraphQLRequest<{\n /** DataCategories */\n processingPurposeSubCategories: {\n /** List */\n nodes: ProcessingPurposeSubCategory[];\n };\n }>(client, PROCESSING_PURPOSE_SUB_CATEGORIES, {\n variables: { first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n processingPurposeSubCategories.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return processingPurposeSubCategories.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import {\n ConsentPrecedenceOption,\n UnknownRequestPolicy,\n TelemetryPartitionStrategy,\n SignedIabAgreementOption,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { FETCH_CONSENT_MANAGER } from './gqls/consentManager.js';\n\nexport interface ConsentManager {\n /** ID of consent manager */\n id: string;\n /** Production bundle URL */\n bundleURL: string;\n /** Test bundle URL */\n testBundleURL: string;\n /** Configuration of consent manager */\n configuration: {\n /** Domain list */\n domains: string[];\n /** Consent precedence of user vs signal */\n consentPrecedence: ConsentPrecedenceOption;\n /** Unknown request policy */\n unknownRequestPolicy: UnknownRequestPolicy;\n /** Unknown cookie policy */\n unknownCookiePolicy: UnknownRequestPolicy;\n /** Sync endpoint */\n syncEndpoint: string;\n /** Telemetry partitioning */\n telemetryPartitioning: TelemetryPartitionStrategy;\n /** Signed IAB agreement */\n signedIabAgreement: SignedIabAgreementOption;\n /** Sync groups */\n syncGroups: string;\n /** Partition parameter */\n partition: string;\n };\n /** When using a custom partition, this is the partition value */\n partition?: {\n /** Partition value */\n partition: string;\n };\n}\n\n/**\n * Fetch consent manager\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager ID in organization\n */\nexport async function fetchConsentManager(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentManager> {\n const {\n consentManager: { consentManager },\n } = await makeGraphQLRequest<{\n /** Consent manager query */\n consentManager: {\n /** Consent manager object */\n consentManager: ConsentManager;\n };\n }>(client, FETCH_CONSENT_MANAGER, { logger: options.logger });\n return consentManager;\n}\n","import { gql } from 'graphql-request';\n\nexport const CONSENT_MANAGER_ANALYTICS_DATA = gql`\n query TranscendCliConsentManagerAnalyticsData($input: AnalyticsInput!) {\n analyticsData(input: $input) {\n series {\n name\n points {\n key\n value\n }\n }\n }\n }\n`;\n","import { ConsentManagerMetricBin } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CONSENT_MANAGER_ANALYTICS_DATA } from './gqls/consentManagerMetrics.js';\n\nexport interface ConsentManagerMetric {\n /** Name of metric */\n name: string;\n /** The metrics */\n points: {\n /** Key of metric */\n key: string;\n /** Value of metric */\n value: string;\n }[];\n}\n\n/**\n * Fetch consent manager analytics data\n *\n * @param client - GraphQL client\n * @param input - Input for fetching data\n * @param options - Options\n * @returns Consent manager purposes in the organization\n */\nexport async function fetchConsentManagerAnalyticsData(\n client: GraphQLClient,\n input: {\n /** Data source */\n dataSource:\n | 'PRIVACY_SIGNAL_TIMESERIES'\n | 'CONSENT_CHANGES_TIMESERIES'\n | 'CONSENT_SESSIONS_BY_REGIME';\n /** Start date, in ISO string format */\n startDate: string;\n /** End date, in ISO string format */\n endDate: string;\n /** Force refetching */\n forceRefetch?: boolean;\n /** Airgap bundle ID */\n airgapBundleId: string;\n /** Bin interval */\n binInterval: ConsentManagerMetricBin;\n /** Whether or not to smooth the time series */\n smoothTimeseries: false;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentManagerMetric[]> {\n const {\n analyticsData: { series },\n } = await makeGraphQLRequest<{\n /** Analytics data response */\n analyticsData: {\n /** Consent manager metrics */\n series: ConsentManagerMetric[];\n };\n }>(client, CONSENT_MANAGER_ANALYTICS_DATA, {\n variables: { input },\n logger: options.logger,\n });\n return series;\n}\n","import { gql } from 'graphql-request';\n\n// ---- Shared experience types ----\n\n/** Region associated with a consent experience */\nexport interface TranscendExperienceRegionGql {\n /** ISO country subdivision code */\n countrySubDivision?: string;\n /** ISO country code */\n country?: string;\n}\n\n/** Experience purpose reference */\nexport interface TranscendExperiencePurposeGql {\n /** Purpose display name */\n name: string;\n /** Purpose slug */\n trackingType: string;\n}\n\n/** Consent experience (regional regime) returned by EXPERIENCES query */\nexport interface TranscendExperienceGql {\n /** Experience ID */\n id: string;\n /** Internal name */\n name: string;\n /** Display name shown to users */\n displayName?: string;\n /** Regions where this experience applies */\n regions: TranscendExperienceRegionGql[];\n /** Whether regions list is inclusive or exclusive */\n operator: string;\n /** Display priority for ordering */\n displayPriority: number;\n /** Behavior on consent expiry */\n onConsentExpiry: string;\n /** Consent expiry duration */\n consentExpiry: number;\n /** Initial view state for auto-prompting */\n viewState: string;\n /** Purposes that can be opted out of */\n purposes: TranscendExperiencePurposeGql[];\n /** Purposes opted out by default */\n optedOutPurposes: TranscendExperiencePurposeGql[];\n /** Browser languages for this experience */\n browserLanguages: string[];\n /** Browser time zones for this experience */\n browserTimeZones: string[];\n /** Consent UI variant assigned to this experience */\n consentUiVariant?: {\n /** Variant ID */\n id: string;\n };\n}\n\n// ---- EXPERIENCES query ----\n\n/** Full response from the EXPERIENCES query */\nexport interface TranscendCliExperiencesResponse {\n /** Experiences result */\n experiences: {\n /** Total count of experiences */\n totalCount: number;\n /** List of experience nodes */\n nodes: TranscendExperienceGql[];\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const EXPERIENCES = gql`\n query TranscendCliExperiences($first: Int!, $offset: Int!) {\n experiences(first: $first, offset: $offset, useMaster: false) {\n totalCount\n nodes {\n id\n name\n displayName\n regions {\n countrySubDivision\n country\n }\n operator\n displayPriority\n onConsentExpiry\n consentExpiry\n viewState\n purposes {\n name\n trackingType\n }\n optedOutPurposes {\n name\n trackingType\n }\n browserLanguages\n browserTimeZones\n consentUiVariant {\n id\n slug\n }\n }\n }\n }\n`;\n\n// ---- UPDATE_CONSENT_EXPERIENCE mutation ----\n\n/** Full response from the UPDATE_CONSENT_EXPERIENCE mutation */\nexport interface TranscendCliUpdateConsentExperienceResponse {\n /** Mutation result */\n updateExperience: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const UPDATE_CONSENT_EXPERIENCE = gql`\n mutation TranscendCliUpdateConsentExperience($input: UpdateExperienceInput!) {\n updateExperience(input: $input) {\n clientMutationId\n }\n }\n`;\n\n// ---- CREATE_CONSENT_EXPERIENCE mutation ----\n\n/** Full response from the CREATE_CONSENT_EXPERIENCE mutation */\nexport interface TranscendCliCreateConsentExperienceResponse {\n /** Mutation result */\n createExperience: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const CREATE_CONSENT_EXPERIENCE = gql`\n mutation TranscendCliCreateConsentExperience($input: CreateExperienceInput!) {\n createExperience(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { InitialViewState, BrowserLanguage, OnConsentExpiry } from '@transcend-io/airgap.js-types';\nimport {\n RegionsOperator,\n IsoCountrySubdivisionCode,\n IsoCountryCode,\n BrowserTimeZone,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { EXPERIENCES } from './gqls/experiences.js';\n\nconst PAGE_SIZE = 50;\n\nexport interface ConsentExperience {\n /** ID of experience */\n id: string;\n /** Name of experience */\n name: string;\n /** Experience display name */\n displayName?: string;\n /** Region that define this regional experience */\n regions: {\n /** Sub division */\n countrySubDivision?: IsoCountrySubdivisionCode;\n /** Country */\n country?: IsoCountryCode;\n }[];\n /** In vs not in operator */\n operator: RegionsOperator;\n /** Priority of experience */\n displayPriority: number;\n /** View state to prompt when auto prompting is enabled */\n viewState: InitialViewState;\n /** Consent expiry setting */\n onConsentExpiry: OnConsentExpiry;\n /** Consent expiry */\n consentExpiry: number;\n /** Purposes that can be opted out of in a particular experience */\n purposes: {\n /** Name of purpose */\n name: string;\n /** Purpose slug */\n trackingType: string;\n }[];\n /** Purposes that are opted out by default in a particular experience */\n optedOutPurposes: {\n /** Name of purpose */\n name: string;\n /** Purpose slug */\n trackingType: string;\n }[];\n /** Browser languages that define this regional experience */\n browserLanguages: BrowserLanguage[];\n /** Browser time zones that define this regional experience */\n browserTimeZones: BrowserTimeZone[];\n /** Consent UI variant assigned to this experience */\n consentUiVariant?: {\n /** Variant ID */\n id: string;\n /** Variant slug */\n slug: string;\n };\n}\n\n/**\n * Fetch consent manager experiences\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager experiences in the organization\n */\nexport async function fetchConsentManagerExperiences(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentExperience[]> {\n const experiences: ConsentExperience[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n experiences: { nodes },\n } = await makeGraphQLRequest<{\n /** Consent experience */\n experiences: {\n /** List */\n nodes: ConsentExperience[];\n };\n }>(client, EXPERIENCES, {\n variables: { first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n experiences.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return experiences.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport {\n FETCH_CONSENT_MANAGER_THEME,\n type TranscendConsentManagerThemeGql,\n type TranscendCliFetchConsentManagerThemeResponse,\n} from './gqls/consentManager.js';\n\n/**\n * Fetch consent manager theme\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Consent manager theme\n */\nexport async function fetchConsentManagerTheme(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** Airgap bundle ID to fetch for */\n airgapBundleId: string;\n };\n },\n): Promise<TranscendConsentManagerThemeGql> {\n const {\n consentManagerTheme: { theme },\n } = await makeGraphQLRequest<TranscendCliFetchConsentManagerThemeResponse>(\n client,\n FETCH_CONSENT_MANAGER_THEME,\n {\n variables: { airgapBundleId: options.filterBy.airgapBundleId },\n logger: options.logger,\n },\n );\n return theme;\n}\n","import type { ThemeConfiguration } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { FETCH_CONSENT_UI_THEMES } from './gqls/consentManager.js';\n\nconst PAGE_SIZE = 50;\n\n/** A consent UI theme returned by the consentUiThemes query */\nexport interface ConsentUiTheme {\n /** The id of the consent UI theme */\n id: string;\n /** The display name for this UI theme */\n name: string;\n /** The unique identifier for this UI theme */\n slug: string;\n /** Theme styling configuration */\n configuration: ThemeConfiguration;\n}\n\n/** Paginated response from the consentUiThemes GraphQL query */\nexport interface ConsentUiThemesQueryResponse {\n /** Consent UI themes for the airgap bundle */\n nodes: ConsentUiTheme[];\n /** Total number of themes matching the filter */\n totalCount: number;\n}\n\n/**\n * Fetch consent themes\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns consent UI themes\n */\nexport async function fetchConsentThemes(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentUiTheme[]> {\n const themes: ConsentUiTheme[] = [];\n let offset = 0;\n const airgapBundleId = await fetchConsentManagerId(client, { logger: options.logger });\n\n let shouldContinue = false;\n do {\n const {\n consentUiThemes: { nodes },\n } = await makeGraphQLRequest<{\n consentUiThemes: ConsentUiThemesQueryResponse;\n }>(client, FETCH_CONSENT_UI_THEMES, {\n variables: { airgapBundleId, first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n themes.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return themes;\n}\n","import type {\n ConsentUiUserFlow,\n UIConfiguration,\n UiVariantStatus,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { FETCH_CONSENT_UI_VARIANTS } from './gqls/consentManager.js';\n\nconst PAGE_SIZE = 50;\n\n/** A consent UI variant returned by the consentUiVariants query */\nexport interface ConsentUiVariant {\n /** The id of the consent UI variant */\n id: string;\n /** The display name for this UI variant */\n name: string;\n /** The unique identifier for this UI variant */\n slug: string;\n /** Description of this UI variant (null when unset) */\n description?: string | null;\n /** Locales this UI variant applies to */\n locales: string[];\n /** Variant behavior and layer configuration */\n configuration: UIConfiguration;\n /** Status of this UI variant */\n status: UiVariantStatus;\n /** The consent UI theme associated with this UI variant */\n theme?: {\n /** The id of the consent UI theme */\n id: string;\n /** The slug of the consent UI theme */\n slug: string;\n } | null;\n /** The user flow for this UI variant (null when unset) */\n userFlow?: ConsentUiUserFlow | null;\n}\n\n/** Paginated response from the consentUiVariants GraphQL query */\nexport interface ConsentUiVariantsQueryResponse {\n /** Consent UI variants for the airgap bundle */\n nodes: ConsentUiVariant[];\n /** Total number of variants matching the filter */\n totalCount: number;\n}\n\n/**\n * Fetch consent variants\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns consent UI variants\n */\nexport async function fetchConsentVariants(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentUiVariant[]> {\n const variants: ConsentUiVariant[] = [];\n let offset = 0;\n const airgapBundleId = await fetchConsentManagerId(client, { logger: options.logger });\n\n let shouldContinue = false;\n do {\n const {\n consentUiVariants: { nodes },\n } = await makeGraphQLRequest<{\n consentUiVariants: ConsentUiVariantsQueryResponse;\n }>(client, FETCH_CONSENT_UI_VARIANTS, {\n variables: { airgapBundleId, first: PAGE_SIZE, offset },\n logger: options.logger,\n });\n variants.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return variants;\n}\n","import { gql } from 'graphql-request';\n\n/** Dimension values on an aggregate analytics row */\nexport interface AirgapBundleAggregateAnalyticsDimensionsGql {\n /** Consent value (true = opted in, false = opted out) */\n NEW_VALUE?: string;\n /** Privacy regime */\n REGIME?: string;\n /** Tracking purpose */\n PURPOSE?: string;\n}\n\n/** Single row from airgapBundleAggregateAnalytics */\nexport interface AirgapBundleAggregateAnalyticsItemGql {\n /** Count or measure value */\n measure: string;\n /** Breakdown dimensions when requested */\n dimensions?: AirgapBundleAggregateAnalyticsDimensionsGql;\n}\n\n/** Single row from airgapBundleTimeseriesAnalytics */\nexport interface AirgapBundleTimeseriesAnalyticsItemGql {\n /** Bin timestamp */\n time: string;\n /** Metric name */\n metric: string;\n /** Count or measure value */\n measure: string;\n}\n\n/** Full response from AIRGAP_BUNDLE_AGGREGATE_ANALYTICS */\nexport interface TranscendCliAirgapBundleAggregateAnalyticsResponse {\n /** Aggregate analytics result */\n airgapBundleAggregateAnalytics: {\n /** Analytics rows */\n items: AirgapBundleAggregateAnalyticsItemGql[];\n };\n}\n\n/** Full response from AIRGAP_BUNDLE_TIMESERIES_ANALYTICS */\nexport interface TranscendCliAirgapBundleTimeseriesAnalyticsResponse {\n /** Timeseries analytics result */\n airgapBundleTimeseriesAnalytics: {\n /** Timeseries rows */\n items: AirgapBundleTimeseriesAnalyticsItemGql[];\n };\n}\n\nexport const AIRGAP_BUNDLE_AGGREGATE_ANALYTICS = gql`\n query TranscendCliAirgapBundleAggregateAnalytics(\n $id: ID!\n $input: AirgapBundleAggregateAnalyticsInput!\n ) {\n airgapBundleAggregateAnalytics(id: $id, input: $input) {\n items {\n measure\n dimensions {\n NEW_VALUE\n REGIME\n PURPOSE\n }\n }\n }\n }\n`;\n\nexport const AIRGAP_BUNDLE_TIMESERIES_ANALYTICS = gql`\n query TranscendCliAirgapBundleTimeseriesAnalytics(\n $id: ID!\n $input: AirgapBundleTimeseriesAnalyticsInput!\n ) {\n airgapBundleTimeseriesAnalytics(id: $id, input: $input) {\n items {\n time\n metric\n measure\n }\n }\n }\n`;\n","import type {\n AirgapBundleAnalyticsBinInterval,\n AirgapBundleAnalyticsDimension,\n AirgapBundleAnalyticsMetric,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport {\n AIRGAP_BUNDLE_AGGREGATE_ANALYTICS,\n AIRGAP_BUNDLE_TIMESERIES_ANALYTICS,\n type AirgapBundleAggregateAnalyticsItemGql,\n type AirgapBundleTimeseriesAnalyticsItemGql,\n type TranscendCliAirgapBundleAggregateAnalyticsResponse,\n type TranscendCliAirgapBundleTimeseriesAnalyticsResponse,\n} from './gqls/airgapBundleAnalytics.js';\n\n/**\n * Fetch aggregate consent analytics for an airgap bundle.\n *\n * @see https://docs.transcend.io/docs/articles/consent-management/configuration/consent-stats-with-gql-api\n */\nexport async function fetchAirgapBundleAggregateAnalytics(\n client: GraphQLClient,\n input: {\n /** Airgap bundle ID */\n airgapBundleId: string;\n /** Analytics metric to query */\n metric: AirgapBundleAnalyticsMetric;\n /** Start time (epoch seconds) */\n start: number;\n /** End time (epoch seconds) */\n end: number;\n /** Optional dimension breakdowns */\n includeDimensions?: AirgapBundleAnalyticsDimension[];\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AirgapBundleAggregateAnalyticsItemGql[]> {\n const {\n airgapBundleAggregateAnalytics: { items },\n } = await makeGraphQLRequest<TranscendCliAirgapBundleAggregateAnalyticsResponse>(\n client,\n AIRGAP_BUNDLE_AGGREGATE_ANALYTICS,\n {\n variables: {\n id: input.airgapBundleId,\n input: {\n metric: input.metric,\n start: input.start,\n end: input.end,\n ...(input.includeDimensions?.length\n ? { includeDimensions: input.includeDimensions }\n : {}),\n },\n },\n logger: options.logger,\n },\n );\n return items;\n}\n\n/**\n * Fetch timeseries consent analytics for an airgap bundle.\n *\n * @see https://docs.transcend.io/docs/articles/consent-management/configuration/consent-stats-with-gql-api\n */\nexport async function fetchAirgapBundleTimeseriesAnalytics(\n client: GraphQLClient,\n input: {\n /** Airgap bundle ID */\n airgapBundleId: string;\n /** Analytics metric to query */\n metric: AirgapBundleAnalyticsMetric;\n /** Start time (epoch seconds) */\n start: number;\n /** End time (epoch seconds) */\n end: number;\n /** Time bin size */\n binInterval: AirgapBundleAnalyticsBinInterval;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AirgapBundleTimeseriesAnalyticsItemGql[]> {\n const {\n airgapBundleTimeseriesAnalytics: { items },\n } = await makeGraphQLRequest<TranscendCliAirgapBundleTimeseriesAnalyticsResponse>(\n client,\n AIRGAP_BUNDLE_TIMESERIES_ANALYTICS,\n {\n variables: {\n id: input.airgapBundleId,\n input: {\n metric: input.metric,\n start: input.start,\n end: input.end,\n binInterval: input.binInterval,\n },\n },\n logger: options.logger,\n },\n );\n return items;\n}\n","import { gql } from 'graphql-request';\n\n/** Preference topic title translation */\nexport interface TranscendPreferenceTopicTitle {\n /** Translated description */\n description: string;\n}\n\n/** Preference topic linked to a purpose */\nexport interface TranscendPreferenceTopicGql {\n /** Topic ID */\n id: string;\n /** Topic slug */\n slug: string;\n /** Topic title with translations */\n title: TranscendPreferenceTopicTitle;\n /** Display color */\n color: string;\n}\n\n/** Data silo (integration) mapped to a purpose */\nexport interface TranscendMappedDataSiloGql {\n /** Data silo ID */\n id: string;\n /** Data silo display title */\n title: string;\n /** Data silo type */\n type: string;\n}\n\n/** Tracking purpose returned by the PURPOSES query */\nexport interface TranscendPurposeGql {\n /** Purpose ID */\n id: string;\n /** Purpose display name */\n name: string;\n /** Purpose description */\n description: string | null;\n /** Default consent state */\n defaultConsent: string;\n /** Purpose slug */\n trackingType: string;\n /** Whether the purpose is user-configurable */\n configurable: boolean;\n /** Whether the purpose is essential (non-optional) */\n essential: boolean;\n /** Whether the purpose appears in the consent manager UI */\n showInConsentManager: boolean;\n /** Whether the purpose is currently active */\n isActive: boolean;\n /** Display ordering weight */\n displayOrder: number;\n /** Signals that trigger automatic opt-out (e.g. GPC) */\n optOutSignals: string[];\n /** Soft-deletion timestamp, null if active */\n deletedAt: string | null;\n /** Authentication level required */\n authLevel: string;\n /** Whether the purpose appears in the privacy center */\n showInPrivacyCenter: boolean;\n /** Localized display title */\n title: string | null;\n /** Linked preference topics */\n preferenceTopics: TranscendPreferenceTopicGql[];\n /** Number of data points tracked under this purpose */\n purposeDataPointCount: number;\n /** Number of preference topic option value data points */\n preferenceTopicOptionValueDataPointCount: number;\n /** Data silos mapped to this purpose */\n mappedDataSilos: TranscendMappedDataSiloGql[];\n}\n\n/** Full response from the PURPOSES query */\nexport interface TranscendCliPurposesResponse {\n /** Purposes result */\n purposes: {\n /** List of purpose nodes */\n nodes: TranscendPurposeGql[];\n /** Total count of purposes */\n totalCount: number;\n };\n}\n\nexport const PURPOSES = gql`\n query TranscendCliPurposes($first: Int!) {\n purposes(first: $first) {\n nodes {\n id\n name\n description\n defaultConsent\n trackingType\n configurable\n essential\n showInConsentManager\n isActive\n displayOrder\n optOutSignals\n deletedAt\n authLevel\n showInPrivacyCenter\n title\n preferenceTopics {\n id\n slug\n title {\n description\n }\n color\n }\n purposeDataPointCount\n preferenceTopicOptionValueDataPointCount\n mappedDataSilos {\n id\n title\n type\n }\n }\n totalCount\n }\n }\n`;\n","import { gql } from 'graphql-request';\n\n// ---- Shared partition types ----\n\n/** Consent partition node */\nexport interface TranscendConsentPartitionGql {\n /** Partition ID */\n id: string;\n /** Partition name */\n name: string;\n /** Partition key */\n partition: string;\n}\n\n// ---- CONSENT_PARTITIONS query ----\n\n/** Full response from the CONSENT_PARTITIONS query */\nexport interface TranscendCliConsentPartitionsResponse {\n /** Partitions result */\n consentPartitions: {\n /** List of partition nodes */\n nodes: TranscendConsentPartitionGql[];\n };\n}\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// useMaster: false\n// isExportCsv: true\nexport const CONSENT_PARTITIONS = gql`\n query TranscendCliConsentPartitions($first: Int!, $offset: Int!) {\n consentPartitions(first: $first, offset: $offset) {\n nodes {\n id\n name\n partition\n }\n }\n }\n`;\n\n// ---- CREATE_CONSENT_PARTITION mutation ----\n\n/** Full response from the CREATE_CONSENT_PARTITION mutation */\nexport interface TranscendCliCreateConsentPartitionResponse {\n /** Mutation result */\n createConsentPartition: {\n /** Client mutation ID */\n clientMutationId: string | null;\n };\n}\n\nexport const CREATE_CONSENT_PARTITION = gql`\n mutation TranscendCliCreateConsentPartition($input: CreateConsentPartitionInput!) {\n createConsentPartition(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { gql } from 'graphql-request';\n\n/** Stats counts returned by COOKIE_STATS and DATA_FLOW_STATS */\nexport interface TranscendTrackerStatsGql {\n /** Number of live (approved) items */\n liveCount: number;\n /** Number of items needing review */\n needReviewCount: number;\n /** Number of junked items */\n junkCount: number;\n}\n\n/** Full response from the COOKIE_STATS query */\nexport interface TranscendCliCookieStatsResponse {\n /** Cookie stats result */\n cookieStats: TranscendTrackerStatsGql;\n}\n\nexport const COOKIE_STATS = gql`\n query TranscendCliCookieStats($input: AirgapBundleInput!) {\n cookieStats(input: $input) {\n liveCount\n needReviewCount\n junkCount\n }\n }\n`;\n\n/** Full response from the DATA_FLOW_STATS query */\nexport interface TranscendCliDataFlowStatsResponse {\n /** Data flow stats result */\n dataFlowStats: TranscendTrackerStatsGql;\n}\n\nexport const DATA_FLOW_STATS = gql`\n query TranscendCliDataFlowStats($input: AirgapBundleInput!) {\n dataFlowStats(input: $input) {\n liveCount\n needReviewCount\n junkCount\n }\n }\n`;\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const CONSENT_WORKFLOW_TRIGGERS: DocumentNode = parse(gql`\n query TranscendCliConsentWorkflowTriggers($first: Int!, $offset: Int!) {\n consentWorkflowTriggers(first: $first, offset: $offset) {\n nodes {\n id\n name\n triggerCondition\n isSilent\n allowUnauthenticated\n isActive\n workflowConfigId\n action {\n type\n }\n subject {\n type\n }\n dataSilos {\n title\n }\n }\n totalCount\n }\n }\n`);\n\nexport const CREATE_OR_UPDATE_CONSENT_WORKFLOW_TRIGGER: DocumentNode = parse(gql`\n mutation TranscendCliCreateOrUpdateConsentWorkflowTrigger(\n $input: CreateOrUpdateConsentWorkflowTriggerInput!\n ) {\n createOrUpdateConsentWorkflowTrigger(input: $input) {\n consentWorkflowTrigger {\n id\n name\n }\n }\n }\n`);\n","/**\n * Resolve displayed child organization URIs or IDs to organization IDs.\n *\n * Each value may be either an organization ID or URI. IDs are matched first,\n * then URIs. Throws if any value cannot be resolved.\n *\n * @param childOrganizations - Child organizations available on the privacy center\n * @param urisOrIds - URIs and/or IDs from transcend.yml\n * @returns Resolved organization IDs (same order as input)\n */\nexport function resolveDisplayedChildOrganizationIds(\n childOrganizations: Array<{\n /** Organization ID */\n id: string;\n /** Organization URI */\n uri: string;\n }>,\n urisOrIds: string[],\n): string[] {\n return urisOrIds.map((value) => {\n const byId = childOrganizations.find((child) => child.id === value);\n if (byId) {\n return byId.id;\n }\n const byUri = childOrganizations.find((child) => child.uri === value);\n if (byUri) {\n return byUri.id;\n }\n const available =\n childOrganizations.length > 0\n ? childOrganizations.map((child) => `${child.uri} (${child.id})`).join(', ')\n : '(none)';\n throw new Error(\n `Failed to resolve displayed child organization URI or ID: \"${value}\". Available: ${available}`,\n );\n });\n}\n","import type { ConsentThemeInput, ConsentVariantInput } from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentThemes, type ConsentUiTheme } from './fetchConsentThemes.js';\nimport { fetchConsentVariants, type ConsentUiVariant } from './fetchConsentVariants.js';\nimport {\n CREATE_CONSENT_UI_THEME,\n CREATE_CONSENT_UI_VARIANT,\n UPDATE_CONSENT_UI_THEME,\n UPDATE_CONSENT_UI_VARIANT,\n} from './gqls/consentManager.js';\n\n/**\n * Parse a JSON configuration string from transcend.yml\n *\n * @param configuration - JSON configuration string\n * @param resourceLabel - Label for error messages\n * @returns Parsed configuration object\n */\nfunction parseConsentUiConfiguration(\n configuration: string,\n resourceLabel: string,\n): Record<string, unknown> {\n try {\n return JSON.parse(configuration) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON configuration for ${resourceLabel}`);\n }\n}\n\n/** Consent UI theme synced to Transcend (id + slug for variant theme assignment) */\nexport type SyncedConsentUiTheme = Pick<ConsentUiTheme, 'id' | 'slug'>;\n\n/** Consent UI variant synced to Transcend (id + slug for experience consentUiVariantSlug assignment) */\nexport type SyncedConsentUiVariant = Pick<ConsentUiVariant, 'id' | 'slug'>;\n\n/**\n * Sync consent UI themes from transcend.yml\n *\n * @param client - GraphQL client\n * @param airgapBundleId - Airgap bundle ID\n * @param yamlThemes - Theme inputs from transcend.yml\n * @param options - Options\n * @returns Synced themes (id + slug) for resolving variant themeSlug assignments\n */\nexport async function syncConsentUiThemes(\n client: GraphQLClient,\n airgapBundleId: string,\n yamlThemes: ConsentThemeInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SyncedConsentUiTheme[]> {\n const { logger = NOOP_LOGGER } = options;\n const remoteThemes = await fetchConsentThemes(client, { logger });\n const remoteThemeBySlug = keyBy(remoteThemes, 'slug');\n const syncedThemes: SyncedConsentUiTheme[] = [];\n\n await map(\n yamlThemes,\n async (yamlTheme, index) => {\n const resourceLabel = `consentManager.consentThemes[${index}]`;\n const configuration = parseConsentUiConfiguration(yamlTheme.configuration, resourceLabel);\n const remoteTheme = remoteThemeBySlug[yamlTheme.slug];\n\n if (remoteTheme) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_UI_THEME, {\n variables: {\n input: {\n id: remoteTheme.id,\n airgapBundleId,\n name: yamlTheme.name,\n configuration,\n },\n },\n logger,\n });\n syncedThemes.push({ id: remoteTheme.id, slug: yamlTheme.slug });\n logger.info(`Successfully synced consent UI theme \"${yamlTheme.name}\"!`);\n } else {\n const result = await makeGraphQLRequest<{\n createConsentUiTheme: {\n consentUiTheme: Pick<ConsentUiTheme, 'id' | 'name'>;\n };\n }>(client, CREATE_CONSENT_UI_THEME, {\n variables: {\n input: {\n airgapBundleId,\n name: yamlTheme.name,\n slug: yamlTheme.slug,\n configuration,\n },\n },\n logger,\n });\n syncedThemes.push({\n id: result.createConsentUiTheme.consentUiTheme.id,\n slug: yamlTheme.slug,\n });\n logger.info(`Successfully created consent UI theme \"${yamlTheme.name}\"!`);\n }\n },\n { concurrency: 5 },\n );\n\n return syncedThemes;\n}\n\n/**\n * Sync consent UI variants from transcend.yml\n *\n * @param client - GraphQL client\n * @param airgapBundleId - Airgap bundle ID\n * @param yamlVariants - Variant inputs from transcend.yml\n * @param syncedThemes - Themes synced by syncConsentUiThemes, used to resolve themeSlug\n * @param options - Options\n * @returns Synced variants (id + slug) for resolving experience consentUiVariantSlug assignments\n */\nexport async function syncConsentUiVariants(\n client: GraphQLClient,\n airgapBundleId: string,\n yamlVariants: ConsentVariantInput[],\n syncedThemes: SyncedConsentUiTheme[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SyncedConsentUiVariant[]> {\n const { logger = NOOP_LOGGER } = options;\n const remoteVariants = await fetchConsentVariants(client, { logger });\n const remoteVariantBySlug = keyBy(remoteVariants, 'slug');\n const syncedThemeBySlug = keyBy(syncedThemes, 'slug');\n const syncedVariants: SyncedConsentUiVariant[] = [];\n\n await map(\n yamlVariants,\n async (yamlVariant, index) => {\n const resourceLabel = `consentManager.consentVariants[${index}]`;\n const configuration = parseConsentUiConfiguration(yamlVariant.configuration, resourceLabel);\n const remoteThemeId = yamlVariant.themeSlug\n ? syncedThemeBySlug[yamlVariant.themeSlug]?.id\n : undefined;\n const remoteVariant = remoteVariantBySlug[yamlVariant.slug];\n\n // Missing/null description means leave unset — do not clear on sync\n const descriptionInput =\n yamlVariant.description != null ? { description: yamlVariant.description } : {};\n\n if (remoteVariant) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_UI_VARIANT, {\n variables: {\n input: {\n id: remoteVariant.id,\n airgapBundleId,\n name: yamlVariant.name,\n ...descriptionInput,\n locales: yamlVariant.locales,\n configuration,\n status: yamlVariant.status,\n ...(remoteThemeId ? { themeId: remoteThemeId } : {}),\n },\n },\n logger,\n });\n syncedVariants.push({ id: remoteVariant.id, slug: yamlVariant.slug });\n logger.info(`Successfully synced consent UI variant \"${yamlVariant.name}\"!`);\n } else {\n const result = await makeGraphQLRequest<{\n createConsentUiVariant: {\n consentUiVariant: Pick<ConsentUiVariant, 'id'>;\n };\n }>(client, CREATE_CONSENT_UI_VARIANT, {\n variables: {\n input: {\n airgapBundleId,\n name: yamlVariant.name,\n slug: yamlVariant.slug,\n ...descriptionInput,\n locales: yamlVariant.locales,\n configuration,\n status: yamlVariant.status,\n ...(remoteThemeId ? { themeId: remoteThemeId } : {}),\n },\n },\n logger,\n });\n syncedVariants.push({\n id: result.createConsentUiVariant.consentUiVariant.id,\n slug: yamlVariant.slug,\n });\n logger.info(`Successfully created consent UI variant \"${yamlVariant.name}\"!`);\n }\n },\n { concurrency: 5 },\n );\n\n return syncedVariants;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { difference } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport {\n CONSENT_PARTITIONS,\n CREATE_CONSENT_PARTITION,\n type TranscendConsentPartitionGql,\n type TranscendCliConsentPartitionsResponse,\n} from './gqls/partitions.js';\n\nconst PAGE_SIZE = 50;\n\nexport interface PartitionInput {\n /** Name of partition */\n name: string;\n /** Value of partition, cannot be pushed, can only be pulled */\n partition?: string;\n}\n\n/**\n * Fetch the list of partitions\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Partition list\n */\nexport async function fetchPartitions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<TranscendConsentPartitionGql[]> {\n const { logger = NOOP_LOGGER } = options;\n const partitions: TranscendConsentPartitionGql[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n consentPartitions: { nodes },\n } = await makeGraphQLRequest<TranscendCliConsentPartitionsResponse>(\n client,\n CONSENT_PARTITIONS,\n {\n variables: { first: PAGE_SIZE, offset },\n logger,\n },\n );\n partitions.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return partitions.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Sync the consent partitions\n *\n * @param client - GraphQL client\n * @param partitionInputs - The partition inputs\n * @param options - Options\n * @returns True on success\n */\nexport async function syncPartitions(\n client: GraphQLClient,\n partitionInputs: PartitionInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n let encounteredError = false;\n const partitions = await fetchPartitions(client, { logger });\n const newPartitionNames = difference(\n partitionInputs.map(({ name }) => name),\n partitions.map(({ name }) => name),\n );\n await mapSeries(newPartitionNames, async (name) => {\n try {\n await makeGraphQLRequest(client, CREATE_CONSENT_PARTITION, {\n variables: {\n input: {\n id: airgapBundleId,\n name,\n },\n },\n logger,\n });\n logger.info(`Successfully created consent partition: ${name}!`);\n } catch (err) {\n logger.error(`Failed to create consent partition: ${name}! - ${(err as Error).message}`);\n encounteredError = true;\n }\n });\n return !encounteredError;\n}\n","import { InitialViewState, OnConsentExpiry, BrowserLanguage } from '@transcend-io/airgap.js-types';\nimport {\n ConsentBundleType,\n ConsentPrecedenceOption,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RegionsOperator,\n UnknownRequestPolicy,\n TelemetryPartitionStrategy,\n SignedIabAgreementOption,\n BrowserTimeZone,\n type ConsentThemeInput,\n type ConsentVariantInput,\n} from '@transcend-io/privacy-types';\nimport { map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllPurposes } from '../preference-management/fetchAllPurposes.js';\nimport { fetchConsentManagerExperiences } from './fetchConsentManagerExperiences.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { fetchPrivacyCenterId } from './fetchPrivacyCenterId.js';\nimport {\n UPDATE_CONSENT_MANAGER_DOMAINS,\n CREATE_CONSENT_MANAGER,\n UPDATE_LOAD_OPTIONS,\n UPDATE_CONSENT_MANAGER_PARTITION,\n UPDATE_CONSENT_MANAGER_VERSION,\n TOGGLE_TELEMETRY_PARTITION_STRATEGY,\n TOGGLE_UNKNOWN_COOKIE_POLICY,\n TOGGLE_CONSENT_PRECEDENCE,\n TOGGLE_UNKNOWN_REQUEST_POLICY,\n UPDATE_CONSENT_MANAGER_THEME,\n} from './gqls/consentManager.js';\nimport { UPDATE_CONSENT_EXPERIENCE, CREATE_CONSENT_EXPERIENCE } from './gqls/experiences.js';\nimport {\n syncConsentUiThemes,\n syncConsentUiVariants,\n type SyncedConsentUiVariant,\n} from './syncConsentUi.js';\nimport { fetchPartitions } from './syncPartitions.js';\n\nconst PURPOSES_LINK = 'https://app.transcend.io/consent-manager/regional-experiences/purposes';\n\nexport interface ConsentManageExperienceInput {\n /** Name of experience */\n name: string;\n /** Display name of experience */\n displayName?: string;\n /** Regions that define this regional experience */\n regions?: {\n /** Country */\n country?: IsoCountryCode;\n /** Country subdivision */\n countrySubDivision?: IsoCountrySubdivisionCode;\n }[];\n /** How to handle consent expiry */\n onConsentExpiry?: OnConsentExpiry;\n /** Consent expiration value */\n consentExpiry?: number;\n /** In vs not in operator */\n operator?: RegionsOperator;\n /** Priority of experience */\n displayPriority?: number;\n /** View state to prompt when auto prompting is enabled */\n viewState?: InitialViewState;\n /** Purposes that can be opted out of in a particular experience */\n purposes?: {\n /** Slug of purpose */\n trackingType: string;\n }[];\n /** Purposes that are opted out by default in a particular experience */\n optedOutPurposes?: {\n /** Slug of purpose */\n trackingType: string;\n }[];\n /** Browser languages that define this regional experience */\n browserLanguages?: BrowserLanguage[];\n /** Browser time zones that define this regional experience */\n browserTimeZones?: BrowserTimeZone[];\n /** Slug of the consent UI variant assigned to this experience */\n consentUiVariantSlug?: string;\n}\n\nexport interface ConsentManagerInput {\n /** Airgap version */\n version?: string;\n /** Bundle URLs per bundle type */\n bundleUrls?: Partial<Record<ConsentBundleType, string>>;\n /** The consent manager domains */\n domains?: string[];\n /** Key used to partition consent records */\n partition?: string;\n /** Precedence of signals vs user input */\n consentPrecedence?: ConsentPrecedenceOption;\n /** The consent manager unknown request policy */\n unknownRequestPolicy?: UnknownRequestPolicy;\n /** The consent manager unknown cookie policy */\n unknownCookiePolicy?: UnknownRequestPolicy;\n /** The XDI sync endpoint */\n syncEndpoint?: string;\n /** The telemetry partitioning strategy */\n telemetryPartitioning?: TelemetryPartitionStrategy;\n /** Whether the site owner has signed the IAB agreement */\n signedIabAgreement?: SignedIabAgreementOption;\n /** Regional experience configurations */\n experiences?: ConsentManageExperienceInput[];\n /** Theme configuration */\n theme?: {\n /** Primary color */\n primaryColor?: string;\n /** Font color */\n fontColor?: string;\n /** Privacy policy URL */\n privacyPolicy?: string;\n /** Auto-prompt setting */\n prompt?: number;\n };\n /** The Shared XDI host sync groups config (JSON) */\n syncGroups?: string;\n /** Consent UI variant configurations */\n consentVariants?: ConsentVariantInput[];\n /** Consent UI theme configurations */\n consentThemes?: ConsentThemeInput[];\n}\n\n/**\n * Sync consent manager experiences up to Transcend\n *\n * @param client - GraphQL client\n * @param experiences - The experience inputs\n * @param syncedVariants - Variants synced by syncConsentUiVariants, used to resolve consentUiVariantSlug\n * @param options - Options\n */\nexport async function syncConsentManagerExperiences(\n client: GraphQLClient,\n experiences: ConsentManageExperienceInput[],\n syncedVariants: SyncedConsentUiVariant[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n\n const existingExperiences = await fetchConsentManagerExperiences(client, {\n logger,\n });\n const experienceLookup = keyBy(existingExperiences, 'name');\n\n const purposes = await fetchAllPurposes(client, { logger });\n const purposeLookup = keyBy(purposes, 'trackingType');\n\n const syncedVariantBySlug = keyBy(syncedVariants, 'slug');\n\n await map(\n experiences,\n async (exp, ind) => {\n const purposeIds = exp.purposes?.map((purpose, ind2) => {\n const existingPurpose = purposeLookup[purpose.trackingType];\n if (!existingPurpose) {\n throw new Error(\n `Invalid purpose trackingType provided at consentManager.experiences[${ind}].purposes[${ind2}]: ` +\n `${purpose.trackingType}. See list of valid purposes ${PURPOSES_LINK}`,\n );\n }\n return existingPurpose.id;\n });\n const optedOutPurposeIds = exp.optedOutPurposes?.map((purpose, ind2) => {\n const existingPurpose = purposeLookup[purpose.trackingType];\n if (!existingPurpose) {\n throw new Error(\n `Invalid purpose trackingType provided at consentManager.experiences[${ind}].optedOutPurposes[${ind2}]: ` +\n `${purpose.trackingType}. See list of valid purposes ${PURPOSES_LINK}`,\n );\n }\n return existingPurpose.id;\n });\n\n const remoteConsentUiVariantId = exp.consentUiVariantSlug\n ? syncedVariantBySlug[exp.consentUiVariantSlug]?.id\n : undefined;\n const existingExperience = experienceLookup[exp.name];\n if (existingExperience) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_EXPERIENCE, {\n variables: {\n input: {\n id: existingExperience.id,\n name: exp.displayName,\n regions: exp.regions,\n operator: exp.operator,\n onConsentExpiry: exp.onConsentExpiry,\n consentExpiry: exp.consentExpiry,\n displayPriority:\n exp.displayPriority !== existingExperience.displayPriority\n ? exp.displayPriority\n : undefined,\n viewState: exp.viewState,\n purposes: purposeIds,\n optedOutPurposes: optedOutPurposeIds,\n browserLanguages: exp.browserLanguages,\n browserTimeZones: exp.browserTimeZones,\n ...(remoteConsentUiVariantId ? { consentUiVariantId: remoteConsentUiVariantId } : {}),\n },\n },\n logger,\n });\n logger.info(`Successfully synced consent experience \"${exp.name}\"!`);\n } else {\n await makeGraphQLRequest(client, CREATE_CONSENT_EXPERIENCE, {\n variables: {\n input: {\n name: exp.name,\n displayName: exp.displayName,\n regions: exp.regions,\n operator: exp.operator,\n onConsentExpiry: exp.onConsentExpiry || OnConsentExpiry.Prompt,\n consentExpiry: exp.consentExpiry,\n displayPriority: exp.displayPriority,\n viewState: exp.viewState || InitialViewState.Hidden,\n purposes: purposeIds || [],\n optedOutPurposes: optedOutPurposeIds || [],\n browserLanguages: exp.browserLanguages,\n browserTimeZones: exp.browserTimeZones,\n ...(remoteConsentUiVariantId ? { consentUiVariantId: remoteConsentUiVariantId } : {}),\n },\n },\n logger,\n });\n logger.info(`Successfully created consent experience \"${exp.name}\"!`);\n }\n },\n {\n concurrency: 10,\n },\n );\n}\n\n/**\n * Sync the consent manager\n *\n * @param client - GraphQL client\n * @param consentManager - The consent manager input\n * @param options - Options\n */\nexport async function syncConsentManager(\n client: GraphQLClient,\n consentManager: ConsentManagerInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n let airgapBundleId: string;\n\n try {\n airgapBundleId = await fetchConsentManagerId(client, {\n logger,\n maxRequests: 1,\n });\n } catch (err) {\n // TODO: https://transcend.height.app/T-23778\n if ((err as Error).message.includes('AirgapBundle not found')) {\n const privacyCenterId = await fetchPrivacyCenterId(client, {\n logger,\n });\n\n const { createConsentManager } = await makeGraphQLRequest<{\n /** Create consent manager */\n createConsentManager: {\n /** Consent manager */\n consentManager: {\n /** ID */\n id: string;\n };\n };\n }>(client, CREATE_CONSENT_MANAGER, {\n variables: { privacyCenterId },\n logger,\n });\n airgapBundleId = createConsentManager.consentManager.id;\n } else {\n throw err;\n }\n }\n\n if (consentManager.domains) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_DOMAINS, {\n variables: { domains: consentManager.domains, airgapBundleId },\n logger,\n });\n }\n\n if (consentManager.partition) {\n const partitions = await fetchPartitions(client, { logger });\n const partitionToUpdate = partitions.find((part) => part.name === consentManager.partition);\n if (!partitionToUpdate) {\n throw new Error(\n `Partition \"${consentManager.partition}\" not found. Please create the partition first.`,\n );\n }\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_PARTITION, {\n variables: { partitionId: partitionToUpdate.id, airgapBundleId },\n logger,\n });\n }\n\n if (consentManager.version) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_VERSION, {\n variables: { airgapBundleId, version: consentManager.version },\n logger,\n });\n }\n\n if (consentManager.signedIabAgreement) {\n await makeGraphQLRequest(client, UPDATE_LOAD_OPTIONS, {\n variables: {\n input: {\n id: airgapBundleId,\n ...(consentManager.signedIabAgreement\n ? { signedIabAgreement: consentManager.signedIabAgreement }\n : {}),\n },\n },\n logger,\n });\n }\n\n if (consentManager.unknownRequestPolicy) {\n await makeGraphQLRequest(client, TOGGLE_UNKNOWN_REQUEST_POLICY, {\n variables: {\n input: {\n id: airgapBundleId,\n unknownRequestPolicy: consentManager.unknownRequestPolicy,\n },\n },\n logger,\n });\n }\n\n if (consentManager.unknownRequestPolicy) {\n await makeGraphQLRequest(client, TOGGLE_UNKNOWN_COOKIE_POLICY, {\n variables: {\n input: {\n id: airgapBundleId,\n unknownCookiePolicy: consentManager.unknownCookiePolicy,\n },\n },\n logger,\n });\n }\n\n if (consentManager.telemetryPartitioning) {\n await makeGraphQLRequest(client, TOGGLE_TELEMETRY_PARTITION_STRATEGY, {\n variables: {\n input: {\n id: airgapBundleId,\n strategy: consentManager.telemetryPartitioning,\n },\n },\n logger,\n });\n }\n\n if (consentManager.consentPrecedence) {\n await makeGraphQLRequest(client, TOGGLE_CONSENT_PRECEDENCE, {\n variables: {\n input: {\n id: airgapBundleId,\n consentPrecedence: consentManager.consentPrecedence,\n },\n },\n logger,\n });\n }\n\n if (\n consentManager.consentThemes ||\n consentManager.consentVariants ||\n consentManager.experiences\n ) {\n const syncedThemes = consentManager.consentThemes\n ? await syncConsentUiThemes(client, airgapBundleId, consentManager.consentThemes, {\n logger,\n })\n : [];\n\n const syncedVariants = consentManager.consentVariants\n ? await syncConsentUiVariants(\n client,\n airgapBundleId,\n consentManager.consentVariants,\n syncedThemes,\n { logger },\n )\n : [];\n\n if (consentManager.experiences) {\n await syncConsentManagerExperiences(client, consentManager.experiences, syncedVariants, {\n logger,\n });\n }\n }\n\n if (consentManager.theme) {\n await makeGraphQLRequest(client, UPDATE_CONSENT_MANAGER_THEME, {\n variables: {\n input: {\n airgapBundleId,\n ...consentManager.theme,\n },\n },\n logger,\n });\n }\n\n // TODO: https://transcend.height.app/T-23875\n // syncEndpoint: string;\n // TODO: https://transcend.height.app/T-23919\n // syncGroups: string;\n}\n","import { ConsentTrackerStatus } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { UPDATE_OR_CREATE_COOKIES } from './gqls/cookies.js';\n\nexport interface CookieInput {\n /** Name of cookie */\n name: string;\n /** Whether or not the cookie is a regular expression */\n isRegex?: boolean;\n /** Description of cookie */\n description?: string;\n /** The tracking purposes that are required to be opted in for this cookie */\n trackingPurposes?: string[];\n /** Name of the consent service attached */\n service?: string;\n /** Status of the tracker (approved vs triage) */\n status?: ConsentTrackerStatus;\n /** Email addresses of owners */\n owners?: string[];\n /** Names of teams responsible for managing this cookie */\n teams?: string[];\n /** Attribute key-value pairs */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Update or create cookies\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateOrCreateCookies(\n client: GraphQLClient,\n options: {\n /** List of cookie inputs */\n input: CookieInput[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: cookies, logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n await mapSeries(chunk(cookies, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_OR_CREATE_COOKIES, {\n variables: {\n airgapBundleId,\n cookies: page.map((cookie) => ({\n name: cookie.name,\n trackingPurposes:\n cookie.trackingPurposes && cookie.trackingPurposes.length > 0\n ? cookie.trackingPurposes\n : undefined,\n description: cookie.description,\n service: cookie.service,\n status: cookie.status,\n attributes: cookie.attributes,\n isRegex: cookie.isRegex,\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the set of cookies into the product\n *\n * @param client - GraphQL client\n * @param cookies - Cookies to sync\n * @param options - Options\n * @returns True upon success, false upon failure\n */\nexport async function syncCookies(\n client: GraphQLClient,\n cookies: CookieInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${cookies.length}\" cookies...`);\n\n const notUnique = cookies.filter(\n (cookie) =>\n cookies.filter((cook) => cookie.name === cook.name && cookie.isRegex === cook.isRegex)\n .length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload cookies as there were non-unique entries found: ${notUnique\n .map(({ name }) => name)\n .join(',')}`,\n );\n }\n\n try {\n logger.info(`Upserting \"${cookies.length}\" new cookies...`);\n await updateOrCreateCookies(client, { input: cookies, logger });\n logger.info(`Successfully synced ${cookies.length} cookies!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create cookies! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { ConsentTrackerStatus, DataFlowScope } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataFlows } from './fetchAllDataFlows.js';\nimport { fetchConsentManagerId } from './fetchConsentManagerId.js';\nimport { CREATE_DATA_FLOWS, UPDATE_DATA_FLOWS } from './gqls/dataFlows.js';\n\n/** Attribute key-value pair for a data flow */\nexport interface DataFlowAttributeInput {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n}\n\n/**\n * Input to define a data flow for sync\n *\n * @see https://app.transcend.io/consent-manager/data-flows/approved\n */\nexport interface DataFlowInput {\n /** Value of data flow */\n value: string;\n /** Type of data flow */\n type: DataFlowScope;\n /** Description of data flow */\n description?: string;\n /** The tracking purposes that are required to be opted in for this data flow */\n trackingPurposes?: string[];\n /** Name of the consent service attached */\n service?: string;\n /** Status of the tracker (approved vs triage) */\n status?: ConsentTrackerStatus;\n /** Email addresses of owners */\n owners?: string[];\n /** Names of teams responsible for managing this data flow */\n teams?: string[];\n /** Attribute key-value pairs */\n attributes?: DataFlowAttributeInput[];\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Update data flows that already existed\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateDataFlows(\n client: GraphQLClient,\n options: {\n /** [DataFlowInput, Data Flow ID] mappings to update */\n input: [DataFlowInput, string][];\n /** Classify service if missing */\n classifyService?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataFlows, classifyService = false, logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // const purposes = await fetchAllPurposes(client);\n // const purposeNameToId = keyBy(purposes, 'name');\n\n await mapSeries(chunk(dataFlows, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_DATA_FLOWS, {\n variables: {\n airgapBundleId,\n dataFlows: page.map(([flow, id]) => ({\n id,\n value: flow.value,\n type: flow.type,\n trackingType:\n flow.trackingPurposes && flow.trackingPurposes.length > 0\n ? flow.trackingPurposes\n : undefined,\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // purposeIds: flow.trackingPurposes\n // ? flow.trackingPurposes\n // .filter((purpose) => purpose !== 'Unknown')\n // .map((purpose) => purposeNameToId[purpose].id)\n // : undefined,\n description: flow.description,\n service: flow.service,\n status: flow.status,\n attributes: flow.attributes,\n // TODO: https://transcend.height.app/T-23718\n // owners,\n // teams,\n })),\n classifyService,\n },\n logger,\n });\n });\n}\n\n/**\n * Create new data flows\n *\n * @param client - GraphQL client\n * @param dataFlowInputs - List of data flows to create\n * @param options - Options\n */\nexport async function createDataFlows(\n client: GraphQLClient,\n options: {\n /** Data flow inputs to create */\n input: DataFlowInput[];\n /** Classify service if missing */\n classifyService?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataFlowInputs, classifyService = false, logger = NOOP_LOGGER } = options;\n const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // const purposes = await fetchAllPurposes(client);\n // const purposeNameToId = keyBy(purposes, 'name');\n\n await mapSeries(chunk(dataFlowInputs, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, CREATE_DATA_FLOWS, {\n variables: {\n airgapBundleId,\n dataFlows: page.map((flow) => ({\n value: flow.value,\n type: flow.type,\n trackingType:\n flow.trackingPurposes && flow.trackingPurposes.length > 0\n ? flow.trackingPurposes\n : undefined,\n // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n // purposeIds: flow.trackingPurposes\n // ? flow.trackingPurposes\n // .filter((purpose) => purpose !== 'Unknown')\n // .map((purpose) => purposeNameToId[purpose].id)\n // : undefined,\n description: flow.description,\n service: flow.service,\n status: flow.status,\n attributes: flow.attributes,\n // TODO: https://transcend.height.app/T-23718\n // owners,\n // teams,\n })),\n classifyService,\n },\n logger,\n });\n });\n}\n\n/**\n * Sync data flow configurations into Transcend\n *\n * @param client - GraphQL client\n * @param dataFlows - The data flows to upload\n * @param options - Options\n * @returns True if the command ran successfully, returns false if an error occurred\n */\nexport async function syncDataFlows(\n client: GraphQLClient,\n dataFlows: DataFlowInput[],\n options: {\n /** When true, auto classify the service based on the data flow value */\n classifyService?: boolean;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { classifyService = false, logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${dataFlows.length}\" data flows...`);\n\n const notUnique = dataFlows.filter(\n (dataFlow) =>\n dataFlows.filter((flow) => dataFlow.value === flow.value && dataFlow.type === flow.type)\n .length > 1,\n );\n\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload data flows as there were non-unique entries found: ${notUnique\n .map(({ value }) => value)\n .join(',')}`,\n );\n }\n\n logger.info('Fetching data flows...');\n const [existingLiveDataFlows, existingInReviewDataFlows] = await Promise.all([\n fetchAllDataFlows(client, {\n logger,\n filterBy: { status: ConsentTrackerStatus.Live },\n }),\n fetchAllDataFlows(client, {\n logger,\n filterBy: { status: ConsentTrackerStatus.NeedsReview },\n }),\n ]);\n const allDataFlows = [...existingLiveDataFlows, ...existingInReviewDataFlows];\n\n const mapDataFlowsToExisting = dataFlows.map((dataFlow) => [\n dataFlow,\n allDataFlows.find((flow) => dataFlow.value === flow.value && dataFlow.type === flow.type)?.id,\n ]);\n\n const newDataFlows = mapDataFlowsToExisting\n .filter(([, existing]) => !existing)\n .map(([flow]) => flow as DataFlowInput);\n try {\n logger.info(`Creating \"${newDataFlows.length}\" new data flows...`);\n await createDataFlows(client, { input: newDataFlows, classifyService, logger });\n logger.info(`Successfully synced ${newDataFlows.length} data flows!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create data flows! - ${(err as Error).message}`);\n }\n\n const existingDataFlows = mapDataFlowsToExisting.filter(\n (x): x is [DataFlowInput, string] => !!x[1],\n );\n try {\n logger.info(`Updating \"${existingDataFlows.length}\" data flows...`);\n await updateDataFlows(client, { input: existingDataFlows, classifyService, logger });\n logger.info(`Successfully updated \"${existingDataFlows.length}\" data flows!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to update data flows! - ${(err as Error).message}`);\n }\n\n logger.info(`Synced \"${dataFlows.length}\" data flows!`);\n\n return !encounteredError;\n}\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport { PolicyType } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllPolicies } from './fetchAllPolicies.js';\nimport { fetchPrivacyCenterId } from './fetchPrivacyCenterId.js';\nimport { UPDATE_POLICIES } from './gqls/policy.js';\n\nexport interface PolicyInput {\n /** The title of the policy */\n title: string;\n /**\n * Policy type. Required by the API on create; optional on update.\n * When omitted on create, inferred from title.\n */\n type?: PolicyType;\n /** Effective date of policy */\n effectiveOn?: string;\n /** Whether or not to disable the effective date */\n disableEffectiveOn?: boolean;\n /** Content of the policy */\n content?: string;\n /** The languages for which the policy is disabled for */\n disabledLocales?: LocaleValue[];\n}\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Infer PolicyType from a policy title when type is not provided on create.\n *\n * @param title - Policy title\n * @returns Inferred policy type\n */\nexport function inferPolicyTypeFromTitle(title: string): PolicyType {\n if (title === 'Privacy Policy') {\n return PolicyType.PrivacyPolicy;\n }\n if (title === 'Cookie Policy') {\n return PolicyType.CookiePolicy;\n }\n return PolicyType.Custom;\n}\n\n/**\n * Update or create policies\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updatePolicies(\n client: GraphQLClient,\n options: {\n /** [PolicyInput, policyId] list */\n input: [PolicyInput, string | undefined][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: policyInputs, logger = NOOP_LOGGER } = options;\n const privacyCenterId = await fetchPrivacyCenterId(client, { logger });\n\n await mapSeries(chunk(policyInputs, MAX_PAGE_SIZE), async (page) => {\n await makeGraphQLRequest(client, UPDATE_POLICIES, {\n variables: {\n privacyCenterId,\n policies: page.map(([policy, policyId]) => ({\n id: policyId,\n title: policy.title,\n // Backend requires type on create; leave existing type alone on update\n ...(!policyId\n ? {\n type: policy.type ?? inferPolicyTypeFromTitle(policy.title),\n }\n : {}),\n disableEffectiveOn: policy.disableEffectiveOn,\n disabledLocales: policy.disabledLocales,\n ...(policy.effectiveOn || policy.content\n ? {\n version: {\n ...(policy.effectiveOn ? { effectiveOn: policy.effectiveOn } : {}),\n ...(policy.content\n ? {\n content: {\n defaultMessage: policy.content,\n },\n }\n : {}),\n },\n }\n : {}),\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Sync the set of policies from the YML interface into the product\n *\n * @param client - GraphQL client\n * @param policies - policies to sync\n * @param options - Options\n * @returns True upon success, false upon failure\n */\nexport async function syncPolicies(\n client: GraphQLClient,\n policies: PolicyInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${policies.length}\" policies...`);\n\n const notUnique = policies.filter(\n (policy) => policies.filter((pol) => policy.title === pol.title).length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to upload policies as there were non-unique entries found: ${notUnique\n .map(({ title }) => title)\n .join(',')}`,\n );\n }\n\n const existingPolicies = await fetchAllPolicies(client, { logger });\n const policiesById = keyBy(existingPolicies, ({ title }) => title.defaultMessage);\n\n try {\n logger.info(`Upserting \"${policies.length}\" new policies...`);\n await updatePolicies(client, {\n input: policies.map((policy) => [policy, policiesById[policy.title]?.id]),\n logger,\n });\n logger.info(`Successfully synced ${policies.length} policies!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create policies! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { PrivacyCenterFooterLink } from './fetchAllPrivacyCenters.js';\nimport {\n DELETE_PRIVACY_CENTER_FOOTER_LINKS,\n UPDATE_PRIVACY_CENTER_FOOTER_LINKS,\n} from './gqls/privacyCenter.js';\n\nexport interface PrivacyCenterFooterLinkInput {\n /** Link title (default locale) */\n title: string;\n /** Link URL (default locale); optional for icon-only links */\n url?: string;\n /**\n * When true with an icon set, render as icon-only (title is used for\n * accessibility). When false with an icon set, render icon beside the\n * visible label.\n */\n iconOnly?: boolean;\n}\n\n/**\n * Sync privacy center footer links. Matches existing links by title, upserts\n * the provided list, and deletes any existing links omitted from the YAML.\n *\n * @param client - GraphQL client\n * @param privacyCenterId - Privacy center ID\n * @param footerLinks - Desired footer links\n * @param existingFooterLinks - Existing footer links from the privacy center\n * @param options - Options\n */\nexport async function syncPrivacyCenterFooterLinks(\n client: GraphQLClient,\n privacyCenterId: string,\n footerLinks: PrivacyCenterFooterLinkInput[],\n existingFooterLinks: PrivacyCenterFooterLink[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n\n const notUnique = footerLinks.filter(\n (link) => footerLinks.filter((other) => other.title === link.title).length > 1,\n );\n if (notUnique.length > 0) {\n throw new Error(\n `Failed to sync privacy center footer links as there were non-unique titles: ${[\n ...new Set(notUnique.map(({ title }) => title)),\n ].join(', ')}`,\n );\n }\n\n const existingByTitle = keyBy(existingFooterLinks, ({ title }) => title.defaultMessage);\n\n const resolved = footerLinks.map((link) => {\n const byTitle = existingByTitle[link.title];\n return {\n id: byTitle?.id,\n title: link.title,\n ...(link.url !== undefined ? { url: link.url } : {}),\n ...(link.iconOnly !== undefined ? { iconOnly: link.iconOnly } : {}),\n };\n });\n\n const keptIds = new Set(resolved.map((link) => link.id).filter((id): id is string => !!id));\n const idsToDelete = existingFooterLinks.map(({ id }) => id).filter((id) => !keptIds.has(id));\n\n if (idsToDelete.length > 0) {\n logger.info(`Deleting \"${idsToDelete.length}\" privacy center footer links...`);\n await makeGraphQLRequest(client, DELETE_PRIVACY_CENTER_FOOTER_LINKS, {\n variables: {\n input: {\n privacyCenterId,\n ids: idsToDelete,\n },\n },\n logger,\n });\n }\n\n if (resolved.length > 0) {\n logger.info(`Upserting \"${resolved.length}\" privacy center footer links...`);\n await makeGraphQLRequest(client, UPDATE_PRIVACY_CENTER_FOOTER_LINKS, {\n variables: {\n input: {\n privacyCenterId,\n footerLinks: resolved,\n },\n },\n logger,\n });\n }\n}\n","import type { LocaleValue } from '@transcend-io/internationalization';\nimport type { PrivacyCenterFooterLayout } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllPrivacyCenters } from './fetchAllPrivacyCenters.js';\nimport { fetchPrivacyCenterId } from './fetchPrivacyCenterId.js';\nimport { UPDATE_PRIVACY_CENTER } from './gqls/privacyCenter.js';\nimport { resolveDisplayedChildOrganizationIds } from './resolveDisplayedChildOrganizationIds.js';\nimport {\n syncPrivacyCenterFooterLinks,\n type PrivacyCenterFooterLinkInput,\n} from './syncPrivacyCenterFooterLinks.js';\n\nexport interface PrivacyCenterInput {\n /** Whether or not the entire privacy center is enabled or disabled */\n isDisabled?: boolean;\n /** Whether or not to show the privacy requests button */\n showPrivacyRequestButton?: boolean;\n /** Whether or not to show the policies page */\n showPolicies?: boolean;\n /** Whether or not to show the tracking technologies page */\n showTrackingTechnologies?: boolean;\n /** Whether or not to show the cookies on the tracking technologies page */\n showCookies?: boolean;\n /** Whether or not to show the data flows on the tracking technologies page */\n showDataFlows?: boolean;\n /** Whether or not to show the consent manager opt out options on the tracking technologies page */\n showConsentManager?: boolean;\n /** Whether or not to show the manage your privacy page */\n showManageYourPrivacy?: boolean;\n /** Whether or not to show the marketing preferences page */\n showMarketingPreferences?: boolean;\n /** What languages are supported for the privacy center */\n locales?: LocaleValue[];\n /** The default locale for the privacy center */\n defaultLocale?: LocaleValue;\n /** Whether or not to prefer the browser default locale */\n preferBrowserDefaultLocale?: boolean;\n /** The support email address */\n supportEmail?: string;\n /** The reply-to email address */\n replyToEmail?: string;\n /** Whether or not to send emails from a no reply email */\n useNoReplyEmailAddress?: boolean;\n /** Whether or not to use a custom email domain */\n useCustomEmailDomain?: boolean;\n /** Whether or not to transcend access requests from JSON to CSV */\n transformAccessReportJsonToCsv?: boolean;\n /**\n * Privacy Center \"home\" / back-to-site link URL shown in the privacy center\n * header\n */\n home?: string;\n /** Whether the side menu is expanded by default on the privacy center */\n expandSideMenuByDefault?: boolean;\n /** Whether custom fields are required on privacy center workflows */\n workflowsCustomFieldsRequired?: boolean;\n /**\n * Child organization URIs and/or IDs to display on a unified multi-brand\n * privacy center\n */\n displayedChildOrganizationUris?: string[];\n /** Footer layout for privacy center footer links */\n footerLayout?: PrivacyCenterFooterLayout;\n /** Footer links displayed on the privacy center */\n footerLinks?: PrivacyCenterFooterLinkInput[];\n /** The theme object of colors to display on the privacy center */\n theme?: {\n /** The theme colors */\n colors?: Record<string, string | undefined>;\n /** Styles to apply to components */\n componentStyles?: Record<string, unknown>;\n /** Override styles */\n textStyles?: Record<string, unknown>;\n };\n}\n\n/**\n * Sync the privacy center\n *\n * @param client - GraphQL client\n * @param privacyCenter - The privacy center input\n * @param options - Options\n * @returns Whether the privacy center was synced successfully\n */\nexport async function syncPrivacyCenter(\n client: GraphQLClient,\n privacyCenter: PrivacyCenterInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** When true, skip publishing the privacy center after update */\n skipPublish?: boolean;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER, skipPublish } = options;\n let encounteredError = false;\n logger.info('Syncing privacy center...');\n\n const privacyCenterId = await fetchPrivacyCenterId(client, { logger });\n\n const { displayedChildOrganizationUris, footerLinks } = privacyCenter;\n const needsExisting = !!displayedChildOrganizationUris || footerLinks !== undefined;\n const [existing] = needsExisting ? await fetchAllPrivacyCenters(client, { logger }) : [];\n\n let displayedChildOrganizationIds: string[] | undefined;\n if (displayedChildOrganizationUris) {\n displayedChildOrganizationIds = resolveDisplayedChildOrganizationIds(\n existing?.childOrganizations ?? [],\n displayedChildOrganizationUris,\n );\n }\n\n try {\n await makeGraphQLRequest(client, UPDATE_PRIVACY_CENTER, {\n variables: {\n input: {\n privacyCenterId,\n transformAccessReportJsonToCsv: privacyCenter.transformAccessReportJsonToCsv,\n useCustomEmailDomain: privacyCenter.useCustomEmailDomain,\n useNoReplyEmailAddress: privacyCenter.useNoReplyEmailAddress,\n replyToEmail: privacyCenter.replyToEmail,\n supportEmail: privacyCenter.supportEmail,\n preferBrowserDefaultLocale: privacyCenter.preferBrowserDefaultLocale,\n defaultLocale: privacyCenter.defaultLocale,\n locales: privacyCenter.locales,\n showMarketingPreferences: privacyCenter.showMarketingPreferences,\n showManageYourPrivacy: privacyCenter.showManageYourPrivacy,\n showPolicies: privacyCenter.showPolicies,\n showConsentManager: privacyCenter.showConsentManager,\n showDataFlows: privacyCenter.showDataFlows,\n showCookies: privacyCenter.showCookies,\n showTrackingTechnologies: privacyCenter.showTrackingTechnologies,\n showPrivacyRequestButton: privacyCenter.showPrivacyRequestButton,\n isDisabled: privacyCenter.isDisabled,\n home: privacyCenter.home,\n expandSideMenuByDefault: privacyCenter.expandSideMenuByDefault,\n workflowsCustomFieldsRequired: privacyCenter.workflowsCustomFieldsRequired,\n footerLayout: privacyCenter.footerLayout,\n ...(displayedChildOrganizationIds ? { displayedChildOrganizationIds } : {}),\n ...(skipPublish !== undefined ? { skipPublish } : {}),\n ...(privacyCenter.theme\n ? {\n colorPalette: privacyCenter.theme.colors,\n componentStyles: privacyCenter.theme.componentStyles,\n textStyles: privacyCenter.theme.textStyles,\n }\n : {}),\n },\n },\n logger,\n });\n\n if (footerLinks !== undefined) {\n await syncPrivacyCenterFooterLinks(\n client,\n privacyCenterId,\n footerLinks,\n existing?.footerLinks ?? [],\n { logger },\n );\n }\n\n logger.info('Successfully synced privacy center!');\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync privacy center! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import type { RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CONSENT_WORKFLOW_TRIGGERS } from './gqls/consentWorkflowTrigger.js';\n\nexport interface ConsentWorkflowTrigger {\n /** ID of the trigger */\n id: string;\n /** Name of the trigger */\n name: string;\n /** JSON string of the trigger condition */\n triggerCondition: string | null;\n /** Whether the trigger runs silently */\n isSilent: boolean;\n /** Whether unauthenticated requests are allowed */\n allowUnauthenticated: boolean;\n /** Whether the trigger is active */\n isActive: boolean;\n /** The workflow config ID */\n workflowConfigId: string | null;\n /** The request action associated with the trigger */\n action: {\n /** Action type (e.g. ERASURE, ACCESS) */\n type: RequestAction;\n };\n /** The data subject associated with the trigger */\n subject: {\n /** Data subject type */\n type: string;\n };\n /** Data silos associated with this trigger */\n dataSilos: {\n /** Title of data silo */\n title: string;\n }[];\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all consent workflow triggers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All consent workflow triggers in the organization\n */\nexport async function fetchAllConsentWorkflowTriggers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<ConsentWorkflowTrigger[]> {\n const { logger } = options;\n const triggers: ConsentWorkflowTrigger[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n consentWorkflowTriggers: { nodes },\n } = await makeGraphQLRequest<{\n /** Consent workflow triggers */\n consentWorkflowTriggers: {\n /** List */\n nodes: ConsentWorkflowTrigger[];\n };\n }>(client, CONSENT_WORKFLOW_TRIGGERS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n triggers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return triggers.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\nexport const ACTIONS = gql`\n query TranscendCliActions($first: Int!, $offset: Int!) {\n actions(\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: type, direction: ASC }]\n ) {\n nodes {\n id\n type\n skipSecondaryIfNoFiles\n skipDownloadableStep\n requiresReview\n regionList\n regionDetectionMethod\n waitingPeriod\n }\n }\n }\n`;\n\nexport const UPDATE_ACTION = gql`\n mutation TranscendCliUpdateAction($input: UpdateActionInput!) {\n updateAction(input: $input) {\n clientMutationId\n }\n }\n`;\n","import {\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RegionDetectionMethod,\n RequestAction,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ACTIONS } from './gqls/action.js';\n\nexport interface Action {\n /** ID of identifier */\n id: string;\n /** Type of action */\n type: RequestAction;\n /** Whether to skip secondary when no files exist */\n skipSecondaryIfNoFiles: boolean;\n /** Whether to skip downloadable step */\n skipDownloadableStep: boolean;\n /** Whether action requires review */\n requiresReview: boolean;\n /** Waiting period for action */\n waitingPeriod: number;\n /** Method in which the data subject's region is detected */\n regionDetectionMethod: RegionDetectionMethod;\n /** The list of regions to show in the form */\n regionList: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all actions in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All actions in the organization\n */\nexport async function fetchAllActions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Action[]> {\n const { logger } = options;\n const actions: Action[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n actions: { nodes },\n } = await makeGraphQLRequest<{\n /** Actions */\n actions: {\n /** List */\n nodes: Action[];\n };\n }>(client, ACTIONS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n actions.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return actions.sort((a, b) => a.type.localeCompare(b.type));\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const WORKFLOW_CONFIGS: DocumentNode = parse(gql`\n query TranscendCliWorkflowConfigs($first: Int!, $offset: Int!, $filterBy: WorkflowFilterInput) {\n workflows(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n title {\n defaultMessage\n }\n subtitle {\n defaultMessage\n }\n description {\n defaultMessage\n }\n internalName\n workflowConfigVisibility\n workflowConfigType\n collectDataSubjectRegions\n regionList\n expiryTime {\n region\n value\n }\n action {\n id\n type\n }\n subject {\n id\n type\n }\n WorkflowConfigAttributeKeys {\n attributeKey {\n id\n name\n }\n }\n }\n totalCount\n }\n }\n`);\n\nexport const CREATE_WORKFLOW: DocumentNode = parse(gql`\n mutation TranscendCliCreateWorkflow($input: CreateWorkflowInput!) {\n createWorkflow(input: $input) {\n workflowConfig {\n id\n internalName\n }\n }\n }\n`);\n\nexport const UPDATE_WORKFLOW_CONFIG: DocumentNode = parse(gql`\n mutation TranscendCliUpdateWorkflowConfig($input: UpdateWorkflowConfigInput!) {\n updateWorkflowConfig(input: $input) {\n success\n }\n }\n`);\n","import {\n CollectDataSubjectRegions,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RequestAction,\n WorkflowConfigType,\n WorkflowConfigVisibility,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { WORKFLOW_CONFIGS } from './gqls/workflowConfig.js';\n\n/** Raw workflow config node returned by GraphQL */\nexport interface WorkflowConfigNode {\n /** Workflow config ID */\n id: string;\n /** Title */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** Subtitle */\n subtitle: {\n /** Default message */\n defaultMessage: string;\n } | null;\n /** Description */\n description: {\n /** Default message */\n defaultMessage: string;\n } | null;\n /** Internal name */\n internalName: string | null;\n /** Visibility tier */\n workflowConfigVisibility: WorkflowConfigVisibility;\n /** Workflow type (DSR or PREFERENCE_MANAGEMENT) */\n workflowConfigType: WorkflowConfigType;\n /** Whether the workflow collects the data subject's region */\n collectDataSubjectRegions: CollectDataSubjectRegions | null;\n /** Region allow list */\n regionList: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** Per-region request expiry times */\n expiryTime:\n | {\n /** Region code (or 'default') */\n region: 'default' | IsoCountryCode | IsoCountrySubdivisionCode;\n /** Expiry time in days */\n value: number;\n }[]\n | null;\n /** Request action */\n action: {\n /** Action ID */\n id: string;\n /** Action type */\n type: RequestAction;\n };\n /** Data subject */\n subject: {\n /** Data subject ID */\n id: string;\n /** Data subject type */\n type: string;\n } | null;\n /** Attribute keys (custom fields) associated with the workflow */\n WorkflowConfigAttributeKeys:\n | {\n /** Attribute key */\n attributeKey: {\n /** Attribute key ID */\n id: string;\n /** Attribute key name */\n name: string;\n };\n }[]\n | null;\n}\n\n/** Alias used by consent-workflow-trigger helpers */\nexport type WorkflowConfigSummary = WorkflowConfigNode;\n\nconst PAGE_SIZE = 50;\n\n/**\n * Display title for a workflow config, matching the admin UI:\n * `internalName` when set, otherwise the external title.\n *\n * @param workflow - Workflow config\n * @returns Display title used in Preference Workflows dropdown\n */\nexport function getWorkflowConfigDisplayTitle(workflow: WorkflowConfigNode): string {\n return workflow.internalName || workflow.title.defaultMessage;\n}\n\n/**\n * Fetch all workflow configs in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All workflow configs\n */\nexport async function fetchAllWorkflowConfigs(\n client: GraphQLClient,\n options: {\n /** Filter by workflow config type */\n workflowConfigType?: WorkflowConfigType;\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<WorkflowConfigNode[]> {\n const { logger, workflowConfigType } = options;\n const configs: WorkflowConfigNode[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n workflows: { nodes },\n } = await makeGraphQLRequest<{\n /** Workflows */\n workflows: {\n /** List */\n nodes: WorkflowConfigNode[];\n };\n }>(client, WORKFLOW_CONFIGS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n filterBy: workflowConfigType ? { workflowConfigType } : undefined,\n },\n logger,\n });\n\n configs.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return configs.sort((a, b) =>\n getWorkflowConfigDisplayTitle(a).localeCompare(getWorkflowConfigDisplayTitle(b)),\n );\n}\n\n/**\n * Resolve a YAML `workflow-title` to a DSR workflow config.\n * Matches against display title (`internalName` or external title).\n *\n * @param workflows - Candidate workflow configs\n * @param workflowTitle - Title from transcend.yml\n * @returns Matching workflow\n */\nexport function resolveWorkflowConfigByTitle(\n workflows: WorkflowConfigNode[],\n workflowTitle: string,\n): WorkflowConfigNode {\n const matches = workflows.filter(\n (workflow) => getWorkflowConfigDisplayTitle(workflow) === workflowTitle,\n );\n if (matches.length === 0) {\n throw new Error(\n `Failed to find DSR workflow with title: ${workflowTitle}. ` +\n `Use the workflow internal name when set, otherwise the external title.`,\n );\n }\n if (matches.length > 1) {\n throw new Error(\n `Found multiple DSR workflows with title: ${workflowTitle}. ` +\n `Set a unique internalName on the workflow in the Admin Dashboard.`,\n );\n }\n const [match] = matches;\n if (!match) {\n throw new Error(`Failed to find DSR workflow with title: ${workflowTitle}.`);\n }\n return match;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const DATA_SUBJECTS: DocumentNode = parse(gql`\n query TranscendCliDataSubjects {\n internalSubjects {\n id\n title {\n defaultMessage\n }\n active\n type\n adminDashboardDefaultSilentMode\n supportsAuthorizedAgent\n actions {\n type\n }\n }\n }\n`);\n\nexport const CREATE_DATA_SUBJECT: DocumentNode = parse(gql`\n mutation TranscendCliCreateDataSubject($type: String!) {\n createSubject(input: { type: $type, title: $type, subjectClass: OTHER }) {\n subject {\n id\n type\n }\n }\n }\n`);\n\nexport const UPDATE_DATA_SUBJECT: DocumentNode = parse(gql`\n mutation TranscendCliUpdateDataSubject($input: UpdateSubjectInput!) {\n updateSubject(input: $input) {\n clientMutationId\n }\n }\n`);\n\nexport const TOGGLE_DATA_SUBJECT: DocumentNode = parse(gql`\n mutation TranscendCliToggleDataSubject($input: ToggleSubjectInput!) {\n toggleSubject(input: $input) {\n clientMutationId\n }\n }\n`);\n","import { RequestActionObjectResolver } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { CREATE_DATA_SUBJECT, DATA_SUBJECTS } from './gqls/dataSubject.js';\n\nexport interface DataSubject {\n /** ID of data subject */\n id: string;\n /** Type of data subject */\n type: string;\n /** Whether active */\n active: boolean;\n /** Title of data subject */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** Whether silent mode is enabled by default */\n adminDashboardDefaultSilentMode: boolean;\n /** Whether requests can be submitted on behalf of this data subject by an authorized agent */\n supportsAuthorizedAgent: boolean;\n /** Enabled actions */\n actions: {\n /** Type of action */\n type: RequestActionObjectResolver;\n }[];\n}\n\n/**\n * Fetch all data subjects in an organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of data subject configurations\n */\nexport async function fetchAllDataSubjects(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<DataSubject[]> {\n const { logger = NOOP_LOGGER } = options;\n const { internalSubjects } = await makeGraphQLRequest<{\n /** Query response */\n internalSubjects: DataSubject[];\n }>(client, DATA_SUBJECTS, { logger });\n return internalSubjects;\n}\n\n/**\n * Create a single data subject\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Created data subject\n */\nexport async function createDataSubject(\n client: GraphQLClient,\n options: {\n /** The type string for the new subject */\n input: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<DataSubject> {\n const { input: dataSubjectType, logger = NOOP_LOGGER } = options;\n const { createSubject } = await makeGraphQLRequest<{\n /** Create Subject Response */\n createSubject: {\n /** Created Data Subject */\n subject: DataSubject;\n };\n }>(client, CREATE_DATA_SUBJECT, {\n variables: { type: dataSubjectType, skipPublish: true },\n logger,\n });\n return createSubject.subject;\n}\n\n/**\n * Convert a list of data subject types into the block list of IDs to assign to the data silo\n *\n * @param dataSubjectTypes - The list of data subject types that the data silo should be for\n * @param allDataSubjects - All data subjects in the organization\n * @returns The block list of data subject ids to not process against this data silo\n */\nexport function convertToDataSubjectBlockList(\n dataSubjectTypes: string[],\n allDataSubjects: { [type in string]: DataSubject },\n): string[] {\n dataSubjectTypes.forEach((type) => {\n if (!allDataSubjects[type]) {\n throw new Error(`Expected to find data subject definition: ${type}`);\n }\n });\n\n return Object.values(allDataSubjects)\n .filter((silo) => !dataSubjectTypes.includes(silo.type))\n .map(({ id }) => id);\n}\n\n/**\n * Convert a list of data subject types into the allow list of types\n *\n * @param dataSubjectTypes - The list of data subject types that the data silo should be for\n * @param allDataSubjects - All data subjects in the organization\n * @returns The allow list of data subjects for that silo\n */\nexport function convertToDataSubjectAllowlist(\n dataSubjectTypes: string[],\n allDataSubjects: { [type in string]: DataSubject },\n): string[] {\n dataSubjectTypes.forEach((type) => {\n if (!allDataSubjects[type]) {\n throw new Error(`Expected to find data subject definition: ${type}`);\n }\n });\n\n return Object.values(allDataSubjects)\n .filter((silo) => !dataSubjectTypes.includes(silo.type))\n .map(({ type }) => type);\n}\n","import { RequestAction, WorkflowConfigType } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchAllDataSilos } from '../data-inventory/fetchAllDataSilos.js';\nimport { fetchAllActions, type Action } from '../dsr-automation/fetchAllActions.js';\nimport {\n fetchAllWorkflowConfigs,\n getWorkflowConfigDisplayTitle,\n resolveWorkflowConfigByTitle,\n type WorkflowConfigSummary,\n} from '../dsr-automation/fetchAllWorkflowConfigs.js';\nimport { fetchAllDataSubjects, type DataSubject } from '../dsr-automation/fetchDataSubjects.js';\nimport { fetchAllPurposes, type Purpose } from '../preference-management/fetchAllPurposes.js';\nimport { fetchAllConsentWorkflowTriggers } from './fetchAllConsentWorkflowTriggers.js';\nimport { CREATE_OR_UPDATE_CONSENT_WORKFLOW_TRIGGER } from './gqls/consentWorkflowTrigger.js';\n\nexport interface ConsentWorkflowTriggerPurposeInput {\n /** The tracking type slug of the purpose */\n 'tracking-type': string;\n /** The matching consent state for the purpose */\n 'matching-state': boolean;\n}\n\nexport interface ConsentWorkflowTriggerSyncInput {\n /** The name of the consent workflow trigger */\n name: string;\n /**\n * Purposes and their matching consent states.\n * Required (non-empty). Used to derive triggerCondition.\n */\n purposes: ConsentWorkflowTriggerPurposeInput[];\n /**\n * DSR workflow display title (internalName or external title).\n * V2 mode — mutually exclusive with action-type / data-silo-titles.\n */\n 'workflow-title'?: string;\n /** The action type (e.g. ERASURE, ACCESS). Legacy mode; required on create when no workflow-title. */\n 'action-type'?: RequestAction;\n /** The data subject type. Required on create (legacy) or always when set. */\n 'data-subject-type'?: string;\n /** Whether the trigger runs silently */\n 'is-silent'?: boolean;\n /** Whether unauthenticated requests are allowed */\n 'allow-unauthenticated'?: boolean;\n /** Whether the trigger is active */\n 'is-active'?: boolean;\n /** Titles of data silos associated with this trigger (legacy mode only) */\n 'data-silo-titles'?: string[];\n}\n\n/**\n * Build the GraphQL triggerCondition JSON string from purposes,\n * matching the admin dashboard Preferences → Consent Workflows UI.\n *\n * @param purposes - Purpose tracking types and matching states\n * @returns JSON string of `{ And: [{ [trackingType]: matchingState }, ...] }`\n */\nexport function buildTriggerConditionFromPurposes(\n purposes: ConsentWorkflowTriggerPurposeInput[],\n): string {\n return JSON.stringify({\n And: purposes.map((purpose) => ({\n [purpose['tracking-type']]: purpose['matching-state'],\n })),\n });\n}\n\n/**\n * Parse GraphQL triggerCondition JSON into purposes for transcend.yml.\n * Expects ConditionalExpression shape: `{ And: [{ [trackingType]: boolean }, ...] }`.\n *\n * @param triggerCondition - JSON string from GraphQL (or null)\n * @returns Purpose entries for YAML, or empty if unparseable\n */\nexport function parsePurposesFromTriggerCondition(\n triggerCondition: string | null | undefined,\n): ConsentWorkflowTriggerPurposeInput[] {\n if (!triggerCondition) {\n return [];\n }\n\n try {\n const parsed: unknown = JSON.parse(triggerCondition);\n if (!parsed || typeof parsed !== 'object') {\n return [];\n }\n\n const andEntries = (parsed as { And?: unknown }).And;\n if (!Array.isArray(andEntries)) {\n return [];\n }\n\n const purposes: ConsentWorkflowTriggerPurposeInput[] = [];\n for (const entry of andEntries) {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n continue;\n }\n for (const [trackingType, matchingState] of Object.entries(\n entry as Record<string, unknown>,\n )) {\n if (typeof matchingState === 'boolean') {\n purposes.push({\n 'tracking-type': trackingType,\n 'matching-state': matchingState,\n });\n }\n }\n }\n return purposes;\n } catch {\n return [];\n }\n}\n\n/**\n * Whether this YAML entry is Workflows V2 mode (links a DSR workflow config).\n *\n * @param trigger - Sync input\n * @returns True when workflow-title is set\n */\nexport function isConsentWorkflowTriggerV2Mode(\n trigger: Pick<ConsentWorkflowTriggerSyncInput, 'workflow-title'>,\n): boolean {\n return Boolean(trigger['workflow-title']);\n}\n\n/**\n * Validate mutual exclusivity and required fields for a trigger input.\n * Pure helper for unit tests and sync.\n *\n * @param trigger - Sync input\n * @param options - Whether the trigger already exists in the org\n */\nexport function validateConsentWorkflowTriggerMode(\n trigger: ConsentWorkflowTriggerSyncInput,\n options: {\n /** True when updating an existing trigger by name */\n isUpdate: boolean;\n },\n): void {\n if (!trigger.purposes?.length) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" requires a non-empty \"purposes\" list ` +\n `(each entry needs tracking-type and matching-state). ` +\n `See Preferences → Preference Workflows.`,\n );\n }\n\n const isV2 = isConsentWorkflowTriggerV2Mode(trigger);\n const hasLegacyAction = Boolean(trigger['action-type']);\n const hasLegacySilos = Boolean(trigger['data-silo-titles']?.length);\n\n if (isV2 && (hasLegacyAction || hasLegacySilos)) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" cannot set \"workflow-title\" together with ` +\n `\"action-type\" or \"data-silo-titles\". Use workflow-title for Workflows V2, ` +\n `or action-type / data-silo-titles for legacy mode.`,\n );\n }\n\n if (!options.isUpdate) {\n if (isV2) {\n // workflow-title already present; data-subject-type may come from the workflow\n return;\n }\n if (!trigger['action-type']) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" requires \"action-type\" when creating ` +\n `a legacy trigger (or set \"workflow-title\" for Workflows V2).`,\n );\n }\n if (!trigger['data-subject-type']) {\n throw new Error(\n `Consent workflow trigger \"${trigger.name}\" requires \"data-subject-type\" when creating a new trigger.`,\n );\n }\n }\n}\n\n/**\n * Resolve workflow display title from a workflowConfigId.\n *\n * @param workflows - DSR workflows\n * @param workflowConfigId - Linked workflow config ID\n * @returns Display title, or undefined if not found\n */\nexport function resolveWorkflowTitleFromId(\n workflows: WorkflowConfigSummary[],\n workflowConfigId: string | null | undefined,\n): string | undefined {\n if (!workflowConfigId) {\n return undefined;\n }\n const workflow = workflows.find((candidate) => candidate.id === workflowConfigId);\n return workflow ? getWorkflowConfigDisplayTitle(workflow) : undefined;\n}\n\n/**\n * Sync consent workflow triggers to Transcend\n *\n * @param client - GraphQL client\n * @param inputs - Consent workflow trigger inputs from YAML\n * @param options - Options\n * @returns True if run without error\n */\nexport async function syncConsentWorkflowTriggers(\n client: GraphQLClient,\n inputs: ConsentWorkflowTriggerSyncInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Page size when fetching data silos */\n pageSize?: number;\n } = {},\n): Promise<boolean> {\n const { logger, pageSize = 50 } = options;\n logger?.info(`Syncing \"${inputs.length}\" consent workflow triggers...`);\n\n let encounteredError = false;\n\n const needsV2 = inputs.some((t) => isConsentWorkflowTriggerV2Mode(t));\n const needsActions = inputs.some((t) => t['action-type']);\n const needsSubjects = inputs.some((t) => t['data-subject-type']);\n const needsDataSilos = inputs.some((t) => t['data-silo-titles']?.length);\n\n const [existingTriggers, actions, dataSubjects, purposes, dataSilos, dsrWorkflows] =\n await Promise.all([\n fetchAllConsentWorkflowTriggers(client, { logger }),\n needsActions ? fetchAllActions(client, { logger }) : ([] as Action[]),\n needsSubjects ? fetchAllDataSubjects(client, { logger }) : ([] as DataSubject[]),\n // Purposes are always required to resolve purposeId for the mutation\n fetchAllPurposes(client, { logger }),\n needsDataSilos ? fetchAllDataSilos(client, { pageSize, logger }) : [],\n needsV2\n ? fetchAllWorkflowConfigs(client, {\n logger,\n workflowConfigType: WorkflowConfigType.DSR,\n })\n : ([] as WorkflowConfigSummary[]),\n ]);\n\n const triggerByName = keyBy(existingTriggers, 'name');\n const actionByType = keyBy(actions, 'type') as Record<string, Action>;\n const dataSubjectByType = keyBy(dataSubjects, 'type') as Record<string, DataSubject>;\n const purposeByTrackingType = keyBy(purposes, 'trackingType') as Record<string, Purpose>;\n const dataSiloByTitle = keyBy(dataSilos, 'title') as Record<\n string,\n { id: string; title: string }\n >;\n\n await mapSeries(inputs, async (trigger) => {\n try {\n const existingTrigger = triggerByName[trigger.name];\n validateConsentWorkflowTriggerMode(trigger, { isUpdate: Boolean(existingTrigger) });\n\n const consentWorkflowTriggerPurposes = trigger.purposes.map((purposeInput) => {\n const purpose = purposeByTrackingType[purposeInput['tracking-type']];\n if (!purpose) {\n throw new Error(\n `Failed to find purpose with trackingType: ${purposeInput['tracking-type']}`,\n );\n }\n return {\n purposeId: purpose.id,\n matchingState: purposeInput['matching-state'],\n };\n });\n\n const mutationInput: Record<string, unknown> = {\n name: trigger.name,\n ...(existingTrigger ? { id: existingTrigger.id } : {}),\n triggerCondition: buildTriggerConditionFromPurposes(trigger.purposes),\n consentWorkflowTriggerPurposes,\n ...(trigger['is-silent'] !== undefined ? { isSilent: trigger['is-silent'] } : {}),\n ...(trigger['allow-unauthenticated'] !== undefined\n ? { allowUnauthenticated: trigger['allow-unauthenticated'] }\n : {}),\n ...(trigger['is-active'] !== undefined ? { isActive: trigger['is-active'] } : {}),\n };\n\n if (isConsentWorkflowTriggerV2Mode(trigger)) {\n const workflow = resolveWorkflowConfigByTitle(dsrWorkflows, trigger['workflow-title']!);\n mutationInput.workflowConfigId = workflow.id;\n\n // Backend create still requires actionId + dataSubjectId — take from the workflow\n if (!existingTrigger) {\n mutationInput.actionId = workflow.action.id;\n if (!workflow.subject) {\n throw new Error(\n `DSR workflow \"${trigger['workflow-title']}\" has no data subject; ` +\n `cannot create consent workflow trigger \"${trigger.name}\".`,\n );\n }\n mutationInput.dataSubjectId = workflow.subject.id;\n } else if (trigger['data-subject-type']) {\n // Allow updating data-subject-type explicitly on V2 updates if provided\n const subject = dataSubjectByType[trigger['data-subject-type']];\n if (!subject) {\n // May need to fetch subjects if only V2 creates were expected — fetch on demand via workflow\n throw new Error(\n `Failed to find data subject with type: ${trigger['data-subject-type']}`,\n );\n }\n mutationInput.dataSubjectId = subject.id;\n }\n } else {\n // Legacy mode\n let actionId: string | undefined;\n if (trigger['action-type']) {\n const action = actionByType[trigger['action-type']];\n if (!action) {\n throw new Error(`Failed to find action with type: ${trigger['action-type']}`);\n }\n actionId = action.id;\n }\n\n let dataSubjectId: string | undefined;\n if (trigger['data-subject-type']) {\n const subject = dataSubjectByType[trigger['data-subject-type']];\n if (!subject) {\n throw new Error(\n `Failed to find data subject with type: ${trigger['data-subject-type']}`,\n );\n }\n dataSubjectId = subject.id;\n }\n\n let dataSiloIds: string[] | undefined;\n if (trigger['data-silo-titles']?.length) {\n dataSiloIds = trigger['data-silo-titles'].map((title) => {\n const dataSilo = dataSiloByTitle[title];\n if (!dataSilo) {\n throw new Error(`Failed to find data silo with title: ${title}`);\n }\n return dataSilo.id;\n });\n }\n\n if (actionId) {\n mutationInput.actionId = actionId;\n }\n if (dataSubjectId) {\n mutationInput.dataSubjectId = dataSubjectId;\n }\n if (dataSiloIds) {\n mutationInput.dataSiloIds = dataSiloIds;\n }\n }\n\n await makeGraphQLRequest<{\n /** Mutation result */\n createOrUpdateConsentWorkflowTrigger: {\n /** Created or updated trigger */\n consentWorkflowTrigger: {\n /** Trigger ID */\n id: string;\n };\n };\n }>(client, CREATE_OR_UPDATE_CONSENT_WORKFLOW_TRIGGER, {\n variables: { input: mutationInput },\n logger,\n });\n\n logger?.info(`Successfully synced consent workflow trigger \"${trigger.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger?.error(\n `Failed to sync consent workflow trigger \"${trigger.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n logger?.info(`Synced \"${inputs.length}\" consent workflow triggers!`);\n return !encounteredError;\n}\n","import { ProcessingPurpose } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n fetchAllProcessingPurposes,\n type ProcessingPurposeSubCategory,\n} from './fetchAllProcessingPurposes.js';\nimport {\n UPDATE_PROCESSING_PURPOSE_SUB_CATEGORIES,\n CREATE_PROCESSING_PURPOSE_SUB_CATEGORY,\n} from './gqls/processingPurpose.js';\n\nexport interface ProcessingPurposeInput {\n /** Name of processing purpose */\n name: string;\n /** Type of processing purpose */\n purpose: ProcessingPurpose;\n /** Description of processing purpose */\n description?: string;\n /** Owner email addresses */\n owners?: string[];\n /** Team names */\n teams?: string[];\n /** Attribute key-value pairs */\n attributes?: {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n }[];\n}\n\n/**\n * Input to create a new processing purpose\n *\n * @param client - GraphQL client\n * @param processingPurpose - Input\n * @param options - Options\n * @returns Created processing purpose\n */\nexport async function createProcessingPurpose(\n client: GraphQLClient,\n options: {\n /** Processing purpose to create */\n input: ProcessingPurposeInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<ProcessingPurposeSubCategory, 'id' | 'name' | 'purpose'>> {\n const { input: processingPurpose, logger } = options;\n const gqlInput = {\n name: processingPurpose.name,\n purpose: processingPurpose.purpose,\n description: processingPurpose.description,\n // TODO: https://transcend.height.app/T-31994 - add attributes, teams, owners\n };\n\n const { createProcessingPurposeSubCategory } = await makeGraphQLRequest<{\n /** Create processing purpose mutation */\n createProcessingPurposeSubCategory: {\n /** Created processing purpose */\n processingPurposeSubCategory: ProcessingPurposeSubCategory;\n };\n }>(client, CREATE_PROCESSING_PURPOSE_SUB_CATEGORY, {\n variables: { input: gqlInput },\n logger,\n });\n return createProcessingPurposeSubCategory.processingPurposeSubCategory;\n}\n\n/**\n * Input to update processing purposes\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateProcessingPurposes(\n client: GraphQLClient,\n options: {\n /** [ProcessingPurposeInput, processingPurposeId] list */\n input: [ProcessingPurposeInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: processingPurposes, logger } = options;\n await makeGraphQLRequest(client, UPDATE_PROCESSING_PURPOSE_SUB_CATEGORIES, {\n variables: {\n input: {\n processingPurposeSubCategories: processingPurposes.map(([processingPurpose, id]) => ({\n id,\n description: processingPurpose.description,\n // TODO: https://transcend.height.app/T-31994 - add teams, owners\n attributes: processingPurpose.attributes,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the data inventory processing purposes\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncProcessingPurposes(\n client: GraphQLClient,\n inputs: ProcessingPurposeInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n\n logger.info(`Syncing \"${inputs.length}\" processing purposes...`);\n\n let encounteredError = false;\n\n const existingProcessingPurposes = await fetchAllProcessingPurposes(client, { logger });\n\n const processingPurposeByName: {\n [k in string]: Pick<ProcessingPurposeSubCategory, 'id' | 'name'>;\n } = keyBy(existingProcessingPurposes, ({ name, purpose }) => `${name}:${purpose}`);\n\n const newProcessingPurposes = inputs.filter(\n (input) => !processingPurposeByName[`${input.name}:${input.purpose}`],\n );\n\n await mapSeries(newProcessingPurposes, async (processingPurpose) => {\n try {\n const newProcessingPurpose = await createProcessingPurpose(client, {\n input: processingPurpose,\n logger,\n });\n processingPurposeByName[`${newProcessingPurpose.name}:${newProcessingPurpose.purpose}`] =\n newProcessingPurpose;\n logger.info(`Successfully synced processing purpose \"${processingPurpose.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync processing purpose \"${processingPurpose.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" processing purposes!`);\n await updateProcessingPurposes(client, {\n input: inputs.map((input) => [\n input,\n processingPurposeByName[`${input.name}:${input.purpose}`]!.id,\n ]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" processing purposes!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync \"${inputs.length}\" processing purposes ! - ${(err as Error).message}`,\n );\n }\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const REQUEST_ENRICHERS = gql`\n query TranscendCliRequestEnrichers($first: Int!, $offset: Int!, $requestId: ID!) {\n requestEnrichers(\n input: { requestId: $requestId }\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [\n { field: createdAt, direction: ASC }\n { field: title, direction: ASC, model: enricher }\n ]\n ) {\n nodes {\n id\n status\n enricher {\n id\n type\n title\n }\n }\n totalCount\n }\n }\n`;\n\nexport const RETRY_REQUEST_ENRICHER = gql`\n mutation TranscendCliRetryRequestEnricher($requestEnricherId: ID!) {\n retryRequestEnricher(id: $requestEnricherId) {\n requestEnricher {\n id\n }\n }\n }\n`;\n\nexport const SKIP_REQUEST_ENRICHER = gql`\n mutation TranscendCliSkipRequestEnricher($requestEnricherId: ID!) {\n skipRequestEnricher(id: $requestEnricherId) {\n requestEnricher {\n id\n }\n }\n }\n`;\n","import { RequestEnricherStatus } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { REQUEST_ENRICHERS } from './gqls/requestEnricher.js';\n\nexport interface RequestEnricher {\n /** ID of request enricher */\n id: string;\n /** Name of identifier */\n enricher: {\n /** ID of enricher */\n id: string;\n /** Title of enricher */\n title: string;\n /** Typeof of enricher */\n type: string;\n };\n /** The status of the enricher */\n status: RequestEnricherStatus;\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all request enrichers for a particular request\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of request enrichers\n */\nexport async function fetchAllRequestEnrichers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** ID of request to filter on */\n requestId: string;\n };\n },\n): Promise<RequestEnricher[]> {\n const {\n logger = NOOP_LOGGER,\n filterBy: { requestId },\n } = options;\n const requestEnrichers: RequestEnricher[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n requestEnrichers: { nodes },\n } = await makeGraphQLRequest<{\n /** Request Enrichers */\n requestEnrichers: {\n /** List */\n nodes: RequestEnricher[];\n };\n }>(client, REQUEST_ENRICHERS, {\n variables: { first: PAGE_SIZE, offset, requestId },\n logger,\n });\n requestEnrichers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return requestEnrichers;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const SOMBRA_VERSION: DocumentNode = parse(gql`\n query TranscendSombraVersion {\n organization {\n sombra {\n version\n }\n }\n }\n`);\n","import { IdentifierType } from '@transcend-io/privacy-types';\nimport { decodeCodec, valuesOf } from '@transcend-io/type-utils';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\nimport { GraphQLClient } from 'graphql-request';\nimport * as t from 'io-ts';\nimport semver from 'semver';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { withTransientRetry } from '../api/withTransientRetry.js';\nimport { SOMBRA_VERSION } from './gqls/sombraVersion.js';\n\nconst MIN_SOMBRA_VERSION_TO_DECRYPT = '7.180.0';\n\nconst RequestIdentifier = t.type({\n /** ID of request */\n id: t.string,\n /** Name of identifier */\n name: t.string,\n /** The underlying identifier value */\n value: t.string,\n /** Type of identifier */\n type: valuesOf(IdentifierType),\n});\n\n/** Type override */\nexport type RequestIdentifier = t.TypeOf<typeof RequestIdentifier>;\n\nconst PAGE_SIZE = 50;\n\nexport const RequestIdentifiersResponse = t.type({\n identifiers: t.array(RequestIdentifier),\n});\n\n/**\n * Validate that the Sombra version meets the minimum requirement for\n * decrypting request identifiers. Call once before bulk-fetching identifiers\n * to avoid repeating this check on every request.\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function validateSombraVersion(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n const {\n organization: {\n sombra: { version },\n },\n } = await makeGraphQLRequest<{\n /** The organization */\n organization: {\n /** Sombra */\n sombra: {\n /** Version string */\n version: string;\n };\n };\n }>(client, SOMBRA_VERSION, { logger });\n\n if (version && semver.lt(version, MIN_SOMBRA_VERSION_TO_DECRYPT)) {\n throw new Error(\n `Please upgrade Sombra to ${MIN_SOMBRA_VERSION_TO_DECRYPT} or greater to use this command.`,\n );\n }\n}\n\n/**\n * Fetch all request identifiers for a particular request\n *\n * @param client - GraphQL client\n * @param sombra - Sombra client\n * @param options - Options\n * @returns List of request identifiers\n */\nexport async function fetchAllRequestIdentifiers(\n client: GraphQLClient,\n sombra: Got,\n options: {\n /** Filter options */\n filterBy: {\n /** ID of request to filter on */\n requestId: string;\n };\n /** Skip the Sombra version check (caller already validated) */\n skipSombraCheck?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<RequestIdentifier[]> {\n const {\n filterBy: { requestId },\n skipSombraCheck = false,\n logger = NOOP_LOGGER,\n } = options;\n const requestIdentifiers: RequestIdentifier[] = [];\n let offset = 0;\n let shouldContinue = false;\n\n if (!skipSombraCheck) {\n await validateSombraVersion(client, { logger });\n }\n\n do {\n // `POST /v1/request-identifiers` is effectively a paginated read (the server\n // never mutates state from this call), so it is safe to retry on transient\n // gateway / network errors. Retries matter here because a single export can\n // span thousands of pages and occasional 502s from the Sombra reverse\n // tunnel or upstream load balancer would otherwise abort the whole chunk.\n const response = await withTransientRetry(\n 'Failed to fetch request identifiers',\n () =>\n sombra!\n .post<{\n /** Decrypted identifiers */\n identifiers: RequestIdentifier[];\n }>('v1/request-identifiers', {\n json: {\n first: PAGE_SIZE,\n offset,\n requestId,\n },\n })\n .json(),\n { logger, maxAttempts: 6, baseDelayMs: 500 },\n );\n\n const { identifiers: nodes } = decodeCodec(RequestIdentifiersResponse, response);\n\n requestIdentifiers.push(...nodes);\n\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return requestIdentifiers;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const REMOVE_REQUEST_IDENTIFIERS: DocumentNode = parse(gql`\n mutation TranscendCliRemoveRequestIdentifiers($input: RemoveRequestIdentifiersInput!) {\n removeRequestIdentifiers(input: $input) {\n count\n }\n }\n`);\n\nexport const REQUEST_IDENTIFIERS: DocumentNode = parse(gql`\n query TranscendCliRequestIdentifiers(\n $first: Int!\n $offset: Int!\n $requestIds: [ID!]\n $updatedAtBefore: Date\n $updatedAtAfter: Date\n ) {\n requestIdentifiers(\n input: {\n requestIds: $requestIds\n updatedAtBefore: $updatedAtBefore\n updatedAtAfter: $updatedAtAfter\n }\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n isVerifiedAtLeastOnce\n }\n totalCount\n }\n }\n`);\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { REQUEST_IDENTIFIERS } from './gqls/requestIdentifier.js';\n\nexport interface RequestIdentifierMetadata {\n /** ID of request identifier */\n id: string;\n /** Name of identifier */\n name: string;\n /** Status of identifier */\n isVerifiedAtLeastOnce: boolean;\n}\n\nconst PAGE_SIZE = 50;\n\n/**\n * Fetch all request identifier metadata for a particular request\n *\n * @param client - GraphQL client\n * @param options - Filter options\n * @returns List of request identifiers\n */\nexport async function fetchAllRequestIdentifierMetadata(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** ID of request to filter on */\n requestId?: string;\n /** IDs of requests to filter on */\n requestIds?: string[];\n /** Filter for request identifiers updated before this date */\n updatedAtBefore?: Date;\n /** Filter for request identifiers updated after this date */\n updatedAtAfter?: Date;\n };\n } = {},\n): Promise<RequestIdentifierMetadata[]> {\n const { logger = NOOP_LOGGER, filterBy } = options;\n const { requestId, requestIds, updatedAtBefore, updatedAtAfter } = filterBy ?? {};\n const resolvedRequestIds = requestIds ?? (requestId ? [requestId] : undefined);\n const requestIdentifiers: RequestIdentifierMetadata[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n requestIdentifiers: { nodes },\n } = await makeGraphQLRequest<{\n /** Request Identifiers */\n requestIdentifiers: {\n /** List */\n nodes: RequestIdentifierMetadata[];\n };\n }>(client, REQUEST_IDENTIFIERS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n requestIds: resolvedRequestIds,\n updatedAtBefore: updatedAtBefore ? updatedAtBefore.toISOString() : undefined,\n updatedAtAfter: updatedAtAfter ? updatedAtAfter.toISOString() : undefined,\n },\n logger,\n });\n requestIdentifiers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return requestIdentifiers;\n}\n","import { parse, type DocumentNode } from 'graphql';\nimport { gql } from 'graphql-request';\n\nexport const NEW_IDENTIFIER_TYPES: DocumentNode = parse(gql`\n query TranscendCliNewIdentifierTypes {\n newIdentifierTypes {\n name\n }\n }\n`);\n\nexport const CREATE_IDENTIFIER: DocumentNode = parse(gql`\n mutation TranscendCliCreateIdentifier($input: IdentifierInput!) {\n createIdentifier(input: $input) {\n identifier {\n id\n name\n }\n }\n }\n`);\n\nexport const UPDATE_IDENTIFIER: DocumentNode = parse(gql`\n mutation TranscendCliUpdateIdentifier($input: UpdateIdentifierInput!) {\n updateIdentifier(input: $input) {\n clientMutationId\n }\n }\n`);\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, uniq, flatten, difference } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllIdentifiers, type Identifier } from '../data-inventory/fetchAllIdentifiers.js';\nimport { CREATE_IDENTIFIER, NEW_IDENTIFIER_TYPES } from './gqls/dsrIdentifier.js';\n\nexport interface IdentifiersAndCreateMissingInput {\n /** Enricher configurations */\n enrichers?: {\n /** Input identifier name */\n 'input-identifier'?: string;\n /** Output identifier names */\n 'output-identifiers': string[];\n }[];\n /** Data silo configurations */\n 'data-silos'?: {\n /** Identity keys */\n 'identity-keys'?: string[];\n }[];\n /** Identifier configurations */\n identifiers?: {\n /** Identifier name */\n name: string;\n }[];\n}\n\n/**\n * Fetch all identifiers and if any are found in the config that are\n * missing, create those identifiers.\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns A map from identifier name to Identifier\n */\nexport async function fetchIdentifiersAndCreateMissing(\n client: GraphQLClient,\n options: {\n /** Transcend input configuration */\n input: IdentifiersAndCreateMissingInput;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<{ [k in string]: Identifier }> {\n const { input, skipPublish = false, logger = NOOP_LOGGER } = options;\n const { enrichers = [], 'data-silos': dataSilos = [], identifiers = [] } = input;\n const allIdentifiers = await fetchAllIdentifiers(client, { logger });\n const identifiersByName = keyBy(allIdentifiers, 'name');\n\n const expectedIdentifiers = uniq([\n ...flatten(\n enrichers.map((enricher) => [\n enricher['input-identifier'],\n ...enricher['output-identifiers'],\n ]),\n ),\n ...flatten(dataSilos.map((dataSilo) => dataSilo['identity-keys'])),\n ...identifiers.map(({ name }) => name),\n ]).filter((x) => !!x);\n const missingIdentifiers = difference(\n expectedIdentifiers,\n allIdentifiers.map(({ name }) => name),\n );\n\n if (missingIdentifiers.length > 0) {\n logger.info(`Creating ${missingIdentifiers.length} new identifiers...`);\n const { newIdentifierTypes } = await makeGraphQLRequest<{\n /** Query response */\n newIdentifierTypes: {\n /** Name of identifier type remaining */\n name: string;\n }[];\n }>(client, NEW_IDENTIFIER_TYPES, { logger });\n const nativeTypesRemaining = newIdentifierTypes.map(({ name }) => name);\n await mapSeries(missingIdentifiers, async (identifier) => {\n logger.info(`Creating identifier ${identifier}...`);\n try {\n const { createIdentifier } = await makeGraphQLRequest<{\n /** createIdentifier Response */\n createIdentifier: {\n /** Created identifier */\n identifier: Identifier;\n };\n }>(client, CREATE_IDENTIFIER, {\n variables: {\n input: {\n name: identifier,\n type: nativeTypesRemaining.includes(identifier!) ? identifier : 'custom',\n skipPublish,\n },\n },\n logger,\n });\n logger.info(`Created identifier ${identifier}!`);\n\n identifiersByName[identifier!] = createIdentifier.identifier;\n } catch (err) {\n logger.error(`Failed to create identifier \"${identifier}\" - ${(err as Error).message}`);\n }\n });\n }\n return identifiersByName;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { RETRY_REQUEST_ENRICHER } from './gqls/requestEnricher.js';\n\n/**\n * Retry a request enricher\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function retryRequestEnricher(\n client: GraphQLClient,\n options: {\n /** The ID of the request enricher to restart */\n id: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { id, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, RETRY_REQUEST_ENRICHER, {\n variables: { requestEnricherId: id },\n logger,\n });\n}\n","import { RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { TOGGLE_DATA_SUBJECT, UPDATE_DATA_SUBJECT } from './gqls/dataSubject.js';\n\nexport interface DataSubjectInput {\n /** The type of the data subject */\n type: string;\n /** Whether the data subject is active on the Privacy Center & DSR API */\n active?: boolean;\n /** The title of the data subject */\n title?: string;\n /** Whether or not to default new requests made in the admin dashboard to silent mode */\n adminDashboardDefaultSilentMode?: boolean;\n /** Whether requests can be submitted on behalf of this data subject by an authorized agent */\n supportsAuthorizedAgent?: boolean;\n /** Enabled request actions for the data subject */\n actions?: RequestAction[];\n}\n\n/**\n * Sync the data subjects\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function syncDataSubject(\n client: GraphQLClient,\n options: {\n /** DataSubject update input */\n input: DataSubjectInput;\n /** Existing data subject Id */\n dataSubjectId: string;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: dataSubject, dataSubjectId, skipPublish = false, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_DATA_SUBJECT, {\n variables: {\n input: {\n id: dataSubjectId,\n title: dataSubject.title,\n adminDashboardDefaultSilentMode: dataSubject.adminDashboardDefaultSilentMode,\n supportsAuthorizedAgent: dataSubject.supportsAuthorizedAgent,\n actions: dataSubject.actions,\n skipPublish: skipPublish && typeof dataSubject.active === 'undefined',\n },\n },\n logger,\n });\n\n if (typeof dataSubject.active === 'boolean') {\n await makeGraphQLRequest(client, TOGGLE_DATA_SUBJECT, {\n variables: {\n input: {\n id: dataSubjectId,\n active: dataSubject.active,\n skipPublish,\n },\n },\n logger,\n });\n }\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const ENRICHERS = gql`\n query TranscendCliEnrichers($title: String, $first: Int!, $offset: Int!) {\n enrichers(\n filterBy: { text: $title }\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [{ field: createdAt, direction: ASC }, { field: title, direction: ASC }]\n ) {\n nodes {\n id\n title\n url\n type\n expirationDuration\n lookerQueryTitle\n testRegex\n transitionRequestStatus\n phoneNumbers\n regionList\n inputIdentifier {\n name\n }\n identifiers {\n name\n }\n dataSubjects {\n type\n }\n actions\n }\n }\n }\n`;\n\nexport interface Initializer {\n /** ID of enricher */\n id: string;\n /** Identifiers */\n identifiers: {\n /** Name of identifier */\n name: string;\n }[];\n}\n\nexport const INITIALIZER = gql`\n query TranscendCliInitializer {\n initializer {\n id\n identifiers {\n name\n }\n }\n }\n`;\n\nexport const CREATE_ENRICHER = gql`\n mutation TranscendCliCreateEnricher($input: EnricherInput!) {\n createEnricher(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const UPDATE_ENRICHER = gql`\n mutation TranscendCliUpdateEnricher($input: UpdateEnricherInput!) {\n updateEnricher(input: $input) {\n clientMutationId\n }\n }\n`;\n","import {\n EnricherType,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n PreflightRequestStatus,\n RequestAction,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { Identifier } from '../data-inventory/fetchAllIdentifiers.js';\nimport { ENRICHERS, CREATE_ENRICHER, UPDATE_ENRICHER } from './gqls/enricher.js';\n\nexport interface EnricherInput {\n /** The display title of the enricher */\n title: string;\n /** The names of the identifiers that can be resolved by this enricher */\n 'output-identifiers': string[];\n /** Internal description for why the enricher is needed */\n description?: string;\n /** The URL of the enricher */\n url?: string;\n /** The type of enricher */\n type?: EnricherType;\n /** The name of the identifier that will be the input to this enricher */\n 'input-identifier'?: string;\n /** A regular expression that can be used to match on for cancellation */\n testRegex?: string;\n /** For looker integration - the title of the looker query to run */\n lookerQueryTitle?: string;\n /** The duration (in ms) that the enricher should take to execute */\n expirationDuration?: number;\n /** The status that the enricher should transfer to when condition is met */\n transitionRequestStatus?: PreflightRequestStatus;\n /** For twilio integration - the phone numbers that can be used to send text codes */\n phoneNumbers?: string[];\n /** The list of regions that should trigger the preflight check */\n regionList?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** Specify which data subjects the enricher should run for */\n 'data-subjects'?: string[];\n /** Headers to include in the webhook */\n headers?: {\n /** Header name */\n name: string;\n /** Header value */\n value: string;\n /** Whether the value is a secret */\n isSecret?: boolean;\n }[];\n /** The privacy actions that the enricher should run against */\n 'privacy-actions'?: RequestAction[];\n}\n\nexport interface Enricher {\n /** ID of enricher */\n id: string;\n /** Title of enricher */\n title: string;\n /** URL of enricher */\n url: string;\n /** Server silo */\n type: EnricherType;\n /** Input identifier */\n inputIdentifier: {\n /** Identifier name */\n name: string;\n };\n /** The selected actions */\n actions: RequestAction[];\n /** Output identifiers */\n identifiers: {\n /** Identifier name */\n name: string;\n }[];\n /** Data subjects that the preflight check is configured for */\n dataSubjects: {\n /** Data subject type */\n type: string;\n }[];\n /** The duration (in ms) that the enricher should take to execute. - BigInt */\n expirationDuration: string;\n /** Looker query title */\n lookerQueryTitle?: string;\n /** A regular expression that can be used to match on for cancelation */\n testRegex?: string;\n /** The status that the enricher should transfer to when condition is met. */\n transitionRequestStatus?: PreflightRequestStatus;\n /** The twilio phone number to send from */\n phoneNumbers: string[];\n /** The list of regions that should trigger the enrichment condition */\n regionList: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n}\n\nexport interface DataSubjectRef {\n /** ID of data subject */\n id: string;\n /** Type of data subject */\n type: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all enrichers in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All enrichers in the organization\n */\nexport async function fetchAllEnrichers(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter by title */\n title?: string;\n };\n } = {},\n): Promise<Enricher[]> {\n const { logger = NOOP_LOGGER, filterBy } = options;\n const { title } = filterBy ?? {};\n const enrichers: Enricher[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n enrichers: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n enrichers: {\n /** List of matches */\n nodes: Enricher[];\n };\n }>(client, ENRICHERS, {\n variables: { first: PAGE_SIZE, offset, title },\n logger,\n });\n enrichers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return enrichers.sort((a, b) => a.title.localeCompare(b.title));\n}\n\n/**\n * Sync an enricher configuration\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function syncEnricher(\n client: GraphQLClient,\n options: {\n /** The enricher input */\n input: EnricherInput;\n /** Index of identifiers in the organization */\n identifierByName: { [name in string]: Identifier };\n /** Lookup data subject by name */\n dataSubjectsByName: { [name in string]: DataSubjectRef };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: enricher, identifierByName, dataSubjectsByName, logger = NOOP_LOGGER } = options;\n const matches = await fetchAllEnrichers(client, {\n filterBy: { title: enricher.title },\n logger,\n });\n const existingEnricher = matches.find(({ title }) => title === enricher.title);\n\n const dataSubjectIds = enricher['data-subjects']?.map((subject) => {\n const existing = dataSubjectsByName[subject];\n if (!existing) {\n throw new Error(`Failed to find a data subject with name: ${subject}`);\n }\n return existing.id;\n });\n\n const inputIdentifier = enricher['input-identifier'];\n const actionUpdates = enricher['privacy-actions'] || Object.values(RequestAction);\n if (existingEnricher) {\n await makeGraphQLRequest(client, UPDATE_ENRICHER, {\n variables: {\n input: {\n id: existingEnricher.id,\n title: enricher.title,\n url: enricher.url,\n headers: enricher.headers,\n testRegex: enricher.testRegex,\n lookerQueryTitle: enricher.lookerQueryTitle,\n expirationDuration:\n typeof enricher.expirationDuration === 'number'\n ? enricher.expirationDuration.toString()\n : undefined,\n transitionRequestStatus: enricher.transitionRequestStatus,\n phoneNumbers: enricher.phoneNumbers,\n regionList: enricher.regionList,\n dataSubjectIds,\n description: enricher.description || '',\n inputIdentifier: inputIdentifier ? identifierByName[inputIdentifier]!.id : undefined,\n identifiers: enricher['output-identifiers']!.map((id) => identifierByName[id]!.id),\n ...(existingEnricher.type === EnricherType.Sombra ? {} : { actions: actionUpdates }),\n },\n },\n logger,\n });\n } else if (inputIdentifier) {\n await makeGraphQLRequest(client, CREATE_ENRICHER, {\n variables: {\n input: {\n title: enricher.title,\n url: enricher.url,\n type: enricher.type || EnricherType.Server,\n headers: enricher.headers,\n testRegex: enricher.testRegex,\n lookerQueryTitle: enricher.lookerQueryTitle,\n expirationDuration:\n typeof enricher.expirationDuration === 'number'\n ? enricher.expirationDuration.toString()\n : undefined,\n transitionRequestStatus: enricher.transitionRequestStatus,\n phoneNumbers: enricher.phoneNumbers,\n dataSubjectIds,\n regionList: enricher.regionList,\n description: enricher.description || '',\n inputIdentifier: identifierByName[inputIdentifier]!.id,\n identifiers: enricher['output-identifiers'].map((id) => identifierByName[id]!.id),\n actions: actionUpdates,\n },\n },\n logger,\n });\n }\n}\n","import { RequestAction } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { DataSubject } from './fetchDataSubjects.js';\nimport { UPDATE_IDENTIFIER } from './gqls/dsrIdentifier.js';\n\nexport interface IdentifierInput {\n /** The name of the identifier */\n name: string;\n /** The type of the identifier */\n type: string;\n /** Regular expression to verify the identifier */\n regex?: string;\n /** The fixed set of options that an identifier can take on */\n selectOptions?: string[];\n /** Whether or not the identifier is shown in the privacy center form */\n privacyCenterVisibility?: RequestAction[];\n /** The set of data subjects that this identifier is enabled for */\n dataSubjects?: string[];\n /** When true, the identifier is a required field on the privacy center form */\n isRequiredInForm?: boolean;\n /** Placeholder message for identifier */\n placeholder?: string;\n /** Display title for identifier */\n displayTitle?: string;\n /** Display description for identifier */\n displayDescription?: string;\n /** The display order for the identifier */\n displayOrder?: number;\n /** Whether or not the identifier is unique on the preference store */\n isUniqueOnPreferenceStore?: boolean;\n}\n\n/**\n * Sync a single identifier\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function syncIdentifier(\n client: GraphQLClient,\n options: {\n /** Identifier update input */\n input: IdentifierInput;\n /** Data subject lookup by name */\n dataSubjectsByName: { [k in string]: DataSubject };\n /** Existing identifier Id */\n identifierId: string;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const {\n input: identifier,\n dataSubjectsByName,\n identifierId,\n skipPublish = false,\n logger = NOOP_LOGGER,\n } = options;\n await makeGraphQLRequest(client, UPDATE_IDENTIFIER, {\n variables: {\n input: {\n id: identifierId,\n selectOptions: identifier.selectOptions,\n isRequiredInForm: identifier.isRequiredInForm,\n regex: identifier.regex,\n placeholder: identifier.placeholder,\n displayTitle: identifier.displayTitle,\n displayDescription: identifier.displayDescription,\n displayOrder: identifier.displayOrder,\n isUniqueOnPreferenceStore: identifier.isUniqueOnPreferenceStore,\n privacyCenterVisibility: identifier.privacyCenterVisibility,\n dataSubjectIds: identifier.dataSubjects\n ? identifier.dataSubjects.map((type) => dataSubjectsByName[type]!.id)\n : undefined,\n skipPublish,\n },\n },\n logger,\n });\n}\n","import { gql } from 'graphql-request';\n\nexport const ADD_SILO_DISCOVERY_RESULTS = gql`\n mutation AddSiloDiscoveryResults($pluginId: ID!, $rawResults: [SiloDiscoveryRawResultInput!]!) {\n addSiloDiscoveryResults(input: { pluginId: $pluginId, rawResults: $rawResults }) {\n success\n }\n }\n`;\n\nexport const ENABLED_PLUGINS = gql`\n query Plugins($dataSiloId: String!, $type: PluginType!) {\n plugins(filterBy: { dataSiloId: $dataSiloId, type: $type, enabled: true }) {\n plugins {\n id\n dataSilo {\n type\n }\n }\n totalCount\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { ENABLED_PLUGINS } from './gqls/siloDiscovery.js';\n\nexport interface Plugin {\n /** Associated data silo */\n dataSilo: {\n /** The type of plugin */\n type: string;\n };\n /** The ID of this plugin */\n id: string;\n}\n\nexport interface PluginResponse {\n /** The key object of the response */\n plugins: {\n /** The total count */\n totalCount: number;\n /** The list of plugins */\n plugins: Plugin[];\n };\n}\n\n/**\n * Fetch a data silo discovery plugin\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns An active data silo plugin (if multiple, returns the first)\n */\nexport async function fetchActiveSiloDiscoPlugin(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** The data silo to look up plugins for */\n dataSiloId: string;\n };\n },\n): Promise<Plugin> {\n const {\n logger = NOOP_LOGGER,\n filterBy: { dataSiloId },\n } = options;\n const response = await makeGraphQLRequest<PluginResponse>(client, ENABLED_PLUGINS, {\n variables: {\n dataSiloId,\n type: 'DATA_SILO_DISCOVERY',\n },\n logger,\n });\n\n const { plugins, totalCount } = response.plugins;\n if (totalCount === 0) {\n logger.error('No active data silo plugins found for this data silo.');\n process.exit(1);\n }\n\n return plugins[0]!;\n}\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { ATTRIBUTE_KEYS_REQUESTS } from '../administration/gqls/attributeKey.js';\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\n\nexport interface AttributeKey {\n /** ID of attribute key */\n id: string;\n /** Name of attribute key */\n name: string;\n /** Attribute key type */\n type: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all attribute keys enabled for privacy requests\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All attribute keys in the organization\n */\nexport async function fetchAllRequestAttributeKeys(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AttributeKey[]> {\n const { logger } = options;\n const attributeKeys: AttributeKey[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n attributeKeys: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n attributeKeys: {\n /** List of matches */\n nodes: AttributeKey[];\n };\n }>(client, ATTRIBUTE_KEYS_REQUESTS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n attributeKeys.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return attributeKeys.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const SILO_DISCOVERY_RESULTS = gql`\n query TranscendCliSiloDiscoveryResults($first: Int!, $offset: Int!) {\n siloDiscoveryResults(first: $first, offset: $offset) {\n nodes {\n title\n resourceId\n country\n countrySubDivision\n plaintextContext\n status\n containsSensitiveData\n suggestedCatalog {\n title\n }\n plugin {\n dataSilo {\n title\n }\n }\n }\n }\n }\n`;\n","import type { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { SILO_DISCOVERY_RESULTS } from './gqls/siloDiscoveryResult.js';\n\nexport interface SiloDiscoveryResult {\n /** Title of silo discovery result */\n title?: string;\n /** Resource ID of silo discovery result */\n resourceId: string;\n /** Suggested catalog */\n suggestedCatalog: {\n /** Title for the suggested catalog */\n title: string;\n };\n /** The likelihood that data is sensitive for this results */\n containsSensitiveData: string;\n /** The status of silo discovery triage */\n status: string;\n /** Hosting country of data silo discovery result */\n country?: IsoCountryCode;\n /** Hosting subdivision data silo discovery result */\n countrySubDivision?: IsoCountrySubdivisionCode;\n /** Plaintext context data silo discovery result */\n plaintextContext: string;\n /** The plugin that found this result */\n plugin: {\n /** The data silo the plugin belongs to */\n dataSilo: {\n /** The internal display title */\n title: string;\n };\n };\n}\n\nconst PAGE_SIZE = 30;\n\n/**\n * Fetch all silo discovery results in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All silo discovery results in the organization\n */\nexport async function fetchAllSiloDiscoveryResults(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SiloDiscoveryResult[]> {\n const { logger } = options;\n const siloDiscoveryResults: SiloDiscoveryResult[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n siloDiscoveryResults: { nodes },\n } = await makeGraphQLRequest<{\n /** Discovery results */\n siloDiscoveryResults: {\n /** Nodes */\n nodes: SiloDiscoveryResult[];\n };\n }>(client, SILO_DISCOVERY_RESULTS, {\n variables: { first: PAGE_SIZE, offset, input: {}, filterBy: {} },\n logger,\n });\n\n const titledNodes = nodes.map((node) =>\n node.title === null && node.suggestedCatalog?.title\n ? { ...node, title: node.suggestedCatalog.title }\n : node,\n );\n\n siloDiscoveryResults.push(...titledNodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return siloDiscoveryResults;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const CATALOGS = gql`\n query TranscendCliCatalogs($first: Int!, $offset: Int!) {\n catalogs(first: $first, offset: $offset, filterBy: {}, useMaster: false) {\n nodes {\n integrationName\n title\n hasApiFunctionality\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CATALOGS } from './gqls/catalog.js';\n\nexport interface Catalog {\n /** Integration name */\n integrationName: string;\n /** Title of Data Silo */\n title: string;\n /** Whether API is supported */\n hasApiFunctionality: boolean;\n}\n\nconst PAGE_SIZE = 100;\n\n/**\n * Fetch all integration catalogs in an organization\n *\n * @param client - Client\n * @param options - Options\n * @returns Integration catalogs\n */\nexport async function fetchAllCatalogs(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Catalog[]> {\n const { logger } = options;\n const catalogs: Catalog[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n catalogs: { nodes },\n } = await makeGraphQLRequest<{\n /** integration catalogs */\n catalogs: {\n /** List */\n nodes: Catalog[];\n };\n }>(client, CATALOGS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n catalogs.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n return catalogs.sort((a, b) => a.integrationName.localeCompare(b.integrationName));\n}\n\nexport interface IndexedCatalogs {\n /** Mapping from service name to service title */\n serviceToTitle: { [k in string]: string };\n /** Mapping from service name to boolean indicate if service has API integration support */\n serviceToSupportedIntegration: { [k in string]: boolean };\n}\n\n/**\n * Fetch all integration catalogs and index them for usage in common utility manners\n *\n * @param client - Client\n * @param options - Options\n * @returns Integration catalogs\n */\nexport async function fetchAndIndexCatalogs(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<\n {\n /** List of all catalogs */\n catalogs: Catalog[];\n } & IndexedCatalogs\n> {\n const catalogs = await fetchAllCatalogs(client, options);\n\n const serviceToTitle = catalogs.reduce(\n (acc, catalog) => Object.assign(acc, { [catalog.integrationName]: catalog.title }),\n {} as { [k in string]: string },\n );\n\n const serviceToSupportedIntegration = catalogs.reduce(\n (acc, catalog) =>\n Object.assign(acc, {\n [catalog.integrationName]: catalog.hasApiFunctionality,\n }),\n {} as { [k in string]: boolean },\n );\n\n return {\n catalogs,\n serviceToTitle,\n serviceToSupportedIntegration,\n };\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const REQUEST_DATA_SILOS = gql`\n query TranscendCliRequestDataSilos(\n $first: Int!\n $offset: Int!\n $filterBy: RequestDataSiloFiltersInput!\n ) {\n requestDataSilos(\n filterBy: $filterBy\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [\n { field: createdAt, direction: DESC }\n { field: title, direction: ASC, model: dataSilo }\n ]\n ) {\n nodes {\n id\n request {\n type\n }\n }\n totalCount\n }\n }\n`;\n\nexport const CHANGE_REQUEST_DATA_SILO_STATUS = gql`\n mutation TranscendCliMarkRequestDataSiloCompleted(\n $requestDataSiloId: ID!\n $status: UpdateRequestDataSiloStatus!\n ) {\n changeRequestDataSiloStatus(input: { id: $requestDataSiloId, status: $status }) {\n requestDataSilo {\n id\n }\n }\n }\n`;\n\nexport const RETRY_REQUEST_DATA_SILO = gql`\n mutation TranscendCliRetryRequestDataSilo($requestDataSiloId: ID!) {\n retryRequestDataSilo(id: $requestDataSiloId) {\n requestDataSilo {\n id\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: DESC }\n// { field: title, direction: ASC, model: dataSilo }\n// ]\nexport const REDUCED_REQUESTS_FOR_DATA_SILO_COUNT = gql`\n query TranscendCliListReducedRequestsForDataSiloCount(\n $input: BulkCompletionReducedRequestInput!\n ) {\n listReducedRequestsForDataSilo(input: $input) {\n totalCount\n }\n }\n`;\n","import { RequestAction, RequestDataSiloStatus, RequestStatus } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { REQUEST_DATA_SILOS } from './gqls/requestDataSilo.js';\n\nexport interface RequestDataSilo {\n /** ID of RequestDataSilo */\n id: string;\n /** Request */\n request: {\n /** Type of request */\n type: RequestAction;\n };\n}\n\nexport interface RequestDataSiloFilters {\n /** ID of request to filter on */\n requestId?: string;\n /** Data silo ID */\n dataSiloId?: string;\n /** The statuses to filter on */\n statuses?: RequestDataSiloStatus[];\n /** The request statuses to filter on */\n requestStatuses?: RequestStatus[];\n}\n\n/**\n * Fetch a count of request data silos\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Count of request data silos\n */\nexport async function fetchRequestDataSilosCount(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: RequestDataSiloFilters;\n } = {},\n): Promise<number> {\n const { logger, filterBy: { requestId, dataSiloId, requestStatuses, statuses } = {} } = options;\n const {\n requestDataSilos: { totalCount },\n } = await makeGraphQLRequest<{\n /** Request Data Silos */\n requestDataSilos: {\n /** List */\n nodes: RequestDataSilo[];\n /** Total count */\n totalCount: number;\n };\n }>(client, REQUEST_DATA_SILOS, {\n variables: {\n first: 1,\n offset: 0,\n filterBy: {\n dataSiloId,\n requestId,\n status: statuses,\n requestStatus: requestStatuses,\n },\n },\n logger,\n });\n\n return totalCount;\n}\n\nconst PAGE_SIZE = 100;\n\n/**\n * Fetch all request data silos by some filter criteria\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns List of request data silos\n */\nexport async function fetchRequestDataSilos(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: RequestDataSiloFilters;\n /** Limit on number of requests */\n limit?: number;\n /** Handle progress updates */\n onProgress?: (numUpdated: number) => void;\n } = {},\n): Promise<RequestDataSilo[]> {\n const {\n logger,\n filterBy: { requestId, dataSiloId, requestStatuses, statuses } = {},\n limit,\n onProgress,\n } = options;\n const requestDataSilos: RequestDataSilo[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n requestDataSilos: { nodes },\n } = await makeGraphQLRequest<{\n /** Request Data Silos */\n requestDataSilos: {\n /** List */\n nodes: RequestDataSilo[];\n /** Total count */\n totalCount: number;\n };\n }>(client, REQUEST_DATA_SILOS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n filterBy: {\n dataSiloId,\n requestId,\n status: statuses,\n requestStatus: requestStatuses,\n },\n },\n logger,\n });\n requestDataSilos.push(...nodes);\n\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n onProgress?.(nodes.length);\n } while (shouldContinue && (!limit || offset < limit));\n\n return requestDataSilos;\n}\n\n/**\n * Fetch a single request data silo by request and data silo IDs\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns The matching request data silo\n */\nexport async function fetchRequestDataSilo(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** ID of request to filter on */\n requestId: string;\n /** Data silo ID */\n dataSiloId: string;\n };\n },\n): Promise<RequestDataSilo> {\n const {\n logger,\n filterBy: { requestId, dataSiloId },\n } = options;\n const nodes = await fetchRequestDataSilos(client, {\n logger,\n filterBy: { requestId, dataSiloId },\n });\n if (nodes.length !== 1) {\n throw new Error(\n `Failed to find RequestDataSilo with requestId:${requestId},dataSiloId:${dataSiloId}`,\n );\n }\n\n return nodes[0]!;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const REQUEST_FILES = gql`\n query TranscendCliRequestFiles($first: Int!, $offset: Int!, $filterBy: RequestFileFiltersInput!) {\n requestFiles(\n filterBy: $filterBy\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: id, direction: ASC }]\n ) {\n nodes {\n remoteId\n fileName\n }\n }\n }\n`;\n\nexport const BULK_REQUEST_FILES = gql`\n query TranscendCliBulkRequestFiles(\n $filterBy: BulkRequestFilesFiltersInput!\n $first: Int!\n $after: String\n ) {\n bulkRequestFiles(filterBy: $filterBy, first: $first, after: $after) {\n nodes {\n remoteId\n fileName\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { BULK_REQUEST_FILES } from './gqls/requestFile.js';\n\nexport interface RequestFileCursor {\n /** The ID of the request file */\n id: string;\n /** The created at timestamp */\n createdAt: string;\n}\n\nexport interface RequestFile {\n /** The remote ID */\n remoteId: string;\n /** The file name */\n fileName: string;\n}\n\nexport interface RequestFileResponse {\n /** RequestFiles */\n bulkRequestFiles: {\n /** List */\n nodes: RequestFile[];\n /** The page info */\n pageInfo: {\n /** Whether there is a next page */\n hasNextPage: boolean;\n /** The end cursor */\n endCursor: string;\n };\n };\n}\n\n/**\n * Fetch all RequestFiles for a single request\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All RequestFiles in the organization\n */\nexport async function fetchRequestFilesForRequest(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** How many request files to fetch per API call */\n pageSize?: number;\n /** Filter options */\n filterBy: {\n /** Filter by request IDs */\n requestIds: string[];\n /** Filter by data silo ID */\n dataSiloIds: string[];\n };\n },\n): Promise<RequestFile[]> {\n const { logger, pageSize = 100, filterBy } = options;\n const requestFiles: RequestFile[] = [];\n let cursor: string | null = null;\n\n let shouldContinue = false;\n do {\n const response: RequestFileResponse = await makeGraphQLRequest<RequestFileResponse>(\n client,\n BULK_REQUEST_FILES,\n {\n variables: {\n filterBy: {\n ...filterBy,\n },\n first: pageSize,\n after: cursor ?? undefined,\n },\n logger,\n },\n );\n const {\n bulkRequestFiles: { nodes, pageInfo },\n } = response;\n requestFiles.push(...nodes);\n shouldContinue = pageInfo.hasNextPage;\n cursor = pageInfo.endCursor;\n } while (shouldContinue);\n\n return requestFiles.sort((a, b) => a.remoteId.localeCompare(b.remoteId));\n}\n","export interface DataSiloAttributeValue {\n /** Key associated to value */\n attributeKey: {\n /** Name of key */\n name: string;\n };\n /** Name of value */\n name: string;\n}\n\nexport interface FormattedAttribute {\n /** Attribute key */\n key: string;\n /** Attribute values */\n values: string[];\n}\n\n/**\n * Format attribute value objects to key-pair values\n *\n * @param vals - Attribute values\n * @returns formatted attributes\n */\nexport function formatAttributeValues(vals: DataSiloAttributeValue[]): FormattedAttribute[] {\n const attributes: FormattedAttribute[] = [];\n\n vals.map((val) => {\n let foundKey = attributes.find((att) => att.key === val.attributeKey.name);\n\n if (foundKey === undefined) {\n foundKey = {\n key: val.attributeKey.name,\n values: [val.name],\n };\n attributes.push(foundKey);\n } else {\n foundKey.values.push(val.name);\n }\n return attributes;\n });\n\n return attributes;\n}\n","import { IsoCountryCode, IsoCountrySubdivisionCode } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { difference } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { UPDATE_ACTION } from './gqls/action.js';\n\nconst ALL_COUNTRIES_AND_SUBDIVISIONS = [\n ...Object.values(IsoCountryCode),\n ...Object.values(IsoCountrySubdivisionCode),\n];\n\nexport interface SyncActionInput {\n /** Whether to skip secondary when no files exist */\n skipSecondaryIfNoFiles?: boolean;\n /** Whether to skip downloadable step */\n skipDownloadableStep?: boolean;\n /** Whether the request action requires review */\n requiresReview?: boolean;\n /** The wait period for the action */\n waitingPeriod?: number;\n /** The method in which the data subject's region is detected */\n regionDetectionMethod?: string;\n /** The list of regions to show in the form */\n regionList?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** The list of regions NOT to show in the form */\n regionBlockList?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n}\n\n/**\n * Sync the consent manager\n *\n * @param client - GraphQL client\n * @param actionInput - Action update details\n * @param options - Options\n */\nexport async function syncAction(\n client: GraphQLClient,\n actionInput: {\n /** Action update input */\n action: SyncActionInput;\n /** Existing action Id */\n actionId: string;\n /** When true, skip publishing to privacy center */\n skipPublish?: boolean;\n },\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger } = options;\n const { action, actionId, skipPublish = false } = actionInput;\n await makeGraphQLRequest(client, UPDATE_ACTION, {\n variables: {\n input: {\n id: actionId,\n skipSecondaryIfNoFiles: action.skipSecondaryIfNoFiles,\n skipDownloadableStep: action.skipDownloadableStep,\n requiresReview: action.requiresReview,\n waitingPeriod: action.waitingPeriod,\n skipPublish,\n regionList: action.regionBlockList\n ? difference(ALL_COUNTRIES_AND_SUBDIVISIONS, action.regionBlockList)\n : action.regionList,\n regionDetectionMethod: action.regionDetectionMethod,\n },\n },\n logger,\n });\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - order by createdAt\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const TEMPLATES = gql`\n query TranscendCliTemplates($title: String, $first: Int!, $offset: Int!) {\n templates(filterBy: { text: $title }, first: $first, offset: $offset, useMaster: false) {\n nodes {\n id\n title\n subject {\n defaultMessage\n }\n template {\n defaultMessage\n }\n }\n }\n }\n`;\n\nexport const CREATE_TEMPLATE = gql`\n mutation TranscendCliCreateTemplate($title: String!) {\n createTemplate(input: { title: $title, template: \"\", subject: $title }) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { TEMPLATES, CREATE_TEMPLATE } from './gqls/template.js';\n\nexport interface Template {\n /** ID of Template */\n id: string;\n /** Title of Template */\n title: string;\n /** Template subject (e.g. email subject) */\n subject: {\n /** Default message for template subject */\n defaultMessage: string;\n };\n /** Template body - rich text HTML */\n template: {\n /** Default message for template body */\n defaultMessage: string;\n };\n}\n\nexport interface SyncTemplateInput {\n /** The title of the template */\n title: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all Templates in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All Templates in the organization\n */\nexport async function fetchAllTemplates(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter by title */\n title?: string;\n };\n } = {},\n): Promise<Template[]> {\n const { logger, filterBy: { title } = {} } = options;\n const templates: Template[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n templates: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n templates: {\n /** List of matches */\n nodes: Template[];\n };\n }>(client, TEMPLATES, {\n variables: { first: PAGE_SIZE, offset, title },\n logger,\n });\n templates.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return templates.sort((a, b) => a.title.localeCompare(b.title));\n}\n\n/**\n * Sync an email template configuration\n *\n * @param client - GraphQL client\n * @param template - The email template input\n * @param options - Options\n */\nexport async function syncTemplate(\n client: GraphQLClient,\n template: SyncTemplateInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger } = options;\n const matches = await fetchAllTemplates(client, {\n logger,\n filterBy: { title: template.title },\n });\n const existingTemplate = matches.find(({ title }) => title === template.title);\n\n if (!existingTemplate) {\n await makeGraphQLRequest(client, CREATE_TEMPLATE, {\n variables: { title: template.title },\n logger,\n });\n }\n}\n","import type { WorkflowConfigNode } from './fetchAllWorkflowConfigs.js';\nimport type { WorkflowConfigSyncInput } from './syncWorkflowConfigs.js';\n\n/** Fields used to resolve a workflow config match */\nexport type WorkflowConfigMatchInput = Pick<\n WorkflowConfigSyncInput,\n 'internal-name' | 'title' | 'action-type' | 'data-subject-type' | 'region-list'\n>;\n\n/** Result of resolving a workflow config match */\nexport type WorkflowConfigMatchResolution =\n | {\n /** Matched an existing workflow config */\n kind: 'match';\n /** Matched workflow config */\n config: WorkflowConfigNode;\n }\n | {\n /** No existing workflow config matched; create a new one */\n kind: 'create';\n }\n | {\n /** Multiple workflow configs matched after the full cascade */\n kind: 'ambiguous';\n /** Ambiguous workflow config candidates */\n candidates: WorkflowConfigNode[];\n };\n\n/**\n * Stable key for title + action + data subject + region-list identity.\n *\n * @param config - Workflow config node\n * @returns Match key string\n */\nexport function workflowConfigMatchKey(config: WorkflowConfigNode): string {\n return [\n config.title.defaultMessage,\n config.action.type,\n config.subject?.type ?? '',\n workflowRegionListKey(config.regionList),\n ].join('\\0');\n}\n\n/**\n * Order-independent region list key for workflow matching.\n *\n * @param regions - Region list\n * @returns Sorted comma-separated region codes\n */\nexport function workflowRegionListKey(regions: readonly string[] | undefined | null): string {\n return [...(regions ?? [])].sort().join(',');\n}\n\n/**\n * Human-readable label for logging and errors.\n *\n * @param input - Workflow config sync input\n * @returns Label string\n */\nexport function workflowConfigInputLabel(input: WorkflowConfigSyncInput): string {\n return input['internal-name'] ?? input.title;\n}\n\nfunction sameRegionList(\n inputRegions: WorkflowConfigMatchInput['region-list'],\n configRegions: WorkflowConfigNode['regionList'],\n): boolean {\n return workflowRegionListKey(inputRegions) === workflowRegionListKey(configRegions);\n}\n\nfunction narrowToOneOrContinue(\n candidates: WorkflowConfigNode[],\n next: (remaining: WorkflowConfigNode[]) => WorkflowConfigMatchResolution,\n): WorkflowConfigMatchResolution {\n const [only] = candidates;\n if (candidates.length === 1 && only !== undefined) {\n return { kind: 'match', config: only };\n }\n if (candidates.length === 0) {\n return { kind: 'create' };\n }\n return next(candidates);\n}\n\nfunction continueCascade(\n candidates: WorkflowConfigNode[],\n input: WorkflowConfigMatchInput,\n): WorkflowConfigMatchResolution {\n return narrowToOneOrContinue(\n candidates.filter((config) => config.title.defaultMessage === input.title),\n (byTitle) =>\n narrowToOneOrContinue(\n byTitle.filter((config) => config.action.type === input['action-type']),\n (byAction) => {\n const dataSubjectType = input['data-subject-type'];\n if (dataSubjectType) {\n return narrowToOneOrContinue(\n byAction.filter((config) => config.subject?.type === dataSubjectType),\n (bySubject) => filterByRegions(bySubject, input),\n );\n }\n return filterByRegions(byAction, input);\n },\n ),\n );\n}\n\nfunction filterByRegions(\n candidates: WorkflowConfigNode[],\n input: WorkflowConfigMatchInput,\n): WorkflowConfigMatchResolution {\n const matches = candidates.filter((config) =>\n sameRegionList(input['region-list'], config.regionList),\n );\n const [only] = matches;\n if (matches.length === 1 && only !== undefined) {\n return { kind: 'match', config: only };\n }\n if (matches.length === 0) {\n return { kind: 'create' };\n }\n\n // When YAML omits internal-name, prefer unnamed candidates so a named\n // sibling with the same title/action/subject/regions is not treated as\n // ambiguous (common after pull when only some workflows have internal names).\n // If multiple unnamed remain, pick the first in stable fetch order — sync\n // claims matched ids so later identical YAML rows map to the rest.\n // TODO: https://linear.app/transcend/issue/WAL-10312 - remove once internalName is unique in DB\n if (!input['internal-name']) {\n const unnamed = matches.filter((config) => config.internalName == null);\n const [firstUnnamed] = unnamed;\n if (firstUnnamed !== undefined) {\n return { kind: 'match', config: firstUnnamed };\n }\n }\n\n return { kind: 'ambiguous', candidates: matches };\n}\n\n/**\n * Resolve which existing workflow config a YAML entry should update, if any.\n *\n * Cascade order: internal-name → title → action-type → data-subject-type (when\n * provided) → region-list. A provided internal-name with zero matches creates a\n * new workflow instead of falling through to title. When internal-name is\n * omitted and the cascade still matches multiple configs, prefer the candidate\n * with a null internal name.\n *\n * @param input - Workflow config sync input\n * @param existingConfigs - Existing workflow configs in the org\n * @returns Match resolution\n */\nexport function resolveWorkflowConfigMatch(\n input: WorkflowConfigMatchInput,\n existingConfigs: WorkflowConfigNode[],\n): WorkflowConfigMatchResolution {\n const internalName = input['internal-name'];\n\n if (internalName) {\n const byName = existingConfigs.filter((config) => config.internalName === internalName);\n const [onlyByName] = byName;\n if (byName.length === 1 && onlyByName !== undefined) {\n return { kind: 'match', config: onlyByName };\n }\n if (byName.length === 0) {\n return { kind: 'create' };\n }\n return continueCascade(byName, input);\n }\n\n return continueCascade(existingConfigs, input);\n}\n","import {\n CollectDataSubjectRegions,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n RequestAction,\n WorkflowConfigType,\n WorkflowConfigVisibility,\n} from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { fetchAllActions } from './fetchAllActions.js';\nimport { fetchAllRequestAttributeKeys, type AttributeKey } from './fetchAllAttributeKeys.js';\nimport { fetchAllWorkflowConfigs, type WorkflowConfigNode } from './fetchAllWorkflowConfigs.js';\nimport { fetchAllDataSubjects, type DataSubject } from './fetchDataSubjects.js';\nimport { CREATE_WORKFLOW, UPDATE_WORKFLOW_CONFIG } from './gqls/workflowConfig.js';\nimport {\n resolveWorkflowConfigMatch,\n workflowConfigInputLabel,\n} from './resolveWorkflowConfigMatch.js';\n\nexport interface WorkflowConfigSyncInput {\n /** User-facing title */\n title: string;\n /** Request action type */\n 'action-type': RequestAction;\n /**\n * Internal name of the workflow config. When provided, used as the first match\n * key; when omitted, matching falls back through title, action, subject, and\n * regions.\n */\n 'internal-name'?: string;\n /** Subtitle */\n subtitle?: string;\n /** Description */\n description?: string;\n /** Data subject type */\n 'data-subject-type'?: string;\n /** Visibility tier (DRAFT, INTERNAL, PUBLISHED) */\n visibility?: WorkflowConfigVisibility;\n /**\n * Workflow config type. Only DSR is supported for sync; preference-management\n * workflows must be managed in the Admin Dashboard.\n */\n type?: WorkflowConfigType;\n /** Whether to collect the data subject's region (COLLECT, DO_NOT_COLLECT) */\n 'collect-data-subject-regions'?: CollectDataSubjectRegions;\n /** Region allow list */\n 'region-list'?: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n /** Per-region request expiry times */\n 'expiry-time'?: {\n /** Region code (or 'default') */\n region: 'default' | IsoCountryCode | IsoCountrySubdivisionCode;\n /** Expiry time in days */\n value: number;\n }[];\n /** Attribute key (custom field) names to associate with the workflow */\n 'attribute-keys'?: string[];\n}\n\n/**\n * Sync workflow configs to Transcend.\n *\n * Matches each input using a cascading key: internal-name → title → action-type\n * → data-subject-type (when provided) → region-list. Missing configs are\n * created via `createWorkflow` (DSR only; starts as Draft with default\n * associations), then updated with the remaining YAML fields. Successfully\n * matched configs are claimed (removed from the match pool) so later YAML\n * entries can resolve remaining duplicates.\n *\n * @param client - GraphQL client\n * @param inputs - Workflow config inputs from YAML\n * @param options - Options\n * @returns True if run without error\n */\nexport async function syncWorkflowConfigs(\n client: GraphQLClient,\n inputs: WorkflowConfigSyncInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger } = options;\n logger?.info(`Syncing \"${inputs.length}\" workflow configs...`);\n\n let encounteredError = false;\n\n const needsSubjects = inputs.some((config) => config['data-subject-type']);\n const needsAttributeKeys = inputs.some((config) => config['attribute-keys']?.length);\n\n const [fetchedConfigs, actions] = await Promise.all([\n fetchAllWorkflowConfigs(client, {\n logger,\n workflowConfigType: WorkflowConfigType.DSR,\n }),\n fetchAllActions(client, { logger }),\n ]);\n\n const existingConfigs: WorkflowConfigNode[] = [...fetchedConfigs];\n\n let dataSubjects: DataSubject[] = [];\n if (needsSubjects) {\n dataSubjects = await fetchAllDataSubjects(client, { logger });\n }\n\n let attributeKeys: AttributeKey[] = [];\n if (needsAttributeKeys) {\n attributeKeys = await fetchAllRequestAttributeKeys(client, { logger });\n }\n\n const actionByType = keyBy(actions, 'type');\n const dataSubjectByType = keyBy(dataSubjects, 'type');\n const attributeKeyByName = keyBy(attributeKeys, 'name');\n\n await mapSeries(inputs, async (config) => {\n const label = workflowConfigInputLabel(config);\n try {\n // TODO: https://linear.app/transcend/issue/WAL-10313 - support preference-management workflow sync\n if (config.type && config.type !== WorkflowConfigType.DSR) {\n throw new Error(\n `Workflow config \"${label}\" has type ` +\n `\"${config.type}\". Only DSR workflow configs can be synced — ` +\n 'preference management workflows have purpose-based semantics and must be managed in the Admin Dashboard.',\n );\n }\n\n const resolution = resolveWorkflowConfigMatch(config, existingConfigs);\n if (resolution.kind === 'ambiguous') {\n throw new Error(\n `Found \"${resolution.candidates.length}\" workflow configs matching ` +\n `\"${label}\" after applying title, action-type, data-subject-type, and region-list. ` +\n 'Add a unique internal-name or disambiguating region-list to sync this workflow.',\n );\n }\n\n const existingConfig = resolution.kind === 'match' ? resolution.config : undefined;\n\n if (existingConfig && existingConfig.workflowConfigType !== WorkflowConfigType.DSR) {\n throw new Error(\n `Workflow config \"${label}\" has type ` +\n `\"${existingConfig.workflowConfigType}\". Only DSR workflow configs can be synced — ` +\n 'preference management workflows have purpose-based semantics and must be managed in the Admin Dashboard.',\n );\n }\n\n const expiryTime = config['expiry-time'];\n if (expiryTime && expiryTime.length > 0) {\n // The backend validates expiryTime against RegionExpiryMap, which requires\n // a `default` entry and all values > 0 — fail fast with a clearer error\n if (!expiryTime.some(({ region }) => region === 'default')) {\n throw new Error(\n `Workflow config \"${label}\" has an expiry-time list ` +\n 'without a \"default\" region entry. A `region: default` entry is required.',\n );\n }\n const invalidEntries = expiryTime.filter(({ value }) => value <= 0);\n if (invalidEntries.length > 0) {\n throw new Error(\n `Workflow config \"${label}\" has expiry-time values that are ` +\n `not positive: ${invalidEntries\n .map(({ region, value }) => `${region}=${value}`)\n .join(', ')}. All expiry times must be greater than 0 days.`,\n );\n }\n }\n\n let subjectId: string | undefined;\n if (config['data-subject-type']) {\n const subject = dataSubjectByType[config['data-subject-type']];\n if (!subject) {\n throw new Error(`Failed to find data subject with type: ${config['data-subject-type']}`);\n }\n subjectId = subject.id;\n }\n\n if (!actionByType[config['action-type']]) {\n throw new Error(`Failed to find action with type: ${config['action-type']}`);\n }\n\n let attributeKeyIds: string[] | undefined;\n if (config['attribute-keys'] !== undefined) {\n attributeKeyIds = config['attribute-keys'].map((name) => {\n const attributeKey = attributeKeyByName[name];\n if (!attributeKey) {\n throw new Error(`Failed to find attribute key with name: ${name}`);\n }\n return attributeKey.id;\n });\n }\n\n let workflowConfigId: string;\n if (!existingConfig) {\n // createWorkflow only accepts title/actionType/internalName (+ type).\n // Remaining fields are applied via updateWorkflowConfig below.\n // New workflows start as Draft with default associations (data silos, receipt email).\n const internalName = config['internal-name'];\n const {\n createWorkflow: { workflowConfig: created },\n } = await makeGraphQLRequest<{\n /** createWorkflow mutation */\n createWorkflow: {\n /** Created workflow config */\n workflowConfig: {\n /** ID */\n id: string;\n /** Internal name */\n internalName: string | null;\n };\n };\n }>(client, CREATE_WORKFLOW, {\n variables: {\n input: {\n title: config.title,\n actionType: config['action-type'],\n ...(internalName ? { internalName } : {}),\n workflowConfigType: WorkflowConfigType.DSR,\n },\n },\n logger,\n });\n workflowConfigId = created.id;\n // Track the new config so a later YAML entry with the same match key\n // updates instead of attempting a second create in this sync run.\n existingConfigs.push({\n id: created.id,\n title: { defaultMessage: config.title },\n subtitle: null,\n description: null,\n internalName: created.internalName ?? internalName ?? null,\n workflowConfigVisibility: WorkflowConfigVisibility.Draft,\n workflowConfigType: WorkflowConfigType.DSR,\n collectDataSubjectRegions: null,\n regionList: config['region-list'] ?? [],\n expiryTime: null,\n // action.id is unused for match-key resolution within this sync run\n action: { id: '', type: config['action-type'] },\n subject: subjectId\n ? { id: subjectId, type: config['data-subject-type'] as string }\n : null,\n WorkflowConfigAttributeKeys: null,\n });\n logger?.info(`Successfully created workflow config \"${label}\" (${created.id})!`);\n } else {\n workflowConfigId = existingConfig.id;\n }\n\n const mutationInput: Record<string, unknown> = {\n workflowConfigId,\n ...(config.title !== undefined ? { title: config.title } : {}),\n ...(config.subtitle !== undefined ? { subtitle: config.subtitle } : {}),\n ...(config.description !== undefined ? { description: config.description } : {}),\n ...(config.visibility !== undefined ? { workflowConfigVisibility: config.visibility } : {}),\n ...(config['internal-name'] !== undefined ? { internalName: config['internal-name'] } : {}),\n actionType: config['action-type'],\n ...(config['collect-data-subject-regions'] !== undefined\n ? { collectDataSubjectRegions: config['collect-data-subject-regions'] }\n : {}),\n ...(subjectId ? { subjectId } : {}),\n ...(config['region-list'] !== undefined ? { regionList: config['region-list'] } : {}),\n ...(config['expiry-time'] !== undefined ? { expiryTime: config['expiry-time'] } : {}),\n ...(attributeKeyIds !== undefined ? { attributeKeyIds } : {}),\n };\n\n await makeGraphQLRequest(client, UPDATE_WORKFLOW_CONFIG, {\n variables: { input: mutationInput },\n logger,\n });\n\n // Claim the matched config so a later YAML entry can resolve remaining\n // duplicates (e.g. named sibling synced first, then unnamed pull row).\n // TODO: https://linear.app/transcend/issue/WAL-10312 - remove once internalName is unique in DB\n if (existingConfig) {\n const claimedIndex = existingConfigs.findIndex((config) => config.id === existingConfig.id);\n if (claimedIndex >= 0) {\n existingConfigs.splice(claimedIndex, 1);\n }\n }\n\n logger?.info(`Successfully synced workflow config \"${label}\" (${workflowConfigId})!`);\n } catch (err) {\n encounteredError = true;\n logger?.error(`Failed to sync workflow config \"${label}\"! - ${(err as Error).message}`);\n }\n });\n\n logger?.info(`Synced \"${inputs.length}\" workflow configs!`);\n return !encounteredError;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { ADD_SILO_DISCOVERY_RESULTS } from './gqls/siloDiscovery.js';\n\nexport interface SiloDiscoveryRawResult {\n /** The name of the potential data silo entry */\n name: string;\n /** A unique UUID (represents the same resource across different silo discovery runs) */\n resourceId: string;\n /** Any hosts associated with the entry */\n host?: string;\n /** Type of data silo */\n type?: string | undefined;\n}\n\nconst CHUNK_SIZE = 1000;\n\n/**\n * Uploads silo discovery results for Transcend to classify\n *\n * @param client - GraphQL Client\n * @param options - Options\n */\nexport async function uploadSiloDiscoveryResults(\n client: GraphQLClient,\n options: {\n /** Plugin ID to associate with the results */\n pluginId: string;\n /** The discovery results to upload */\n results: SiloDiscoveryRawResult[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { pluginId, results, logger = NOOP_LOGGER } = options;\n const chunks = chunk(results, CHUNK_SIZE);\n\n await mapSeries(chunks, async (rawResults) => {\n await makeGraphQLRequest<{\n /** Whether we successfully uploaded the results */\n success: boolean;\n }>(client, ADD_SILO_DISCOVERY_RESULTS, {\n variables: { pluginId, rawResults },\n logger,\n });\n });\n}\n","import { gql } from 'graphql-request';\n\nexport const ADD_MESSAGES_TO_PROMPT_RUN = gql`\n mutation TranscendCliAddMessagesToPromptRun($input: AddMessagesToPromptRunInput!) {\n addMessagesToPromptRun(input: $input) {\n clientMutationId\n promptRun {\n id\n }\n }\n }\n`;\n","import { QueueStatus, ChatCompletionRole } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ADD_MESSAGES_TO_PROMPT_RUN } from './gqls/promptRun.js';\n\nexport interface AddMessagesToPromptRunInput {\n /** ID of run */\n promptRunId:\n | {\n /** Report by prompt run name */\n name: string;\n /** Don't report by ID */\n id?: undefined;\n }\n | {\n /** Don't report by name */\n name?: undefined;\n /** Report by prompt run ID */\n id: string;\n };\n /** Messages to report on */\n promptRunMessages?: {\n /** Message reported */\n content: string;\n /** Role of message */\n role: ChatCompletionRole;\n /** Template used if created from prompt */\n template?: string;\n }[];\n /** Error message (if one exists) */\n error?: string;\n /** The status of the run */\n status?: QueueStatus;\n /** Duration of time that it took to execute the prompt */\n duration?: number;\n}\n\n/**\n * Record a new prompt run\n *\n * @param client - GraphQL client\n * @param input - Prompt input\n * @param options - Options\n * @returns Prompt ID\n */\nexport async function addMessagesToPromptRun(\n client: GraphQLClient,\n { promptRunId, promptRunMessages = [], ...rest }: AddMessagesToPromptRunInput,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<string> {\n const { logger } = options;\n const {\n addMessagesToPromptRun: { promptRun },\n } = await makeGraphQLRequest<{\n /** addMessagesToPromptRun mutation */\n addMessagesToPromptRun: {\n /** Prompt */\n promptRun: {\n /** ID */\n id: string;\n };\n };\n }>(client, ADD_MESSAGES_TO_PROMPT_RUN, {\n variables: {\n input: {\n ...rest,\n ...promptRunId,\n promptRunMessages: promptRunMessages.map(({ content, ...rest }) => ({\n ...rest,\n message: content,\n })),\n },\n },\n logger,\n });\n return promptRun.id;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const AGENT_FILES = gql`\n query TranscendCliAgentFiles($first: Int!, $offset: Int!, $filterBy: AgentFileFiltersInput) {\n agentFiles(\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n fileId\n size\n purpose\n initialFileName\n }\n }\n }\n`;\n\nexport const CREATE_AGENT_FILE = gql`\n mutation TranscendCliCreateAgentFile($input: CreateAgentFileInput!) {\n createAgentFile(input: $input) {\n agentFile {\n id\n name\n fileId\n initialFileName\n }\n }\n }\n`;\n\nexport const UPDATE_AGENT_FILES = gql`\n mutation TranscendCliUpdateAgentFiles($input: UpdateAgentFilesInput!) {\n updateAgentFiles(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { PromptFilePurpose } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { AGENT_FILES } from './gqls/agentFile.js';\n\nexport interface AgentFile {\n /** ID of agentFile */\n id: string;\n /** Name of agentFile */\n name: string;\n /** Description of the agentFile */\n description: string;\n /** Initial file name, useful to track if a file was split into multiple chunks */\n initialFileName?: string;\n /** File ID */\n fileId: string;\n /** File size */\n size: number;\n /** File purpose */\n purpose: PromptFilePurpose;\n}\n\nexport interface AgentFileFilterBy {\n /** Filter by remote file IDs */\n fileIds?: string[];\n /** Filter by file names */\n names?: string[];\n /** Filter by initial file names (when split into chunks) */\n initialFileNames?: string[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all agent files in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All agent files in the organization\n */\nexport async function fetchAllAgentFiles(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: AgentFileFilterBy;\n } = {},\n): Promise<AgentFile[]> {\n const { logger, filterBy = {} } = options;\n const agentFiles: AgentFile[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n agentFiles: { nodes },\n } = await makeGraphQLRequest<{\n /** AgentFiles */\n agentFiles: {\n /** List */\n nodes: AgentFile[];\n };\n }>(client, AGENT_FILES, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n agentFiles.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return agentFiles.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const AGENT_FUNCTIONS = gql`\n query TranscendCliAgentFunctions($first: Int!, $offset: Int!) {\n agentFunctions(\n first: $first\n offset: $offset\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n parameters\n }\n }\n }\n`;\n\nexport const CREATE_AGENT_FUNCTION = gql`\n mutation TranscendCliCreateAgentFunction($input: CreateAgentFunctionInput!) {\n createAgentFunction(input: $input) {\n agentFunction {\n id\n name\n }\n }\n }\n`;\n\nexport const UPDATE_AGENT_FUNCTIONS = gql`\n mutation TranscendCliUpdateAgentFunctions($input: UpdateAgentFunctionsInput!) {\n updateAgentFunctions(input: $input) {\n clientMutationId\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport type { JSONSchema7 } from 'json-schema';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { AGENT_FUNCTIONS } from './gqls/agentFunction.js';\n\nexport interface AgentFunction {\n /** ID of agentFunction */\n id: string;\n /** Name of agentFunction */\n name: string;\n /** Description of the agentFunction */\n description: string;\n /** The JSON schema */\n parameters: JSONSchema7;\n}\n\ninterface AgentFunctionRaw extends Omit<AgentFunction, 'parameters'> {\n /** Stringified parameters */\n parameters: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all agent functions in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All agent functions in the organization\n */\nexport async function fetchAllAgentFunctions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<AgentFunction[]> {\n const { logger } = options;\n const agentFunctions: AgentFunction[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n agentFunctions: { nodes },\n } = await makeGraphQLRequest<{\n /** AgentFunctions */\n agentFunctions: {\n /** List */\n nodes: AgentFunctionRaw[];\n };\n }>(client, AGENT_FUNCTIONS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n agentFunctions.push(\n ...nodes.map((node) => ({\n ...node,\n parameters: JSON.parse(node.parameters),\n })),\n );\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return agentFunctions.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const AGENTS = gql`\n query TranscendCliAgents($first: Int!, $offset: Int!, $filterBy: AgentFiltersInput) {\n agents(\n first: $first\n offset: $offset\n filterBy: $filterBy\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n agentId\n instructions\n description\n codeInterpreterEnabled\n retrievalEnabled\n prompt {\n title\n }\n largeLanguageModel {\n name\n client\n }\n teams {\n name\n }\n owners {\n email\n }\n agentFunctions {\n name\n }\n agentFiles {\n name\n }\n }\n }\n }\n`;\n\nexport const CREATE_AGENT = gql`\n mutation TranscendCliCreateAgent($input: CreateAgentInput!) {\n createAgent(input: $input) {\n agent {\n id\n name\n agentId\n }\n }\n }\n`;\n\nexport const UPDATE_AGENTS = gql`\n mutation TranscendCliUpdateAgents($input: UpdateAgentsInput!) {\n updateAgents(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { LargeLanguageModelClient } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { AGENTS } from './gqls/agent.js';\n\nexport interface Agent {\n /** ID of agent */\n id: string;\n /** Name of agent */\n name: string;\n /** Agent instructions */\n instructions: string;\n /** The ID of the agent */\n agentId: string;\n /** Description of the agent */\n description: string;\n /** Whether the agent has code interpreter enabled */\n codeInterpreterEnabled: boolean;\n /** Whether the agent has retrieval enabled */\n retrievalEnabled: boolean;\n /** The prompt that the agent is based on */\n prompt?: {\n /** Title of the prompt */\n title: string;\n };\n /** Large language model that the agent is based on */\n largeLanguageModel: {\n /** Name of model */\n name: string;\n /** Client */\n client: LargeLanguageModelClient;\n };\n /** Teams assigned to the agent */\n teams: {\n /** Team name */\n name: string;\n }[];\n /** Users assigned to the agent */\n owners: {\n /** User email */\n email: string;\n }[];\n /** Functions that the agent has access to */\n agentFunctions: {\n /** Function name */\n name: string;\n }[];\n /** Files that the agent has access to */\n agentFiles: {\n /** File name */\n name: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all agents in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All agents in the organization\n */\nexport async function fetchAllAgents(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: {\n /** Names of the agents to filter for */\n names?: string[];\n /** IDs of agents */\n agentIds?: string[];\n };\n } = {},\n): Promise<Agent[]> {\n const { logger, filterBy = {} } = options;\n const agents: Agent[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n agents: { nodes },\n } = await makeGraphQLRequest<{\n /** Agents */\n agents: {\n /** List */\n nodes: Agent[];\n };\n }>(client, AGENTS, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n agents.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return agents.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const LARGE_LANGUAGE_MODELS = gql`\n query TranscendCliLargeLanguageModels(\n $first: Int!\n $offset: Int!\n $filterBy: LargeLanguageModelFiltersInput\n ) {\n largeLanguageModels(\n first: $first\n orderBy: [\n { field: name, direction: ASC }\n { field: client, direction: ASC }\n { field: isTranscendHosted, direction: ASC }\n ]\n offset: $offset\n filterBy: $filterBy\n ) {\n nodes {\n id\n name\n client\n isTranscendHosted\n }\n }\n }\n`;\n","import { LargeLanguageModelClient } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { LARGE_LANGUAGE_MODELS } from './gqls/largeLanguageModel.js';\n\nexport interface LargeLanguageModel {\n /** ID of prompts */\n id: string;\n /** The name of the large language model. */\n name: string;\n /** The content of the prompt template. */\n client: LargeLanguageModelClient;\n /** Whether hosted by Transcend or not */\n isTranscendHosted: boolean;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all LargeLanguageModels in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All LargeLanguageModels in the organization\n */\nexport async function fetchAllLargeLanguageModels(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<LargeLanguageModel[]> {\n const { logger } = options;\n const largeLanguageModels: LargeLanguageModel[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n largeLanguageModels: { nodes },\n } = await makeGraphQLRequest<{\n /** LargeLanguageModels */\n largeLanguageModels: {\n /** List */\n nodes: LargeLanguageModel[];\n };\n }>(client, LARGE_LANGUAGE_MODELS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n largeLanguageModels.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return largeLanguageModels.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\nexport const PROMPT_THREADS = gql`\n query TranscendCliPromptThreads(\n $first: Int!\n $offset: Int!\n $filterBy: PromptThreadFiltersInput!\n ) {\n promptThreads(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n threadId\n slackMessageTs\n slackTeamId\n slackChannelId\n slackChannelName\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { PROMPT_THREADS } from './gqls/promptThread.js';\n\nexport interface PromptThread {\n /** ID of prompts */\n id: string;\n /** Thread ID from API */\n threadId: string;\n /** Related slack message TS */\n slackMessageTs?: string;\n /** Related slack message team ID */\n slackTeamId?: string;\n /** Related slack channel ID */\n slackChannelId?: string;\n /** Related slack channel name */\n slackChannelName?: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all PromptThreads in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All PromptThreads in the organization\n */\nexport async function fetchAllPromptThreads(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy: {\n /** Thread IDs to filter on */\n threadIds?: string[];\n /** Slack message timestamps to filter on */\n slackMessageTs?: string[];\n };\n },\n): Promise<PromptThread[]> {\n const { logger, filterBy } = options;\n const promptThreads: PromptThread[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n promptThreads: { nodes },\n } = await makeGraphQLRequest<{\n /** PromptThreads */\n promptThreads: {\n /** List */\n nodes: PromptThread[];\n };\n }>(client, PROMPT_THREADS, {\n variables: { first: PAGE_SIZE, offset, filterBy },\n logger,\n });\n promptThreads.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return promptThreads.sort((a, b) => a.threadId.localeCompare(b.threadId));\n}\n","import { PromptFilePurpose } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllAgentFiles, type AgentFile } from './fetchAllAgentFiles.js';\nimport { UPDATE_AGENT_FILES, CREATE_AGENT_FILE } from './gqls/agentFile.js';\n\nexport interface AgentFileInput {\n /** Name of the agent file */\n name: string;\n /** Description of the agent file */\n description?: string;\n /** File ID */\n fileId: string;\n /** File size */\n size: number;\n /** File purpose */\n purpose: PromptFilePurpose;\n}\n\n/**\n * Create a new agent file\n *\n * @param client - GraphQL client\n * @param agentFile - Input\n * @param options - Options\n * @returns Created agent file\n */\nexport async function createAgentFile(\n client: GraphQLClient,\n options: {\n /** Agent file to create */\n input: AgentFileInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<AgentFile, 'id' | 'name' | 'fileId'>> {\n const { input: agentFile, logger = NOOP_LOGGER } = options;\n const input = {\n name: agentFile.name,\n description: agentFile.description,\n fileId: agentFile.fileId,\n size: agentFile.size,\n purpose: agentFile.purpose,\n fileUploadedAt: new Date(),\n agentIds: [],\n // TODO: https://transcend.height.app/T-31994 - sync agents\n };\n\n const { createAgentFile } = await makeGraphQLRequest<{\n /** Create agent file mutation */\n createAgentFile: {\n /** Created agent file */\n agentFile: AgentFile;\n };\n }>(client, CREATE_AGENT_FILE, {\n variables: { input },\n logger,\n });\n return createAgentFile.agentFile;\n}\n\n/**\n * Update agent files\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateAgentFiles(\n client: GraphQLClient,\n options: {\n /** [AgentFileInput, agentFileId] list */\n input: [AgentFileInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: agentFiles, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_AGENT_FILES, {\n variables: {\n input: {\n agentFiles: agentFiles.map(([agentFile, id]) => ({\n id,\n name: agentFile.name,\n description: agentFile.description,\n fileId: agentFile.fileId,\n size: agentFile.size,\n purpose: agentFile.purpose,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the agent files\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncAgentFiles(\n client: GraphQLClient,\n inputs: AgentFileInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" agent files...`);\n\n let encounteredError = false;\n\n const existingAgentFiles = await fetchAllAgentFiles(client, { logger });\n\n const agentFileByName = keyBy(existingAgentFiles, 'name') as {\n [k in string]: Pick<AgentFile, 'id' | 'name' | 'fileId'>;\n };\n\n const newAgentFiles = inputs.filter((input) => !agentFileByName[input.name]);\n\n await mapSeries(newAgentFiles, async (agentFile) => {\n try {\n const newAgentFile = await createAgentFile(client, { input: agentFile, logger });\n agentFileByName[newAgentFile.name] = newAgentFile;\n logger.info(`Successfully synced agent file \"${agentFile.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync agent file \"${agentFile.name}\"! - ${(err as Error).message}`);\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" agent files!`);\n await updateAgentFiles(client, {\n input: inputs.map((input) => [input, agentFileByName[input.name]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" agent files!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" agent files! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport type { JSONSchema7 } from 'json-schema';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllAgentFunctions, type AgentFunction } from './fetchAllAgentFunctions.js';\nimport { UPDATE_AGENT_FUNCTIONS, CREATE_AGENT_FUNCTION } from './gqls/agentFunction.js';\n\nexport interface AgentFunctionInput {\n /** Name of the agent function */\n name: string;\n /** Description of the agent function */\n description: string;\n /** The JSON schema parameters (string or parsed) */\n parameters: JSONSchema7 | string;\n}\n\n/**\n * Create a new agent function\n *\n * @param client - GraphQL client\n * @param agentFunction - Input\n * @param options - Options\n * @returns Created agent function\n */\nexport async function createAgentFunction(\n client: GraphQLClient,\n options: {\n /** Agent function to create */\n input: AgentFunctionInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<AgentFunction, 'id' | 'name'>> {\n const { input: agentFunction, logger = NOOP_LOGGER } = options;\n const input = {\n name: agentFunction.name,\n description: agentFunction.description,\n parameters: agentFunction.parameters,\n agentIds: [],\n // TODO: https://transcend.height.app/T-31994 - sync agents\n };\n\n const { createAgentFunction } = await makeGraphQLRequest<{\n /** Create agent function mutation */\n createAgentFunction: {\n /** Created agent function */\n agentFunction: AgentFunction;\n };\n }>(client, CREATE_AGENT_FUNCTION, {\n variables: { input },\n logger,\n });\n return createAgentFunction.agentFunction;\n}\n\n/**\n * Update agent functions\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateAgentFunctions(\n client: GraphQLClient,\n options: {\n /** [AgentFunctionInput, agentFunctionId] list */\n input: [AgentFunctionInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: agentFunctions, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_AGENT_FUNCTIONS, {\n variables: {\n input: {\n agentFunctions: agentFunctions.map(([agentFunction, id]) => ({\n id,\n name: agentFunction.name,\n description: agentFunction.description,\n parameters: agentFunction.parameters,\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the agent functions\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncAgentFunctions(\n client: GraphQLClient,\n inputs: AgentFunctionInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" agent functions...`);\n\n let encounteredError = false;\n\n const existingAgentFunctions = await fetchAllAgentFunctions(client, { logger });\n\n const agentFunctionByName = keyBy(existingAgentFunctions, 'name') as {\n [k in string]: Pick<AgentFunction, 'id' | 'name'>;\n };\n\n const newAgentFunctions = inputs.filter((input) => !agentFunctionByName[input.name]);\n\n await mapSeries(newAgentFunctions, async (agentFunction) => {\n try {\n const newAgentFunction = await createAgentFunction(client, { input: agentFunction, logger });\n agentFunctionByName[newAgentFunction.name] = newAgentFunction;\n logger.info(`Successfully synced agent function \"${agentFunction.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync agent function \"${agentFunction.name}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" agent functions!`);\n await updateAgentFunctions(client, {\n input: inputs.map((input) => [input, agentFunctionByName[input.name]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" agent functions!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" agent functions! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { LargeLanguageModelClient } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllAgents, type Agent } from './fetchAllAgents.js';\nimport { UPDATE_AGENTS, CREATE_AGENT } from './gqls/agent.js';\n\nexport interface AgentInput {\n /** Name of the agent */\n name: string;\n /** Description of the agent */\n description?: string;\n /** Whether the agent has code interpreter enabled */\n codeInterpreterEnabled?: boolean;\n /** Whether the agent has retrieval enabled */\n retrievalEnabled?: boolean;\n /** The prompt title */\n prompt?: string;\n /** Large language model config */\n 'large-language-model': {\n /** Name of model */\n name: string;\n /** Client */\n client: LargeLanguageModelClient;\n };\n}\n\n/**\n * Create a new agent\n *\n * @param client - GraphQL client\n * @param agent - Input\n * @param options - Options\n * @returns Created agent\n */\nexport async function createAgent(\n client: GraphQLClient,\n options: {\n /** Agent to create */\n input: AgentInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<Agent, 'id' | 'name' | 'agentId'>> {\n const { input: agent, logger = NOOP_LOGGER } = options;\n const input = {\n name: agent.name,\n description: agent.description,\n codeInterpreterEnabled: agent.codeInterpreterEnabled,\n retrievalEnabled: agent.retrievalEnabled,\n promptTitle: agent.prompt,\n largeLanguageModelName: agent['large-language-model'].name,\n largeLanguageModelClient: agent['large-language-model'].client,\n // TODO: https://transcend.height.app/T-32760 - agentFunction, agentFile\n // TODO: https://transcend.height.app/T-31994 - owners and teams\n };\n\n const { createAgent } = await makeGraphQLRequest<{\n /** Create agent mutation */\n createAgent: {\n /** Created agent */\n agent: Agent;\n };\n }>(client, CREATE_AGENT, {\n variables: { input },\n logger,\n });\n return createAgent.agent;\n}\n\n/**\n * Update agents\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateAgents(\n client: GraphQLClient,\n options: {\n /** [AgentInput, agentId] list */\n input: [AgentInput, string][];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: agents, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_AGENTS, {\n variables: {\n input: {\n agents: agents.map(([agent, id]) => ({\n id,\n name: agent.name,\n description: agent.description,\n codeInterpreterEnabled: agent.codeInterpreterEnabled,\n retrievalEnabled: agent.retrievalEnabled,\n // TODO: https://transcend.height.app/T-31995 - prompt, largeLanguageModel, agentFunction, agentFile\n })),\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the agents\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncAgents(\n client: GraphQLClient,\n inputs: AgentInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n logger.info(`Syncing \"${inputs.length}\" agents...`);\n\n let encounteredError = false;\n\n const existingAgents = await fetchAllAgents(client, { logger });\n\n const agentByName = keyBy(existingAgents, 'name') as {\n [k in string]: Pick<Agent, 'id' | 'name' | 'agentId'>;\n };\n\n const newAgents = inputs.filter((input) => !agentByName[input.name]);\n\n await mapSeries(newAgents, async (agent) => {\n try {\n const newAgent = await createAgent(client, { input: agent, logger });\n agentByName[newAgent.name] = newAgent;\n logger.info(`Successfully synced agent \"${agent.name}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync agent \"${agent.name}\"! - ${(err as Error).message}`);\n }\n });\n\n try {\n logger.info(`Updating \"${inputs.length}\" agents!`);\n await updateAgents(client, {\n input: inputs.map((input) => [input, agentByName[input.name]!.id]),\n logger,\n });\n logger.info(`Successfully synced \"${inputs.length}\" agents!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync \"${inputs.length}\" agents! - ${(err as Error).message}`);\n }\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: title, direction: ASC }\n// ]\nexport const GLOBAL_ACTION_ITEM_COLLECTIONS = gql`\n query TranscendCliGlobalActionItemCollectionss(\n $filterBy: GlobalActionItemCollectionFiltersInput!\n ) {\n globalActionItemCollections(filterBy: $filterBy) {\n nodes {\n id\n title\n description\n hidden\n productLine\n }\n }\n }\n`;\n\nexport const CREATE_ACTION_ITEM_COLLECTION = gql`\n mutation TranscendCliCreateActionItemCollection($input: CreateActionItemCollectionInput!) {\n createActionItemCollection(input: $input) {\n created {\n id\n title\n }\n }\n }\n`;\n\nexport const UPDATE_ACTION_ITEM_COLLECTION = gql`\n mutation TranscendCliUpdateActionItemCollection($input: UpdateActionItemCollectionInput!) {\n updateActionItemCollection(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { TranscendProduct } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { GLOBAL_ACTION_ITEM_COLLECTIONS } from './gqls/actionItemCollection.js';\n\nexport interface ActionItemCollection {\n /** ID of collection */\n id: string;\n /** Title of collection */\n title: string;\n /** Description of collection */\n description: string;\n /** Whether section is hidden */\n hidden: boolean;\n /** Which locations/products the action item shows up in */\n productLine: TranscendProduct;\n}\n\n/**\n * Fetch all action item collections in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All action item collections in the organization\n */\nexport async function fetchAllActionItemCollections(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: {\n /** Filter on location */\n location?: TranscendProduct;\n };\n } = {},\n): Promise<ActionItemCollection[]> {\n const { logger, filterBy = {} } = options;\n const {\n globalActionItemCollections: { nodes },\n } = await makeGraphQLRequest<{\n /** ActionItemCollections */\n globalActionItemCollections: {\n /** List */\n nodes: ActionItemCollection[];\n };\n }>(client, GLOBAL_ACTION_ITEM_COLLECTIONS, {\n variables: {\n filterBy: {\n ...filterBy,\n },\n },\n logger,\n });\n return nodes;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: title, direction: ASC }\n// ]\nexport const GLOBAL_ACTION_ITEMS = gql`\n query TranscendCliGlobalActionItems(\n $first: Int!\n $offset: Int!\n $filterBy: GlobalActionItemFiltersInput!\n ) {\n globalActionItems(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n ids\n count\n teams {\n id\n name\n }\n customerExperienceActionItemIds\n users {\n id\n email\n }\n collections {\n title\n id\n }\n dueDate\n priority\n titles\n resolved\n notes\n links\n type\n additionalContexts {\n iconOverride\n requestId\n dataSiloId\n requestType\n latestAirgapVersion\n parentTitle\n }\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n }\n }\n }\n`;\n\nexport const UPDATE_ACTION_ITEMS = gql`\n mutation TranscendCliUpdateActionItems($input: UpdateActionItemsInput!) {\n updateActionItems(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const CREATE_ACTION_ITEMS = gql`\n mutation TranscendCliCreateActionItems($input: [CreateActionItemsInput!]!) {\n createActionItems(input: $input) {\n clientMutationId\n }\n }\n`;\n","import { ActionItemCode, ActionItemPriorityOverride } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { GLOBAL_ACTION_ITEMS } from './gqls/actionItem.js';\n\nexport interface ActionItemRaw {\n /** ID of action item */\n ids: string[];\n /** Count of action items */\n count: number;\n /** Teams assigned to action items */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** Customer experience action item ID */\n customerExperienceActionItemIds: string[];\n /** Users assigned to the action item */\n users: {\n /** ID of user */\n id: string;\n /** User email */\n email: string;\n }[];\n /** Due date of action item */\n dueDate?: string;\n /** Priority of action item */\n priority?: ActionItemPriorityOverride;\n /** Titles of action items */\n titles: string[];\n /** Description of the action item */\n resolved: boolean;\n /** Notes */\n notes: string[];\n /** links */\n links: string[];\n /** Action item types */\n type: ActionItemCode;\n /** Custom fields */\n attributeValues: {\n /** Name of attribute value */\n name: string;\n /** Attribute key that the value represents */\n attributeKey: {\n /** Name of attribute team */\n name: string;\n };\n }[];\n /** Additional context related to action item */\n additionalContexts?: {\n /** Override of icon */\n iconOverride?: string;\n /** Request ID */\n requestId?: string;\n /** Data Silo ID */\n dataSiloId?: string;\n /** Request type */\n requestType?: string;\n /** Airgap version */\n latestAirgapVersion?: string;\n /** Parent title */\n parentTitle?: string;\n };\n /** Sections where action item is grouped under */\n collections: {\n /** ID of collection that action item belongs to */\n id: string;\n /** Title of collection */\n title: string;\n }[];\n}\n\nexport interface ActionItem extends Omit<ActionItemRaw, 'ids' | 'titles' | 'links' | 'notes'> {\n /** ID of action item */\n id: string;\n /** Title of action item */\n title: string;\n /** Notes */\n notes: string;\n /** Links */\n link: string;\n /** Sections where action item is grouped under */\n collections: {\n /** ID of collection that action item belongs to */\n id: string;\n /** Title of collection */\n title: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all action items in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All action items in the organization\n */\nexport async function fetchAllActionItems(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter by */\n filterBy?: {\n /** Names of the action items to filter for */\n priority?: ActionItemPriorityOverride[];\n /** Type of action item */\n type?: ActionItemCode[];\n /** Whether resolved or not */\n resolved?: boolean;\n /** Filter for action items due before this date */\n startDueDate?: Date;\n /** Filter for action items due after this date */\n endDueDate?: Date;\n };\n } = {},\n): Promise<ActionItem[]> {\n const { logger, filterBy = {} } = options;\n const actionItems: ActionItem[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n globalActionItems: { nodes },\n } = await makeGraphQLRequest<{\n /** ActionItems */\n globalActionItems: {\n /** List */\n nodes: ActionItemRaw[];\n };\n }>(client, GLOBAL_ACTION_ITEMS, {\n variables: {\n first: PAGE_SIZE,\n offset,\n filterBy: {\n ...filterBy,\n ...(filterBy.startDueDate ? { startDueDate: filterBy.startDueDate.toISOString() } : {}),\n ...(filterBy.endDueDate ? { endDueDate: filterBy.endDueDate.toISOString() } : {}),\n },\n },\n logger,\n });\n actionItems.push(\n ...nodes.map((node) => ({\n ...node,\n id: node.ids[0]!,\n title: node.titles[0]!,\n notes: node.notes[0]!,\n link: node.links[0]!,\n })),\n );\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return actionItems;\n}\n","import { gql } from 'graphql-request';\n\nexport const ASSESSMENT_SECTION_FIELDS = `\n id\n title\n status\n index\n questions {\n id\n title\n index\n type\n subType\n placeholder\n description\n isRequired\n displayLogic\n riskLogic\n requireRiskEvaluation\n requireRiskMatrixEvaluation\n riskCategories {\n id\n title\n }\n riskFramework {\n id\n title\n description\n riskLevels {\n id\n title\n }\n riskCategories {\n id\n title\n }\n riskMatrixColumns {\n id\n title\n }\n riskMatrixRows {\n id\n title\n }\n riskMatrix {\n id\n title\n }\n creator {\n id\n email\n name\n }\n riskMatrixRowTitle\n riskMatrixColumnTitle\n }\n riskLevel {\n id\n title\n }\n reviewerRiskLevel {\n id\n title\n }\n riskLevelFromRiskMatrix {\n id\n title\n }\n answerOptions {\n id\n index\n value\n }\n selectedAnswers {\n ... on AssessmentAnswerInterface {\n id\n index\n value\n }\n }\n respondent {\n id\n email\n name\n }\n attributeKey {\n name\n }\n externalRespondentEmail\n comments {\n id\n content\n createdAt\n updatedAt\n author {\n id\n email\n name\n }\n }\n allowedMimeTypes\n updatedAt\n referenceId\n previousSubmissions {\n id\n updatedAt\n assessmentQuestionId\n answers {\n ... on AssessmentAnswerInterface {\n id\n index\n value\n }\n }\n }\n allowSelectOther\n syncModel\n syncColumn\n syncRowIds\n syncOverride\n }\n assignees {\n id\n email\n name\n }\n externalAssignees {\n id\n email\n }\n isReviewed\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: ASC }\n// { field: name, direction: ASC }\n// ]\nexport const ASSESSMENTS = gql`\n query TranscendCliAssessments(\n $first: Int!\n $offset: Int!\n $filterBy: AssessmentFormFiltersInput\n ) {\n assessmentForms(first: $first, offset: $offset, filterBy: $filterBy) {\n nodes {\n id\n creator {\n id\n email\n name\n }\n lastEditor {\n id\n email\n name\n }\n title\n description\n status\n assignees {\n id\n email\n name\n }\n externalAssignees {\n id\n email\n }\n reviewers {\n id\n email\n name\n }\n isLocked\n isArchived\n isExternallyCreated\n dueDate\n createdAt\n updatedAt\n assignedAt\n submittedAt\n approvedAt\n rejectedAt\n titleIsInternal\n retentionSchedule {\n id\n type\n durationDays\n operation\n }\n attributeValues {\n name\n attributeKey {\n name\n }\n }\n sections {\n ${ASSESSMENT_SECTION_FIELDS}\n }\n assessmentGroup {\n id\n title\n description\n }\n resources {\n resourceType\n ... on AttributeBusinessEntityResource {\n id\n title\n }\n ... on AttributeDataSiloResource {\n id\n title\n }\n ... on AttributeDataSubCategoryResource {\n id\n name\n category\n }\n ... on AttributeSubDataPointResource {\n id\n name\n }\n ... on AttributeProcessingPurposeSubCategoryResource {\n id\n name\n purpose\n }\n ... on AttributeRequestResource {\n id\n type\n }\n ... on AttributeVendorResource {\n id\n title\n }\n ... on AttributePromptResource {\n id\n title\n }\n ... on AttributePromptRunResource {\n id\n title\n }\n ... on AttributePromptGroupResource {\n id\n title\n }\n }\n syncedRows {\n resourceType\n ... on AttributeBusinessEntityResource {\n id\n title\n }\n ... on AttributeDataSiloResource {\n id\n title\n }\n ... on AttributeDataSubCategoryResource {\n id\n name\n category\n }\n ... on AttributeSubDataPointResource {\n id\n name\n }\n ... on AttributeProcessingPurposeSubCategoryResource {\n id\n name\n purpose\n }\n ... on AttributeVendorResource {\n id\n title\n }\n }\n }\n }\n }\n`;\n\nexport const IMPORT_ONE_TRUST_ASSESSMENT_FORMS = gql`\n mutation TranscendCliImportOneTrustAssessmentForms($input: ImportOnetrustAssessmentsInput!) {\n importOneTrustAssessmentForms(input: $input) {\n assessmentForms {\n id\n title\n }\n }\n }\n`;\n","import {\n AssessmentFormStatus,\n AssessmentQuestionSubType,\n AssessmentQuestionType,\n AssessmentSyncColumn,\n AssessmentSyncModel,\n AttributeSupportedResourceType,\n DataCategoryType,\n ProcessingPurpose,\n RetentionScheduleOperation,\n RetentionScheduleType,\n} from '@transcend-io/privacy-types';\n/* eslint-disable max-lines */\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { ASSESSMENTS } from './gqls/assessment.js';\n\n/**\n * Represents an assessment with various properties and metadata.\n */\nexport interface Assessment {\n /** The ID of the assessment */\n id: string;\n /** The user who created the assessment */\n creator: UserPreview;\n /** The user who last edited the assessment */\n lastEditor: UserPreview;\n /** The title of the assessment */\n title: string;\n /** The description of the assessment */\n description: string;\n /** The current status of the assessment */\n status: AssessmentFormStatus;\n /** The users assigned to the assessment */\n assignees: UserPreview[];\n /** The external users assigned to the assessment */\n externalAssignees: ExternalUser[];\n /** The users who are reviewers of the assessment */\n reviewers: UserPreview[];\n /** Indicates if the assessment is locked */\n isLocked: boolean;\n /** Indicates if the assessment is archived */\n isArchived: boolean;\n /** Indicates if the assessment was created externally */\n isExternallyCreated: boolean;\n /** The due date of the assessment */\n dueDate: string;\n /** The date when the assessment was created */\n createdAt: string;\n /** The date when the assessment was last updated */\n updatedAt: string;\n /** The date when the assessment was assigned */\n assignedAt: string;\n /** The date when the assessment was submitted */\n submittedAt: string;\n /** The date when the assessment was approved */\n approvedAt: string;\n /** The date when the assessment was rejected */\n rejectedAt: string;\n /** Indicates if the title of the assessment is internal */\n titleIsInternal: boolean;\n /** The retention schedule of the assessment */\n retentionSchedule?: RetentionSchedule;\n /** The attribute values associated with the assessment */\n attributeValues: AttributeValue[];\n /** The sections of the assessment */\n sections: AssessmentSection[];\n /** The group to which the assessment belongs */\n assessmentGroup: AssessmentGroup;\n /** The resources associated with the assessment */\n resources: AssessmentResource[];\n /** The rows that are synced with the assessment */\n syncedRows: AssessmentResource[];\n}\n\nexport interface UserPreview {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n /** Name of user */\n name: string;\n}\n\nexport interface ExternalUser {\n /** ID of external user */\n id: string;\n /** Email of external user */\n email: string;\n}\n\nexport interface RetentionSchedule {\n /** ID of retention schedule */\n id: string;\n /** Type */\n type: RetentionScheduleType;\n /** Duration of retention schedule */\n durationDays: number;\n /** The operation to perform on the retention schedule */\n operation: RetentionScheduleOperation;\n}\n\ninterface AttributeValue {\n /** Name of attribute value */\n name: string;\n /** Key */\n attributeKey: {\n /** Name of key */\n name: string;\n };\n}\n\nexport interface AssessmentSection {\n /** ID of section */\n id: string;\n /** Title of section */\n title: string;\n /** Status of section */\n status: string;\n /** Index of section */\n index: number;\n /** Questions */\n questions: AssessmentQuestion[];\n /** Assignees */\n assignees: UserPreview[];\n /** External assignees */\n externalAssignees: ExternalUser[];\n /** Whether is reviewed */\n isReviewed: boolean;\n}\n\n/**\n * Represents a question in the assessment.\n */\nexport interface AssessmentQuestion {\n /**\n * Unique identifier for the question.\n */\n id: string;\n /** Title of the question */\n title: string;\n /** Index of the question in the assessment */\n index: number;\n /** Type of the question */\n type: AssessmentQuestionType;\n /** Subtype of the question */\n subType: AssessmentQuestionSubType;\n /** Placeholder text for the question */\n placeholder: string;\n /** Description of the question */\n description: string;\n /** Indicates if the question is required */\n isRequired: boolean;\n /** Logic for displaying the question */\n displayLogic: string;\n /** Logic for assessing risk related to the question */\n riskLogic: string[];\n /** Indicates if risk evaluation is required for the question */\n requireRiskEvaluation: boolean;\n /** Indicates if risk matrix evaluation is required for the question */\n requireRiskMatrixEvaluation: boolean;\n /** Categories of risk associated with the question */\n riskCategories: RiskCategory[];\n /** Framework used for risk assessment */\n riskFramework?: RiskFramework;\n /** Level of risk associated with the question */\n riskLevel?: RiskLevel;\n /** Risk level assigned by the reviewer */\n reviewerRiskLevel?: RiskLevel;\n /** Risk level derived from the risk matrix */\n riskLevelFromRiskMatrix?: RiskLevel;\n /** Options available for answering the question */\n answerOptions: AssessmentAnswerOption[];\n /** Answers selected for the question */\n selectedAnswers: AssessmentAnswer[];\n /** User who responded to the question */\n respondent: UserPreview;\n /** Key attribute associated with the question */\n attributeKey?: {\n /** Name of key */\n name: string;\n };\n /** Email of the external respondent */\n externalRespondentEmail?: string;\n /** Comments related to the question */\n comments: unknown[];\n /** Allowed MIME types for file uploads in the question */\n allowedMimeTypes: string[];\n /** Timestamp of the last update to the question */\n updatedAt: string;\n /** Reference identifier for the question */\n referenceId: string;\n /** Previous submissions related to the question */\n previousSubmissions: AssessmentPreviousSubmission[];\n /** Indicates if selecting \"Other\" is allowed for the question */\n allowSelectOther: boolean;\n /** Model used for synchronization */\n syncModel: AssessmentSyncModel;\n /** Column used for synchronization */\n syncColumn: AssessmentSyncColumn;\n /** Row IDs used for synchronization */\n syncRowIds: string[];\n /** Indicates if synchronization override is allowed */\n syncOverride: boolean;\n}\n\nexport interface RiskCategory {\n /** ID of category */\n id: string;\n /** Title of category */\n title: string;\n}\n\nexport interface RiskFramework {\n /** ID of framework */\n id: string;\n /** Title of framework */\n title: string;\n /** Description of framework */\n description: string;\n /** Risk levels */\n riskLevels: RiskLevel[];\n /** Risk categories */\n riskCategories: RiskCategory[];\n /** Risk matrix columns */\n riskMatrixColumns: RiskMatrixColumn[];\n /** Risk matrix rows */\n riskMatrixRows: RiskMatrixRow[];\n /** Risk matrix settings */\n riskMatrix: RiskMatrix[][];\n /** Creator of risk framework */\n creator?: UserPreview;\n /** Risk matrix row title */\n riskMatrixRowTitle: string;\n /** Risk matrix column title */\n riskMatrixColumnTitle: string;\n}\n\nexport interface RiskLevel {\n /** ID of risk level */\n id: string;\n /** Title of risk level */\n title: string;\n}\n\nexport interface RiskMatrix {\n /** ID of risk matrix */\n id: string;\n /** Title of risk matrix */\n title: string;\n}\n\nexport interface RiskMatrixColumn {\n /** ID of column */\n id: string;\n /** Title of column */\n title: string;\n}\n\nexport interface RiskMatrixRow {\n /** ID of row */\n id: string;\n /** Title of row */\n title: string;\n}\n\nexport interface AssessmentAnswerOption {\n /** ID of answer option */\n id: string;\n /** Index of answer option */\n index: number;\n /** Value of answer */\n value: string;\n}\n\nexport interface AssessmentAnswer {\n /** ID of answer */\n id: string;\n /** Index of answer */\n index: number;\n /** Value of answer */\n value: string;\n}\n\nexport interface AssessmentComment {\n /** ID of comment */\n id: string;\n /** Content of comment */\n content: string;\n /** Date comment made */\n createdAt: string;\n /** Date comment updated */\n updatedAt: string;\n /** Author of comment */\n author?: UserPreview;\n}\n\nexport interface AssessmentPreviousSubmission {\n /** Id of submission */\n id: string;\n /** Date updated */\n updatedAt: string;\n /** ID of question */\n assessmentQuestionId: string;\n /** Answers */\n answers: AssessmentAnswer[];\n}\n\nexport interface AssessmentGroup {\n /** ID of group */\n id: string;\n /** Title of group */\n title: string;\n /** Description of group */\n description: string;\n}\n\nexport interface AssessmentResource {\n /** Type of resource */\n resourceType: AttributeSupportedResourceType;\n /** ID of resource */\n id: string;\n /** Title of resource */\n title?: string;\n /** Name of resource */\n name?: string;\n /** Category of resource */\n category?: DataCategoryType;\n /** Purpose of resource */\n purpose?: ProcessingPurpose;\n /** Type of integration */\n type?: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all assessments in the organization\n *\n * @param client - GraphQL client\n * @returns All assessments in the organization\n */\nexport async function fetchAllAssessments(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Assessment[]> {\n const { logger } = options;\n const assessments: Assessment[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n assessmentForms: { nodes },\n } = await makeGraphQLRequest<{\n /** Forms */\n assessmentForms: {\n /** Nodes */\n nodes: Assessment[];\n };\n }>(client, ASSESSMENTS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n assessments.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return assessments.sort((a, b) => a.title.localeCompare(b.title));\n}\n/* eslint-enable max-lines */\n","import {\n AssessmentsDisplayLogicAction,\n ComparisonOperator,\n LogicOperator,\n} from '@transcend-io/privacy-types';\nimport { decodeCodec, valuesOf } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\n\n// This codec is for rules that logically require a list of values to compare against.\nexport const AssessmentRuleWithOperands = t.type({\n dependsOnQuestionReferenceId: t.string,\n comparisonOperator: t.union([\n t.literal(ComparisonOperator.IsEqualTo),\n t.literal(ComparisonOperator.IsNotEqualTo),\n t.literal(ComparisonOperator.IsOneOf),\n t.literal(ComparisonOperator.IsNotOneOf),\n t.literal(ComparisonOperator.Contains),\n t.literal(ComparisonOperator.DoesNotContain),\n ]),\n comparisonOperands: t.array(t.string),\n});\n\n// This codec is for the specific rule that does NOT require comparison operands.\nexport const AssessmentRuleWithoutOperands = t.type({\n dependsOnQuestionReferenceId: t.string,\n comparisonOperator: t.union([\n t.literal(ComparisonOperator.IsNotShown),\n t.literal(ComparisonOperator.IsShown),\n ]),\n});\n\n/**\n * The final, flexible codec that accepts EITHER a rule with operands OR a rule without them.\n */\nexport const AssessmentRule = t.union([AssessmentRuleWithOperands, AssessmentRuleWithoutOperands]);\n/** Type override */\nexport type AssessmentRule = t.TypeOf<typeof AssessmentRule>;\n\nexport interface AssessmentNestedRule {\n /** The operator to use when comparing the nested rules */\n logicOperator: LogicOperator;\n /** The rules to evaluate and be compared with to other using the LogicOperator */\n rules?: AssessmentRule[];\n /** The nested rules to add one more level of nesting to the rules. They are also compared to each other. */\n nestedRules?: AssessmentNestedRule[];\n}\n\nexport const AssessmentNestedRule: t.RecursiveType<t.Type<AssessmentNestedRule>> = t.recursion(\n 'AssessmentNestedRule',\n (self) =>\n t.intersection([\n t.type({\n /** The operator to use when comparing the nested rules */\n logicOperator: valuesOf(LogicOperator),\n }),\n t.partial({\n /** The rules to evaluate and be compared with to other using the LogicOperator */\n rules: t.array(AssessmentRule),\n /** The nested rules to add one more level of nesting to the rules. They are also compared to each other. */\n nestedRules: t.array(self),\n }),\n ]),\n);\n\nexport const AssessmentAction = t.partial({\n action: valuesOf(AssessmentsDisplayLogicAction),\n rule: AssessmentRule,\n nestedRule: AssessmentNestedRule,\n});\n\n/** Type override */\nexport type AssessmentAction = t.TypeOf<typeof AssessmentAction>;\n\n/**\n * Parse the assessment display logic\n *\n * @param displayLogic - Stringified rule\n * @returns The parsed assessment display logic\n */\nexport function parseAssessmentDisplayLogic(displayLogic: string): AssessmentAction {\n return decodeCodec(AssessmentAction, displayLogic);\n}\n","import { ComparisonOperator } from '@transcend-io/privacy-types';\nimport { decodeCodec, valuesOf } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\n\nexport const AssessmentRiskLogic = t.intersection([\n t.partial({\n riskAssignment: t.partial({\n riskLevelId: t.string,\n riskMatrixRowId: t.string,\n riskMatrixColumnId: t.string,\n }),\n }),\n t.type({\n comparisonOperands: t.array(t.string),\n comparisonOperator: valuesOf(ComparisonOperator),\n }),\n]);\n\n/** Type override */\nexport type AssessmentRiskLogic = t.TypeOf<typeof AssessmentRiskLogic>;\n\n/**\n * Parse the assessment risk logic\n *\n * @param riskLogic - Stringified rule\n * @returns The parsed assessment risk logic\n */\nexport function parseAssessmentRiskLogic(riskLogic: string): AssessmentRiskLogic {\n return decodeCodec(AssessmentRiskLogic, riskLogic);\n}\n","import { TranscendProduct } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n type ActionItemCollection,\n fetchAllActionItemCollections,\n} from './fetchAllActionItemCollections.js';\nimport {\n CREATE_ACTION_ITEM_COLLECTION,\n UPDATE_ACTION_ITEM_COLLECTION,\n} from './gqls/actionItemCollection.js';\n\nexport interface ActionItemCollectionInput {\n /** The display title of the collection */\n title: string;\n /** Locations where collection is shown */\n productLine: TranscendProduct;\n /** Description of collection */\n description?: string;\n /** Whether hidden */\n hidden?: boolean;\n}\n\n/**\n * Create a new action item collection\n *\n * @param client - GraphQL client\n * @param actionItemCollection - Input\n * @param options - Options\n * @returns Created action item collection\n */\nexport async function createActionItemCollection(\n client: GraphQLClient,\n options: {\n /** Action item collection to create */\n input: ActionItemCollectionInput;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Pick<ActionItemCollection, 'id' | 'title'>> {\n const { input: actionItemCollection, logger = NOOP_LOGGER } = options;\n const input = {\n title: actionItemCollection.title,\n description: actionItemCollection.description || '',\n hidden: actionItemCollection.hidden || false,\n productLine: actionItemCollection.productLine,\n };\n\n const { createActionItemCollection } = await makeGraphQLRequest<{\n /** Create actionItemCollection mutation */\n createActionItemCollection: {\n /** Created actionItemCollection */\n created: ActionItemCollection;\n };\n }>(client, CREATE_ACTION_ITEM_COLLECTION, {\n variables: { input },\n logger,\n });\n return createActionItemCollection.created;\n}\n\n/**\n * Update an action item collection\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateActionItemCollection(\n client: GraphQLClient,\n options: {\n /** Action item collection input to update */\n input: ActionItemCollectionInput & {\n /** ID of action item collection to update */\n id: string;\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<void> {\n const { input: actionItemCollection, logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, UPDATE_ACTION_ITEM_COLLECTION, {\n variables: {\n input: {\n id: actionItemCollection.id,\n title: actionItemCollection.title,\n description: actionItemCollection.description,\n hidden: actionItemCollection.hidden,\n productLine: actionItemCollection.productLine,\n },\n },\n logger,\n });\n}\n\n/**\n * Sync the action item collections\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncActionItemCollections(\n client: GraphQLClient,\n inputs: ActionItemCollectionInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${inputs.length}\" action item collections...`);\n\n const existingActionItemCollections = await fetchAllActionItemCollections(client, { logger });\n\n const collectionByTitle: { [k in string]: ActionItemCollection } = keyBy(\n existingActionItemCollections,\n 'title',\n );\n\n const newCollections = inputs.filter((input) => !collectionByTitle[input.title]);\n\n await mapSeries(newCollections, async (input) => {\n try {\n await createActionItemCollection(client, { input, logger });\n logger.info(`Successfully created action item collection \"${input.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to create action item collection \"${input.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n const actionItemsToUpdate = inputs\n .map((input) => [input, collectionByTitle[input.title]?.id])\n .filter((x): x is [ActionItemCollectionInput, string] => !!x[1]);\n await mapSeries(actionItemsToUpdate, async ([input, actionItemId]) => {\n try {\n await updateActionItemCollection(client, {\n input: { ...input, id: actionItemId },\n logger,\n });\n logger.info(`Successfully synced action item collection \"${input.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(\n `Failed to sync action item collection \"${input.title}\"! - ${(err as Error).message}`,\n );\n }\n });\n\n return !encounteredError;\n}\n","import { ActionItemCode, ActionItemPriorityOverride } from '@transcend-io/privacy-types';\nimport { mapSeries, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { uniq, keyBy, chunk } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n type ActionItemCollection,\n fetchAllActionItemCollections,\n} from './fetchAllActionItemCollections.js';\nimport { fetchAllActionItems, type ActionItem } from './fetchAllActionItems.js';\nimport { UPDATE_ACTION_ITEMS, CREATE_ACTION_ITEMS } from './gqls/actionItem.js';\n\n/** Minimal attribute key shape needed for action item sync */\nexport interface ActionItemAttributeKey {\n /** ID of attribute */\n id: string;\n /** Name of attribute */\n name: string;\n}\n\nexport interface ActionItemInput {\n /** The display title of the action item */\n title: string;\n /** Action item type */\n type: ActionItemCode;\n /** The titles of the collections that the action item is grouped within */\n collections: string[];\n /** Priority of the action item */\n priority?: ActionItemPriorityOverride;\n /** Customer experience action item key */\n customerExperienceActionItemId?: string;\n /** Due date of the action item */\n dueDate?: string;\n /** Whether action item has been resolved */\n resolved?: boolean;\n /** Notes */\n notes?: string;\n /** Links to action items */\n link?: string;\n /** The email addresses of the employees assigned to the action item */\n users?: string[];\n /** The names of teams assigned to the action item */\n teams?: string[];\n /** Attribute value and its corresponding attribute key */\n attributes?: {\n /** Attribute key name */\n key: string;\n /** Attribute value names */\n values: string[];\n }[];\n}\n\n/**\n * Create new action items\n *\n * @param client - GraphQL client\n * @param actionItems - Action item inputs\n * @param actionItemCollectionByTitle - Action item collections indexed by title\n * @param options - Options\n */\nexport async function createActionItems(\n client: GraphQLClient,\n options: {\n /** Action items to create */\n input: ActionItemInput[];\n /** Logger instance */\n logger?: Logger;\n /** Action item collections indexed by title */\n actionItemCollectionByTitle: {\n [k in string]: ActionItemCollection;\n };\n // TODO: https://transcend.height.app/T-38961 - insert attributes\n /** Attribute keys indexed by name */\n attributeKeysByName?: {\n [k in string]: ActionItemAttributeKey;\n };\n },\n): Promise<void> {\n const { input: actionItems, logger = NOOP_LOGGER, actionItemCollectionByTitle } = options;\n // TODO: https://transcend.height.app/T-38961 - insert attributes\n // const getAttribute = (key: string): string => {\n // const existing = attributeKeysByName[key];\n // if (!existing) {\n // throw new Error(`Attribute key \"${key}\" does not exist!`);\n // }\n // return existing.id;\n // };\n const chunked = chunk(actionItems, 100);\n await mapSeries(chunked, async (chunkToUpload) => {\n await makeGraphQLRequest(client, CREATE_ACTION_ITEMS, {\n variables: {\n input: chunkToUpload.map((actionItem) => ({\n title: actionItem.title,\n type: actionItem.type,\n priorityOverride: actionItem.priority,\n dueDate: actionItem.dueDate,\n customerExperienceActionItemId: actionItem.customerExperienceActionItemId,\n resolved: actionItem.resolved,\n notes: actionItem.notes,\n link: actionItem.link,\n assigneesUserEmails: actionItem.users,\n assigneesTeamNames: actionItem.teams,\n ...(actionItem.attributes\n ? {\n // TODO: https://transcend.height.app/T-38961 - insert attributes\n // attributes: actionItem.attributes.map(({ key, values }) => ({\n // attributeKeyId: getAttribute(key),\n // attributeValueNames: values,\n // })),\n }\n : {}),\n collectionIds: actionItem.collections.map(\n (collectionTitle) => actionItemCollectionByTitle[collectionTitle]!.id,\n ),\n })),\n },\n logger,\n });\n });\n}\n\n/**\n * Update an action item\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function updateActionItem(\n client: GraphQLClient,\n options: {\n /** Action item input to update */\n input: ActionItemInput & {\n /** ID of action item to update */\n id: string;\n };\n /** Logger instance */\n logger?: Logger;\n /** Attribute keys indexed by name */\n attributeKeysByName?: {\n [k in string]: ActionItemAttributeKey;\n };\n },\n): Promise<void> {\n const { input: actionItem, logger = NOOP_LOGGER, attributeKeysByName = {} } = options;\n const getAttribute = (key: string): string => {\n const existing = attributeKeysByName[key];\n if (!existing) {\n throw new Error(`Attribute key \"${key}\" does not exist!`);\n }\n return existing.id;\n };\n await makeGraphQLRequest(client, UPDATE_ACTION_ITEMS, {\n variables: {\n input: {\n ids: [actionItem.id],\n title: actionItem.title,\n priorityOverride: actionItem.priority,\n dueDate: actionItem.dueDate,\n resolved: actionItem.resolved,\n customerExperienceActionItemId: actionItem.customerExperienceActionItemId,\n notes: actionItem.notes,\n link: actionItem.link,\n assigneesUserEmails: actionItem.users,\n assigneesTeamNames: actionItem.teams,\n ...(actionItem.attributes\n ? {\n attributes: actionItem.attributes.map(({ key, values }) => ({\n attributeKeyId: getAttribute(key),\n attributeValueNames: values,\n })),\n }\n : {}),\n },\n },\n logger,\n });\n}\n\n/**\n * Convert action item to a unique key\n *\n * @param actionItem - action item\n * @returns Unique key\n */\nfunction actionItemToUniqueCode({\n title,\n collections,\n}: Pick<ActionItem, 'title' | 'collections'>): string {\n return `${title}-${collections\n .map((c) => c.title)\n .sort()\n .join('-')}`;\n}\n\n/**\n * Convert action item input to a unique key\n *\n * @param actionItem - action item\n * @returns Unique key\n */\nfunction actionItemInputToUniqueCode({\n title,\n collections,\n}: Pick<ActionItemInput, 'title' | 'collections'>): string {\n return `${title}-${collections.sort().join('-')}`;\n}\n\n/**\n * Sync the action items\n *\n * @param client - GraphQL client\n * @param inputs - Inputs to create\n * @param options - Options\n * @returns True if run without error, returns false if an error occurred\n */\nexport async function syncActionItems(\n client: GraphQLClient,\n inputs: ActionItemInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Pre-fetched attribute keys (pass result of fetchAllAttributes) */\n attributeKeys?: ActionItemAttributeKey[];\n } = {},\n): Promise<boolean> {\n const { logger = NOOP_LOGGER, attributeKeys = [] } = options;\n let encounteredError = false;\n logger.info(`Syncing \"${inputs.length}\" actionItems...`);\n\n const [existingActionItems, existingActionItemCollections] = await Promise.all([\n fetchAllActionItems(client, { logger }),\n fetchAllActionItemCollections(client, { logger }),\n ]);\n\n const actionItemCollectionByTitle: { [k in string]: ActionItemCollection } = keyBy(\n existingActionItemCollections,\n 'title',\n );\n const actionItemByTitle = keyBy(existingActionItems, actionItemToUniqueCode) as {\n [k in string]: ActionItem;\n };\n const attributeKeysByName = keyBy(attributeKeys, 'name');\n const actionItemByCxId = keyBy(\n existingActionItems.filter((x) => !!x.customerExperienceActionItemIds),\n ({ customerExperienceActionItemIds }) => customerExperienceActionItemIds[0],\n ) as { [k in string]: ActionItem };\n\n const missingCollections = uniq(inputs.map((input) => input.collections).flat()).filter(\n (collectionTitle) => !actionItemCollectionByTitle[collectionTitle],\n );\n if (missingCollections.length > 0) {\n logger.error(\n `Missing action item collections: \"${missingCollections.join(\n '\", \"',\n )}\" - please create them first!`,\n );\n return false;\n }\n\n const newActionItems = inputs.filter(\n (input) =>\n !actionItemByTitle[actionItemInputToUniqueCode(input)] &&\n !actionItemByCxId[input.customerExperienceActionItemId!],\n );\n\n if (newActionItems.length > 0) {\n try {\n logger.info(`Creating \"${newActionItems.length}\" actionItems...`);\n await createActionItems(client, {\n input: newActionItems,\n logger,\n actionItemCollectionByTitle,\n attributeKeysByName,\n });\n logger.info(`Successfully created \"${newActionItems.length}\" actionItems!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create action items! - ${(err as Error).message}`);\n }\n }\n\n const actionItemsToUpdate = inputs\n .map((input) => [\n input,\n actionItemByTitle[actionItemInputToUniqueCode(input)]?.id ||\n actionItemByCxId[input.customerExperienceActionItemId!]?.id,\n ])\n .filter((x): x is [ActionItemInput, string] => !!x[1]);\n await mapSeries(actionItemsToUpdate, async ([input, actionItemId]) => {\n try {\n await updateActionItem(client, {\n input: { ...input, id: actionItemId },\n logger,\n attributeKeysByName,\n });\n logger.info(`Successfully synced action item \"${input.title}\"!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to sync action item \"${input.title}\"! - ${(err as Error).message}`);\n }\n });\n\n return !encounteredError;\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const CODE_PACKAGES = gql`\n query TranscendCliCodePackages($first: Int!, $offset: Int!, $input: CodePackageFiltersInput) {\n codePackages(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n type\n relativePath\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n repository {\n id\n name\n }\n dataSilo {\n id\n title\n type\n }\n }\n }\n }\n`;\n\nexport const UPDATE_CODE_PACKAGES = gql`\n mutation TranscendCliUpdateCodePackages($input: UpdateCodePackagesInput!) {\n updateCodePackages(input: $input) {\n clientMutationId\n codePackages {\n id\n name\n description\n type\n relativePath\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n repository {\n id\n name\n }\n dataSilo {\n id\n title\n type\n }\n }\n }\n }\n`;\n\nexport const CREATE_CODE_PACKAGE = gql`\n mutation TranscendCliCreateCodePackage($input: CreateCodePackageInput!) {\n createCodePackage(input: $input) {\n clientMutationId\n codePackage {\n id\n name\n description\n type\n relativePath\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n repository {\n id\n name\n }\n dataSilo {\n id\n title\n type\n }\n }\n }\n }\n`;\n","import { CodePackageType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { CODE_PACKAGES } from './gqls/codePackage.js';\n\nexport interface CodePackage {\n /** ID of code package */\n id: string;\n /** Name of code package */\n name: string;\n /** Description of code package */\n description: string;\n /** Type of code package */\n type: CodePackageType;\n /** Relative path to code package in repository */\n relativePath: string;\n /** The teams that manage the code package */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** The users that manage the code package */\n owners: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n }[];\n /** The repository where the code package belongs */\n repository: {\n /** ID of repository */\n id: string;\n /** Name of repository */\n name: string;\n };\n /** The data silo that the code package relates to */\n dataSilo?: {\n /** ID of repository */\n id: string;\n /** Title of repository */\n title: string;\n /** Type of data silo */\n type: string;\n };\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all code packages in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All code packages in the organization\n */\nexport async function fetchAllCodePackages(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<CodePackage[]> {\n const { logger } = options;\n const codePackages: CodePackage[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n codePackages: { nodes },\n } = await makeGraphQLRequest<{\n /** Code packages */\n codePackages: {\n /** List */\n nodes: CodePackage[];\n };\n }>(client, CODE_PACKAGES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n codePackages.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return codePackages.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const REPOSITORIES = gql`\n query TranscendCliRepositories($first: Int!, $offset: Int!, $input: RepositoryFiltersInput) {\n repositories(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n url\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const UPDATE_REPOSITORIES = gql`\n mutation TranscendCliUpdateRepositories($input: UpdateRepositoriesInput!) {\n updateRepositories(input: $input) {\n clientMutationId\n repositories {\n id\n name\n url\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const CREATE_REPOSITORY = gql`\n mutation TranscendCliCreateRepository($input: CreateRepositoryInput!) {\n createRepository(input: $input) {\n clientMutationId\n repository {\n id\n name\n url\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { REPOSITORIES } from './gqls/repository.js';\n\nexport interface Repository {\n /** ID of repository */\n id: string;\n /** Name of repository */\n name: string;\n /** Description of repository */\n description: string;\n /** URL of repo */\n url: string;\n /** The teams that manage the repository */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** The users that manage the repository */\n owners: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all repositories in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All repositories in the organization\n */\nexport async function fetchAllRepositories(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<Repository[]> {\n const { logger } = options;\n const repositories: Repository[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n repositories: { nodes },\n } = await makeGraphQLRequest<{\n /** Repositories */\n repositories: {\n /** List */\n nodes: Repository[];\n };\n }>(client, REPOSITORIES, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n repositories.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return repositories.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\nexport const SOFTWARE_DEVELOPMENT_KITS = gql`\n query TranscendCliSoftwareDevelopmentKits(\n $first: Int!\n $offset: Int!\n $input: SoftwareDevelopmentKitFiltersInput\n ) {\n softwareDevelopmentKits(\n first: $first\n offset: $offset\n filterBy: $input\n orderBy: [{ field: createdAt, direction: ASC }, { field: name, direction: ASC }]\n ) {\n nodes {\n id\n name\n description\n codePackageType\n documentationLinks\n repositoryUrl\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const UPDATE_SOFTWARE_DEVELOPMENT_KITS = gql`\n mutation TranscendCliUpdateSoftwareDevelopmentKits($input: UpdateSoftwareDevelopmentKitsInput!) {\n updateSoftwareDevelopmentKits(input: $input) {\n clientMutationId\n softwareDevelopmentKits {\n id\n name\n description\n codePackageType\n documentationLinks\n repositoryUrl\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n\nexport const CREATE_SOFTWARE_DEVELOPMENT_KIT = gql`\n mutation TranscendCliCreateSoftwareDevelopmentKit($input: CreateSoftwareDevelopmentKitInput!) {\n createSoftwareDevelopmentKit(input: $input) {\n clientMutationId\n softwareDevelopmentKit {\n id\n name\n description\n codePackageType\n documentationLinks\n repositoryUrl\n teams {\n id\n name\n }\n owners {\n id\n email\n }\n }\n }\n }\n`;\n","import { CodePackageType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest } from '../api/makeGraphQLRequest.js';\nimport { SOFTWARE_DEVELOPMENT_KITS } from './gqls/softwareDevelopmentKit.js';\n\nexport interface SoftwareDevelopmentKit {\n /** ID of software development kit */\n id: string;\n /** Name of software development kit */\n name: string;\n /** Description of software development kit */\n description: string;\n /** Type of software development kit */\n codePackageType: CodePackageType;\n /** Related documentation */\n documentationLinks: string[];\n /** Link to git repository */\n repositoryUrl?: string;\n /** The teams that manage the software development kit */\n teams: {\n /** ID of team */\n id: string;\n /** Name of team */\n name: string;\n }[];\n /** The users that manage the software development kit */\n owners: {\n /** ID of user */\n id: string;\n /** Email of user */\n email: string;\n }[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all software development kits in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All software development kits in the organization\n */\nexport async function fetchAllSoftwareDevelopmentKits(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<SoftwareDevelopmentKit[]> {\n const { logger } = options;\n const softwareDevelopmentKits: SoftwareDevelopmentKit[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n softwareDevelopmentKits: { nodes },\n } = await makeGraphQLRequest<{\n /** Software development kits */\n softwareDevelopmentKits: {\n /** List */\n nodes: SoftwareDevelopmentKit[];\n };\n }>(client, SOFTWARE_DEVELOPMENT_KITS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n softwareDevelopmentKits.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return softwareDevelopmentKits.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import { mapSeries, map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { fetchAllRepositories, type Repository } from './fetchAllRepositories.js';\nimport { UPDATE_REPOSITORIES, CREATE_REPOSITORY } from './gqls/repository.js';\n\nexport interface RepositoryInput {\n /** Title of repository */\n name: string;\n /** Description of the repository */\n description?: string;\n /** Github repository URL */\n url: string;\n}\n\nconst CHUNK_SIZE = 100;\n\n/**\n * Create a new repository\n *\n * @param client - GraphQL client\n * @param input - Repository input\n * @param options - Options\n * @returns Created repository\n */\nexport async function createRepository(\n client: GraphQLClient,\n options: {\n /** Repository to create */\n input: {\n /** Title of repository */\n name: string;\n /** Description of the repository */\n description?: string;\n /** Github repository */\n url: string;\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Repository> {\n const { input, logger = NOOP_LOGGER } = options;\n const {\n createRepository: { repository },\n } = await makeGraphQLRequest<{\n /** createRepository mutation */\n createRepository: {\n /** Software development kit */\n repository: Repository;\n };\n }>(client, CREATE_REPOSITORY, {\n variables: { input },\n logger,\n });\n logger.info(`Successfully created repository \"${input.name}\"!`);\n return repository;\n}\n\n/**\n * Update an existing repository\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Updated repositories\n */\nexport async function updateRepositories(\n client: GraphQLClient,\n options: {\n /** Repository inputs to update */\n input: {\n /** ID of repository */\n id: string;\n /** Title of repository */\n name?: string;\n /** Description of the repository */\n description?: string;\n /** Github repository */\n url?: string;\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n }[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<Repository[]> {\n const { input: repositories, logger = NOOP_LOGGER } = options;\n const {\n updateRepositories: { repositories: updatedRepositories },\n } = await makeGraphQLRequest<{\n /** updateRepositories mutation */\n updateRepositories: {\n /** Software development kit */\n repositories: Repository[];\n };\n }>(client, UPDATE_REPOSITORIES, {\n variables: {\n input: {\n repositories,\n },\n },\n logger,\n });\n logger.info(`Successfully updated ${repositories.length} repositories!`);\n return updatedRepositories;\n}\n\n/**\n * Sync the repositories\n *\n * @param client - GraphQL client\n * @param repositories - Repositories\n * @param options - Options\n * @returns The repositories that were upserted and whether the sync was successful\n */\nexport async function syncRepositories(\n client: GraphQLClient,\n repositories: RepositoryInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Concurrency */\n concurrency?: number;\n } = {},\n): Promise<{\n /** The repositories that were upserted */\n repositories: Repository[];\n /** If successful */\n success: boolean;\n}> {\n const { logger = NOOP_LOGGER, concurrency = 20 } = options;\n let encounteredError = false;\n const repos: Repository[] = [];\n\n // Index existing repositories\n const existing = await fetchAllRepositories(client, { logger });\n const repositoryByName = keyBy(existing, 'name');\n\n // Determine which repositories are new vs existing\n const mapRepositoriesToExisting = repositories.map((repoInput) => [\n repoInput,\n repositoryByName[repoInput.name]?.id,\n ]);\n\n // Create the new repositories\n const newRepositories = mapRepositoriesToExisting\n .filter(([, existing]) => !existing)\n .map(([repoInput]) => repoInput as RepositoryInput);\n try {\n logger.info(`Creating \"${newRepositories.length}\" new repositories...`);\n await map(\n newRepositories,\n async (repo) => {\n const newRepo = await createRepository(client, { input: repo, logger });\n repos.push(newRepo);\n },\n {\n concurrency,\n },\n );\n logger.info(`Successfully synced ${newRepositories.length} repositories!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create repositories! - ${(err as Error).message}`);\n }\n\n // Update existing repositories\n const existingRepositories = mapRepositoriesToExisting.filter(\n (x): x is [RepositoryInput, string] => !!x[1],\n );\n const chunks = chunk(existingRepositories, CHUNK_SIZE);\n logger.info(`Updating \"${existingRepositories.length}\" repositories...`);\n\n await mapSeries(chunks, async (chunk) => {\n try {\n const updatedRepos = await updateRepositories(client, {\n input: chunk.map(([input, id]) => ({\n ...input,\n id,\n })),\n logger,\n });\n repos.push(...updatedRepos);\n logger.info(`Successfully updated \"${existingRepositories.length}\" repositories!`);\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to update repositories! - ${(err as Error).message}`);\n }\n\n logger.info(`Synced \"${repositories.length}\" repositories!`);\n });\n\n // Return true upon success\n return {\n repositories: repos,\n success: !encounteredError,\n };\n}\n","import { CodePackageType } from '@transcend-io/privacy-types';\nimport { mapSeries, map, type Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, keyBy } from 'lodash-es';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport {\n fetchAllSoftwareDevelopmentKits,\n type SoftwareDevelopmentKit,\n} from './fetchAllSoftwareDevelopmentKits.js';\nimport {\n UPDATE_SOFTWARE_DEVELOPMENT_KITS,\n CREATE_SOFTWARE_DEVELOPMENT_KIT,\n} from './gqls/softwareDevelopmentKit.js';\n\nexport interface SoftwareDevelopmentKitInput {\n /** Title of software development kit */\n name: string;\n /** Code package type */\n codePackageType: CodePackageType;\n /** Description of the SDK */\n description?: string;\n /** Github repository URL */\n repositoryUrl?: string;\n /** Integration name */\n catalogIntegrationName?: string;\n /** Documentation links */\n documentationLinks?: string[];\n}\n\nconst CHUNK_SIZE = 100;\n\n/**\n * Create a new software development kit\n *\n * @param client - GraphQL client\n * @param input - Software development kit input\n * @param options - Options\n * @returns Created software development kit\n */\nexport async function createSoftwareDevelopmentKit(\n client: GraphQLClient,\n options: {\n /** Software development kit to create */\n input: {\n /** Title of software development kit */\n name: string;\n /** Code package type */\n codePackageType: CodePackageType;\n /** Description of the SDK */\n description?: string;\n /** Github repository */\n repositoryUrl?: string;\n /** Integration name */\n catalogIntegrationName?: string;\n /** Doc links */\n documentationLinks?: string[];\n /** Code package IDs */\n codePackageIds?: string[];\n /** Code package names */\n codePackageNames?: string[];\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n };\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<SoftwareDevelopmentKit> {\n const { input, logger = NOOP_LOGGER } = options;\n const {\n createSoftwareDevelopmentKit: { softwareDevelopmentKit },\n } = await makeGraphQLRequest<{\n /** createSoftwareDevelopmentKit mutation */\n createSoftwareDevelopmentKit: {\n /** Software development kit */\n softwareDevelopmentKit: SoftwareDevelopmentKit;\n };\n }>(client, CREATE_SOFTWARE_DEVELOPMENT_KIT, {\n variables: { input },\n logger,\n });\n logger.info(`Successfully created software development kit \"${input.name}\"!`);\n return softwareDevelopmentKit;\n}\n\n/**\n * Update an existing software development kit\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns Updated software development kits\n */\nexport async function updateSoftwareDevelopmentKits(\n client: GraphQLClient,\n options: {\n /** Software development kit inputs to update */\n input: {\n /** ID of software development kit */\n id: string;\n /** Title of software development kit */\n name?: string;\n /** Description of the SDK */\n description?: string;\n /** Github repository */\n repositoryUrl?: string;\n /** Integration name */\n catalogIntegrationName?: string;\n /** Doc links */\n documentationLinks?: string[];\n /** Code package IDs */\n codePackageIds?: string[];\n /** Code package names */\n codePackageNames?: string[];\n /** User IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** Team IDs */\n teamIds?: string[];\n /** Team names */\n teamNames?: string[];\n }[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<SoftwareDevelopmentKit[]> {\n const { input: sdks, logger = NOOP_LOGGER } = options;\n const {\n updateSoftwareDevelopmentKits: { softwareDevelopmentKits },\n } = await makeGraphQLRequest<{\n /** updateSoftwareDevelopmentKits mutation */\n updateSoftwareDevelopmentKits: {\n /** Software development kit */\n softwareDevelopmentKits: SoftwareDevelopmentKit[];\n };\n }>(client, UPDATE_SOFTWARE_DEVELOPMENT_KITS, {\n variables: {\n input: {\n softwareDevelopmentKits: sdks,\n },\n },\n logger,\n });\n logger.info(`Successfully updated ${sdks.length} software development kits!`);\n return softwareDevelopmentKits;\n}\n\n/**\n * Sync the software development kits\n *\n * @param client - GraphQL client\n * @param softwareDevelopmentKits - Software development kits\n * @param options - Options\n * @returns The software development kits that were upserted and whether the sync was successful\n */\nexport async function syncSoftwareDevelopmentKits(\n client: GraphQLClient,\n softwareDevelopmentKits: SoftwareDevelopmentKitInput[],\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Concurrency */\n concurrency?: number;\n } = {},\n): Promise<{\n /** The SDKs that were upserted */\n softwareDevelopmentKits: SoftwareDevelopmentKit[];\n /** If successful */\n success: boolean;\n}> {\n const { logger = NOOP_LOGGER, concurrency = 20 } = options;\n let encounteredError = false;\n const sdks: SoftwareDevelopmentKit[] = [];\n logger.info('Syncing software development kits...');\n\n // Index existing software development kits\n const existing = await fetchAllSoftwareDevelopmentKits(client, { logger });\n const softwareDevelopmentKitByTitle = keyBy(existing, ({ name, codePackageType }) =>\n JSON.stringify({ name, codePackageType }),\n );\n\n // Determine which software development kits are new vs existing\n const mapSoftwareDevelopmentKitsToExisting = softwareDevelopmentKits.map((sdkInput) => [\n sdkInput,\n softwareDevelopmentKitByTitle[\n JSON.stringify({\n name: sdkInput.name,\n codePackageType: sdkInput.codePackageType,\n })\n ]?.id,\n ]);\n\n // Create the new software development kits\n const newSoftwareDevelopmentKits = mapSoftwareDevelopmentKitsToExisting\n .filter(([, existing]) => !existing)\n .map(([sdkInput]) => sdkInput as SoftwareDevelopmentKitInput);\n try {\n logger.info(`Creating \"${newSoftwareDevelopmentKits.length}\" new software development kits...`);\n await map(\n newSoftwareDevelopmentKits,\n async (sdk) => {\n const newSdk = await createSoftwareDevelopmentKit(client, { input: sdk, logger });\n sdks.push(newSdk);\n },\n {\n concurrency,\n },\n );\n logger.info(\n `Successfully synced ${newSoftwareDevelopmentKits.length} software development kits!`,\n );\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to create software development kits! - ${(err as Error).message}`);\n }\n\n // Update existing software development kits\n const existingSoftwareDevelopmentKits = mapSoftwareDevelopmentKitsToExisting.filter(\n (x): x is [SoftwareDevelopmentKitInput, string] => !!x[1],\n );\n const chunks = chunk(existingSoftwareDevelopmentKits, CHUNK_SIZE);\n logger.info(`Updating \"${existingSoftwareDevelopmentKits.length}\" software development kits...`);\n\n await mapSeries(chunks, async (chunk) => {\n try {\n const updatedSdks = await updateSoftwareDevelopmentKits(client, {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n input: chunk.map(([{ codePackageType, ...input }, id]) => ({\n ...input,\n id,\n })),\n logger,\n });\n sdks.push(...updatedSdks);\n logger.info(\n `Successfully updated \"${existingSoftwareDevelopmentKits.length}\" software development kits!`,\n );\n } catch (err) {\n encounteredError = true;\n logger.error(`Failed to update software development kits! - ${(err as Error).message}`);\n }\n\n logger.info(`Synced \"${softwareDevelopmentKits.length}\" software development kits!`);\n });\n\n // Return true upon success\n return {\n softwareDevelopmentKits: sdks,\n success: !encounteredError,\n };\n}\n","import type { CustomFunctionSignPayload } from './codeSigning.js';\nimport type { CustomFunctionConfigInput } from './syncCustomFunction.js';\n\n/**\n * Build the sign payload for a custom function config.\n *\n * @param input - The custom function config\n * @returns The plaintext sign payload\n */\nexport function buildCustomFunctionSignPayload(\n input: CustomFunctionConfigInput,\n): CustomFunctionSignPayload {\n return {\n code: input.code,\n context: {\n userDefinedEnv: input.env ?? {},\n allowedHosts: input.allowedHosts ?? [],\n ...(input.allowThirdPartyImports !== undefined\n ? { allowThirdPartyImports: input.allowThirdPartyImports }\n : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n },\n };\n}\n","import jwt from 'jsonwebtoken';\n\n/**\n * The execution context for a custom function's code.\n */\nexport interface CustomFunctionCodeContextInput {\n /** Environment variables made available to the function (values are encrypted at sign time) */\n userDefinedEnv: Record<string, string>;\n /** Hosts the function is allowed to make network requests to */\n allowedHosts: string[];\n /** Whether the function may import third party modules */\n allowThirdPartyImports?: boolean;\n /** Execution timeout in milliseconds (capped by the Sombra gateway configuration) */\n timeoutMs?: number;\n}\n\n/**\n * The plaintext payload sent to the Sombra customer-ingress\n * `/v1/custom/sign` endpoint.\n */\nexport interface CustomFunctionSignPayload {\n /** The plaintext TypeScript source code of the custom function */\n code: string;\n /** The execution context for the code */\n context: CustomFunctionCodeContextInput;\n}\n\n/**\n * Decode the payload of a JWT without verifying its signature.\n *\n * Custom function code and context JWTs are HMAC-signed by Sombra but their\n * payloads are readable base64url JSON. Since the JWTs are fetched over the\n * authenticated GraphQL API, decoding without verification is safe for the\n * purposes of change detection.\n *\n * @param token - The JWT string\n * @returns The decoded payload object, or undefined if the JWT is malformed\n */\nexport function decodeJwtPayload<T extends object>(token: string): T | undefined {\n return (jwt.decode(token, { json: true }) as T | null) ?? undefined;\n}\n\n/**\n * The decoded payload of a signed custom function code JWT.\n */\nexport interface SignedCustomFunctionCode {\n /** The base64-encoded source code */\n base64Code: string;\n}\n\n/**\n * The decoded payload of a signed custom function code context JWT. The\n * environment variable values are encrypted at sign time, so only their names\n * are comparable.\n */\nexport interface SignedCustomFunctionCodeContext {\n /** Hosts the function is allowed to make network requests to */\n allowedHosts?: string[];\n /** Whether the function may import third party modules */\n allowThirdPartyImports?: boolean;\n /** Execution timeout in milliseconds */\n timeoutMs?: number;\n /** Environment variables, with values encrypted by the Sombra gateway */\n userDefinedEncryptedEnv?: Record<string, unknown>;\n}\n\n/**\n * The result of comparing a local custom function definition against the\n * signed code and context currently stored in Transcend.\n */\nexport interface CustomFunctionDiff {\n /** Whether any comparable field differs */\n changed: boolean;\n /** Human-readable list of fields that differ */\n changedFields: string[];\n}\n\n/**\n * Compare a desired sign payload against the signed code/context JWTs of an\n * existing custom function version.\n *\n * Environment variable *values* are encrypted server-side and cannot be\n * compared — only their names are diffed. Callers should offer a force flag to\n * re-push when only env values change.\n *\n * @param desired - The desired code and context\n * @param existing - The existing signed JWTs from the API\n * @returns The diff result\n */\nexport function diffCustomFunctionCode(\n desired: CustomFunctionSignPayload,\n existing: {\n /** The signed code JWT of the existing version */\n signedCodeJwt: string;\n /** The signed code context JWT of the existing version */\n signedCodeContextJwt: string;\n },\n): CustomFunctionDiff {\n const changedFields: string[] = [];\n\n const codePayload = decodeJwtPayload<SignedCustomFunctionCode>(existing.signedCodeJwt);\n const existingCode =\n codePayload === undefined\n ? undefined\n : Buffer.from(codePayload.base64Code, 'base64').toString('utf-8');\n if (existingCode !== desired.code) {\n changedFields.push('code');\n }\n\n const contextPayload = decodeJwtPayload<SignedCustomFunctionCodeContext>(\n existing.signedCodeContextJwt,\n );\n const existingHosts = [...(contextPayload?.allowedHosts ?? [])].sort();\n const desiredHosts = [...desired.context.allowedHosts].sort();\n if (JSON.stringify(existingHosts) !== JSON.stringify(desiredHosts)) {\n changedFields.push('allowedHosts');\n }\n if (\n (contextPayload?.allowThirdPartyImports ?? false) !==\n (desired.context.allowThirdPartyImports ?? false)\n ) {\n changedFields.push('allowThirdPartyImports');\n }\n if (contextPayload?.timeoutMs !== desired.context.timeoutMs) {\n changedFields.push('timeoutMs');\n }\n const existingEnvNames = Object.keys(contextPayload?.userDefinedEncryptedEnv ?? {}).sort();\n const desiredEnvNames = Object.keys(desired.context.userDefinedEnv).sort();\n if (JSON.stringify(existingEnvNames) !== JSON.stringify(desiredEnvNames)) {\n changedFields.push('env');\n }\n\n return { changed: changedFields.length > 0, changedFields };\n}\n","import { gql } from 'graphql-request';\n\nexport const CUSTOM_FUNCTIONS = gql`\n query TranscendCliCustomFunctions($first: Int!, $offset: Int!, $text: String) {\n customFunctions(first: $first, offset: $offset, filterBy: { text: $text }) {\n nodes {\n id\n name\n description\n type\n lifecycleState\n sombraId\n dataSiloId\n signedCodeJwt\n signedCodeContextJwt\n hasPendingDraft\n activeVersion {\n id\n versionNumber\n lifecycleState\n signedCodeJwt\n }\n draftVersion {\n id\n versionNumber\n lifecycleState\n signedCodeJwt\n }\n }\n totalCount\n }\n }\n`;\n\nexport const CREATE_CUSTOM_FUNCTION = gql`\n mutation TranscendCliCreateCustomFunction($input: CreateCustomFunctionInput!) {\n createCustomFunction(input: $input) {\n customFunction {\n id\n name\n activeVersion {\n id\n versionNumber\n }\n draftVersion {\n id\n versionNumber\n }\n }\n success\n }\n }\n`;\n\nexport const UPDATE_STANDALONE_CUSTOM_FUNCTION = gql`\n mutation TranscendCliUpdateStandaloneCustomFunction(\n $input: UpdateStandaloneCustomFunctionInput!\n ) {\n updateStandaloneCustomFunction(input: $input) {\n customFunction {\n id\n name\n hasPendingDraft\n activeVersion {\n id\n versionNumber\n }\n draftVersion {\n id\n versionNumber\n }\n }\n success\n }\n }\n`;\n\nexport const CREATE_CUSTOM_FUNCTION_DATA_SILO = gql`\n mutation TranscendCliCreateCustomFunctionDataSilo($input: [CreateDataSilosInput!]!) {\n createDataSilos(input: $input) {\n dataSilos {\n id\n title\n }\n }\n }\n`;\n\nexport const DELETE_DATA_SILOS = gql`\n mutation TranscendCliDeleteDataSilos($input: DeleteDataSilosInput!) {\n deleteDataSilos(input: $input) {\n clientMutationId\n }\n }\n`;\n\nexport const RUN_CUSTOM_FUNCTION = gql`\n mutation TranscendCliRunCustomFunction($input: RunCustomFunctionInput!) {\n runCustomFunction(input: $input) {\n result {\n exitCode\n error {\n message\n stack\n }\n logs {\n message\n file\n }\n profile {\n timeMs\n }\n }\n }\n }\n`;\n\nexport const PROMOTE_CUSTOM_FUNCTION_VERSION = gql`\n mutation TranscendCliPromoteCustomFunctionVersion($input: PromoteCustomFunctionVersionInput!) {\n promoteCustomFunctionVersion(input: $input) {\n customFunction {\n id\n activeVersion {\n id\n versionNumber\n }\n }\n dependencyWarnings {\n dependencyType\n dependencyTitle\n message\n }\n success\n }\n }\n`;\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { CREATE_CUSTOM_FUNCTION_DATA_SILO, DELETE_DATA_SILOS } from './gqls/index.js';\n\n/**\n * The integration/catalog name for custom function DSR integrations. Silos\n * created with this catalog get `customSiloConnectionStrategy =\n * CUSTOM_FUNCTION` and start `NOT_CONFIGURED` until a custom function is\n * attached — the same shell the Admin Dashboard creates for a new DSR\n * function.\n */\nexport const CUSTOM_FUNCTION_INTEGRATION_NAME = 'customFunction';\n\n/**\n * A newly created custom function data silo (DSR integration).\n */\nexport interface CustomFunctionDataSilo {\n /** Data silo ID */\n id: string;\n /** Data silo title */\n title: string;\n}\n\n/**\n * Create the data silo (DSR integration) backing a new DSR custom function.\n *\n * The silo is an inert `customFunction`-catalog shell: it has no function\n * attached yet and stays `NOT_CONFIGURED` until `createCustomFunction` links\n * one (which flips it to `Connected`). The `customFunction` catalog requires\n * a Sombra gateway on create — the function's code runs on that gateway.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param options - Options\n * @returns The created data silo\n */\nexport async function createCustomFunctionDataSilo(\n client: GraphQLClient,\n options: {\n /** Title of the integration (conventionally the custom function name) */\n title: string;\n /** The Sombra gateway the function belongs to */\n sombraId: string;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CustomFunctionDataSilo> {\n const { title, sombraId, logger = NOOP_LOGGER } = options;\n const {\n createDataSilos: { dataSilos },\n } = await makeGraphQLRequest<{\n /** Mutation response */\n createDataSilos: {\n /** The created data silos */\n dataSilos: CustomFunctionDataSilo[];\n };\n }>(client, CREATE_CUSTOM_FUNCTION_DATA_SILO, {\n variables: {\n input: [\n {\n name: CUSTOM_FUNCTION_INTEGRATION_NAME,\n title,\n sombraId,\n },\n ],\n },\n logger,\n });\n const [dataSilo] = dataSilos;\n if (!dataSilo) {\n throw new Error(`Failed to create a custom function data silo titled \"${title}\".`);\n }\n return dataSilo;\n}\n\n/**\n * Delete a data silo. Used to roll back a just-created custom function data\n * silo when the function's test run fails before anything was linked to it.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param dataSiloId - The data silo ID to delete\n * @param options - Options\n */\nexport async function deleteDataSilo(\n client: GraphQLClient,\n dataSiloId: string,\n options: {\n /** Logger instance */\n logger?: Logger;\n } = {},\n): Promise<void> {\n const { logger = NOOP_LOGGER } = options;\n await makeGraphQLRequest(client, DELETE_DATA_SILOS, {\n variables: {\n input: { ids: [dataSiloId] },\n },\n logger,\n });\n}\n","import type {\n CustomFunctionLifecycleState,\n CustomFunctionType,\n CustomFunctionVersionLifecycleState,\n} from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { CUSTOM_FUNCTIONS } from './gqls/index.js';\n\n/**\n * Preview of a custom function version.\n */\nexport interface CustomFunctionVersionPreview {\n /** Version ID */\n id: string;\n /** Monotonic version number (e.g. \"1.0\") */\n versionNumber: string;\n /** Lifecycle state of the version */\n lifecycleState: CustomFunctionVersionLifecycleState;\n /** Signed code JWT for this specific version's code */\n signedCodeJwt: string;\n}\n\n/**\n * A custom function as returned by the `customFunctions` query.\n */\nexport interface CustomFunction {\n /** Custom function ID */\n id: string;\n /** Display name */\n name: string;\n /** Description */\n description?: string | null;\n /** Product-facing type */\n type: CustomFunctionType;\n /** Lifecycle state */\n lifecycleState: CustomFunctionLifecycleState;\n /** ID of the Sombra gateway the function belongs to (signs its code and runs it) */\n sombraId?: string | null;\n /** ID of the data silo (DSR integration) the function is linked to, for DSR functions */\n dataSiloId?: string | null;\n /** Signed code JWT of the preferred (draft if pending, else active) version */\n signedCodeJwt: string;\n /** Signed code context JWT of the preferred version */\n signedCodeContextJwt: string;\n /** Whether a draft version is newer than the current active version */\n hasPendingDraft: boolean;\n /** Current active version metadata */\n activeVersion?: CustomFunctionVersionPreview | null;\n /** Pending draft version metadata */\n draftVersion?: CustomFunctionVersionPreview | null;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all custom functions in the organization\n *\n * @param client - GraphQL client\n * @param options - Options\n * @returns All custom functions in the organization\n */\nexport async function fetchAllCustomFunctions(\n client: GraphQLClient,\n options: {\n /** Logger instance */\n logger?: Logger;\n /** Filter options */\n filterBy?: {\n /** Filter by text match on name or description */\n text?: string;\n };\n } = {},\n): Promise<CustomFunction[]> {\n const { logger = NOOP_LOGGER, filterBy } = options;\n const { text } = filterBy ?? {};\n const customFunctions: CustomFunction[] = [];\n let offset = 0;\n\n let shouldContinue = false;\n do {\n const {\n customFunctions: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n customFunctions: {\n /** List of matches */\n nodes: CustomFunction[];\n };\n }>(client, CUSTOM_FUNCTIONS, {\n variables: { first: PAGE_SIZE, offset, text },\n logger,\n });\n customFunctions.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return customFunctions.sort((a, b) => a.name.localeCompare(b.name));\n}\n","/**\n * Inject the resolved data silo into a DSR test payload.\n *\n * The backend's unsaved-DSR test path resolves the execution Sombra from\n * `extras.dataSilo.id`, so the payload must reference the function's actual\n * data silo — which the payload file cannot know for silos created during\n * the same push. The silo `id` is always overridden; other `extras.dataSilo`\n * fields from the payload file are preserved. The backend validates DSR\n * payloads against the full webhook notification codec, which requires\n * `title`, `description`, and `link` on the data silo — those are defaulted\n * when the payload file omits them.\n *\n * @param payload - The DSR test payload from the manifest\n * @param dataSilo - The resolved data silo\n * @returns The payload with `extras.dataSilo` pointing at the resolved silo\n */\nexport function injectDataSiloIntoDsrTestPayload(\n payload: object,\n dataSilo: {\n /** Data silo ID */\n id: string;\n /** Fallback title when the payload does not carry one */\n title: string;\n },\n): object {\n const record = payload as {\n /** DSR payload extras */\n extras?: Record<string, unknown>;\n };\n const extras = record.extras ?? {};\n const existingDataSilo = (extras['dataSilo'] ?? {}) as Record<string, unknown>;\n return {\n ...record,\n extras: {\n ...extras,\n dataSilo: {\n title: dataSilo.title,\n description: '',\n link: '',\n ...existingDataSilo,\n id: dataSilo.id,\n },\n },\n };\n}\n","import type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { ORGANIZATION } from '../api/gqls/organization.js';\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport type { CustomFunction } from './fetchAllCustomFunctions.js';\nimport type { CustomFunctionConfigInput } from './syncCustomFunction.js';\n\n/**\n * Resolve which Sombra gateway a custom function's code must be signed\n * against.\n *\n * Each custom function belongs to a single gateway, whose keys sign the code\n * and encrypt the env values — signing against any other gateway would\n * produce JWTs that fail verification at execution time. The gateway is\n * resolved as: the config's `sombraId`, else the existing function's\n * gateway, else the caller's default (e.g. a CLI flag), else `undefined`\n * meaning the organization's primary Sombra.\n *\n * A config that pins a different gateway than the existing function is an\n * error — the gateway of an existing function cannot be changed by a push.\n *\n * @param input - The custom function config\n * @param existing - The matching existing custom function, when there is one\n * @param defaultSombraId - Fallback gateway ID when neither the config nor the existing function specify one\n * @returns The Sombra gateway ID to sign against, or undefined for the primary gateway\n */\nexport function resolveEffectiveSombraId(\n input: Pick<CustomFunctionConfigInput, 'name' | 'sombraId'>,\n existing: Pick<CustomFunction, 'id' | 'sombraId'> | undefined,\n defaultSombraId?: string,\n): string | undefined {\n if (input.sombraId && existing?.sombraId && input.sombraId !== existing.sombraId) {\n throw new Error(\n `Custom function \"${input.name}\" specifies sombra-id \"${input.sombraId}\" but the ` +\n `existing function (id: ${existing.id}) belongs to Sombra gateway \"${existing.sombraId}\". ` +\n 'A push cannot move a custom function between gateways — fix the sombra-id in the ' +\n 'manifest, or remove it to keep the existing gateway.',\n );\n }\n return input.sombraId ?? existing?.sombraId ?? defaultSombraId;\n}\n\n/**\n * Resolve the organization's primary Sombra gateway ID.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param logger - Logger instance\n * @returns The primary Sombra ID\n */\nexport async function resolvePrimarySombraId(\n client: GraphQLClient,\n logger: Logger = NOOP_LOGGER,\n): Promise<string> {\n const { organization } = await makeGraphQLRequest<{\n /** Organization query response */\n organization: {\n /** Primary Sombra gateway */\n sombra: {\n /** Sombra ID */\n id: string;\n } | null;\n };\n }>(client, ORGANIZATION, { logger });\n if (!organization.sombra?.id) {\n throw new Error(\n 'Could not resolve the primary Sombra gateway of the organization, which is ' +\n 'required to create a custom function. Specify a sombra-id on the manifest ' +\n 'entry instead.',\n );\n }\n return organization.sombra.id;\n}\n","import type { CustomFunction } from './fetchAllCustomFunctions.js';\nimport type { CustomFunctionConfigInput } from './syncCustomFunction.js';\n\n/**\n * Resolve which existing custom function a config refers to.\n *\n * When the config has an `id`, it must match an existing function. Otherwise\n * the function is matched by exact name; an ambiguous name (multiple existing\n * functions with the same name) is an error that the caller should resolve by\n * adding an `id` to the config.\n *\n * @param existing - All existing custom functions in the organization\n * @param input - The custom function config\n * @returns The matching custom function, or undefined when it should be created\n */\nexport function resolveExistingCustomFunction(\n existing: CustomFunction[],\n input: Pick<CustomFunctionConfigInput, 'id' | 'name'>,\n): CustomFunction | undefined {\n if (input.id) {\n const match = existing.find(({ id }) => id === input.id);\n if (!match) {\n throw new Error(\n `Custom function \"${input.name}\" specifies id \"${input.id}\" but no custom function ` +\n 'with that ID exists in the organization. Remove the id to create a new function, ' +\n 'or fix the ID.',\n );\n }\n return match;\n }\n\n const matches = existing.filter(({ name }) => name === input.name);\n if (matches.length > 1) {\n throw new Error(\n `Multiple custom functions are named \"${input.name}\" ` +\n `(ids: ${matches.map(({ id }) => id).join(', ')}). ` +\n 'Add an `id` field to this manifest entry to disambiguate which one to update.',\n );\n }\n return matches[0];\n}\n","import type { CustomFunctionPayloadType, CustomFunctionType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { RUN_CUSTOM_FUNCTION } from './gqls/index.js';\n\n/**\n * A log line emitted while executing a custom function.\n */\nexport interface CustomFunctionExecutionLog {\n /** The logged message */\n message: string;\n /** The stream/file the message was written to (e.g. stdout, stderr) */\n file: string;\n}\n\n/**\n * The error a custom function execution failed with, when it failed.\n */\nexport interface CustomFunctionExecutionError {\n /** Error message */\n message: string;\n /** Stack trace, when available */\n stack?: string | null;\n}\n\n/**\n * The result of executing a custom function via the `runCustomFunction`\n * mutation.\n */\nexport interface CustomFunctionExecutionResult {\n /** Process exit code (negative values are internal \"success with warnings\" codes) */\n exitCode: number;\n /** The error the execution failed with, when it failed */\n error?: CustomFunctionExecutionError | null;\n /** Log lines emitted during execution */\n logs: CustomFunctionExecutionLog[];\n /** Execution profile */\n profile: {\n /** Wall-clock execution time in milliseconds */\n timeMs: number;\n };\n}\n\n/**\n * The outcome of test-running a custom function.\n */\nexport interface CustomFunctionTestRunResult {\n /** Whether the test passed (no error and a non-failure exit code) */\n passed: boolean;\n /** The raw execution result (error, logs, exit code, timing) */\n result: CustomFunctionExecutionResult;\n}\n\n/**\n * A single test payload to run a custom function with before pushing.\n */\nexport interface CustomFunctionTestPayload {\n /** The JSON test payload */\n payload: object;\n /**\n * Which export the payload invokes for DSR functions (`DATA_POINT` →\n * `default`, `REQUEST_ENRICHER` → `enricher`). Defaults to DATA_POINT.\n * GENERAL functions are always run as Maestro payloads and ignore this\n */\n payloadType?: CustomFunctionPayloadType;\n}\n\n/**\n * Whether an execution result counts as a passing test.\n *\n * Mirrors the Admin Dashboard's function editor: a run passes when it\n * produced no error and exited with a code <= 0 (negative codes are internal\n * success-with-metadata codes).\n *\n * @param result - The execution result\n * @returns True when the run passed\n */\nexport function didCustomFunctionTestPass(\n result: Pick<CustomFunctionExecutionResult, 'error' | 'exitCode'>,\n): boolean {\n return !result.error && result.exitCode <= 0;\n}\n\n/**\n * Test-run custom function code that has been signed but not yet saved.\n *\n * Sends the pre-signed code/context JWT pair to the `runCustomFunction`\n * mutation as a test run (`isCustomFunctionTestRun: true`). The backend\n * verifies JWT provenance against the target Sombra gateway, executes the\n * code on that gateway, and returns the execution result — nothing is\n * persisted.\n *\n * - GENERAL functions run against `sombraId` (or the organization's primary\n * Sombra) with the payload validated as a Maestro payload.\n * - DSR functions run against the Sombra of the data silo referenced by\n * `extras.dataSilo.id` in the payload; `payloadType` selects which export\n * is invoked (`DATA_POINT` → `default`, `REQUEST_ENRICHER` → `enricher`).\n *\n * Requires backend support for pre-signed JWTs on `runCustomFunction`; older\n * backends reject the JWT fields with a friendly upgrade error.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param options - Options\n * @returns The execution result plus a `passed` boolean\n */\nexport async function runCustomFunctionTest(\n client: GraphQLClient,\n options: {\n /** Product-facing type of the custom function under test */\n type: CustomFunctionType;\n /** Pre-signed code JWT from the Sombra customer-ingress `/v1/custom/sign` route */\n signedCodeJwt: string;\n /** Pre-signed code context JWT from the same sign call */\n signedCodeContextJwt: string;\n /** The JSON test payload to invoke the function with (base64-encoded on the wire) */\n payload: object;\n /** Dedicated Sombra gateway ID (GENERAL only; DSR derives it from the payload's data silo) */\n sombraId?: string;\n /**\n * Which export to invoke for DSR functions (`DATA_POINT` → `default`,\n * `REQUEST_ENRICHER` → `enricher`). Defaults to DATA_POINT. GENERAL\n * functions are always run as Maestro payloads and ignore this setting\n */\n payloadType?: CustomFunctionPayloadType;\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CustomFunctionTestRunResult> {\n const {\n type,\n signedCodeJwt,\n signedCodeContextJwt,\n payload,\n sombraId,\n payloadType,\n logger = NOOP_LOGGER,\n } = options;\n\n let response: {\n /** Mutation response */\n runCustomFunction: {\n /** The execution result */\n result: CustomFunctionExecutionResult;\n };\n };\n try {\n response = await makeGraphQLRequest<typeof response>(client, RUN_CUSTOM_FUNCTION, {\n variables: {\n input: {\n type,\n isCustomFunctionTestRun: true,\n payload: Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64'),\n signedCodeJwt,\n signedCodeContextJwt,\n ...(sombraId !== undefined ? { sombraId } : {}),\n ...(payloadType !== undefined ? { payloadType } : {}),\n },\n },\n logger,\n });\n } catch (err) {\n const message = (err as Error).message ?? '';\n // Older backends don't know the pre-signed JWT fields on RunCustomFunctionInput\n if (\n /signedCodeJwt|signedCodeContextJwt/.test(message) &&\n /is not defined|Unknown argument|got invalid value/i.test(message)\n ) {\n throw new Error(\n 'The Transcend backend does not support test-running custom functions from ' +\n 'pre-signed JWTs yet. Re-run with tests skipped, or contact Transcend support. ' +\n `Underlying error: ${message}`,\n );\n }\n throw err;\n }\n\n const { result } = response.runCustomFunction;\n return {\n passed: didCustomFunctionTestPass(result),\n result,\n };\n}\n","import type { Got } from 'got';\n\nimport type { CustomFunctionSignPayload } from './codeSigning.js';\n\n/**\n * The signed JWT pair returned by Sombra's customer-ingress\n * `/v1/custom/sign` endpoint.\n */\nexport interface SignedCustomFunctionJwts {\n /** HMAC-signed JWT wrapping the function code */\n signedCodeJwt: string;\n /** HMAC-signed JWT wrapping the execution context (env values encrypted at sign time) */\n signedCodeContextJwt: string;\n}\n\n/**\n * Sign custom function code against the Sombra customer-ingress\n * `/v1/custom/sign` route.\n *\n * The route is authenticated by the sombra instance's bearer headers (the\n * Transcend API key, plus the Sombra internal key when self-hosting — see\n * `createSombraGotInstance`). Code and env values travel to the customer's\n * own Sombra gateway over TLS and never reach Transcend's backend in\n * plaintext.\n *\n * @param sombra - Got instance authenticated against the Sombra customer ingress\n * @param payload - The plaintext code and execution context to sign\n * @param options - Options\n * @returns The signed code and context JWTs to store via the GraphQL API\n */\nexport async function signCustomFunctionCode(\n sombra: Got,\n payload: CustomFunctionSignPayload,\n options: {\n /** Existing custom function ID, forwarded for Sombra log context */\n customFunctionId?: string;\n } = {},\n): Promise<SignedCustomFunctionJwts> {\n const { customFunctionId } = options;\n try {\n return await sombra\n .post('v1/custom/sign', {\n json: {\n code: payload.code,\n context: payload.context,\n ...(customFunctionId ? { customFunction: { id: customFunctionId } } : {}),\n },\n })\n .json<SignedCustomFunctionJwts>();\n } catch (err) {\n const statusCode = (err as { response?: { statusCode?: number } })?.response?.statusCode;\n if (statusCode === 404) {\n throw new Error(\n 'The Sombra gateway does not support the /v1/custom/sign route. ' +\n 'Upgrade your Sombra gateway to a version that supports custom function code signing.',\n );\n }\n throw err;\n }\n}\n","import { CustomFunctionType } from '@transcend-io/privacy-types';\nimport type { Logger } from '@transcend-io/utils';\nimport type { Got } from 'got';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { makeGraphQLRequest, NOOP_LOGGER } from '../api/makeGraphQLRequest.js';\nimport { buildCustomFunctionSignPayload } from './buildCustomFunctionSignPayload.js';\nimport { diffCustomFunctionCode } from './codeSigning.js';\nimport { createCustomFunctionDataSilo, deleteDataSilo } from './customFunctionDataSilo.js';\nimport type { CustomFunction } from './fetchAllCustomFunctions.js';\nimport {\n CREATE_CUSTOM_FUNCTION,\n PROMOTE_CUSTOM_FUNCTION_VERSION,\n UPDATE_STANDALONE_CUSTOM_FUNCTION,\n} from './gqls/index.js';\nimport { injectDataSiloIntoDsrTestPayload } from './injectDataSiloIntoDsrTestPayload.js';\nimport { resolveEffectiveSombraId, resolvePrimarySombraId } from './resolveEffectiveSombraId.js';\nimport { resolveExistingCustomFunction } from './resolveExistingCustomFunction.js';\nimport {\n runCustomFunctionTest,\n type CustomFunctionTestPayload,\n type CustomFunctionTestRunResult,\n} from './runCustomFunctionTest.js';\nimport { signCustomFunctionCode } from './signCustomFunctionCode.js';\n\n/**\n * The desired state of a custom function to sync to Transcend.\n */\nexport interface CustomFunctionConfigInput {\n /**\n * Custom function ID to update. When set, this is the sync key and the\n * function must already exist. When omitted, the function is matched by\n * exact name — which fails if multiple functions share the name.\n */\n id?: string;\n /** Display name of the custom function (the sync key when no `id` is set) */\n name: string;\n /** The plaintext TypeScript source code */\n code: string;\n /** Description of the custom function */\n description?: string;\n /** Product-facing type. Defaults to GENERAL */\n type?: CustomFunctionType;\n /** Data silo ID the function is attached to (required for DSR functions) */\n dataSiloId?: string;\n /** Dedicated Sombra gateway ID (defaults to the organization's primary Sombra) */\n sombraId?: string;\n /** Hosts the function is allowed to make network requests to */\n allowedHosts?: string[];\n /** Whether the function may import third party modules */\n allowThirdPartyImports?: boolean;\n /** Execution timeout in milliseconds */\n timeoutMs?: number;\n /** Environment variables to expose to the function */\n env?: Record<string, string>;\n}\n\n/**\n * The outcome of syncing a single custom function.\n */\nexport type CustomFunctionSyncOutcome =\n /** A new custom function was created */\n | 'created'\n /** A new revision was pushed to an existing custom function */\n | 'updated'\n /** Only metadata (name/description) changed; updated without a new code revision */\n | 'metadata-updated'\n /** No changes detected; nothing was pushed */\n | 'skipped'\n /** The test run failed; nothing was pushed */\n | 'test-failed'\n /** Dry run: a new custom function would be created */\n | 'would-create'\n /** Dry run: a new revision would be pushed */\n | 'would-update';\n\n/**\n * The result of running one test payload during a sync, tagged with the\n * export the payload invoked.\n */\nexport interface CustomFunctionSyncTestResult extends CustomFunctionTestRunResult {\n /** Which export the payload invoked (DSR functions; DATA_POINT when omitted) */\n payloadType?: CustomFunctionTestPayload['payloadType'];\n}\n\n/**\n * The result of syncing a single custom function.\n */\nexport interface CustomFunctionSyncResult {\n /** The sync outcome */\n outcome: CustomFunctionSyncOutcome;\n /** The custom function ID (when it exists or was created) */\n customFunctionId?: string;\n /** The version number that is now active or drafted (when a revision was pushed) */\n versionNumber?: string;\n /** Fields that changed, driving the update (empty on create/skip) */\n changedFields: string[];\n /** Whether the pushed revision was promoted to active */\n promoted: boolean;\n /** The test-run results (one per payload), when the code was tested */\n testResults?: CustomFunctionSyncTestResult[];\n /** The data silo (DSR integration) the function is linked to, for DSR functions */\n dataSiloId?: string;\n /**\n * Whether a new data silo (DSR integration) was created during this sync.\n * On a `test-failed` outcome this means the auto-created silo was rolled\n * back (deleted) — `dataSiloId` is absent in that case.\n */\n createdDataSilo?: boolean;\n}\n\n/**\n * Sync a custom function definition (metadata + code revision) to Transcend.\n *\n * - When no custom function with the given name exists, one is created.\n * - A new DSR function without a `dataSiloId` also gets its DSR integration\n * created: a `customFunction`-catalog data silo shell is created first (the\n * backend needs it to exist for the test run), the code is tested against\n * it, and on a passing test the function is created and linked. A failing\n * test rolls the silo back (deletes it).\n * - When one exists and the code/context changed, a new draft revision is\n * created and (unless `promote` is false) promoted to active.\n * - When only metadata changed (description, or name for id-pinned entries),\n * the function record is updated in place — no signing, no test runs, and\n * no new code revision.\n * - When nothing changed, the function is skipped (unless `force` is set).\n *\n * Code is signed against the Sombra customer-ingress `/v1/custom/sign` route\n * before being saved via the GraphQL API, so plaintext code and env values\n * never reach Transcend's backend.\n *\n * Environment variable values are encrypted server-side and cannot be diffed;\n * use `force` to re-push when only env values changed.\n *\n * @param client - GraphQL client authenticated with a Transcend API key\n * @param options - Options\n * @returns The sync result\n */\nexport async function syncCustomFunction(\n client: GraphQLClient,\n options: {\n /** The desired custom function state */\n input: CustomFunctionConfigInput;\n /**\n * Got instance authenticated against the customer ingress of the Sombra\n * gateway the function belongs to (see `resolveEffectiveSombraId`).\n * Required unless `dryRun` is set\n */\n sombra?: Got;\n /** Default Sombra gateway ID when neither the config nor the existing function specify one */\n defaultSombraId?: string;\n /** All existing custom functions in the organization (see `fetchAllCustomFunctions`) */\n existing: CustomFunction[];\n /** Whether to promote new revisions to active. Defaults to true */\n promote?: boolean;\n /** When true, report what would happen without mutating anything */\n dryRun?: boolean;\n /** When true, push a new revision even if no changes were detected */\n force?: boolean;\n /**\n * JSON test payloads to run the freshly signed code with before pushing.\n * Every payload is run via the `runCustomFunction` mutation (DSR\n * functions can cover both the default and enricher exports via\n * `payloadType`) and all must pass — any failure rejects the push\n * (outcome `test-failed`). Skipped on dry runs (nothing is signed).\n */\n testPayloads?: CustomFunctionTestPayload[];\n /** Logger instance */\n logger?: Logger;\n },\n): Promise<CustomFunctionSyncResult> {\n const {\n input,\n sombra,\n defaultSombraId,\n existing: allExisting,\n promote = true,\n dryRun = false,\n force = false,\n testPayloads,\n logger = NOOP_LOGGER,\n } = options;\n const type: CustomFunctionType = input.type ?? CustomFunctionType.General;\n\n const existing = resolveExistingCustomFunction(allExisting, input);\n // Validates config-vs-existing gateway mismatches, including on dry runs\n const effectiveSombraId = resolveEffectiveSombraId(input, existing, defaultSombraId);\n const signPayload = buildCustomFunctionSignPayload(input);\n\n // Diff against the existing preferred (draft if pending, else active) version\n let changedFields: string[] = [];\n let metadataOnly = false;\n if (existing) {\n const diff = diffCustomFunctionCode(signPayload, {\n signedCodeJwt: existing.signedCodeJwt,\n signedCodeContextJwt: existing.signedCodeContextJwt,\n });\n changedFields = diff.changedFields;\n if (!diff.changed && !force) {\n // Code and context are identical — check the plaintext metadata on the\n // function record. A rename can only surface for id-pinned entries\n // (name is the sync key otherwise), and a manifest without a\n // description leaves the existing one unmanaged.\n const metadataChanges: string[] = [];\n if (input.name !== existing.name) {\n metadataChanges.push('name');\n }\n if (input.description !== undefined && input.description !== (existing.description ?? '')) {\n metadataChanges.push('description');\n }\n if (metadataChanges.length === 0) {\n logger.info(`No changes detected for custom function \"${input.name}\" — skipping.`);\n return {\n outcome: 'skipped',\n customFunctionId: existing.id,\n changedFields: [],\n promoted: false,\n ...(existing.dataSiloId ? { dataSiloId: existing.dataSiloId } : {}),\n };\n }\n metadataOnly = true;\n changedFields = metadataChanges;\n }\n }\n\n if (dryRun) {\n return {\n outcome: existing ? 'would-update' : 'would-create',\n ...(existing ? { customFunctionId: existing.id } : {}),\n changedFields,\n promoted: false,\n };\n }\n\n // Metadata-only changes update the function record in place: no signing,\n // no test runs, no new code revision — the active version keeps running\n // byte-identical code.\n if (existing && metadataOnly) {\n logger.info(\n `Updating metadata (${changedFields.join(', ')}) for custom function ` +\n `\"${input.name}\" — code is unchanged, no new revision.`,\n );\n await makeGraphQLRequest(client, UPDATE_STANDALONE_CUSTOM_FUNCTION, {\n variables: {\n input: {\n id: existing.id,\n name: input.name,\n ...(input.description !== undefined ? { description: input.description } : {}),\n },\n },\n logger,\n });\n return {\n outcome: 'metadata-updated',\n customFunctionId: existing.id,\n changedFields,\n promoted: false,\n ...(existing.dataSiloId ? { dataSiloId: existing.dataSiloId } : {}),\n };\n }\n\n if (!sombra) {\n throw new Error('A Sombra customer-ingress client is required to push custom function code.');\n }\n\n // Sign the code and context against the Sombra gateway; the resulting JWTs\n // are stored via the GraphQL API\n const { signedCodeJwt, signedCodeContextJwt } = await signCustomFunctionCode(\n sombra,\n signPayload,\n { customFunctionId: existing?.id },\n );\n\n // Resolve the data silo (DSR integration) backing a DSR function: the\n // config's, else the existing function's. A brand-new DSR function without\n // one gets its integration created here — an inert `customFunction`-catalog\n // shell that stays NOT_CONFIGURED until the function is linked to it. It\n // must exist before the test run, because the backend resolves the\n // execution Sombra from the payload's `extras.dataSilo.id`.\n let dataSiloId = input.dataSiloId ?? existing?.dataSiloId ?? undefined;\n let createdDataSilo = false;\n if (type === CustomFunctionType.Dsr && !existing && dataSiloId === undefined) {\n const siloSombraId = effectiveSombraId ?? (await resolvePrimarySombraId(client, logger));\n logger.info(`Creating DSR integration (data silo) for custom function \"${input.name}\"...`);\n const dataSilo = await createCustomFunctionDataSilo(client, {\n title: input.name,\n sombraId: siloSombraId,\n logger,\n });\n dataSiloId = dataSilo.id;\n createdDataSilo = true;\n }\n\n // Test the freshly signed code before pushing anything. Every payload runs\n // (so one CI run reports every failing export) and all must pass — any\n // failure rejects the push so a broken revision never reaches (or is\n // promoted on) the function.\n let testResults: CustomFunctionSyncTestResult[] | undefined;\n if (testPayloads !== undefined && testPayloads.length > 0) {\n logger.info(\n `Testing custom function \"${input.name}\" before push ` +\n `(${testPayloads.length} payload${testPayloads.length === 1 ? '' : 's'})...`,\n );\n testResults = [];\n for (const { payload, payloadType } of testPayloads) {\n const run: CustomFunctionTestRunResult = await runCustomFunctionTest(client, {\n type,\n signedCodeJwt,\n signedCodeContextJwt,\n // DSR payloads must reference the function's actual data silo — the\n // backend derives the execution gateway from `extras.dataSilo.id`\n payload:\n type === CustomFunctionType.Dsr && dataSiloId !== undefined\n ? injectDataSiloIntoDsrTestPayload(payload, {\n id: dataSiloId,\n title: input.name,\n })\n : payload,\n // GENERAL runs on the function's gateway\n ...(type === CustomFunctionType.General && effectiveSombraId !== undefined\n ? { sombraId: effectiveSombraId }\n : {}),\n ...(type === CustomFunctionType.Dsr && payloadType !== undefined ? { payloadType } : {}),\n logger,\n });\n testResults.push({ ...run, ...(payloadType !== undefined ? { payloadType } : {}) });\n }\n if (testResults.some(({ passed }) => !passed)) {\n // Roll back the integration created for this function — nothing was\n // linked to it yet, so a failed test leaves no trace behind\n if (createdDataSilo && dataSiloId !== undefined) {\n logger.info(\n `Rolling back DSR integration (data silo ${dataSiloId}) for \"${input.name}\" — test failed.`,\n );\n await deleteDataSilo(client, dataSiloId, { logger });\n dataSiloId = undefined;\n }\n return {\n outcome: 'test-failed',\n ...(existing ? { customFunctionId: existing.id } : {}),\n changedFields,\n promoted: false,\n testResults,\n ...(createdDataSilo ? { createdDataSilo } : {}),\n };\n }\n }\n\n // Create fresh\n if (!existing) {\n // The backend requires an explicit gateway for GENERAL functions (and\n // rejects one for DSR functions, whose data silo dictates the gateway)\n const createSombraId =\n type === CustomFunctionType.General\n ? (effectiveSombraId ?? (await resolvePrimarySombraId(client, logger)))\n : undefined;\n // DSR functions are always created active — the backend rejects setActive\n if (type === CustomFunctionType.Dsr && !promote) {\n logger.warn(\n `DSR custom functions are always created active — \"${input.name}\" ` +\n 'will be created promoted despite promote being disabled.',\n );\n }\n let response: {\n /** Mutation response */\n createCustomFunction: {\n /** The created custom function */\n customFunction: {\n /** Custom function ID */\n id: string;\n /** Active version, when created active */\n activeVersion?: {\n /** Version ID */\n id: string;\n /** Version number */\n versionNumber: string;\n } | null;\n /** Draft version, when created inactive */\n draftVersion?: {\n /** Version ID */\n id: string;\n /** Version number */\n versionNumber: string;\n } | null;\n };\n };\n };\n try {\n response = await makeGraphQLRequest<typeof response>(client, CREATE_CUSTOM_FUNCTION, {\n variables: {\n input: {\n type,\n ...(createSombraId !== undefined ? { sombraId: createSombraId } : {}),\n ...(type === CustomFunctionType.Dsr ? { dataSiloId } : {}),\n name: input.name,\n ...(input.description !== undefined ? { description: input.description } : {}),\n ...(type === CustomFunctionType.General ? { setActive: promote } : {}),\n signedCodeJwt,\n signedCodeContextJwt,\n },\n },\n logger,\n });\n } catch (err) {\n // Roll back the integration created for this function — nothing was\n // linked to it yet, so a failed create must not strand an orphaned silo\n if (createdDataSilo && dataSiloId !== undefined) {\n logger.warn(\n `Rolling back DSR integration (data silo ${dataSiloId}) for ` +\n `\"${input.name}\" — creating the custom function failed.`,\n );\n await deleteDataSilo(client, dataSiloId, { logger });\n }\n throw err;\n }\n const { customFunction } = response.createCustomFunction;\n const version = customFunction.activeVersion ?? customFunction.draftVersion;\n return {\n outcome: 'created',\n customFunctionId: customFunction.id,\n ...(version ? { versionNumber: version.versionNumber } : {}),\n changedFields,\n promoted: type === CustomFunctionType.Dsr ? true : promote,\n ...(testResults ? { testResults } : {}),\n ...(dataSiloId !== undefined ? { dataSiloId } : {}),\n ...(createdDataSilo ? { createdDataSilo } : {}),\n };\n }\n\n // Push a new draft revision to the existing function\n const {\n updateStandaloneCustomFunction: { customFunction: updated },\n } = await makeGraphQLRequest<{\n /** Mutation response */\n updateStandaloneCustomFunction: {\n /** The updated custom function */\n customFunction: {\n /** Custom function ID */\n id: string;\n /** The new draft version */\n draftVersion?: {\n /** Version ID */\n id: string;\n /** Version number */\n versionNumber: string;\n } | null;\n };\n };\n }>(client, UPDATE_STANDALONE_CUSTOM_FUNCTION, {\n variables: {\n input: {\n id: existing.id,\n name: input.name,\n ...(input.description !== undefined ? { description: input.description } : {}),\n signedCodeJwt,\n signedCodeContextJwt,\n },\n },\n logger,\n });\n\n const draft = updated.draftVersion;\n if (!draft) {\n throw new Error(\n `Expected a draft version to be created for custom function \"${input.name}\" but none was returned.`,\n );\n }\n\n if (promote) {\n await makeGraphQLRequest(client, PROMOTE_CUSTOM_FUNCTION_VERSION, {\n variables: {\n input: {\n customFunctionId: updated.id,\n versionId: draft.id,\n },\n },\n logger,\n });\n }\n\n return {\n outcome: 'updated',\n customFunctionId: updated.id,\n versionNumber: draft.versionNumber,\n changedFields,\n promoted: promote,\n ...(testResults ? { testResults } : {}),\n ...(dataSiloId !== undefined ? { dataSiloId } : {}),\n };\n}\n","import { describePackageName } from '@transcend-io/utils';\n\nexport interface MonorepoPackageDefinition {\n directory: string;\n displayName: string;\n packageName: string;\n}\n\nexport function createMonorepoPackageDefinition(\n name: string,\n directory: string,\n): MonorepoPackageDefinition {\n const packageNameParts = describePackageName(name);\n\n return {\n directory,\n displayName: packageNameParts.displayName,\n packageName: `@transcend-io/${packageNameParts.slug}`,\n };\n}\n\nexport * from './gqls/index.js';\nexport * from './api/index.js';\nexport * from './data-inventory/index.js';\nexport * from './preference-management/index.js';\nexport * from './administration/index.js';\nexport * from './consent/index.js';\nexport * from './dsr-automation/index.js';\nexport * from './ai/index.js';\nexport * from './assessments/index.js';\nexport * from './code-intelligence/index.js';\nexport * from './custom-functions/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;AAMA,MAAa,eAAe;;;;;;AAO5B,MAAa,cAAc;;;;;AAM3B,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;ACTtC,SAAgB,mCACd,cACA,SACA,SACe;AACf,QAAO,IAAI,cAAc,GAAG,aAAa,WAAW,EAClD,SAAS;EACP,GAAG;EACH,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;EAC/B,EACF,CAAC;;;;;;;;;;AAWJ,SAAgB,4BACd,cACA,MACA,SACe;AACf,QAAO,mCACL,cACA,EAAE,eAAe,UAAU,QAAQ,EACnC,QACD;;;;;ACpCH,MAAa,cAAsB;CACjC,YAAY;CACZ,YAAY;CACZ,aAAa;CACb,aAAa;CACd;AAED,MAAM,sBAAsB;AAE5B,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CAGA;CACD;;;;;;;;;AAUD,eAAsB,mBACpB,QACA,UACA,SAUY;CACZ,MAAM,EACJ,WACA,SAAS,aACT,gBACA,aAAa,wBACX;CAEJ,IAAI,aAAa;AAEjB,QAAO,KACL,KAAI;AAEF,SAAO,MADc,OAAO,QAAQ,UAAU,WAAW,eAAe;UAGjE,KAAU;AACjB,MAAI,IAAI,SAAS,SAAS,qBAAqB,CAC7C,OAAM,IAAI,MACR,uLAGD;AAGH,MAAI,aAAa,MAAM,QAAQ,IAAI,SAAS,SAAS,IAAI,CAAC,CACxD,OAAM;AAGR,MAAI,IAAI,SAAS,WAAW,kCAAkC,EAAE;GAC9D,MAAM,mBAAmB,IAAI,UAAU,SAAS,IAAI,oBAAoB;GACxE,MAAM,YAAY,mBACd,IAAI,KAAK,iBAAiB,CAAC,SAAS,oBAAG,IAAI,MAAM,EAAC,SAAS,GAAG,MAC9D,MAAO;AACX,UAAO,KAAK,wBAAwB,IAAI,QAAQ,iBAAiB,UAAU,IAAI;AAC/E,SAAM,aAAa,UAAU;;AAG/B,MAAI,cAAc,WAChB,OAAM;AAER,gBAAc;AACd,SAAO,KAAK,4BAA4B,WAAW,KAAK,WAAW,KAAK,IAAI,UAAU;;;;;ACnF5F,MAAa,eAA6B,MAAM,GAAG;;;;;;;;;;;;;EAajD;;;ACDF,MAAM,6BAA6B,CACjC,8CACA,gDACD;;;;;;;;;;AAWD,eAAsB,wBACpB,cACA,iBACA,UAYI,EAAE,EACQ;CACd,MAAM,EAAE,SAAS,aAAa,cAAc,WAAW,aAAa;CAGpE,MAAM,EAAE,iBAAiB,MAAM,mBADhB,4BAA4B,cAAc,gBAShD,EAAE,cAAc,EAAE,QAAQ,CAAC;CAEpC,IAAI;AACJ,KAAI,UAAU;EACZ,MAAM,SAAS,aAAa,QAAQ,MAAM,EAAE,SAAS,OAAO,SAAS;AACrE,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,6CAA6C,SAAS,GAAG;AAE3E,gBAAc,OAAO;OAErB,eAAc,aAAa,OAAO;CAGpC,MAAM,cAAc,aAAa;AAEjC,KAAI,CAAC,cAAc,CAAC,eAAe,2BAA2B,SAAS,YAAY,EACjF,OAAM,IAAI,MACR,gEACE,WAAW,KAAK,SAAS,KAAK,GAC/B,sLAGF;AAEH,QAAO,KAAK,iBAAiB,cAAc;AAE3C,QAAQ,IAAuB,OAAO;EACpC,WAAW;EACX,SAAS;GACP,eAAe,UAAU;GACzB,GAAI,eAAe,EAAE,0BAA0B,UAAU,gBAAgB,GAAG,EAAE;GAC/E;EACF,CAAC;;;;;;;;;;;AC9EJ,MAAa,uBAAiC;CAC5C;CACA;CACA;CACA;CACA;CAKA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,MAAM,EAAE,aAAa,CAAC;;;;;;AAO7B,MAAM,+BAAkD;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAI;;;;;;;;;AA0B3F,SAAgB,iBAAiB,KAAc,SAA0B;CACvE,MAAM,QAAQ,QAAQ,aAAa;AACnC,KAAI,qBAAqB,MAAM,MAAM,MAAM,SAAS,EAAE,CAAC,CACrD,QAAO;CAKT,MAAM,QAAQ;CAWd,MAAM,aAAa,OAAO,UAAU,cAAc,OAAO,UAAU,OAAO;AAC1E,QAAO,OAAO,eAAe,YAAY,6BAA6B,SAAS,WAAW;;;;;;;;;;;;;;AAe5F,eAAsB,mBACpB,MACA,IACA,EACE,SAAS,aACT,cAAc,IACd,cAAc,KACd,cAAc,kBACd,YACgB,EAAE,EACR;CACZ,IAAI,UAAU;AAEd,QAAO,MAAM;AACX,aAAW;AACX,MAAI;AACF,UAAO,MAAM,IAAI;WAEV,KAAU;GACjB,MAAM,MAAc,oBAAoB,IAAI;AAE5C,OAAI,EADc,UAAU,eAAe,YAAY,KAAK,IAAI,EAE9D,OAAM,IAAI,MAAM,GAAG,KAAK,gBAAgB,QAAQ,eAAe,MAAM;AAEvE,aAAU,SAAS,KAAK,IAAI;GAI5B,MAAM,QAFU,cAAc,MAAM,UAAU,KAC/B,KAAK,MAAM,KAAK,QAAQ,GAAG,YACZ;AAC9B,UAAO,KAAK,mBAAmB,QAAQ,GAAG,cAAc,EAAE,gBAAgB,MAAM,MAAM,MAAM;AAC5F,SAAM,aAAa,MAAM;;;;;;AC7H/B,MAAa,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BpC,MAAa,yBAAyB,GAAG;;;;;;;;;;;;;;;;;;;;;;AAuBzC,MAAa,2BAA2B,GAAG;;;;;;;;;ACnB3C,MAAMA,eAAY;;;;;;;AAQlB,eAAsB,yBACpB,QACA,UAGI,EAAE,EACqB;CAC3B,MAAM,EAAE,WAAW;CACnB,MAAM,mBAAqC,EAAE;CAC7C,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,kBAAkB,EAAE,YAClB,MAAM,mBAMP,QAAQ,mBAAmB;GAC5B,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,mBAAiB,KAAK,GAAG,MAAM;AAC/B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,iBAAiB,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;ACpExE,MAAa,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;AA0BtC,MAAa,2BAA2B,GAAG;;;;;;;;;;;AAY3C,MAAa,6BAA6B,GAAG;;;;;;;;;ACL7C,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,uBACpB,QACA,UAGI,EAAE,EACsB;CAC5B,MAAM,EAAE,WAAW;CACnB,MAAM,oBAAuC,EAAE;CAC/C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,mBAAmB,EAAE,YACnB,MAAM,mBAMP,QAAQ,qBAAqB;GAC9B,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,oBAAkB,KAAK,GAAG,MAAM;AAChC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,kBAAkB,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AC3EvE,MAAa,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8C9B,MAAa,mBAAmB,GAAG;;;;;;;AAUnC,MAAa,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgClC,MAAa,wBAAwB,GAAG;;;;;;;AAQxC,MAAa,+BAA+B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC/C,MAAa,8BAA8B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC9C,MAAa,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5CnC,eAAsB,sBACpB,QACA,SAeyB;CACzB,MAAM,EACJ,UAAU,EAAE,eACZ,OACA,0BACA,UACA,SAAS,gBACP;CACJ,MAAM,gBAAgC,EAAE;CAExC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB;AACE,MAAI;AACF,OAAI,MACF,QAAO,KAAK,uCAAuC,SAAS;GAE9D,MAAM,EACJ,eAAe,EAAE,YACf,MAAM,mBAMP,QAAQ,2BAA2B,+BAA+B,iBAAiB;IACpF,WAAW;KACT,OAAO;KACP,UAAU,EACR,YAAY,CAAC,YAAY,EAC1B;KACD;KACD;IACD;IACD,CAAC;AAEF,iBAAc,KAAK,GAAG,MAAM;AAC5B,aAAU;AACV,oBAAiB,MAAM,WAAW;AAElC,OAAI,MACF,QAAO,KAAK,sCAAsC,OAAO,mBAAmB,cAAc;WAErF,KAAK;AACZ,UAAO,MACL,8CAA8C,OAAO,mBAAmB,cACzE;AACD,SAAM;;QAED;AACT,QAAO,OAAO,eAAe,OAAO;;;;;;;;;;AAWtC,eAAsB,mBACpB,QACA,SAiBsC;CACtC,MAAM,EACJ,UAAU,EAAE,cACZ,OACA,UACA,mBACA,0BACA,SAAS,gBACP;CACJ,MAAM,aAA0C,EAAE;CAGlD,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;AACD,MAAI,MACF,QAAO,KAAK,oCAAoC,SAAS;EAG3D,MAAM,EACJ,YAAY,EAAE,YACZ,MAAM,mBAMP,QAAQ,aAAa;GACtB,WAAW;IACT,OAAO;IACP,UAAU,EACR,WAAW,CAAC,WAAW,EACxB;IACD;IACD;GACD;GACD,CAAC;AAEF,MAAI,MACF,QAAO,KAAK,WAAW,MAAM,OAAO,yBAAyB,SAAS;AAGxE,MAAI,CAAC,mBAAmB;AACtB,SAAM,IACJ,OACA,OAAO,SAAS;AACd,QAAI;AACF,SAAI,MACF,QAAO,KAAK,8BAA8B,KAAK,KAAK,wBAAwB,SAAS;KAGvF,MAAM,gBAAgB,MAAM,sBAAsB,QAAQ;MACxD,UAAU,EAAE,aAAa,KAAK,IAAI;MAClC,UAAU;MACV;MACA;MACA;MACD,CAAC;AACF,gBAAW,KAAK;MACd,GAAG;MACH,eAAe,cAAc,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;MAC1E,CAAC;AAEF,SAAI,MACF,QAAO,KAAK,0CAA0C,KAAK,OAAO;aAE7D,KAAK;AACZ,YAAO,MACL,uCAAuC,KAAK,KAAK,oBAAoB,SACtE;AACD,WAAM;;MAGV,EACE,aAAa,GACd,CACF;AAED,OAAI,MACF,QAAO,KAAK,+DAA+D,SAAS;;AAIxF,YAAU;AACV,mBAAiB,MAAM,WAAW;UAC3B;AACT,QAAO,WAAW,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AC/ThE,MAAa,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;AAwB7B,MAAa,mBAAmB,GAAG;;;;;;;;;;AAanC,MAAa,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFtC,MAAa,mBAAmB,GAAG;;;;;;;;;;AAWnC,MAAa,oBAAoB,GAAG;;;;;;;;;;;AAYpC,MAAa,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;AC7GpC,eAAsB,kBACpB,QACA,SAcsB;CACtB,MAAM,EACJ,QACA,UACA,MAAM,EAAE,EACR,MAAM,YACN,mBAAmB,EAAE,EACrB,SAAS,gBACP;AAEJ,QAAO,KAAK,YAAY,IAAI,WAAW,IAAI,QAAQ,IAAI,OAAO,gBAAgB;CAE9E,MAAM,YAAyB,EAAE;CACjC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,WAAW,EAAE,YACX,MAAM,mBAMP,QAAQ,KAAK;GACd,WAAW;IACT,UAAU;KACR,KAAK,IAAI,SAAS,IAAI,MAAM,KAAA;KAC5B,MAAM,iBAAiB,SAAS,IAAI,mBAAmB,KAAA;KACvD;KACD;IACD,OAAO;IACP;IACD;GACD;GACD,CAAC;AACF,YAAU,KAAK,GAAG,MAAM;AACxB,YAAU;AACV,mBAAiB,MAAM,WAAW;UAC3B;AAET,QAAO,KACL,oBAAoB,UAAU,OAAO,YACnC,IAAI,SAAS,IAAI,iBAAiB,IAAI,KAAK,IAAI,KAAK,GACrD,GACC,iBAAiB,SAAS,IAAI,8BAA8B,iBAAiB,KAAK,IAAI,KAAK,KAE9F;AAED,QAAO,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;ACnGjE,MAAa,cAA4B,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BhD;;;ACWF,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,oBACpB,QACA,UAGI,EAAE,EACiB;CACvB,MAAM,EAAE,WAAW;CACnB,MAAM,cAA4B,EAAE;CACpC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,aAAa,EAAE,YACb,MAAM,mBAMP,QAAQ,aAAa;GACtB;GACA,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACxC,CAAC;AACF,cAAY,KAAK,GAAG,MAAM;AAC1B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,YAAY,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AClFjE,MAAa,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDxC,MAAa,6BAA6B,GAAG;;;;;;;;;;AAW7C,MAAa,+BAA+B,GAAG;;;;;;;;;ACoB/C,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,6BACpB,QACA,UAGI,EAAE,EACyB;CAC/B,MAAM,EAAE,WAAW;CACnB,MAAM,uBAA6C,EAAE;CACrD,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,sBAAsB,EAAE,YACtB,MAAM,mBAMP,QAAQ,uBAAuB;GAChC,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,uBAAqB,KAAK,GAAG,MAAM;AACnC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,qBAAqB,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;AC7H5E,MAAa,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC1B,MAAa,gBAAgB,GAAG;;;;;;;;;;AAWhC,MAAa,iBAAiB,GAAG;;;;;;;;;ACCjC,MAAMC,eAAY;;;;;;;AAQlB,eAAsB,gBACpB,QACA,UAGI,EAAE,EACa;CACnB,MAAM,EAAE,WAAW;CACnB,MAAM,UAAoB,EAAE;CAC5B,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,SAAS,EAAE,YACT,MAAM,mBAMP,QAAQ,SAAS;GAClB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,UAAQ,KAAK,GAAG,MAAM;AACtB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,QAAQ,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;;;;;;;;ACkD/D,eAAsB,uBACpB,QACA,SAoB4D;CAC5D,MAAM,EACJ,KACA,UACA,QACA,OACA,gBACA,mBACA,0BACA,kBACA,SAAS,gBACP;CAEJ,MAAM,YAA+D,EAAE;CAEvE,MAAM,QAAQ,MAAM,kBAAoC,QAAQ;EAC9D;EACA;EACA;EACA;EACA,KAAK;EACL;EACD,CAAC;AAGF,OAAM,UAAU,OAAO,OAAO,SAAS;EACrC,MAAM,EACJ,UAAU,EAAE,aACV,MAAM,mBAWP,QAAQ,kBAAkB;GAC3B,WAAW,EAAE,IAAI,KAAK,IAAI;GAC1B;GACD,CAAC;AACF,OAAK,SAAS;GACd;AAEF,KAAI,CAAC,eACH,OAAM,UAAU,OAAO,OAAO,MAAM,UAAU;AAC5C,SAAO,KAAK,IAAI,QAAQ,EAAE,GAAG,MAAM,OAAO,yBAAyB,KAAK,QAAQ;EAEhF,MAAM,aAAa,MAAM,mBAAmB,QAAQ;GAClD,UAAU,EAAE,YAAY,KAAK,IAAI;GACjC;GACA;GACA;GACA;GACA;GACD,CAAC;AAEF,MAAI,MACF,QAAO,KACL,IAAI,QAAQ,EAAE,GAAG,MAAM,OAAO,yCAAyC,KAAK,QAC7E;AAGH,YAAU,KAAK,CAAC,MAAM,WAAW,CAAC;GAClC;KAEF,OAAM,SAAS,SAAS;AACtB,YAAU,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;GAC1B;AAGJ,QAAO,KAAK,4BAA4B,MAAM,OAAO,2BAA2B;AAEhF,QAAO;;;;;;;;;;;ACrMT,eAAsB,qBACpB,QACA,SAMyB;CACzB,MAAM,EAAE,OAAO,gBAAgB,SAAS,gBAAgB;CAcxD,MAAM,EAAE,yBAAyB,MAAM,mBAMpC,QAAQ,wBAAwB;EACjC,WAAW,EAAE,OAAA;GAnBb,OAAO,eAAe;GACtB,aAAa,eAAe;GAC5B,SAAS,eAAe;GACxB,oBAAoB,eAAe;GACnC,wBAAwB,eAAe;GACvC,2BAA2B,eAAe;GAC1C,4BAA4B,eAAe;GAC3C,YAAY,eAAe;GAC3B,WAAW,eAAe;GAC1B,aAAa,eAAe;GAUV,EAAE;EACpB;EACD,CAAC;AACF,QAAO,qBAAqB;;;;;;;;AAS9B,eAAsB,uBACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,kBAAkB,SAAS,gBAAgB;AAE1D,OAAM,UADiB,MAAM,kBAAkB,IACjB,EAAE,OAAO,YAAY;AACjD,QAAM,mBAAmB,QAAQ,0BAA0B;GACzD,WAAW,EACT,OAAO,QAAQ,KAAK,CAAC,gBAAgB,SAAS;IAC5C;IACA,OAAO,eAAe;IACtB,aAAa,eAAe;IAC5B,SAAS,eAAe;IACxB,oBAAoB,eAAe;IACnC,wBAAwB,eAAe;IACvC,2BAA2B,eAAe;IAC1C,4BAA4B,eAAe;IAC3C,YAAY,eAAe;IAC3B,WAAW,eAAe;IAC1B,aAAa,eAAe;IAC7B,EAAE,EACJ;GACD;GACD,CAAC;GACF;;;;;;;;;AAUJ,eAAsB,qBACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAO,KAAK,YAAY,OAAO,OAAO,wBAAwB;CAE9D,IAAI,mBAAmB;CAMvB,MAAM,wBAAwB,MAAM,MAHG,yBAAyB,QAAQ,EAAE,QAAQ,CAAC,EAGrB,QAAQ;AAMtE,OAAM,UAHsB,OAAO,QAAQ,UAAU,CAAC,sBAAsB,MAAM,OAG/C,EAAE,OAAO,mBAAmB;AAC7D,MAAI;GACF,MAAM,oBAAoB,MAAM,qBAAqB,QAAQ;IAC3D,OAAO;IACP;IACD,CAAC;AACF,yBAAsB,kBAAkB,SAAS;AACjD,UAAO,KAAK,wCAAwC,eAAe,MAAM,IAAI;WACtE,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,mCAAmC,eAAe,MAAM,OAAQ,IAAc,UAC/E;;GAEH;AAGF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,sBAAsB;AAC7D,QAAM,uBAAuB,QAAQ;GACnC,OAAO,OAAO,KAAK,UAAU,CAAC,OAAO,sBAAsB,MAAM,OAAQ,GAAG,CAAC;GAC7E;GACD,CAAC;AACF,SAAO,KAAK,wBAAwB,OAAO,OAAO,sBAAsB;UACjE,KAAK;AACZ,qBAAmB;AACnB,SAAO,MACL,mBAAmB,OAAO,OAAO,0BAA2B,IAAc,UAC3E;;AAGH,QAAO,CAAC;;;;;;;;;;;;AC9IV,eAAsB,mBACpB,QACA,SAM4D;CAC5D,MAAM,EAAE,OAAO,cAAc,SAAS,gBAAgB;CAQtD,MAAM,EAAE,uBAAuB,MAAM,mBAMlC,QAAQ,0BAA0B;EACnC,WAAW,EAAE,OAAA;GAbb,MAAM,aAAa;GACnB,UAAU,aAAa;GACvB,aAAa,aAAa;GAWR,EAAE;EACpB;EACD,CAAC;AACF,QAAO,mBAAmB;;;;;;;;AAS5B,eAAsB,qBACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,gBAAgB,SAAS,gBAAgB;AACxD,OAAM,mBAAmB,QAAQ,4BAA4B;EAC3D,WAAW,EACT,OAAO,EACL,mBAAmB,eAAe,KAAK,CAAC,cAAc,SAAS;GAC7D;GACA,aAAa,aAAa;GAE1B,YAAY,aAAa;GAC1B,EAAE,EACJ,EACF;EACD;EACD,CAAC;;;;;;;;;;AAWJ,eAAsB,mBACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AACjC,QAAO,KAAK,YAAY,OAAO,OAAO,sBAAsB;CAE5D,IAAI,mBAAmB;CAQvB,MAAM,qBAEF,MAAM,MAP2B,uBAAuB,QAAQ,EAClE,QACD,CAAC,GAKiC,EAAE,MAAM,eAAe,GAAG,KAAK,GAAG,WAAW;AAOhF,OAAM,UAJoB,OAAO,QAC9B,UAAU,CAAC,mBAAmB,GAAG,MAAM,KAAK,GAAG,MAAM,YAGvB,EAAE,OAAO,iBAAiB;AACzD,MAAI;GACF,MAAM,kBAAkB,MAAM,mBAAmB,QAAQ;IAAE,OAAO;IAAc;IAAQ,CAAC;AACzF,sBAAmB,GAAG,gBAAgB,KAAK,GAAG,gBAAgB,cAAc;AAC5E,UAAO,KAAK,sCAAsC,aAAa,KAAK,IAAI;WACjE,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,iCAAiC,aAAa,KAAK,OAAQ,IAAc,UAC1E;;GAEH;AAGF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,oBAAoB;AAC3D,QAAM,qBAAqB,QAAQ;GACjC,OAAO,OAAO,KAAK,UAAU,CAC3B,OACA,mBAAmB,GAAG,MAAM,KAAK,GAAG,MAAM,YAAa,GACxD,CAAC;GACF;GACD,CAAC;AACF,SAAO,KAAK,wBAAwB,OAAO,OAAO,oBAAoB;UAC/D,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,mBAAmB,OAAO,OAAO,wBAAyB,IAAc,UAAU;;AAGjG,QAAO,CAAC;;;;AC7JV,MAAM,oBAAoB;;;;;;;;AA0B1B,eAAsB,yBACpB,QACA,SAMkB;CAClB,MAAM,EAAE,OAAO,mBAAmB,SAAS,gBAAgB;CAC3D,IAAI,mBAAmB;AACvB,QAAO,KAAK,YAAY,kBAAkB,OAAO,6BAA6B;AAG9E,OAAM,UADiB,MAAM,mBAAmB,kBAClB,EAAE,OAAO,uBAAuB,QAAQ;AACpE,SAAO,KACL,UAAU,IAAI,GAAG,sBAAsB,OAAO,cAAc,sBAAsB,OAAO,iBAC1F;AACD,MAAI;AACF,SAAM,mBAMH,QAAQ,mBAAmB;IAC5B,WAAW,EACT,OAAO,EACL,WAAW,sBAAsB,KAAK,CAAC,IAAI,0BAA0B;KACnE;KACA;KACD,EAAE,EACJ,EACF;IACD;IACD,CAAC;AACF,UAAO,KACL,UAAU,MAAM,EAAE,GAAG,sBAAsB,OAAO,YACrC,sBAAsB,OAAO,eAC3C;WACM,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,UAAU,MAAM,EAAE,GAAG,sBAAsB,OAAO,sBAC3B,sBAAsB,OAAO,aAAc,IAAc,UACjF;;GAEH;AACF,QAAO,CAAC;;;;;;;;;;;;ACaV,eAAe,yBACb,QACA,oBACA,UAGI,EAAE,EAC6C;CACnD,MAAM,EAAE,SAAS,gBAAgB;CAMjC,MAAM,EAAE,6BAA6B,MAAM,mBAMxC,QAAQ,4BAA4B;EACrC,WAAW,EAAE,OAAA;GAXb,OAAO,mBAAmB;GAC1B,aAAa,mBAAmB;GAUd,EAAE;EACpB;EACD,CAAC;AACF,QAAO,yBAAyB;;;;;;;;;AAUlC,eAAe,2BACb,QACA,2BACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,SAAS,gBAAgB;CACjC,MAAM,kCAAkC,0BACrC,QAAQ,GAAG,QAAQ,OAAO,KAAA,EAAU,CACpC,KAAK,CAAC,EAAE,aAAa,MAAM;AAC9B,KAAI,gCAAgC,SAAS,EAC3C,OAAM,IAAI,MACR,iBACE,gCAAgC,OACjC,kEAAkE,gCAAgC,KACjG,SACD,CAAC,GACH;AAEH,OAAM,mBAAmB,QAAQ,8BAA8B;EAC7D,WAAW,EACT,OAAO,EACL,sBAAsB,0BAA0B,KAC7C,CACC,EAAE,uBAAuB,mBAAmB,gBAAgB,GAAG,sBAC/D,SACK;GACL,uBAAuB,mBAAmB,KAAK,EAAE,UAAU,YAAY;IACrE;IACA,MAAM,QAAQ;IACf,EAAE;GACH,oCAAoC,uBAAuB,KAAK,EAAE,SAAS,YAAY;IACrF;IACA,MAAM,QAAQ;IACf,EAAE;GACH,oBAAoB;GACpB,GAAG;GACH;GACD,EACF,EACF,EACF;EACD;EACD,CAAC;;;;;;;;;;AAWJ,eAAsB,yBACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI,mBAAmB;AAGvB,QAAO,KAAK,YAAY,OAAO,OAAO,4BAA4B;CAIlE,MAAM,4BAAsF,MAC1F,MAJyC,6BAA6B,QAAQ,EAAE,QAAQ,CAAC,EAKzF,QACD;CAGD,MAAM,0BAA0B,OAAO,QAAQ,UAAU,CAAC,0BAA0B,MAAM,OAAO;AACjG,KAAI,wBAAwB,SAAS,EACnC,QAAO,KAAK,aAAa,wBAAwB,OAAO,gCAAgC;AAE1F,OAAM,UAAU,yBAAyB,OAAO,uBAAuB;AACrE,MAAI;GACF,MAAM,wBAAwB,MAAM,yBAAyB,QAAQ,oBAAoB,EACvF,QACD,CAAC;AACF,6BAA0B,sBAAsB,SAAS;AACzD,UAAO,KAAK,6CAA6C,mBAAmB,MAAM,IAAI;WAC/E,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,yCAAyC,mBAAmB,MAAM,OAAQ,IAAc,UACzF;;GAEH;AAGF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,0BAA0B;AACjE,QAAM,2BACJ,QACA,OACG,KAAK,UAAU,CAAC,OAAO,0BAA0B,MAAM,QAAQ,GAAG,CAAU,CAC5E,QAAQ,MAA8C,CAAC,CAAC,EAAE,GAAG,EAChE,EAAE,QAAQ,CACX;AACD,SAAO,KAAK,wBAAwB,OAAO,OAAO,yBAAyB;UACpE,KAAK;AACZ,qBAAmB;AACnB,SAAO,MACL,mBAAmB,OAAO,OAAO,4BAA6B,IAAc,UAC7E;;AAGH,QAAO,CAAC;;;;;;;;;;;AClMV,eAAsB,aACpB,QACA,SAMuC;CACvC,MAAM,EAAE,OAAO,QAAQ,SAAS,gBAAgB;CAchD,MAAM,EAAE,iBAAiB,MAAM,mBAM5B,QAAQ,eAAe;EACxB,WAAW,EAAE,OAAA;GAnBb,OAAO,OAAO;GACd,aAAa,OAAO;GACpB,SAAS,OAAO;GAChB,oBAAoB,OAAO;GAC3B,wBAAwB,OAAO;GAC/B,6BAA6B,OAAO;GACpC,aAAa,OAAO;GACpB,cAAc,OAAO;GACrB,YAAY,OAAO;GAWD,EAAE;EACpB;EACD,CAAC;AACF,QAAO,aAAa;;;;;;;;AAStB,eAAsB,cACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,SAAS,SAAS,gBAAgB;AACjD,OAAM,mBAAmB,QAAQ,gBAAgB;EAC/C,WAAW,EACT,OAAO,EACL,SAAS,QAAQ,KAAK,CAAC,QAAQ,SAAS;GACtC;GACA,OAAO,OAAO;GACd,aAAa,OAAO;GACpB,SAAS,OAAO;GAChB,oBAAoB,OAAO;GAC3B,wBAAwB,OAAO;GAC/B,6BAA6B,OAAO;GACpC,aAAa,OAAO;GACpB,cAAc,OAAO;GACrB,YAAY,OAAO;GAEnB,YAAY,OAAO;GACpB,EAAE,EACJ,EACF;EACD;EACD,CAAC;;;;;;;;;AAUJ,eAAsB,YACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAO,KAAK,YAAY,OAAO,OAAO,cAAc;CAEpD,IAAI,mBAAmB;CAMvB,MAAM,gBAAiE,MACrE,MAJ4B,gBAAgB,QAAQ,EAAE,QAAQ,CAAC,EAK/D,QACD;AAMD,OAAM,UAHa,OAAO,QAAQ,UAAU,CAAC,cAAc,MAAM,OAGvC,EAAE,OAAO,WAAW;AAC5C,MAAI;GACF,MAAM,YAAY,MAAM,aAAa,QAAQ;IAAE,OAAO;IAAQ;IAAQ,CAAC;AACvE,iBAAc,UAAU,SAAS;AACjC,UAAO,KAAK,+BAA+B,OAAO,MAAM,IAAI;WACrD,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,0BAA0B,OAAO,MAAM,OAAQ,IAAc,UAAU;;GAEtF;AAGF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,YAAY;AACnD,QAAM,cAAc,QAAQ;GAC1B,OAAO,OAAO,KAAK,UAAU,CAAC,OAAO,cAAc,MAAM,OAAQ,GAAG,CAAC;GACrE;GACD,CAAC;AACF,SAAO,KAAK,wBAAwB,OAAO,OAAO,YAAY;UACvD,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,mBAAmB,OAAO,OAAO,gBAAiB,IAAc,UAAU;;AAGzF,QAAO,CAAC;;;;ACpLV,MAAaC,aAAyB,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2B7C;AAEF,MAAa,iBAA+B,MAAM,GAAG;;;;;;;;;EASnD;AAEF,MAAa,iBAA+B,MAAM,GAAG;;;;;;;;;EASnD;;;ACXF,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,iBACpB,QACA,UAQI,EAAE,EACc;CACpB,MAAM,EAAE,QAAQ,UAAU,EAAE,iBAAiB,UAAU,EAAE,KAAK;CAC9D,MAAM,WAAsB,EAAE;CAC9B,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,UAAU,EAAE,YACV,MAAM,mBAMP,QAAQC,YAAU;GACnB;GACA,WAAW;IAAE,OAAOD;IAAW;IAAQ,OAAO,EAAE,gBAAgB;IAAE;GACnE,CAAC;AACF,WAAS,KAAK,GAAG,MAAM;AACvB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,SAAS,MAAM,GAAG,MAAM,EAAE,aAAa,cAAc,EAAE,aAAa,CAAC;;;;AClF9E,MAAa,oBAAkC,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmCtD;AAEF,MAAa,oCAAkD,MAAM,GAAG;;;;;;;;;EAStE;;;ACIF,MAAME,eAAY;;;;;;;;AASlB,eAAsB,yBACpB,QACA,UAGI,EAAE,EACsB;CAC5B,MAAM,EAAE,WAAW;CACnB,MAAM,mBAAsC,EAAE;CAC9C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,kBAAkB,EAAE,YAClB,MAAM,mBAMP,QAAQ,mBAAmB;GAC5B;GACA,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACxC,CAAC;AACF,mBAAiB,KAAK,GAAG,MAAM;AAC/B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,iBAAiB,MAAM,GAAG,MAC/B,GAAG,EAAE,KAAK,GAAG,EAAE,QAAQ,eAAe,cAAc,GAAG,EAAE,KAAK,GAAG,EAAE,QAAQ,eAAe,CAC3F;;;;;;;;;;;AC5EH,eAAsB,+BACpB,QACA,UAGI,EAAE,EAC6B;CACnC,MAAM,CAAC,UAAU,UAAU,MAAM,QAAQ,IAAI,CAC3C,iBAAiB,QAAQ,QAAQ,EACjC,yBAAyB,QAAQ,QAAQ,CAC1C,CAAC;AAEF,QAAO,SAAS,KAAK,aAAa;EAChC,GAAG;EACH,QAAQ,OAAO,QAAQ,UAAU,MAAM,QAAQ,iBAAiB,QAAQ,aAAa;EACtF,EAAE;;;;AC9BL,MAAa,kCAAgD,MAAM,GAAG;;;;;;;;EAQpE;;;ACeF,MAAMC,mBAAiB;;;;;;;;;AAUvB,eAAe,iCACb,QACA,SACA,QACmB;CACnB,MAAM,EACJ,iCAAiC,EAAE,YACjC,MAAM,mBASP,QAAQ,iCAAiC;EAC1C;EACA,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE;EAClC,CAAC;AACF,QAAO,MAAM,KAAK,SAAS,KAAK,MAAM;;;;;;;;;;AAWxC,eAAsB,6BACpB,QACA,SAiBA;CACA,MAAM,EAAE,SAAS,QAAQ,cAAc,cAAc,OAAO;CAE5D,IAAI,YAAY;AAChB,gBAAe,EAAE;CAEjB,MAAM,UAKA,EAAE;AAER,OAAM,IACJ,MAAM,SAASA,iBAAe,EAC9B,OAAO,mBAAmB;EAOxB,MAAM,iBAAgB,MAND,iCACnB,QAEA,eAAe,KAAK,EAAE,OAAO,GAAG,WAAW,KAAK,EAChD,OACD,EAC4B,KAAK,OAAO,SAAS;GAChD,OAAO,eAAe;GACtB,aAAa;GACd,EAAE;AACH,UAAQ,KAAK,GAAG,cAAc;AAC9B,eAAa,eAAe;AAC5B,iBAAe,UAAU;IAE3B,EAAE,aAAa,CAChB;AAED,QAAO;;;;;;;ACjHT,MAAa,4BAA4B,EAAE,aAAa,CACtD,EAAE,KAAK,EACL,OAAO,EAAE,MAAM,4BAA4B,EAC5C,CAAC,EACF,EAAE,QAAQ;;AAER,QAAQ,EAAE,QACX,CAAC,CACH,CAAC;;;ACHF,MAAa,oBAAoB,EAAE,KAAK;;;;;;CAMtC,SAAS,EAAE;;;;;;;;CAQX,YAAY,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC;;;;;;;;;;;;;;;;;;CAkBvC,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM;EAAC,EAAE;EAAQ,EAAE;EAAS,EAAE;EAAM,EAAE;EAAU,CAAC,CAAC;CACtF,CAAC;;;;;;AAUF,MAAa,mBAAmB,EAAE,OAAO,EAAE,QAAQ,kBAAkB;AAKrE,MAAa,kCAAkC,EAAE,KAAK;;CAEpD,MAAM,EAAE;;CAER,2BAA2B,EAAE;CAC9B,CAAC;;;;;AASF,MAAa,sBAAsB,EAAE,OAAO,EAAE,QAAQ,gCAAgC;;AAMtF,MAAa,kBAAkB,EAAE,KAAK;;AAEpC,KAAK,EAAE,QACR,CAAC;;AAMF,MAAa,oBAAoB,EAAE,OAAO,EAAE,QAAQ,gBAAgB;AAKpE,MAAa,oBAAoB,EAAE,aAAa,CAC9C,EAAE,KAAK;;;;;CAKL,qBAAqB,EAAE,OAAO,EAAE,QAAQ,kBAAkB;;CAE1D,eAAe,EAAE;;;;;CAKjB,oBAAoB,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;;;;;CAKpE,wBAAwB,EAAE,OACxB,EAAE,QACF,EAAE,KAAK;EACL,QAAQ;EACR,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO;EAClC,CAAC,CACH;;;;;CAKD,gBAAgB,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;CACjE,CAAC,EACF,EAAE,QAAQ;;CAER,kBAAkB,EAAE;;CAEpB,gBAAgB,EAAE;CACnB,CAAC,CACH,CAAC;;;;;;;AAWF,MAAa,kBAAkB,EAAE,aAAa,CAC5C,EAAE,KAAK;;CAEL,qBAAqB;;CAErB,eAAe,EAAE;;CAEjB,oBAAoB;CACrB,CAAC,EACF,EAAE,QAAQ;;CAER,iBAAiB,EAAE;;CAEnB,kBAAkB;;CAElB,iBAAiB,EAAE,MAAM,EAAE,OAAO;CACnC,CAAC,CACH,CAAC;;;;;;;AAWF,MAAa,sBAAsB,EAAE,OACnC,EAAE,QAKF,EAAE,MAAM,CAAC,EAAE,SAAS,qBAAqB,CAAC,CAC3C;;;;;;;;AAYD,MAAa,+BAA+B,EAAE,OAC5C,EAAE,QAKF,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CACnD;;;;AAQD,MAAa,2BAA2B,EAAE,OACxC,EAAE,QACF,EAAE,KAAK;;CAEL,YAAY,EAAE;;CAEd,OAAO,EAAE;;CAET,QAAQ;CACT,CAAC,CACH;;;;;;;AAWD,MAAa,uCAAuC,EAAE,OACpD,EAAE,QAEF,EAAE,KAAK;;CAEL,QAAQ;;CAER,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO;CAClC,CAAC,CACH;;;;;;;;;AAeD,MAAa,2BAA2B,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;;AAMxF,MAAa,kBAAkB,EAAE,KAAK;;;;CAIpC,cAAc,EAAE,OAAO,EAAE,QAAQ,kBAAkB;;;;;CAKnD,gBAAgB,EAAE,OAChB,EAAE,QACF,EAAE,KAAK;;EAEL,YAAY,EAAE;;EAEd,OAAO,EAAE;;EAET,QAAQ;EACT,CAAC,CACH;;;;;CAKD,gBAAgB,EAAE,OAAO,EAAE,QAAQ,qBAAqB;CACzD,CAAC;AAKF,MAAa,wBAAwB,EAAE,KAAK;;CAE1C,eAAe,EAAE;;CAEjB,oBAAoB;;CAEpB,wBAAwB;;CAExB,gBAAgB;;CAEhB,gBAAgB;;CAEhB,gBAAgB;;CAEhB,mBAAmB;CACpB,CAAC;AAKF,MAAa,+BAA+B,EAAE,KAAK;;AAEjD,SAAS,EAAE,MACT,EAAE,KAAK;;CAEL,kBAAkB;;CAElB,WAAW,EAAE;CACd,CAAC,CACH,EACF,CAAC;AAKF,MAAa,kCAAkC,EAAE,aAAa,CAC5D,EAAE,KAAK;;CAEL,SAAS,EAAE,MACT,EAAE,aAAa,CACb,EAAE,KAAK;;AAEL,SAAS,EAAE,SACZ,CAAC,EACF,EAAE,QAAQ;;AAER,cAAc,EAAE,QACjB,CAAC,CACH,CAAC,CACH;;CAED,UAAU,EAAE,MACV,EAAE,KAAK;;EAEL,OAAO,EAAE;;EAET,OAAO,EAAE;EACV,CAAC,CACH;CACF,CAAC,EACF,EAAE,QAAQ;;AAER,QAAQ,EAAE,MAAM,EAAE,OAAO,EAC1B,CAAC,CACH,CAAC;;AAMF,MAAa,kCAAkC,EAAE,KAAK;;CAEpD,MAAM,EAAE;;CAER,OAAO,EAAE;CACV,CAAC;;AAMF,MAAa,6BAA6B,EAAE,KAAK;;CAE/C,OAAO,EAAE;;CAET,MAAM,EAAE;CACT,CAAC;;AAMF,MAAa,2BAA2B,EAAE,aAAa,CACrD,EAAE,KAAK;;CAEL,iBAAiB,EAAE;;CAEnB,MAAM,SAAS,oBAAoB;;CAEnC,OAAO,EAAE;;CAET,aAAa,EAAE;;CAEf,MAAM,EAAE;CACT,CAAC,EACF,EAAE,QAAQ;;CAER,yBAAyB,EAAE;;CAE3B,0BAA0B,EAAE;;CAE5B,SAAS,EAAE,MAAM,2BAA2B;CAC7C,CAAC,CACH,CAAC;;AAMF,MAAa,eAAe,EAAE,aAAa,CACzC,EAAE,KAAK;;CAEL,cAAc,EAAE;;CAEhB,MAAM,EAAE;CACT,CAAC,EACF,EAAE,QAAQ;;CAER,OAAO,EAAE;;CAET,aAAa,EAAE;;CAEf,aAAa,EAAE;;CAEf,cAAc,EAAE;;CAEhB,iBAAiB,EAAE;;CAEnB,0BAA0B,EAAE;;CAE5B,2BAA2B,EAAE;;CAE7B,cAAc,SAAS,yBAAyB;;CAEhD,mBAAmB,EAAE,MAAM,SAAS,sBAAsB,CAAC;CAC5D,CAAC,CACH,CAAC;;;;;;;;;ACtaF,SAAgB,6BAA6B,EAC3C,KACA,oBAUC;AACD,QAAO,OAAO,QAAQ,iBAAiB,CACpC,KAAK,CAAC,YAAY,EAAE,WAAW;EAC9B,MAAM,QAAQ,IAAI;AAElB,MAAI,UAAU,KAAA,KAAa,UAAU,GACnC,QAAO;AAET,SAAO;GAAE;GAAK;GAAO;GACrB,CACD,QAEG,MAKG,MAAM,KACZ;;;;;;;;;;AC5BL,SAAgB,gCAAgC,EAC9C,KACA,sBAM8B;AAO9B,QANoB,OAAO,QAAQ,mBAAmB,CACnD,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,KAAK,CAC7B,KAAK,CAAC,KAAK,wBAAwB;EAClC,MAAM,kBAAkB;EACxB,OAAO,IAAI;EACZ,EACe,CAAC,MAChB,GAAG,OACD,EAAE,SAAS,UAAU,KAAK,MAAM,EAAE,SAAS,UAAU,KAAK,MAC3D,EAAE,KAAK,cAAc,EAAE,MAAM,KAAA,GAAW,EAAE,aAAa,QAAQ,CAAC,CACnE;;;;;;;;;;;;ACnBH,SAAgB,0CAA0C,EACxD,KACA,sBAWG;AACH,QAAO,OAAO,QAAQ,mBAAmB,CACtC,MACE,GAAG,IAAI,GAAG,QACR,EAAE,SAAS,UAAU,KAAK,MAAM,EAAE,SAAS,UAAU,KAAK,MAC3D,EAAE,KAAK,cAAc,EAAE,MAAM,KAAA,GAAW,EAAE,aAAa,QAAQ,CAAC,CACnE,CACA,QAAQ,CAAC,SAAS,IAAI,QAAQ,mBAAmB,KAAM,0BAA0B,CACjF,KAAK,CAAC,KAAK,iBAAiB;EAC3B,GAAG;EACH,YAAY;EACZ,OAAO,IAAI;EACZ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACIP,SAAgB,4BAA4B,EAC1C,KACA,qBACA,cACA,oBAYA;CAEA,MAAM,SAEF,EAAE;AAGN,QAAO,QAAQ,oBAAoB,CAAC,SACjC,CAAC,YAAY,EAAE,SAAS,YAAY,oBAAoB;AAEvD,MAAI,CAAC,aAAa,SAAS,QAAQ,CACjC,OAAM,IAAI,MAAM,yBAAyB,QAAQ,cAAc,aAAa,KAAK,KAAK,GAAG;EAI3F,MAAM,WAAW,IAAI,eAAe;AAGpC,MAAI,YAAY;GACd,MAAM,kBAAkB,iBAAiB,MACtC,MAAM,EAAE,SAAS,cAAc,EAAE,QAAQ,iBAAiB,QAC5D;AACD,OAAI,CAAC,iBAAiB;IACpB,MAAM,gBAAgB,iBACnB,QAAQ,MAAM,EAAE,QAAQ,iBAAiB,QAAQ,CACjD,KAAK,MAAM,EAAE,KAAK;AACrB,UAAM,IAAI,MACR,4BAA4B,WAAW,gBAAgB,QAAQ,8CAChB,cAAc,KAAK,IAAI,GACvE;;AAIH,OAAI,CAAC,OAAO,SACV,QAAO,WAAW,EAChB,aAAa,EAAE,EAChB;AAEH,OAAI,CAAC,OAAO,SAAS,YACnB,QAAO,SAAS,cAAc,EAAE;AAIlC,WAAQ,gBAAgB,MAAxB;IACE,KAAK,oBAAoB,SAAS;KAChC,MAAM,cAAc,aAAa;AAEjC,SAAI,gBAAgB,KAAA,KAAa,aAAa,GAC5C,OAAM,IAAI,MACR,0CAA0C,SAAS,eAC7C,WAAW,aAAa,QAAQ,eAAe,WAAW,GACjE;AAIH,SAAI,gBAAgB,QAAQ,gBAAgB,KAAA,EAC1C;AAIF,SAAI,OAAO,gBAAgB,UACzB,OAAM,IAAI,MACR,yCAAyC,WAAW,2BAA2B,WAChF;AAEH,YAAO,SAAS,YAAa,KAAK;MAChC,OAAO;MACP,QAAQ,EAAE,cAAc,aAAa;MACtC,CAAC;AACF;;IAGF,KAAK,oBAAoB,QAAQ;KAC/B,MAAM,cAAc,aAAa;AAEjC,SAAI,gBAAgB,KAAA,KAAa,aAAa,GAC5C,OAAM,IAAI,MACR,0CAA0C,SAAS,eAC7C,WAAW,aAAa,QAAQ,eAAe,WAAW,GACjE;AAIH,SAAI,gBAAgB,QAAQ,gBAAgB,KAAA,EAC1C;AAIF,SAAI,OAAO,gBAAgB,SACzB,OAAM,IAAI,MACR,wCAAwC,WAAW,0BAA0B,WAC9E;KAEH,MAAM,UAAU,YAAY,MAAM,IAAI;AAEtC,SACE,WACA,CAAC,gBAAgB,uBAAuB,KAAK,EAAE,WAAW,KAAK,CAAC,SAAS,QAAQ,CAEjF,OAAM,IAAI,MACR,wCAAwC,WAAW,qBAC9C,gBAAgB,uBAChB,KAAK,EAAE,WAAW,KAAK,CACvB,KAAK,KAAK,CAAC,SAAS,WAC1B;AAGH,YAAO,SAAS,YAAa,KAAK;MAChC,OAAO;MACP,QAAQ,EAAE,aAAa,SAAS;MACjC,CAAC;AACF;;IAGF,KAAK,oBAAoB,aAAa;AACpC,SAAI,OAAO,aAAa,SACtB,OAAM,IAAI,MACR,8CAA8C,WAAW,0BAA0B,WACpF;KAKH,MAAM,eAAe,eAAe,SAAS,CAC1C,KAAK,UAAU;MACd,MAAM,cAAc,aAAa;AAEjC,UAAI,gBAAgB,KAAA,KAAa,aAAa,GAC5C,OAAM,IAAI,MACR,uDAAuD,SAAS,eAC1D,WAAW,aAAa,QAAQ,eAAe,WAAW,GACjE;AAIH,UAAI,gBAAgB,QAAQ,gBAAgB,KAAA,EAC1C,QAAO;AAIT,UAAI,OAAO,gBAAgB,SACzB,OAAM,IAAI,MACR,8CAA8C,WAAW,qBACnC,gBAAgB,uBACjC,KAAK,EAAE,WAAW,KAAK,CACvB,KAAK,KAAK,CAAC,SAAS,QAC1B;AAEH,aAAO;OACP,CACD,QAAQ,MAAmB,MAAM,KAAK,CACtC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;AAGrC,SAAI,aAAa,SAAS,EACxB,QAAO,SAAS,YAAa,KAAK;MAChC,OAAO;MACP,QAAQ,EAAE,cAAc;MACzB,CAAC;AAEJ;;IAGF,QACE,OAAM,IAAI,MAAM,4BAA4B,gBAAgB,OAAO;;SAElE;GAEL,MAAM,cAAc,aAAa;AACjC,OAAI,gBAAgB,KAAA,KAAa,aAAa,GAC5C,OAAM,IAAI,MACR,0CAA0C,SAAS,eAC7C,WAAW,aAAa,QAAQ,kBAAkB,KAAK,UAAU,IAAI,GAC5E;AAEH,OAAI,gBAAgB,KAClB;AAGF,OAAI,CAAC,OAAO,SAEV,QAAO,WAAW,EAAE,SAAS,gBAAgB,MAAM;OAGnD,QAAO,SAAS,UAAU,gBAAgB;;GAIjD;AAID,QAAO,MAAM,SAAS,GAAG,gBAAgB;AACvC,MAAI,OAAO,EAAE,YAAY,UACvB,OAAM,IAAI,MAAM,6DAA6D,cAAc;AAE7F,SAAO;GACL,GAAG;GACH,SAAS,EAAE;GACZ;GACD;;;;;;;;;;ACjPJ,SAAgB,uCAAuC,EACrD,sBACA,gBACA,oBAUU;AAEV,QAAO,OAAO,QAAQ,eAAe,CAAC,OAAO,CAAC,aAAa,EAAE,cAAc,EAAE,EAAE,eAAe;EAE5F,MAAM,iBAAiB,qBAAqB,SAAS,MAClD,oBAAoB,gBAAgB,YAAY,YAClD;AAKD,MAAI,EADoB,CAAC,CAAC,kBAAkB,eAAe,YAAY,SAErE,QAAO;AAIT,SAAO,YAAY,OAChB,EAAE,OAAO,aAER,eAAe,eACf,eAAe,YAAY,MAAM,uBAAuB;AAEtD,OAAI,mBAAmB,UAAU,MAC/B,QAAO;GAIT,MAAM,kBAAkB,iBAAiB,MACtC,MAAM,EAAE,SAAS,SAAS,EAAE,QAAQ,iBAAiB,YACvD;AACD,OAAI,CAAC,gBACH,OAAM,IAAI,MAAM,uCAAuC,QAAQ;AAIjE,WAAQ,gBAAgB,MAAxB;IACE,KAAK,oBAAoB,QACvB,QAAO,mBAAmB,OAAO,iBAAiB,OAAO;IAC3D,KAAK,oBAAoB,OACvB,QAAO,mBAAmB,OAAO,gBAAgB,OAAO;IAC1D,KAAK,oBAAoB;KAEvB,MAAM,uBAAuB,mBAAmB,OAAO,gBAAgB,EAAE,EAAE,MAAM;KAEjF,MAAM,mBAAmB,OAAO,gBAAgB,EAAE,EAAE,MAAM;AAC1D,YACE,oBAAoB,WAAW,gBAAgB,UAC/C,oBAAoB,OAAO,GAAG,MAAM,MAAM,gBAAgB,GAAG;IAEjE,QACE,OAAM,IAAI,MAAM,kCAAkC,gBAAgB,OAAO;;IAE7E,CACL;GACD;;;;;;;;;;;ACjEJ,SAAgB,6CAA6C,EAC3D,sBACA,gBACA,kBACA,UAYU;AAEV,QAAO,CAAC,CAAC,OAAO,QAAQ,eAAe,CAAC,MAAM,CAAC,aAAa,EAAE,cAAc,EAAE,EAAE,eAAe;EAE7F,MAAM,iBAAiB,qBAAqB,SAAS,MAClD,oBAAoB,gBAAgB,YAAY,YAClD;AAGD,MAAI,CAAC,gBAAgB;AACnB,WAAQ,KACN,iCAAiC,YAAY,yBAAyB,qBAAqB,OAAO,GACnG;AACD,UAAO;;AAIT,MAAI,eAAe,YAAY,SAAS;AACtC,WAAQ,KACN,WAAW,YAAY,mCAAmC,qBAAqB,OAAO,mBAClE,QAAQ,mBAAmB,eAAe,UAC/D;AACD,UAAO;;AAIT,SAAO,CAAC,CAAC,YAAY,MAAM,EAAE,OAAO,aAAa;GAE/C,MAAM,qBAAqB,eAAe,eAAe,EAAE,EAAE,MAC1D,uBAAuB,mBAAmB,UAAU,MACtD;AAGD,OAAI,CAAC,mBAAmB;AACtB,YAAQ,KACN,0CAA0C,MAAM,cAC3C,YAAY,YAAY,qBAAqB,OAAO,GAC1D;AACD,WAAO;;GAIT,MAAM,kBAAkB,iBAAiB,MACtC,MAAM,EAAE,SAAS,SAAS,EAAE,QAAQ,iBAAiB,YACvD;AACD,OAAI,CAAC,gBACH,OAAM,IAAI,MAAM,uCAAuC,QAAQ;GAIjE,IAAI;GACJ,IAAI;AACJ,WAAQ,gBAAgB,MAAxB;IACE,KAAK,oBAAoB;AACvB,iBAAY,kBAAkB,OAAO,iBAAiB,OAAO;AAC7D,aAAQ,KACN,oBAAoB,MAAM,mCACrB,qBAAqB,OAAO,cAAc,OAAO,aAAa,WACvD,kBAAkB,OAAO,eACtC;AACD,YAAO;IACT,KAAK,oBAAoB;AACvB,mBAAc,kBAAkB,OAAO,gBAAgB,OAAO;AAC9D,aAAQ,KACN,oBAAoB,MAAM,kCACrB,qBAAqB,OAAO,cAAc,OAAO,YAAY,WACtD,kBAAkB,OAAO,cACtC;AACD,YAAO;IACT,KAAK,oBAAoB;KAEvB,MAAM,uBAAuB,kBAAkB,OAAO,gBAAgB,EAAE,EAAE,MAAM;KAEhF,MAAM,mBAAmB,OAAO,gBAAgB,EAAE,EAAE,MAAM;AAC1D,mBACE,oBAAoB,WAAW,gBAAgB,UAC/C,CAAC,oBAAoB,OAAO,GAAG,MAAM,MAAM,gBAAgB,GAAG;AAChE,aAAQ,KACN,oBAAoB,MAAM,wCACrB,qBAAqB,OAAO,cAAc,gBAAgB,KAC3D,KACD,CAAC,WAAW,oBAAoB,KAAK,KAAK,GAC9C;AACD,YAAO;IACT,QACE,OAAM,IAAI,MAAM,kCAAkC,gBAAgB,OAAO;;IAE7E;GACF;;;;;;;;;;;AC/GJ,SAAgB,+BACd,EACE,cAAc,EAAE,EAChB,WAAW,EAAE,EACb,WAAW,EAAE,EACb,oBAAoB,EAAE,EACtB,SAAS,EACP,kBAAkB,aACnB,EAED,GAAG,YAEL,gCACyB;CAEzB,MAAM,MAA+B;EACnC,GAAG;EACH,GAAG;EACH,GAAG;EACJ;AAGD,KAAI,MAAM,QAAQ,YAAY,EAAE;EAC9B,MAAM,yBAAS,IAAI,KAA0B;AAC7C,OAAK,MAAM,EAAE,MAAM,WAAW,aAAa;AACzC,OAAI,CAAC,OAAO,IAAI,KAAK,CAAE,QAAO,IAAI,sBAAM,IAAI,KAAK,CAAC;AAClD,OAAI,MAAO,QAAO,IAAI,KAAK,CAAE,IAAI,MAAM;;AAEzC,OAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,SAAS,CACxC,KAAI,QAAQ,MAAM,KAAK,IAAI,CAAC,KAAK,+BAA+B;;AAKpE,KAAI,MAAM,QAAQ,SAAS,CACzB,KAAI,WAAW,KAAK,UAClB,SAAS,QACN,KAAK,EAAE,KAAK,YAAY;AACvB,MAAI,OAAO;AACX,SAAO;IAET,EAAE,CACH,CACF;AAMH,KAAI,MAAM,QAAQ,SAAS,CACzB,MAAK,MAAM,EAAE,SAAS,aAAa,aAAa,UAAU;AACxD,MAAI,WAAW,QAAQ,QAAQ;AAG/B,MAAI,MAAM,QAAQ,YAAY,CAC5B,MAAK,MAAM,EAAE,OAAO,YAAY,aAAa;GAC3C,MAAM,MAAM,GAAG,QAAQ,GAAG;GAE1B,IAAI,MAAe;AAEnB,OAAI,OAAO,OAAO,iBAAiB,UACjC,OAAM,OAAO;YACJ,OAAO,YAChB,OAAM,OAAO;YACJ,MAAM,QAAQ,OAAO,aAAa,CAE3C,OADW,OAAO,aAAa,QAAQ,MAAM,EAAE,SAAS,EAChD,CAAC,KAAK,IAAI;OAGlB,OAAM;AAGR,OAAI,OAAO;;;AAMnB,QAAO;;;;;;;;;;;;;;;;;;;;ACnET,SAAgB,mBACd,MACA,OACA,gBACA,YAAY,KACmB;CAC/B,MAAM,UAAU,KAAK,IAAI,GAAG,eAAe,SAAS,GAAG,MAAM,SAAS,CAAC;AACvE,KAAI,YAAY,EAAG,QAAO,EAAE;CAI5B,MAAM,cAAc,IAAI,KAAK,KAAK,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,YAAY;CAGrF,MAAM,aAAa,KAAK,KAAK,UAAU,KAAK,IAAI,GAAG,UAAU,CAAC;CAC9D,MAAM,UAAU,KAAK,IAAI,aAAa,WAAW;CAGjD,MAAM,QAAQ,KAAK,MAAM,eAAe,SAAS,GAAG,YAAY,SAAS,IAAI,QAAQ;CAErF,MAAM,SAAmC,EAAE;AAE3C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK,GAAG;EACjC,MAAM,UAAU,YAAY,SAAS,GAAG,IAAI;EAI5C,MAAM,iBAHiB,KAAK,IAAI,eAAe,SAAS,EAAE,UAAU,QAG/B,GAAG;EAGxC,MAAM,YAAY,KAAK,IAAI,SAAS,eAAe;EAEnD,MAAM,WAAW,IAAI,KAAK,QAAQ,CAAC,aAAa;EAChD,MAAM,YAAY,IAAI,KAAK,UAAU,CAAC,aAAa;AAEnD,MAAI,SAAS,YACX,QAAO,KAAK;GACV,gBAAgB;GAChB,iBAAiB;GAClB,CAAC;MAEF,QAAO,KAAK,EACV,QAAQ;GACN,cAAc;GACd,eAAe;GAChB,EACF,CAAC;;AAIN,QAAO;;;;;;;;;;AC9DT,SAAgB,qBAAqB,UAA6C;AAEhF,QADqB,CAAC,CAAC,SAAS,kBAAkB,CAAC,CAAC,SAAS,kBACvC,cAAc;;;;;;;;;;;ACCtC,SAAgB,2BACd,MACA,MACM;AACN,KAAI,SAAS,YACX,QAAO,IAAI,KAAK,KAAK,UAAU;AAGjC,QAAO,KAAK,QAAQ,YAAY,IAAI,KAAK,KAAK,OAAO,UAAU,mBAAG,IAAI,MAAM;;;;;;;;;;;;;;ACD9E,gBAAuB,oBACrB,QACA,WACA,QACA,UACA,QAC2D;CAC3D,IAAI;AAEJ,QAAO,MAAM;EAEX,MAAM,OAAY,EAAE,OAAO,UAAU;AACrC,MAAI,UAAU,OAAO,KAAK,OAAO,CAAC,OAAQ,MAAK,SAAS;AACxD,MAAI,OAAQ,MAAK,SAAS;EAkB1B,MAAM,EAAE,OAAO,QAAQ,eAAe,YAAY,2BAA2B,MAhB1D,mBACjB,0BAEE,OACG,KAAK,kBAAkB,UAAU,SAAS,EACzC,MAAM,MACP,CAAC,CACD,MAAM,EACX;GACE;GACA,UAAU,SAAS,QAAQ,YAAY;AACrC,WAAO,KAAK,iBAAiB,QAAQ,yCAAyC,UAAU;;GAE3F,CACF,CAEiF;AAClF,MAAI,CAAC,OAAO,OAAQ;AAEpB,QAAM;AAEN,MAAI,CAAC,WAAY;AACjB,WAAS;;;;;;;;;;;;ACxCb,eAAsB,oBACpB,QACA,EACE,WACA,MACA,YACA,UACA,WACA,SAAS,eAcO;CAClB,MAAM,SACJ,SAAS,cACL;EACE,GAAG;EACH,gBAAgB;EAChB,iBAAiB;EACjB,QAAQ,WAAW;EACpB,GACD;EACE,GAAG;EACH,gBAAgB,KAAA;EAChB,iBAAiB,KAAA;EACjB,QAAQ;GACN,GAAG,WAAW;GACd,cAAc;GACd,eAAe;GAChB;EACF;CAiBP,MAAM,EAAE,UAAU,YAAY,2BAA2B,MAhBtC,mBACjB,0BAEE,OACG,KAAK,kBAAkB,UAAU,SAAS,EACzC,MAAM;EAAE,OAAO;EAAG;EAAQ,EAC3B,CAAC,CACD,MAAM,EACX;EACE;EACA,UAAU,SAAS,OAAO,YAAY;AACpC,UAAO,KAAK,iBAAiB,QAAQ,yCAAyC,UAAU;;EAE3F,CACF,CAE6D;AAC9D,QAAO,MAAM,QAAQ,MAAM,IAAI,MAAM,SAAS;;;;;;;;;;;ACvDhD,SAAgB,2BACd,MACA,UAMA;AACA,KAAI,SAAS,YACX,QAAO;EACL,OAAO,SAAS,iBAAiB,IAAI,KAAK,SAAS,eAAe,GAAG,KAAA;EACrE,QAAQ,SAAS,kBAAkB,IAAI,KAAK,SAAS,gBAAgB,GAAG,KAAA;EACzE;CAEH,MAAM,IAAI,SAAS,UAAU,EAAE;AAC/B,QAAO;EACL,OAAO,EAAE,eAAe,IAAI,KAAK,EAAE,aAAa,GAAG,KAAA;EACnD,QAAQ,EAAE,gBAAgB,IAAI,KAAK,EAAE,cAAc,GAAG,KAAA;EACvD;;;;;;;;;;AAWH,SAAS,gBACP,MACA,MACA,WACwB;AACxB,KAAI,SAAS,YACX,QAAO;EACL,GAAG;EACH,iBAAiB,aAAa,KAAK;EACpC;AAEH,QAAO;EACL,GAAG;EACH,QAAQ;GACN,GAAG,KAAK;GACR,GAAI,YAAY,EAAE,eAAe,WAAW,GAAG,EAAE;GAClD;EAED,gBAAgB,KAAA;EAChB,iBAAiB,KAAA;EAClB;;;;;;;;;;;AAYH,eAAe,SACb,QACA,WACA,QACA,QAC6C;AAC7C,QAAO,KAAK,oCAAoC,KAAK,UAAU,OAAO,GAAG;CAEzE,MAAM,MAAM,MADD,oBAAoB,QAAQ,WAAW,QAAuB,GAAG,OACxD,CAAC,MAAM;AAC3B,KAAI,IAAI,QAAQ,CAAC,IAAI,SAAS,IAAI,MAAM,WAAW,GAAG;AACpD,SAAO,KAAK,0BAA0B;AACtC,SAAO;;CAET,MAAM,OAAO,IAAI,MAAM;AACvB,QAAO,KACL,iCAAiC,2BAC/B,qBAAqB,OAAO,EAC5B,KACD,CAAC,aAAa,GAChB;AACD,QAAO;;;;;;;;;;;;;;;;;AAkBT,eAAsB,wBACpB,QACA,MAYe;CACf,MAAM,EAAE,WAAW,MAAM,YAAY,kBAAkB,MAAM,SAAS,gBAAgB;CAGtF,MAAM,SAAS,MAAM,SAAS,QAAQ,WAAW,gBAAgB,MAAM,WAAW,EAAE,OAAO;AAC3F,KAAI,CAAC,QAAQ;AACX,SAAO,KAAK,sDAAsD;AAClE,SAAO,8BAAc,IAAI,MAAM,CAAC;;CAElC,MAAM,gBAAgB,2BAA2B,MAAM,OAAO;AAC9D,QAAO,KAAK,mBAAmB,cAAc,aAAa,GAAG;CAG7D,MAAM,YAAY;EAAC;EAAG;EAAG;EAAG;CAC5B,IAAI,cAAc;CAClB,IAAI,SAAS,UAAU,KAAM;CAE7B,IAAI,mBAAmB;CACvB,IAAI,qBAAkC;AAGtC,QAAO,MAAM;EACX,MAAM,aACJ,cAAc,UAAU,SACpB,IAAI,KAAK,cAAc,SAAS,GAAG,UAAU,eAAgB,OAAO,GACpE,IAAI,KAAK,cAAc,SAAS,GAAG,OAAO;AAKhD,OADG,8BAAc,IAAI,MAAM,CAAC,CAAC,SAAS,GAAG,cAAc,WAAW,CAAC,SAAS,IAAI,SAChE,iBAAiB;AAC/B,UAAO,KACL,6CAA6C,gBAAgB,yBAC9D;AACD,wBAAqB;AACrB;;AAGF,SAAO,KACL,kBAAkB,WAAW,aAAa,CAAC,cACzC,cAAc,UAAU,SACpB,GAAG,UAAU,aAAc,KAC3B,GAAG,KAAK,MAAM,SAAS,OAAO,CAAC,GACpC,IACF;EAED,MAAM,MAAM,MAAM,SAChB,QACA,WACA,gBAAgB,MAAM,YAAY,WAAW,aAAa,CAAC,EAC3D,OACD;AAED,MAAI,KAAK;AACP,sBAAmB,2BAA2B,MAAM,IAAI;AACxD,UAAO,KACL,yBAAyB,iBAAiB,aAAa,CAAC,2BACzD;AAED,OAAI,cAAc,UAAU,SAAS,GAAG;AACtC,mBAAe;AACf,aAAS,UAAU,eAAgB;cAC1B,gBAAgB,UAAU,SAAS,GAAG;AAC/C,mBAAe;AACf,aAAS,UAAU,UAAU,SAAS,KAAM,IAAI;SAEhD,WAAU;AAGZ;;AAIF,uBAAqB;AACrB,SAAO,KAAK,oBAAoB,WAAW,aAAa,CAAC,mCAAmC;AAC5F;;AAIF,KAAI,CAAC,mBACH,sBAAqB,IAAI,KAAK,iBAAiB,SAAS,GAAG,OAAO;CAOpE,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,UAAU,KAAK,IAAI,QAAQ,KAAK,OAAO,GAAG,SAAS,GAAG,GAAG,SAAS,IAAI,GAAG,CAAC;AAC9E,QAAO,KACL,+CAA+C,GAAG,aAAa,CAAC,SAAS,GAAG,aAAa,CAAC,QAAQ,KAAK,MACrG,UAAU,OACX,CAAC,GACH;AAGD,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;EAC7B,MAAM,QAAQ,IAAI,KAAK,GAAG,SAAS,GAAG,QAAQ;AAC9C,MAAI,MAAM,SAAS,IAAI,GAAG,SAAS,CAAE;AAErC,SAAO,KAAK,+BAA+B,MAAM,aAAa,CAAC,GAAG;EAClE,MAAM,MAAM,MAAM,SAChB,QACA,WACA,gBAAgB,MAAM,YAAY,MAAM,aAAa,CAAC,EACtD,OACD;AAED,MAAI,KAAK;AAEP,QAAK,2BAA2B,MAAM,IAAI;AAC1C,UAAO,KAAK,iBAAiB,GAAG,aAAa,CAAC,8CAA8C;AAC5F,aAAU,KAAK,IAAI,QAAQ,KAAK,MAAM,UAAU,EAAE,CAAC;SAC9C;AAEL,MAAG,QAAQ,MAAM,SAAS,CAAC;AAC3B,UAAO,KAAK,0CAA0C,GAAG,aAAa,CAAC,sBAAsB;AAC7F,aAAU,KAAK,IAAI,GAAG,SAAS,GAAG,GAAG,SAAS,EAAE,UAAU,EAAE;AAC5D,OAAI,UAAU,OAAQ,WAAU;;AAGlC,MAAI,GAAG,SAAS,GAAG,GAAG,SAAS,IAAI,OAAQ;;AAI7C,QAAO,GAAG,SAAS,GAAG,GAAG,SAAS,GAAG,QAAQ;EAC3C,MAAM,MAAM,IAAI,KAAK,GAAG,SAAS,GAAG,KAAK,OAAO,GAAG,SAAS,GAAG,GAAG,SAAS,IAAI,EAAE,CAAC;AAClF,SAAO,KAAK,uBAAuB,IAAI,aAAa,CAAC,GAAG;EAExD,MAAM,MAAM,MAAM,SAChB,QACA,WACA,gBAAgB,MAAM,YAAY,IAAI,aAAa,CAAC,EACpD,OACD;AAED,MAAI,KAAK;GACP,MAAM,OAAO,2BAA2B,MAAM,IAAI;AAClD,UAAO,KAAK,gCAAgC,KAAK,aAAa,CAAC,GAAG;AAClE,QAAK;SACA;AACL,UAAO,KAAK,gCAAgC;AAC5C,QAAK;;;CAIT,MAAM,cAAc,cAAc,GAAG;AACrC,QAAO,KACL,kCAAkC,YAAY,aAAa,CAAC,cAAc,GAAG,aAAa,CAAC,IAC5F;AACD,QAAO;;;;;;;;;;;AAYT,eAAsB,sBACpB,QACA,MAYe;CACf,MAAM,EAAE,WAAW,MAAM,YAAY,SAAS,gBAAgB;AAE9D,QAAO,KAAK,+CAA+C;CAC3D,MAAM,SAAS,MAAM,SAAS,QAAQ,WAAW,gBAAgB,MAAM,WAAW,EAAE,OAAO;AAC3F,KAAI,CAAC,QAAQ;AACX,SAAO,KAAK,2DAA2D;AACvE,SAAO,8BAAc,IAAI,MAAM,CAAC;;CAGlC,MAAM,OAAO,2BAA2B,MAAM,OAAO;AACrD,QAAO,KAAK,4BAA4B,KAAK,aAAa,CAAC,GAAG;CAE9D,MAAM,YAAY,cAAc,KAAK;AACrC,QAAO,KACL,gCAAgC,UAAU,aAAa,CAAC,gBAAgB,KAAK,aAAa,CAAC,GAC5F;AAED,QAAO;;;;;;;;;;;;;;;;AClTT,eAAsB,wBACpB,QACA,EACE,WACA,WAAW,EAAE,EACb,QAAQ,IACR,SACA,SAAS,eAY6B;CACxC,MAAM,YAA2C,EAAE;CAGnD,IAAI;CAGJ,MAAM,YACJ,aACC,OAAO,KAAK,SAAS,CAAC,SAAS,KAC7B,SAAS,UAAU,OAAO,KAAK,SAAS,OAAO,CAAC,SAAS;CAG9D,MAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,SAAS,GAAG,CAAC;AAKvD,QAAO,MAAM;EACX,MAAM,OAOF,EAAE,OAAO,UAAU;AAEvB,MAAI,UACF,MAAK,SAAS;AAEhB,MAAI,OACF,MAAK,SAAS;EAqBhB,MAAM,EAAE,OAAO,QAAQ,eAAe,YAAY,2BAA2B,MAlBtD,mBACrB,0BAEE,OACG,KAAK,kBAAkB,UAAU,SAAS,EACzC,MAAM,MACP,CAAC,CACD,MAAM,EACX;GACE;GACA,UAAU,SAAS,QAAQ,YAAY;AACrC,WAAO,KACL,iBAAiB,QAAQ,6CAA6C,UACvE;;GAEJ,CACF,CAEqF;AAEtF,MAAI,CAAC,SAAS,MAAM,WAAW,EAC7B;AAGF,MAAI,QACF,OAAM,QAAQ,MAAM;MAEpB,WAAU,KAAK,GAAG,MAAM;AAG1B,MAAI,CAAC,WACH;AAEF,WAAS;;AAGX,QAAO,UAAU,EAAE,GAAG;;;;;;;;;;;AC9FxB,eAAsB,6BACpB,QACA,EACE,aACA,cACA,cAAc,OACd,cAAc,IACd,SAAS,aACT,cAkBsC;CACxC,MAAM,UAAyC,EAAE;CACjD,MAAM,qBAAqB,MAAM,aAAa,IAAI;CAElD,MAAM,sBAAK,IAAI,MAAM,EAAC,SAAS;CAE/B,IAAI,QAAQ;AACZ,OAAM,IACJ,oBACA,OAAO,UAAU;EAsBf,MAAM,SAAS,YAAY,2BAA2B,MArB9B,mBACtB,0BAEE,OACG,KAAK,kBAAkB,aAAa,SAAS,EAC5C,MAAM;GACJ,QAAQ,EAAE,aAAa,OAAO;GAC9B,OAAO,MAAM;GACd,EACF,CAAC,CACD,MAAM,EACX;GACE;GACA,UAAU,SAAS,MAAM,QAAQ;AAC/B,WAAO,KACL,sBAAsB,MAAM,OAAO,aAAa,aAAa,WAAW,QAAQ,IAAI,MACrF;;GAEJ,CACF,CAE+D;AAChE,UAAQ,KAAK,GAAG,OAAO,MAAM;AAC7B,WAAS,MAAM;AACf,eAAa,OAAO,YAAY,OAAO;IAEzC,EACE,aACD,CACF;CAGD,MAAM,6BADK,IAAI,MAAM,EAAC,SACF,GAAG;AAEvB,KAAI,CAAC,YACH,QAAO,KAAK,0BAA0B,YAAY,IAAK,YAAY;AAGrE,QAAO;;;;;;;;;;;;ACpET,SAAS,YACP,MACA,MACA,QACwB;AACxB,KAAI,SAAS,YACX,QAAO;EACL,GAAG;EACH,gBAAgB,OAAO,kBAAkB,KAAK;EAC9C,iBAAiB,OAAO,mBAAmB,KAAK;EAEhD,QAAQ,KAAA;EACT;AAGH,QAAO;EACL,GAAG;EACH,QAAQ;GACN,GAAG,KAAK;GACR,GAAI,OAAO,QAAQ,eAAe,EAAE,cAAc,OAAO,OAAO,cAAc,GAAG,EAAE;GACnF,GAAI,OAAO,QAAQ,gBAAgB,EAAE,eAAe,OAAO,OAAO,eAAe,GAAG,EAAE;GACvF;EAED,gBAAgB,KAAA;EAChB,iBAAiB,KAAA;EAClB;;;;;;;;;;;;;AAcH,eAAsB,+BACpB,QACA,EACE,WACA,WAAW,EAAE,EACb,QAAQ,IACR,oBAAoB,IACpB,YAAY,KACZ,kBAAkB,MAClB,SACA,SAAS,aACT,cAsBsC;CACxC,MAAM,OAAkB,qBAAqB,SAAS;AACtD,QAAO,KACL,6CACE,SAAS,cAAc,cAAc,mBACtC,KACF;CAGD,IAAI,EAAE,OAAO,WAAW,2BAA2B,MAAM,SAAS;AAClE,QAAO,KACL,yBAAyB,OAAO,aAAa,IAAI,YAAY,UAC3D,QAAQ,aAAa,IAAI,cAE5B;AAED,KAAI,CAAC,SAAS,CAAC,QAAQ;AACrB,MAAI,CAAC,OAAO;AACV,UAAO,KAAK,oDAAoD,UAAU,KAAK;AAC/E,WAAQ,MAAM,wBAAwB,QAAQ;IAC5C;IACA;IACA,YAAY;IACZ;IACA;IACD,CAAC;AACF,UAAO,KAAK,sCAAsC,MAAM,aAAa,GAAG;;AAE1E,MAAI,CAAC,QAAQ;AACX,UAAO,KAAK,kDAAkD,UAAU,KAAK;GAC7E,MAAM,YAAY,MAAM,sBAAsB,QAAQ;IACpD;IACA;IACA,YAAY;IACZ,UAAU;IACV;IACD,CAAC;AAEF,YAAS,WAAW,WAAW,EAAE;AACjC,UAAO,KAAK,oCAAoC,UAAU,aAAa,GAAG;;;AAI9E,QAAO,KAAK,6BAA6B,MAAM,aAAa,CAAC,UAAU,OAAO,aAAa,GAAG;CAG9F,MAAM,SAAS,mBAAmB,MAAM,OAAO,QAAQ,UAAU;AAEjE,QAAO,KACL,+CAA+C,UAAU,MAAM,OAAO,OAAO,YAC9E;CAED,IAAI,YAAY;CAChB,IAAI,UAAU;CAEd,MAAM,KAAK,KAAK,KAAK;CACrB,MAAM,WAAW,cAAc,MAAM;CAGrC,MAAM,MAAqC,EAAE;AAE7C,OAAMC,IACJ,OAAO,KAAK,cAAc,SAAS;EAAE;EAAc;EAAK,EAAE,EAC1D,OAAO,EAAE,mBAAmB;EAC1B,MAAM,SAAS,YAAY,MAAM,UAAU,aAAa;AAGxD,aAAW,MAAM,QAAQ,oBAAoB,QAAQ,WAAW,QAAQ,UAAU,OAAO,EAAE;AACzF,cAAW,KAAK;AAChB,gBAAa,WAAW,OAAO,QAAQ,QAAQ;AAE/C,OAAI,QACF,OAAM,QAAQ,KAAK;OAEnB,KAAI,KAAK,GAAG,KAAK;;AAIrB,eAAa;AACb,eAAa,WAAW,OAAO,QAAQ,QAAQ;IAEjD,EAAE,aAAa,KAAK,IAAI,GAAG,kBAAkB,EAAE,CAChD;AAED,cAAa,WAAW,OAAO,QAAQ,QAAQ;AAE/C,QAAO,KACL,WAAW,QAAQ,6CAA6C,UAAU,OACvE,KAAK,KAAK,GAAG,MAAM,IACrB,IACF;AAED,QAAO,UAAU,EAAE,GAAG;;;;;;;;;;;ACpKxB,eAAsB,kBACpB,QACA,EAAE,UACsC;CACxC,MAAM,CAAC,UAAU,kBAAkB,eAAe,MAAM,QAAQ,IAAI;EAClE,iBAAiB,QAAQ,EAAE,QAAQ,CAAC;EACpC,yBAAyB,QAAQ,EAAE,QAAQ,CAAC;EAC5C,oBAAoB,QAAQ,EAAE,QAAQ,CAAC;EACxC,CAAC;AACF,QAAO;EAAE;EAAU;EAAkB;EAAa;;;;AC7BpD,MAAa,2BAAyC,MAAM,GAAG;;;;;;;;;;;;;EAa7D;AAEF,MAAa,4CAA0D,MAAM,GAAG;;;;;;;;;;;EAW9E;;;ACTF,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,+BACpB,QACA,UAGI,EAAE,EAC4B;CAClC,MAAM,EAAE,WAAW;CACnB,MAAM,yBAAkD,EAAE;CAC1D,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,wBAAwB,EAAE,YACxB,MAAM,mBAMP,QAAQ,0BAA0B;GACnC,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,yBAAuB,KAAK,GAAG,MAAM;AACrC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,uBAAuB,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AC/C5E,MAAM,iBAAiB;;;;;;;;;AAUvB,eAAsB,qCACpB,QACA,cACA,UAGI,EAAE,EAC4B;CAClC,MAAM,EAAE,WAAW;CACnB,MAAM,UAAmC,EAAE;AAE3C,OAAM,UAAU,MAAM,cAAc,eAAe,EAAE,OAAO,UAAU;EACpE,MAAM,SAAS,MAAM,mBAMlB,QAAQ,2CAA2C;GACpD,WAAW,EACT,OAAO,EACL,wBAAwB,MAAM,KAAK,CAAC,aAAa,QAC/C,KACI;IAAE;IAAI,OAAO,YAAY;IAAO,GAChC;IAAE,MAAM,YAAY;IAAM,OAAO,YAAY;IAAO,CACzD,EACF,EACF;GACD;GACD,CAAC;AACF,UAAQ,KAAK,GAAG,OAAO,qCAAqC,uBAAuB;GACnF;AAEF,QAAO;;;;;;;;;;AAWT,eAAsB,2BACpB,QACA,cACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,WAAW;AACnB,SAAQ,KAAK,YAAY,aAAa,OAAO,+BAA+B;CAG5E,MAAM,oBAAoB,MAAM,MADT,+BAA+B,QAAQ,EAAE,QAAQ,CAAC,EAC/B,OAAO;CAEjD,MAAM,sBAAsB,aAAa,QAAQ,qBAAqB;EACpE,MAAM,QAAQ,kBAAkB,iBAAiB;AACjD,SAAO,CAAC,SAAS,MAAM,MAAM,mBAAmB,iBAAiB;GACjE;AAEF,KAAI,oBAAoB,WAAW,GAAG;AACpC,UAAQ,KAAK,aAAa,aAAa,OAAO,sCAAsC;AACpF,SAAO;;AAGT,KAAI;AACF,QAAM,qCACJ,QACA,oBAAoB,KAAK,qBAAqB,CAC5C,kBACA,kBAAkB,iBAAiB,OAAO,GAC3C,CAAC,EACF,EAAE,QAAQ,CACX;AACD,UAAQ,KACN,wBAAwB,oBAAoB,OAAO,8BAA8B,aAAa,SAAS,oBAAoB,OAAO,sBACnI;AACD,SAAO;UACA,KAAK;AACZ,UAAQ,MAAM,8CAA+C,IAAc,UAAU;AACrF,SAAO;;;;;ACtFX,MAAM,iCAAsD,IAAI,IAC9D,OAAO,OAAO,oBAAoB,CACnC;;;;;;;AAQD,SAAS,6BAA6B,cAA+B;AACnE,QAAO,+BAA+B,IAAI,aAAa;;;;;;;;;AAUzD,SAAS,mBACP,MACA,OACS;CACT,MAAM,iBAAiB,CAAC,GAAI,QAAQ,EAAE,CAAE,CAAC,MAAM;CAC/C,MAAM,kBAAkB,CAAC,GAAG,MAAM,CAAC,MAAM;AACzC,QACE,eAAe,WAAW,gBAAgB,UAC1C,eAAe,OAAO,QAAQ,UAAU,WAAW,gBAAgB,OAAO;;;;;;;;;;;AAa9E,SAAS,wBACP,OACA,OACA,UAGI,EAAE,EACgD;CACtD,MAAM,EAAE,WAAW;CACnB,MAAM,YAAY,6BAA6B,MAAM,aAAa;CAClE,MAAM,cAAuC,EAAE,IAAI,MAAM,IAAI;AAE7D,KAAI,MAAM,SAAS,MAAM,KACvB,aAAY,OAAO,MAAM;AAE3B,KAAI,MAAM,UAAU,MAAM,MACxB,aAAY,QAAQ,MAAM;AAE5B,KAAI,MAAM,gBAAgB,MAAM,YAC9B,aAAY,cAAc,MAAM;AAElC,KAAI,MAAM,iBAAiB,MAAM,SAC/B,aAAY,WAAW,MAAM;AAE/B,KAAI,MAAM,qBAAqB,MAAM,aACnC,aAAY,eAAe,MAAM;AAEnC,KAAI,MAAM,8BAA8B,MAAM,oBAC5C,aAAY,sBAAsB,MAAM;AAE1C,KAAI,MAAM,kBAAkB,MAAM,UAChC,aAAY,YAAY,MAAM;AAEhC,KAAI,CAAC,mBAAmB,MAAM,oBAAoB,MAAM,cAAc,CACpE,aAAY,gBAAgB,MAAM;AAGpC,KAAI,WAAW;AACb,MAAI,MAAM,iBAAiB,MAAM,aAC/B,SAAQ,KACN,oDAAoD,MAAM,aAAa,+DAExE;AAEH,MAAI,MAAM,+BAA+B,MAAM,qBAC7C,SAAQ,KACN,+DAA+D,MAAM,aAAa,+DAEnF;QAEE;AACL,MAAI,MAAM,iBAAiB,MAAM,aAC/B,aAAY,eAAe,MAAM;AAEnC,MAAI,MAAM,+BAA+B,MAAM,qBAC7C,aAAY,uBAAuB,MAAM;;AAK7C,QAAO;EAAE,OAAO;EAAa,SADb,OAAO,KAAK,YAAY,CAAC,WAAW;EACd;;;;;;;;;;;AAYxC,eAAsB,aACpB,QACA,UACA,UAGI,EAAE,EACuB;CAC7B,MAAM,EAAE,WAAW;AACnB,SAAQ,KAAK,YAAY,SAAS,OAAO,eAAe;CAExD,MAAM,WAAW,MAAM,iBAAiB,QAAQ,EAAE,QAAQ,CAAC;CAC3D,MAAM,yBAAyB,MAAM,UAAU,eAAe;CAC9D,MAAM,0BAAkD,OAAO,YAC7D,SAAS,KAAK,EAAE,cAAc,SAAS,CAAC,cAAc,GAAG,CAAC,CAC3D;CAED,IAAI,UAAU;AACd,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,QAAQ,uBAAuB,QAAQ;AAC7C,MAAI;AACF,OAAI,OAAO;IACT,MAAM,EAAE,OAAO,aAAa,YAAY,wBAAwB,OAAO,SAAS,EAC9E,QACD,CAAC;AACF,QAAI,SAAS;AACX,aAAQ,KACN,qBAAqB,QAAQ,aAAa,qDAC3C;AACD,6BAAwB,QAAQ,gBAAgB,MAAM;AACtD;;AAGF,UAAM,mBAAmB,QAAQ,gBAAgB;KAC/C,WAAW,EAAE,OAAO,aAAa;KACjC;KACD,CAAC;AACF,4BAAwB,QAAQ,gBAAgB,MAAM;UACjD;IACL,MAAM,EACJ,eAAe,EAAE,SAAS,cACxB,MAAM,mBAWP,QAAQ,gBAAgB;KACzB,WAAW,EACT,OAAO;MACL,MAAM,QAAQ;MACd,aAAa,QAAQ,eAAe;MACpC,cAAc,QAAQ;MACtB,UAAU,QAAQ;MAClB,cAAc,QAAQ;MACtB,cAAc,QAAQ;MACtB,qBAAqB,QAAQ;MAC7B,sBAAsB,QAAQ;MAC9B,WAAW,QAAQ;MACnB,eAAe,QAAQ;MACxB,EACF;KACD;KACD,CAAC;AACF,4BAAwB,QAAQ,gBAAgB,QAAQ;AAGxD,QAAI,QAAQ,UAAU,KAAA,EACpB,OAAM,mBAAmB,QAAQ,gBAAgB;KAC/C,WAAW,EAAE,OAAO;MAAE,IAAI,QAAQ;MAAI,OAAO,QAAQ;MAAO,EAAE;KAC9D;KACD,CAAC;;WAGC,KAAK;AACZ,aAAU;AACV,WAAQ,MACN,2BAA2B,QAAQ,aAAa,OAAQ,IAAc,UACvE;;;AAIL,KAAI,QACF,SAAQ,KAAK,wBAAwB,SAAS,OAAO,aAAa;AAEpE,QAAO;EAAE;EAAS;EAAyB;;;;;;;;;;;;ACxM7C,SAAS,gCACP,OACA,WACA,0BACyB;CACzB,MAAM,QAAiC;EACrC,OAAO,MAAM;EACb,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,oBAAoB,MAAM;EAC1B;EACD;AACD,KAAI,MAAM,8BAA8B,KAAA,EACtC,OAAM,sBAAsB,MAAM;AAEpC,KAAI,MAAM,6BAA6B,KAAA,EACrC,OAAM,uBAAuB,MAAM;AAErC,KAAI,MAAM,SAAS,oBAAoB,WAAW,yBAAyB,SAAS,EAClF,OAAM,2BAA2B;AAEnC,QAAO;;;;;;;;;;;AAYT,SAAS,gCACP,YACA,OACA,0BACyB;CACzB,MAAM,QAAiC;EACrC,IAAI;EACJ,OAAO,MAAM;EACb,oBAAoB,MAAM;EAC3B;AACD,KAAI,MAAM,8BAA8B,KAAA,EACtC,OAAM,sBAAsB,MAAM;AAEpC,KAAI,MAAM,6BAA6B,KAAA,EACrC,OAAM,uBAAuB,MAAM;AAErC,KAAI,MAAM,SAAS,oBAAoB,WAAW,yBAAyB,SAAS,EAClF,OAAM,2BAA2B;AAEnC,QAAO;;;;;;;;;;;AAYT,eAAsB,qBACpB,QACA,QACA,UAKI,EAAE,EACY;CAClB,MAAM,EAAE,WAAW;AACnB,SAAQ,KAAK,YAAY,OAAO,OAAO,wBAAwB;CAG/D,IAAI,EAAE,4BAA4B;AAClC,KAAI,CAAC,yBAAyB;EAC5B,MAAM,WAAW,MAAM,iBAAiB,QAAQ,EAAE,QAAQ,CAAC;AAC3D,4BAA0B,OAAO,YAC/B,SAAS,KAAK,EAAE,cAAc,SAAS,CAAC,cAAc,GAAG,CAAC,CAC3D;;CAIH,MAAM,uBAAuB,MAAM,+BAA+B,QAAQ,EAAE,QAAQ,CAAC;CACrF,MAAM,iBAAyC,OAAO,YACpD,qBAAqB,KAAK,EAAE,MAAM,SAAS,CAAC,MAAM,GAAG,CAAC,CACvD;CACD,MAAM,gBAAgB,OACpB,OACG,QAAQ,UAAU,MAAM,SAAS,oBAAoB,QAAQ,CAC7D,SAAS,UAAU,MAAM,WAAW,EAAE,CAAC,EAC1C,OACD,CAAC,QAAQ,gBAAgB;EACxB,MAAM,QAAQ,qBAAqB,MAAM,EAAE,WAAW,SAAS,YAAY,KAAK;AAChF,SAAO,CAAC,SAAS,MAAM,MAAM,mBAAmB,YAAY;GAC5D;AACF,KAAI,cAAc,SAAS,EACzB,KAAI;AAMF,GAAA,MALuB,qCACrB,QACA,cAAc,KAAK,gBAAgB,CAAC,aAAa,eAAe,YAAY,MAAM,CAAC,EACnF,EAAE,QAAQ,CACX,EACQ,SAAS,EAAE,MAAM,SAAS;AACjC,kBAAe,QAAQ;IACvB;UACK,KAAK;AACZ,UAAQ,MAAM,qDAAsD,IAAc,UAAU;AAC5F,SAAO;;CAMX,MAAM,iBAAiB,MACrB,MAF2B,yBAAyB,QAAQ,EAAE,QAAQ,CAAC,GAGtE,EAAE,SAAS,WAAW,GAAG,QAAQ,aAAa,GAAG,OACnD;CAED,IAAI,UAAU;AACd,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,eAAe,MAAM;EAC3B,MAAM,YAAY,wBAAwB;AAC1C,MAAI,CAAC,WAAW;AACd,aAAU;AACV,WAAQ,MACN,oCAAoC,MAAM,MAAM,wBAAwB,aAAa,GACtF;AACD;;AAGF,MAAI,MAAM,SAAS,oBAAoB,YAAY,MAAM,WAAW,EAAE,EAAE,SAAS,GAAG;AAClF,aAAU;AACV,WAAQ,MACN,oCAAoC,MAAM,MAAM,0GACjD;AACD;;EAGF,MAAM,WAAW,eAAe,GAAG,aAAa,GAAG,MAAM;AAEzD,MAAI,YAAY,SAAS,SAAS,MAAM,MAAM;AAC5C,aAAU;AACV,WAAQ,MACN,oCAAoC,MAAM,MAAM,iDAAiD,SAAS,KAAK,QAAQ,MAAM,KAAK,yBACnI;AACD;;EAGF,MAAM,2BACJ,MAAM,SAAS,oBAAoB,UAC/B,EAAE,IACD,MAAM,WAAW,EAAE,EACjB,KAAK,gBAAgB,eAAe,YAAY,MAAM,CACtD,QAAQ,OAAqB,QAAQ,GAAG,CAAC;AAElD,MAAI;AAKF,SAAM,mBAAmB,QAAQ,mCAAmC;IAClE,WAAW,EAAE,OALD,WACV,gCAAgC,SAAS,IAAI,OAAO,yBAAyB,GAC7E,gCAAgC,OAAO,WAAW,yBAAyB,EAGzD;IACpB;IACD,CAAC;WACK,KAAK;AACZ,aAAU;AACV,WAAQ,MACN,oCAAoC,MAAM,MAAM,OAAQ,IAAc,UACvE;;;AAIL,KAAI,QACF,SAAQ,KAAK,wBAAwB,OAAO,OAAO,sBAAsB;AAE3E,QAAO;;;;ACtMT,MAAa,aAAa,GAAG;;;;;;;;;;;;;;AAe7B,MAAa,0BAA0B,GAAG;;;;;;;AAQ1C,MAAa,0BAA0B,GAAG;;;;;;;AAQ1C,MAAa,yBAAyB,GAAG;;;;;;;AAWzC,MAAa,mBAAmB,GAAG;;;;;;;;;;;;;;;;;AAkBnC,MAAa,mBAAmB,GAAG;;;;;;;;;;;;;;;;;AAkBnC,MAAa,mBAAmB,GAAG;;;;;;;;;;;;;;;;AAiBnC,MAAa,0BAA0B,GAAG;;;;;;;;;ACjE1C,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,wBACpB,QACA,SAS2B;CAC3B,MAAM,EACJ,QACA,UAAU,EAAE,qBACV;CACJ,MAAM,kBAAoC,EAAE;CAC5C,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,iBAAiB,EAAE,YACjB,MAAM,mBAMP,QAAQ,kBAAkB;GAC3B,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAgB;GACvD;GACD,CAAC;AACF,kBAAgB,KAAK,GAAG,MAAM;AAC9B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,gBAAgB,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;AAGrE,MAAa,uBAAuB,CAAC,iBAAiB,aAAa,iBAAiB,aAAa;;;;;;;AAQjG,eAAsB,mBACpB,QACA,UAGI,EAAE,EACgB;CACtB,MAAM,EAAE,WAAW;CACnB,MAAM,aAA0B,EAAE;CAClC,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,eAAe,EAAE,YACf,MAAM,mBAMP,QAAQ,YAAY;GACrB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,aAAW,KACT,GAAI,MAAM,QAAQ,IAChB,MAAM,IAAI,OAAO,UAAU;GACzB,GAAG;GACH,QAAQ,qBAAqB,SAAS,KAAK,KAAK,GAC5C,MAAM,wBAAwB,QAAQ;IACpC;IACA,UAAU,EAAE,gBAAgB,KAAK,IAAI;IACtC,CAAC,GACF,EAAE;GACP,EAAE,CACJ,CACF;AACD,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,WAAW,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACvIhE,MAAa,WAAW,GAAG;;;;;;;;;;;;;;AAe3B,MAAa,uBAAuB,GAAG;;;;;;;;;;;;;;;ACcvC,eAAsB,iBACpB,QACA,UAGI,EAAE,EACc;CACpB,MAAM,EAAE,WAAW;CACnB,MAAM,EAAE,uBAAuB,MAAM,mBAGlC,QAAQ,UAAU,EAAE,QAAQ,CAAC;AAChC,QAAO;;;;ACtCT,MAAa,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCxB,MAAa,4BAA4B,GAAG;;;;;;;;;;;;;;;;;;;;AAqB5C,MAAa,cAAc,GAAG;;;;;;;;;;AAW9B,MAAa,cAAc,GAAG;;;;;;;;;;;;ACxB9B,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,cACpB,QACA,UAGI,EAAE,EACW;CACjB,MAAM,EAAE,WAAW;CACnB,MAAM,QAAgB,EAAE;CACxB,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,OAAO,EAAE,YACP,MAAM,mBAMP,QAAQ,OAAO;GAChB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,QAAM,KAAK,GAAG,MAAM;AACpB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AC1E3D,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,6BACpB,QACA,UAUI,EAAE,EAC6B;CACnC,MAAM,EAAE,QAAQ,aAAa;CAC7B,MAAM,QAAkC,EAAE;CAC1C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,yBAAyB,EAAE,YACzB,MAAM,mBAMP,QAAQ,2BAA2B;GACpC,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAU;GACjD;GACD,CAAC;AACF,QAAM,KAAK,GAAG,MAAM;AACpB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;;;;;;;AAW3D,eAAsB,2BACpB,QACA,iBACA,UAGI,EAAE,EACiC;CACvC,MAAM,EAAE,WAAW;CACnB,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,gBAAgB,CAAC;CACjD,MAAM,sBAAoD,EAAE;AAE5D,MAAK,MAAM,QAAQ,aAAa;EAK9B,MAAM,SAAQ,MAJM,6BAA6B,QAAQ;GACvD;GACA,UAAU,EAAE,MAAM,MAAM;GACzB,CAAC,EACkB,MAAM,SAAS,KAAK,SAAS,KAAK;AACtD,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,sDAAsD,OAAO;AAE/E,sBAAoB,QAAQ,MAAM;;AAGpC,QAAO;;;;ACzFT,MAAa,QAAQ,GAAG;;;;;;;;;;;;;;;;;;ACUxB,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,cACpB,QACA,UAGI,EAAE,EACW;CACjB,MAAM,EAAE,WAAW;CACnB,MAAM,QAAgB,EAAE;CACxB,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,OAAO,EAAE,YACP,MAAM,mBAMP,QAAQ,OAAO;GAChB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,QAAM,KAAK,GAAG,MAAM;AACpB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;ACnD7D,MAAa,WAAW,GAAG;;;;;;;;;;;;;;;;AAiB3B,MAAa,iBAAiB,GAAG;;;;;;;;;;;AAYjC,MAAa,iBAAiB,GAAG;;;;;;;;;ACNjC,MAAMC,eAAY;AAElB,MAAM,aAAa;;;;;;;;AASnB,eAAsB,gBACpB,QACA,UAQI,EAAE,EACa;CACnB,MAAM,EAAE,SAAS,aAAa,UAAU,EAAE,WAAW,EAAE,KAAK;CAC5D,MAAM,UAAoB,EAAE;CAC5B,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,SAAS,EAAE,YACT,MAAM,mBAMP,QAAQ,UAAU;GACnB,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAQ;GAC/C;GACD,CAAC;AACF,UAAQ,KAAK,GAAG,MAAM;AACtB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AACT,QAAO,QAAQ,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;;;;;;;AAW/D,eAAsB,aACpB,QACA,UAOI,EAAE,EAC8B;CACpC,MAAM,EACJ,cAAc,EAAE,YAAY,aAAa,EAAE,EAAE,cAAc,YAAY,EAAE,KAAK,EAAE,EAChF,WAAW,OACX,SAAS,gBACP;AACJ,QAAO,KAAK,YAAY,WAAW,QAAQ,WAAW,OAAO,cAAc;CAC3E,MAAM,SAAS,WAAW,KAAK,EAAE,YAAY,MAAM;CACnD,MAAM,uBAAuB,KAC3B,UAAU,KAAK,SAAS,KAAK,iBAAiB,CAAC,QAAQ,MAAmB,CAAC,CAAC,EAAE,CAC/E;CACD,MAAM,oBAAoB,CAAC,GAAG,sBAAsB,GAAG,OAAO;CAC9D,MAAM,UAAU,MAAM,gBAAgB,QAAQ;EAC5C;EACA,UAAU,EACR,QAAQ,WAAW,KAAA,IAAY,CAAC,GAAG,sBAAsB,GAAG,OAAO,EACpE;EACF,CAAC;CAGF,MAAM,iBAAiB,MAAM,SAAS,QAAQ;CAG9C,MAAM,iBAAiB,WACrB,mBACA,QAAQ,KAAK,EAAE,YAAY,MAAM,CAClC;AAGD,KAAI,eAAe,SAAS,GAAG;AAC7B,SAAO,MACL,4BAA4B,eAAe,KACzC,SACD,CAAC,8CAA8C,aACjD;AACD,UAAQ,KAAK,EAAE;;AAEjB,QAAO;;;;;;;;;;AC5GT,SAAgB,cAAc,MAA+B;AAC3D,QAAO,KAAK,KAAK,EAAE,SAAS,0BAA0B;EACpD;EACA,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACrD,EAAE;;;;ACzBL,MAAa,yBAAyB,GAAG;;;;;;;;;;AAWzC,MAAa,QAAQ,GAAG;;;;;;;;;;;;;;;;;AAkBxB,MAAa,cAAc,GAAG;;;;;;;;;;;;;;;;ACA9B,eAAsB,UACpB,QACA,SAaC;CACD,MAAM,EAAE,OAAO,UAAU,WAAW;CAEpC,MAAM,EACJ,sBAAsB,EAAE,kBACtB,MAAM,mBAWP,QAAQ,wBAAwB;EACjC,WAAW,EAAE,OAAO;EACpB;EACD,CAAC;CAEF,MAAM,MAAM,MAAM,OAAO,WAStB,OAAO;EACR;EACA;EACA,WAAW,YAAY;EACxB,CAAC;CACF,MAAM,EACJ,OAAO,EAAE,WACP,IAAI;CAGR,MAAM,cAAc,IAAI,QAAQ,IAAI,aAAa;AACjD,KAAI,CAAC,eAAe,CAAC,YAAY,SAAS,UAAU,CAClD,OAAM,IAAI,MAAM,yCAAyC;AAG3D,QAAO;EACL,OAAO,KAAK;EACZ;EACD;;;;;;;;AASH,eAAsB,WACpB,QACA,SAQe;CACf,MAAM,EAAE,OAAO,QAAQ,WAAW;CAElC,MAAM,EACJ,sBAAsB,EAAE,kBACtB,MAAM,mBAWP,QAAQ,wBAAwB;EACjC,WAAW;GAAE;GAAO,QAAQ;GAAQ;EACpC;EACD,CAAC;AAEF,OAAM,OAAO,WAMV,aAAa;EACd,IAAI;EACJ,WAAW,YAAY;EACxB,CAAC;;;;;;;;;;;ACxHJ,eAAsB,aACpB,QACA,SAWwB;CACxB,MAAM,EACJ,OAAO,EAAE,OAAO,UAChB,WACE;CACJ,MAAM,EACJ,cAAc,EAAE,aACd,MAAM,mBAMP,QAAQ,gBAAgB;EAAE,WAAW,EAAE,OAAO;GAAE;GAAO;GAAQ,EAAE;EAAE;EAAQ,CAAC;AAE/E,QAAO;;;;;;;;AAST,eAAsB,aACpB,QACA,SAMe;CACf,MAAM,EAAE,IAAI,WAAW;AACvB,OAAM,mBAAmB,QAAQ,gBAAgB;EAAE,WAAW,EAAE,IAAI;EAAE;EAAQ,CAAC;;;;;;;;;;AC5CjF,eAAsB,sBACpB,QACA,OACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,WAAW;AACnB,OAAM,mBAAmB,QAAQ,yBAAyB;EACxD,WAAW,EAAE,OAAO;EACpB;EACD,CAAC;;;;;;;;;;;ACMJ,eAAsB,cACpB,QACA,WACA,EACE,mBACA,4BACA,SAAS,gBAQP,EAAE,EACS;CAEf,MAAM,QAAQ;EACZ,MAAM,UAAU;EAChB,WAAW,UAAU;EACtB;CAGD,IAAI;AACJ,KAAI,CAAC,mBAAmB;EACtB,MAAM,EACJ,oBAAoB,EAAE,mBACpB,MAAM,mBASP,QAAQ,kBAAkB;GAC3B,WAAW;IACT,MAAM,UAAU;IAChB,aAAa,UAAU;IACvB,GAAG;IACJ;GACD;GACD,CAAC;AACF,mBAAiB,aAAa;QACzB;AACL,QAAM,mBAAmB,QAAQ,kBAAkB;GACjD,WAAW;IACT,gBAAgB,kBAAkB;IAClC,aAAa,kBAAkB,WAAW,UAAU,cAAc,KAAA;IAClE,GAAG;IACJ;GACD;GACD,CAAC;AACF,mBAAiB,kBAAkB;;CAIrC,MAAM,uBAAuB,MAAM,mBAAmB,UAAU,EAAE,EAAE,OAAO;CAC3E,MAAM,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,KAAK,QAAQ,UAAU,UAAU,EAAE,GAAG,UAC/E,qBAAqB,MAAM,QAAQ,mBAAmB,YACvD;CACD,MAAM,gBAAgB,YACnB,mBAAmB,UAAU,EAAE,EAAE,KAAK,EAAE,WAAW,KAAK,GACxD,UAAU,UAAU,EAAE,EAAE,KAAK,EAAE,WAAW,KAAK,CACjD;AAGD,KAAI,UAAU,SAAS,GAAG;AACxB,QAAM,mBAAmB,QAAQ,yBAAyB;GACxD,WAAW,EACT,OAAO,UAAU,KAAK,EAAE,MAAM,GAAG,YAAY;IAC3C;IACA;IACA,GAAG;IACJ,EAAE,EACJ;GACD;GACD,CAAC;AACF,SAAO,KAAK,WAAW,UAAU,OAAO,mBAAmB;;AAI7D,KAAI,eAAe,SAAS,GAAG;AAC7B,QAAM,mBAAmB,QAAQ,yBAAyB;GACxD,WAAW,EACT,OAAO,eAAe,KAAK,EAAE,MAAM,GAAG,YAAY;IAChD,IAAI,qBAAqB,MAAO;IAChC;IACA,aAAa,qBAAqB,MAAO;IACzC,OAAO,qBAAqB,MAAO;IACnC,GAAG;IACH;IACD,EAAE,EACJ;GACD;GACD,CAAC;AACF,SAAO,KAAK,WAAW,eAAe,OAAO,mBAAmB;;AAIlE,KAAI,cAAc,SAAS,KAAK,4BAA4B;AAC1D,QAAM,IACJ,eACA,OAAO,UAAU;AACf,SAAM,mBAAmB,QAAQ,wBAAwB;IACvD,WAAW,EAAE,IAAI,qBAAqB,OAAQ,IAAI;IAClD;IACD,CAAC;KAEJ,EACE,aAAa,IACd,CACF;AACD,SAAO,KAAK,WAAW,cAAc,OAAO,mBAAmB;;;;;AC1InE,MAAMC,kBAAgB;;;;;;;AAQtB,eAAsB,mBACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,UAAU,SAAS,gBAAgB;AAElD,OAAM,UAAU,MAAM,UAAUA,gBAAc,EAAE,OAAO,SAAS;AAC9D,QAAM,mBAAmB,QAAQ,sBAAsB;GACrD,WAAW,EACT,UAAU,KAAK,KAAK,aAAa;IAC/B,GAAI,QAAQ,GAAG,SAAS,IAAI,GAAG,EAAE,GAAG,EAAE,IAAI,QAAQ,IAAI;IACtD,gBAAgB,QAAQ;IACxB,mBAAmB,QAAQ;IAC3B,cAAc,CAAC,QAAQ,eACnB,KAAA,IACA,OAAO,QAAQ,QAAQ,aAAa,CAAC,KAAK,CAAC,QAAQ,YAAY;KAC7D;KACA;KACD,EAAE;IACR,EAAE,EACJ;GACD;GACD,CAAC;GACF;;;;;;;;;AAUJ,eAAsB,iBACpB,QACA,UACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI,mBAAmB;AACvB,QAAO,KAAK,YAAY,SAAS,OAAO,eAAe;CAGvD,MAAM,YAAY,SAAS,QACxB,YAAY,SAAS,QAAQ,QAAQ,QAAQ,OAAO,IAAI,GAAG,CAAC,SAAS,EACvE;AACD,KAAI,UAAU,SAAS,EACrB,OAAM,IAAI,MACR,qEAAqE,UAClE,KAAK,EAAE,SAAS,GAAG,CACnB,KAAK,IAAI,GACb;AAGH,KAAI;AACF,SAAO,KAAK,cAAc,SAAS,OAAO,mBAAmB;AAC7D,QAAM,mBAAmB,QAAQ;GAAE,OAAO;GAAU;GAAQ,CAAC;AAC7D,SAAO,KAAK,uBAAuB,SAAS,OAAO,YAAY;UACxD,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,gCAAiC,IAAc,UAAU;;AAGxE,QAAO,CAAC;;;;;;;;;;;AC/DV,SAAS,oBACP,MACA,qBAkBA;CACA,MAAM,iBAAiB,KAAK;CAC5B,MAAM,aAAa,iBAAiB,oBAAoB,kBAAkB,KAAA;AAE1E,QAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,UAAU,KAAK;EACf,eAAe,KAAK;EACpB,UAAU,KAAK;EACf,QAAQ,KAAK;EACb,YAAY,KAAK;EACjB,GAAI,aAAa,EAAE,YAAY,GAAG,EAAE;EACrC;;;;;;;;;;AAWH,eAAsB,WACpB,QACA,SAQoC;CACpC,MAAM,EAAE,OAAO,MAAM,sBAAsB,EAAE,EAAE,SAAS,gBAAgB;CAGxE,MAAM,EAAE,eAAe,MAAM,mBAM1B,QAAQ,aAAa;EACtB,WAAW,EAAE,OATD,oBAAoB,MAAM,oBASpB,EAAE;EACpB;EACD,CAAC;AACF,QAAO,WAAW;;;;;;;;;AAUpB,eAAsB,WACpB,QACA,SAWoC;CACpC,MAAM,EAAE,OAAO,MAAM,sBAAsB,EAAE,EAAE,SAAS,gBAAgB;CACxE,MAAM,SAAS,KAAK;CACpB,MAAM,EAAE,eAAe,MAAM,mBAM1B,QAAQ,aAAa;EACtB,WAAW,EACT,OAAO;GACL,IAAI;GACJ,GAAG,oBAAoB,MAAM,oBAAoB;GAClD,EACF;EACD;EACD,CAAC;AACF,QAAO,WAAW;;;;;;;;;;AAWpB,eAAsB,UACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAO,KAAK,YAAY,OAAO,OAAO,YAAY;CAElD,IAAI,mBAAmB;CAEvB,MAAM,kBAAkB,OACrB,KAAK,UAAU,MAAM,oBAAoB,CACzC,QAAQ,SAAyB,CAAC,CAAC,KAAK;CAC3C,IAAI,sBAAoD,EAAE;AAC1D,KAAI,gBAAgB,SAAS,EAC3B,KAAI;AACF,wBAAsB,MAAM,2BAA2B,QAAQ,iBAAiB,EAAE,QAAQ,CAAC;UACpF,KAAK;AACZ,SAAO,MAAM,iDAAkD,IAAc,UAAU;AACvF,SAAO;;CAQX,MAAM,cAA4D,MAAM,MAH5C,cAAc,QAAQ,EAAE,QAAQ,CAAC,EAG0B,OAAO;CAG9F,MAAM,WAAW,OAAO,QAAQ,UAAU,CAAC,YAAY,MAAM,MAAM;CACnE,MAAM,eAAe,OAAO,QAAQ,UAAU,CAAC,CAAC,YAAY,MAAM,MAAM;AAGxE,OAAM,UAAU,UAAU,OAAO,SAAS;AACxC,MAAI;GACF,MAAM,UAAU,MAAM,WAAW,QAAQ;IAAE,OAAO;IAAM;IAAqB;IAAQ,CAAC;AACtF,eAAY,QAAQ,QAAQ;AAC5B,UAAO,KAAK,8BAA8B,KAAK,KAAK,IAAI;WACjD,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,wBAAwB,KAAK,KAAK,OAAQ,IAAc,UAAU;;GAEjF;AAGF,OAAM,UAAU,cAAc,OAAO,UAAU;AAC7C,MAAI;GACF,MAAM,UAAU,MAAM,WAAW,QAAQ;IACvC,OAAO;KAAE,GAAG;KAAO,IAAI,YAAY,MAAM,MAAO;KAAI;IACpD;IACA;IACD,CAAC;AACF,eAAY,QAAQ,QAAQ;AAC5B,UAAO,KAAK,8BAA8B,MAAM,KAAK,IAAI;WAClD,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,wBAAwB,MAAM,KAAK,OAAQ,IAAc,UAAU;;GAElF;AAEF,QAAO,CAAC;;;;ACxNV,MAAa,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;ACI1C,SAAgB,kCAAkC,cAA2B;AAC3E,QAAQ,IAAuB,OAAO,EACpC,WAAW,cACZ,CAAC;;;;ACIJ,MAAa,2BAA2B,GAAG;;;;;;;;;AA4D3C,MAAa,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;AAiDxC,MAAa,8BAA8B,GAAG;;;;;;;;;;;;AAwB9C,MAAa,yBAAyB,GAAG;;;;;;;;;AAUzC,MAAa,iCAAiC,GAAG;;;;;;;AAQjD,MAAa,mCAAmC,GAAG;;;;;;;;;;AAWnD,MAAa,yBAAyB,GAAG;;;;;;;AAQzC,MAAa,iCAAiC,GAAG;;;;;;;AAQjD,MAAa,mCAAmC,GAAG;;;;;;;AAQnD,MAAa,sBAAsB,GAAG;;;;;;;AAQtC,MAAa,gCAAgC,GAAG;;;;;;;AAQhD,MAAa,+BAA+B,GAAG;;;;;;;AAQ/C,MAAa,sCAAsC,GAAG;;;;;;;;;AAUtD,MAAa,4BAA4B,GAAG;;;;;;;AAQ5C,MAAa,+BAA+B,GAAG;;;;;;;AAQ/C,MAAa,4BAA4B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0D5C,MAAa,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqK1C,MAAa,4BAA4B,GAAG;;;;;;;;;AAU5C,MAAa,4BAA4B,GAAG;;;;;;;;;AAU5C,MAAa,0BAA0B,GAAG;;;;;;;;;;AAW1C,MAAa,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;AC5e1C,eAAsB,qBACpB,QACA,OAMA,UAGI,EAAE,EACS;AACf,OAAM,mBAAmB,QAAQ,wBAAwB;EACvD,WAAW;GAAE,gBAAgB,MAAM;GAAI,YAAY,MAAM;GAAY;EACrE,QAAQ,QAAQ;EACjB,CAAC;;;;;;;;AASJ,eAAsB,6BACpB,QACA,SAWe;CACf,MAAM,EAAE,OAAO,gBAAgB,WAAW;AAC1C,OAAM,mBAAmB,QAAQ,kCAAkC;EACjE,WAAW;GAAE,gBAAgB,eAAe;GAAI,YAAY,eAAe;GAAY;EACvF;EACD,CAAC;;;;;;;;;;;AC5CJ,eAAsB,sBACpB,QACA,UAKI,EAAE,EACW;CACjB,MAAM,EACJ,gBAAgB,EAAE,qBAChB,MAAM,mBASP,QAAQ,0BAA0B;EACnC,QAAQ,QAAQ;EAChB,YAAY,QAAQ;EACrB,CAAC;AACF,QAAO,eAAe;;;;;AC1BxB,MAAa,iBAAiB;;;;;;AAiB9B,MAAa,0BAA0B;;;;;;;ACqFvC,MAAa,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;qBAuBL,wBAAwB;;oBAEzB,eAAe;;;;mBAIhB,aAAa;kBACd,YAAY;4BACF,uBAAuB;;;;;;;;;;;;;;;;AA4BnD,MAAa,2BAA2B,GAAG;;;;;;;;;;AAsB3C,MAAa,iBAAiB,GAAG;;;;;;;;;AC9KjC,MAAMC,eAAY;AAElB,MAAM,uBAAsC,CAC1C;CAAE,OAAO;CAAa,WAAW,eAAe;CAAK,EACrD;CAAE,OAAO;CAAQ,WAAW,eAAe;CAAK,CACjD;;;;;;;;AASD,eAAsB,gBACpB,QACA,UAUI,EAAE,EACyB;CAC/B,MAAM,EACJ,QACA,UAAU,EAAE,SAAS,qBAAqB,SAAS,EAAE,EACrD,UAAU,yBACR;CACJ,MAAM,UAAgC,EAAE;CACxC,IAAI,SAAS;CAEb,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;CAEtE,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,SAAS,EAAE,YACT,MAAM,mBAAgD,QAAQ,SAAS;GACzE,WAAW;IACT,OAAO,EAAE,gBAAgB;IACzB,OAAOA;IACP;IACA,UAAU,EAAE,QAAQ;IACpB;IACD;GACD;GACD,CAAC;AACF,UAAQ,KAAK,GAAG,MAAM;AACtB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACe7D,MAAM,4BAA4B;;;;;;eAMnB,wBAAwB;;cAEzB,eAAe;;;;;;;;;;;AA0B7B,MAAa,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;AA+BpC,MAAa,oBAAoB,GAAG;;;;;;;;;;;;;oBAahB,0BAA0B;;;;AAoB9C,MAAa,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;qBAuBR,wBAAwB;;oBAEzB,eAAe;;;;mBAIhB,aAAa;kBACd,YAAY;4BACF,uBAAuB;;;;;;;;;;;AAuBnD,MAAa,oBAAoB,GAAG;;;;;;;;;AChOpC,MAAMC,eAAY;AAElB,MAAM,0BAA2C,CAC/C;CAAE,OAAO;CAAa,WAAW,eAAe;CAAK,EACrD;CAAE,OAAO;CAAS,WAAW,eAAe;CAAK,CAClD;;;;;;;;AASD,eAAsB,kBACpB,QACA,UAUI,EAAE,EAC2B;CACjC,MAAM,EACJ,QACA,UAAU,EAAE,SAAS,qBAAqB,SAAS,EAAE,EACrD,UAAU,4BACR;CACJ,MAAM,YAAoC,EAAE;CAC5C,IAAI,SAAS;CAEb,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;CAEtE,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,WAAW,EAAE,YACX,MAAM,mBAAkD,QAAQ,YAAY;GAC9E,WAAW;IACT,OAAO,EAAE,gBAAgB;IACzB,OAAOA;IACP;IACA,UAAU;KACR;KACA,GAAI,WAAW,qBAAqB,cAAc,EAAE,kBAAkB,MAAM,GAAG,EAAE;KAClF;IACD;IACD;GACD;GACD,CAAC;AACF,YAAU,KAAK,GAAG,MAAM;AACxB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;AC9EjE,MAAa,0BAA0B,GAAG;;;;;;;AAQ1C,MAAa,8BAA8B,GAAG;;;;;;;AAQ9C,MAAa,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDjC,MAAa,wBAAwB,GAAG;;;;;;;AAQxC,MAAa,qCAAqC,GAAG;;;;;;;;;AAUrD,MAAa,qCAAqC,GAAG;;;;;;;;;;;;;;;;;;;AC/ErD,eAAsB,sBACpB,QACA,UAGI,EAAE,EACW;CACjB,MAAM,EAAE,iBAAiB,MAAM,mBAM5B,QAAQ,6BAA6B,EAAE,QAAQ,QAAQ,QAAQ,CAAC;AACnE,QAAO,aAAa;;;;;;;;;AAUtB,eAAsB,qBACpB,QACA,UAQI,EAAE,EACW;CACjB,MAAM,EAAE,QAAQ,UAAU,EAAE,QAAQ,EAAE,KAAK;CAC3C,IAAI,WAAW;AACf,KAAI,CAAC,SACH,YAAW,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;CAE5D,MAAM,EAAE,kBAAkB,MAAM,mBAM7B,QAAQ,yBAAyB;EAClC,WAAW,EAAE,KAAK,UAAU;EAC5B;EACD,CAAC;AACF,QAAO,cAAc;;;;AC9DvB,MAAa,WAAW,GAAG;;;;;;;;;;;;;;;;;;;AAoB3B,MAAa,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;ACgBlC,eAAsB,iBACpB,QACA,UAGI,EAAE,EACa;CACnB,MAAM,EAAE,WAAW;CAEnB,MAAM,EAAE,0BAA0B,MAAM,mBAGrC,QAAQ,UAAU;EACnB,WAAW,EAAE,KAAK,MALmB,sBAAsB,QAAQ,EAAE,QAAQ,CAAC,EAKlC;EAC5C;EACD,CAAC;AAEF,QAAO,sBAAsB,MAAM,GAAG,MACpC,EAAE,MAAM,eAAe,cAAc,EAAE,MAAM,eAAe,CAC7D;;;;;;;;;;;ACgEH,eAAsB,uBACpB,QACA,UAGI,EAAE,EACoB;CAC1B,MAAM,EAAE,WAAW;CAEnB,MAAM,EACJ,eAAe,EAAE,UAAU,aAAa,MAAM,GAAG,WAC/C,MAAM,mBAgCP,QAAQ,gBAAgB;EACzB,WAAW,EAAE,KAAK,MApCmB,sBAAsB,QAAQ,EAAE,QAAQ,CAAC,EAoClC;EAC5C;EACD,CAAC;AAEF,QAAO,CACL;EACE,GAAG;EACH,MAAM,KAAK;EACX,aAAa,YAAY,KAAK,UAAU;GACtC,IAAI,KAAK;GACT,cAAc,KAAK;GACnB,OAAO,KAAK;GACZ,KAAK,KAAK,IAAI;GACd,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,EAAE;GACxC,UAAU,KAAK;GAChB,EAAE;EACH,OAAO,KAAK,MAAM,SAAS;EAC5B,CACF;;;;AChLH,MAAa,oCAAoC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BpD,MAAa,yCAAyC,GAAG;;;;;;;;;;;;;AAczD,MAAa,2CAA2C,GAAG;;;;;;;;;;;ACb3D,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,2BACpB,QACA,UAGI,EAAE,EACmC;CACzC,MAAM,iCAAiE,EAAE;CACzE,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,gCAAgC,EAAE,YAChC,MAAM,mBAMP,QAAQ,mCAAmC;GAC5C,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC,QAAQ,QAAQ;GACjB,CAAC;AACF,iCAA+B,KAAK,GAAG,MAAM;AAC7C,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,+BAA+B,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;;;;;;;;ACtBpF,eAAsB,oBACpB,QACA,UAGI,EAAE,EACmB;CACzB,MAAM,EACJ,gBAAgB,EAAE,qBAChB,MAAM,mBAMP,QAAQ,uBAAuB,EAAE,QAAQ,QAAQ,QAAQ,CAAC;AAC7D,QAAO;;;;ACpET,MAAa,iCAAiC,GAAG;;;;;;;;;;;;;;;;;;;;;;;ACyBjD,eAAsB,iCACpB,QACA,OAmBA,UAGI,EAAE,EAC2B;CACjC,MAAM,EACJ,eAAe,EAAE,aACf,MAAM,mBAMP,QAAQ,gCAAgC;EACzC,WAAW,EAAE,OAAO;EACpB,QAAQ,QAAQ;EACjB,CAAC;AACF,QAAO;;;;ACMT,MAAa,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+C9B,MAAa,4BAA4B,GAAG;;;;;;;AAmB5C,MAAa,4BAA4B,GAAG;;;;;;;;;AC5H5C,MAAMC,eAAY;;;;;;;;AA4DlB,eAAsB,+BACpB,QACA,UAGI,EAAE,EACwB;CAC9B,MAAM,cAAmC,EAAE;CAC3C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,aAAa,EAAE,YACb,MAAM,mBAMP,QAAQ,aAAa;GACtB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC,QAAQ,QAAQ;GACjB,CAAC;AACF,cAAY,KAAK,GAAG,MAAM;AAC1B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,YAAY,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;;;;;;;;ACrFjE,eAAsB,yBACpB,QACA,SAS0C;CAC1C,MAAM,EACJ,qBAAqB,EAAE,YACrB,MAAM,mBACR,QACA,6BACA;EACE,WAAW,EAAE,gBAAgB,QAAQ,SAAS,gBAAgB;EAC9D,QAAQ,QAAQ;EACjB,CACF;AACD,QAAO;;;;AC/BT,MAAMC,eAAY;;;;;;;;AA6BlB,eAAsB,mBACpB,QACA,UAGI,EAAE,EACqB;CAC3B,MAAM,SAA2B,EAAE;CACnC,IAAI,SAAS;CACb,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,QAAQ,QAAQ,CAAC;CAEtF,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,iBAAiB,EAAE,YACjB,MAAM,mBAEP,QAAQ,yBAAyB;GAClC,WAAW;IAAE;IAAgB,OAAOA;IAAW;IAAQ;GACvD,QAAQ,QAAQ;GACjB,CAAC;AACF,SAAO,KAAK,GAAG,MAAM;AACrB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO;;;;ACnDT,MAAMC,eAAY;;;;;;;;AA4ClB,eAAsB,qBACpB,QACA,UAGI,EAAE,EACuB;CAC7B,MAAM,WAA+B,EAAE;CACvC,IAAI,SAAS;CACb,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,QAAQ,QAAQ,CAAC;CAEtF,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,mBAAmB,EAAE,YACnB,MAAM,mBAEP,QAAQ,2BAA2B;GACpC,WAAW;IAAE;IAAgB,OAAOA;IAAW;IAAQ;GACvD,QAAQ,QAAQ;GACjB,CAAC;AACF,WAAS,KAAK,GAAG,MAAM;AACvB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO;;;;AClCT,MAAa,oCAAoC,GAAG;;;;;;;;;;;;;;;;;AAkBpD,MAAa,qCAAqC,GAAG;;;;;;;;;;;;;;;;;;;;;AC3CrD,eAAsB,oCACpB,QACA,OAYA,UAGI,EAAE,EAC4C;CAClD,MAAM,EACJ,gCAAgC,EAAE,YAChC,MAAM,mBACR,QACA,mCACA;EACE,WAAW;GACT,IAAI,MAAM;GACV,OAAO;IACL,QAAQ,MAAM;IACd,OAAO,MAAM;IACb,KAAK,MAAM;IACX,GAAI,MAAM,mBAAmB,SACzB,EAAE,mBAAmB,MAAM,mBAAmB,GAC9C,EAAE;IACP;GACF;EACD,QAAQ,QAAQ;EACjB,CACF;AACD,QAAO;;;;;;;AAQT,eAAsB,qCACpB,QACA,OAYA,UAGI,EAAE,EAC6C;CACnD,MAAM,EACJ,iCAAiC,EAAE,YACjC,MAAM,mBACR,QACA,oCACA;EACE,WAAW;GACT,IAAI,MAAM;GACV,OAAO;IACL,QAAQ,MAAM;IACd,OAAO,MAAM;IACb,KAAK,MAAM;IACX,aAAa,MAAM;IACpB;GACF;EACD,QAAQ,QAAQ;EACjB,CACF;AACD,QAAO;;;;ACxBT,MAAa,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtD3B,MAAa,qBAAqB,GAAG;;;;;;;;;;;AAuBrC,MAAa,2BAA2B,GAAG;;;;;;;;;AClC3C,MAAa,eAAe,GAAG;;;;;;;;;AAgB/B,MAAa,kBAAkB,GAAG;;;;;;;;;;;AC/BlC,MAAa,4BAA0C,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;EAwB9D;AAEF,MAAa,4CAA0D,MAAM,GAAG;;;;;;;;;;;EAW9E;;;;;;;;;;;;;AC9BF,SAAgB,qCACd,oBAMA,WACU;AACV,QAAO,UAAU,KAAK,UAAU;EAC9B,MAAM,OAAO,mBAAmB,MAAM,UAAU,MAAM,OAAO,MAAM;AACnE,MAAI,KACF,QAAO,KAAK;EAEd,MAAM,QAAQ,mBAAmB,MAAM,UAAU,MAAM,QAAQ,MAAM;AACrE,MAAI,MACF,QAAO,MAAM;EAEf,MAAM,YACJ,mBAAmB,SAAS,IACxB,mBAAmB,KAAK,UAAU,GAAG,MAAM,IAAI,IAAI,MAAM,GAAG,GAAG,CAAC,KAAK,KAAK,GAC1E;AACN,QAAM,IAAI,MACR,8DAA8D,MAAM,gBAAgB,YACrF;GACD;;;;;;;;;;;ACbJ,SAAS,4BACP,eACA,eACyB;AACzB,KAAI;AACF,SAAO,KAAK,MAAM,cAAc;SAC1B;AACN,QAAM,IAAI,MAAM,kCAAkC,gBAAgB;;;;;;;;;;;;AAmBtE,eAAsB,oBACpB,QACA,gBACA,YACA,UAGI,EAAE,EAC2B;CACjC,MAAM,EAAE,SAAS,gBAAgB;CAEjC,MAAM,oBAAoB,MAAM,MADL,mBAAmB,QAAQ,EAAE,QAAQ,CAAC,EACnB,OAAO;CACrD,MAAM,eAAuC,EAAE;AAE/C,OAAM,IACJ,YACA,OAAO,WAAW,UAAU;EAC1B,MAAM,gBAAgB,gCAAgC,MAAM;EAC5D,MAAM,gBAAgB,4BAA4B,UAAU,eAAe,cAAc;EACzF,MAAM,cAAc,kBAAkB,UAAU;AAEhD,MAAI,aAAa;AACf,SAAM,mBAAmB,QAAQ,yBAAyB;IACxD,WAAW,EACT,OAAO;KACL,IAAI,YAAY;KAChB;KACA,MAAM,UAAU;KAChB;KACD,EACF;IACD;IACD,CAAC;AACF,gBAAa,KAAK;IAAE,IAAI,YAAY;IAAI,MAAM,UAAU;IAAM,CAAC;AAC/D,UAAO,KAAK,yCAAyC,UAAU,KAAK,IAAI;SACnE;GACL,MAAM,SAAS,MAAM,mBAIlB,QAAQ,yBAAyB;IAClC,WAAW,EACT,OAAO;KACL;KACA,MAAM,UAAU;KAChB,MAAM,UAAU;KAChB;KACD,EACF;IACD;IACD,CAAC;AACF,gBAAa,KAAK;IAChB,IAAI,OAAO,qBAAqB,eAAe;IAC/C,MAAM,UAAU;IACjB,CAAC;AACF,UAAO,KAAK,0CAA0C,UAAU,KAAK,IAAI;;IAG7E,EAAE,aAAa,GAAG,CACnB;AAED,QAAO;;;;;;;;;;;;AAaT,eAAsB,sBACpB,QACA,gBACA,cACA,cACA,UAGI,EAAE,EAC6B;CACnC,MAAM,EAAE,SAAS,gBAAgB;CAEjC,MAAM,sBAAsB,MAAM,MADL,qBAAqB,QAAQ,EAAE,QAAQ,CAAC,EACnB,OAAO;CACzD,MAAM,oBAAoB,MAAM,cAAc,OAAO;CACrD,MAAM,iBAA2C,EAAE;AAEnD,OAAM,IACJ,cACA,OAAO,aAAa,UAAU;EAC5B,MAAM,gBAAgB,kCAAkC,MAAM;EAC9D,MAAM,gBAAgB,4BAA4B,YAAY,eAAe,cAAc;EAC3F,MAAM,gBAAgB,YAAY,YAC9B,kBAAkB,YAAY,YAAY,KAC1C,KAAA;EACJ,MAAM,gBAAgB,oBAAoB,YAAY;EAGtD,MAAM,mBACJ,YAAY,eAAe,OAAO,EAAE,aAAa,YAAY,aAAa,GAAG,EAAE;AAEjF,MAAI,eAAe;AACjB,SAAM,mBAAmB,QAAQ,2BAA2B;IAC1D,WAAW,EACT,OAAO;KACL,IAAI,cAAc;KAClB;KACA,MAAM,YAAY;KAClB,GAAG;KACH,SAAS,YAAY;KACrB;KACA,QAAQ,YAAY;KACpB,GAAI,gBAAgB,EAAE,SAAS,eAAe,GAAG,EAAE;KACpD,EACF;IACD;IACD,CAAC;AACF,kBAAe,KAAK;IAAE,IAAI,cAAc;IAAI,MAAM,YAAY;IAAM,CAAC;AACrE,UAAO,KAAK,2CAA2C,YAAY,KAAK,IAAI;SACvE;GACL,MAAM,SAAS,MAAM,mBAIlB,QAAQ,2BAA2B;IACpC,WAAW,EACT,OAAO;KACL;KACA,MAAM,YAAY;KAClB,MAAM,YAAY;KAClB,GAAG;KACH,SAAS,YAAY;KACrB;KACA,QAAQ,YAAY;KACpB,GAAI,gBAAgB,EAAE,SAAS,eAAe,GAAG,EAAE;KACpD,EACF;IACD;IACD,CAAC;AACF,kBAAe,KAAK;IAClB,IAAI,OAAO,uBAAuB,iBAAiB;IACnD,MAAM,YAAY;IACnB,CAAC;AACF,UAAO,KAAK,4CAA4C,YAAY,KAAK,IAAI;;IAGjF,EAAE,aAAa,GAAG,CACnB;AAED,QAAO;;;;AC3LT,MAAMC,eAAY;;;;;;;;AAgBlB,eAAsB,gBACpB,QACA,UAGI,EAAE,EACmC;CACzC,MAAM,EAAE,SAAS,gBAAgB;CACjC,MAAM,aAA6C,EAAE;CACrD,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,mBAAmB,EAAE,YACnB,MAAM,mBACR,QACA,oBACA;GACE,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CACF;AACD,aAAW,KAAK,GAAG,MAAM;AACzB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,WAAW,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;;;;;;;AAWhE,eAAsB,eACpB,QACA,iBACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;CACjC,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;CACtE,IAAI,mBAAmB;CACvB,MAAM,aAAa,MAAM,gBAAgB,QAAQ,EAAE,QAAQ,CAAC;AAK5D,OAAM,UAJoB,WACxB,gBAAgB,KAAK,EAAE,WAAW,KAAK,EACvC,WAAW,KAAK,EAAE,WAAW,KAAK,CAEH,EAAE,OAAO,SAAS;AACjD,MAAI;AACF,SAAM,mBAAmB,QAAQ,0BAA0B;IACzD,WAAW,EACT,OAAO;KACL,IAAI;KACJ;KACD,EACF;IACD;IACD,CAAC;AACF,UAAO,KAAK,2CAA2C,KAAK,GAAG;WACxD,KAAK;AACZ,UAAO,MAAM,uCAAuC,KAAK,MAAO,IAAc,UAAU;AACxF,sBAAmB;;GAErB;AACF,QAAO,CAAC;;;;AC1DV,MAAM,gBAAgB;;;;;;;;;AA4FtB,eAAsB,8BACpB,QACA,aACA,gBACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,SAAS,gBAAgB;CAKjC,MAAM,mBAAmB,MAAM,MAHG,+BAA+B,QAAQ,EACvE,QACD,CAAC,EACkD,OAAO;CAG3D,MAAM,gBAAgB,MAAM,MADL,iBAAiB,QAAQ,EAAE,QAAQ,CAAC,EACrB,eAAe;CAErD,MAAM,sBAAsB,MAAM,gBAAgB,OAAO;AAEzD,OAAM,IACJ,aACA,OAAO,KAAK,QAAQ;EAClB,MAAM,aAAa,IAAI,UAAU,KAAK,SAAS,SAAS;GACtD,MAAM,kBAAkB,cAAc,QAAQ;AAC9C,OAAI,CAAC,gBACH,OAAM,IAAI,MACR,uEAAuE,IAAI,aAAa,KAAK,KACxF,QAAQ,aAAa,+BAA+B,gBAC1D;AAEH,UAAO,gBAAgB;IACvB;EACF,MAAM,qBAAqB,IAAI,kBAAkB,KAAK,SAAS,SAAS;GACtE,MAAM,kBAAkB,cAAc,QAAQ;AAC9C,OAAI,CAAC,gBACH,OAAM,IAAI,MACR,uEAAuE,IAAI,qBAAqB,KAAK,KAChG,QAAQ,aAAa,+BAA+B,gBAC1D;AAEH,UAAO,gBAAgB;IACvB;EAEF,MAAM,2BAA2B,IAAI,uBACjC,oBAAoB,IAAI,uBAAuB,KAC/C,KAAA;EACJ,MAAM,qBAAqB,iBAAiB,IAAI;AAChD,MAAI,oBAAoB;AACtB,SAAM,mBAAmB,QAAQ,2BAA2B;IAC1D,WAAW,EACT,OAAO;KACL,IAAI,mBAAmB;KACvB,MAAM,IAAI;KACV,SAAS,IAAI;KACb,UAAU,IAAI;KACd,iBAAiB,IAAI;KACrB,eAAe,IAAI;KACnB,iBACE,IAAI,oBAAoB,mBAAmB,kBACvC,IAAI,kBACJ,KAAA;KACN,WAAW,IAAI;KACf,UAAU;KACV,kBAAkB;KAClB,kBAAkB,IAAI;KACtB,kBAAkB,IAAI;KACtB,GAAI,2BAA2B,EAAE,oBAAoB,0BAA0B,GAAG,EAAE;KACrF,EACF;IACD;IACD,CAAC;AACF,UAAO,KAAK,2CAA2C,IAAI,KAAK,IAAI;SAC/D;AACL,SAAM,mBAAmB,QAAQ,2BAA2B;IAC1D,WAAW,EACT,OAAO;KACL,MAAM,IAAI;KACV,aAAa,IAAI;KACjB,SAAS,IAAI;KACb,UAAU,IAAI;KACd,iBAAiB,IAAI,mBAAmB,gBAAgB;KACxD,eAAe,IAAI;KACnB,iBAAiB,IAAI;KACrB,WAAW,IAAI,aAAa,iBAAiB;KAC7C,UAAU,cAAc,EAAE;KAC1B,kBAAkB,sBAAsB,EAAE;KAC1C,kBAAkB,IAAI;KACtB,kBAAkB,IAAI;KACtB,GAAI,2BAA2B,EAAE,oBAAoB,0BAA0B,GAAG,EAAE;KACrF,EACF;IACD;IACD,CAAC;AACF,UAAO,KAAK,4CAA4C,IAAI,KAAK,IAAI;;IAGzE,EACE,aAAa,IACd,CACF;;;;;;;;;AAUH,eAAsB,mBACpB,QACA,gBACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI;AAEJ,KAAI;AACF,mBAAiB,MAAM,sBAAsB,QAAQ;GACnD;GACA,aAAa;GACd,CAAC;UACK,KAAK;AAEZ,MAAK,IAAc,QAAQ,SAAS,yBAAyB,EAAE;GAK7D,MAAM,EAAE,yBAAyB,MAAM,mBASpC,QAAQ,wBAAwB;IACjC,WAAW,EAAE,iBAAA,MAde,qBAAqB,QAAQ,EACzD,QACD,CAAC,EAY8B;IAC9B;IACD,CAAC;AACF,oBAAiB,qBAAqB,eAAe;QAErD,OAAM;;AAIV,KAAI,eAAe,QACjB,OAAM,mBAAmB,QAAQ,gCAAgC;EAC/D,WAAW;GAAE,SAAS,eAAe;GAAS;GAAgB;EAC9D;EACD,CAAC;AAGJ,KAAI,eAAe,WAAW;EAE5B,MAAM,qBAAoB,MADD,gBAAgB,QAAQ,EAAE,QAAQ,CAAC,EACvB,MAAM,SAAS,KAAK,SAAS,eAAe,UAAU;AAC3F,MAAI,CAAC,kBACH,OAAM,IAAI,MACR,cAAc,eAAe,UAAU,iDACxC;AAEH,QAAM,mBAAmB,QAAQ,kCAAkC;GACjE,WAAW;IAAE,aAAa,kBAAkB;IAAI;IAAgB;GAChE;GACD,CAAC;;AAGJ,KAAI,eAAe,QACjB,OAAM,mBAAmB,QAAQ,gCAAgC;EAC/D,WAAW;GAAE;GAAgB,SAAS,eAAe;GAAS;EAC9D;EACD,CAAC;AAGJ,KAAI,eAAe,mBACjB,OAAM,mBAAmB,QAAQ,qBAAqB;EACpD,WAAW,EACT,OAAO;GACL,IAAI;GACJ,GAAI,eAAe,qBACf,EAAE,oBAAoB,eAAe,oBAAoB,GACzD,EAAE;GACP,EACF;EACD;EACD,CAAC;AAGJ,KAAI,eAAe,qBACjB,OAAM,mBAAmB,QAAQ,+BAA+B;EAC9D,WAAW,EACT,OAAO;GACL,IAAI;GACJ,sBAAsB,eAAe;GACtC,EACF;EACD;EACD,CAAC;AAGJ,KAAI,eAAe,qBACjB,OAAM,mBAAmB,QAAQ,8BAA8B;EAC7D,WAAW,EACT,OAAO;GACL,IAAI;GACJ,qBAAqB,eAAe;GACrC,EACF;EACD;EACD,CAAC;AAGJ,KAAI,eAAe,sBACjB,OAAM,mBAAmB,QAAQ,qCAAqC;EACpE,WAAW,EACT,OAAO;GACL,IAAI;GACJ,UAAU,eAAe;GAC1B,EACF;EACD;EACD,CAAC;AAGJ,KAAI,eAAe,kBACjB,OAAM,mBAAmB,QAAQ,2BAA2B;EAC1D,WAAW,EACT,OAAO;GACL,IAAI;GACJ,mBAAmB,eAAe;GACnC,EACF;EACD;EACD,CAAC;AAGJ,KACE,eAAe,iBACf,eAAe,mBACf,eAAe,aACf;EACA,MAAM,eAAe,eAAe,gBAChC,MAAM,oBAAoB,QAAQ,gBAAgB,eAAe,eAAe,EAC9E,QACD,CAAC,GACF,EAAE;EAEN,MAAM,iBAAiB,eAAe,kBAClC,MAAM,sBACJ,QACA,gBACA,eAAe,iBACf,cACA,EAAE,QAAQ,CACX,GACD,EAAE;AAEN,MAAI,eAAe,YACjB,OAAM,8BAA8B,QAAQ,eAAe,aAAa,gBAAgB,EACtF,QACD,CAAC;;AAIN,KAAI,eAAe,MACjB,OAAM,mBAAmB,QAAQ,8BAA8B;EAC7D,WAAW,EACT,OAAO;GACL;GACA,GAAG,eAAe;GACnB,EACF;EACD;EACD,CAAC;;;;AC5XN,MAAMC,kBAAgB;;;;;;;AAQtB,eAAsB,sBACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,SAAS,SAAS,gBAAgB;CACjD,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;AAEtE,OAAM,UAAU,MAAM,SAASA,gBAAc,EAAE,OAAO,SAAS;AAC7D,QAAM,mBAAmB,QAAQ,0BAA0B;GACzD,WAAW;IACT;IACA,SAAS,KAAK,KAAK,YAAY;KAC7B,MAAM,OAAO;KACb,kBACE,OAAO,oBAAoB,OAAO,iBAAiB,SAAS,IACxD,OAAO,mBACP,KAAA;KACN,aAAa,OAAO;KACpB,SAAS,OAAO;KAChB,QAAQ,OAAO;KACf,YAAY,OAAO;KACnB,SAAS,OAAO;KACjB,EAAE;IACJ;GACD;GACD,CAAC;GACF;;;;;;;;;;AAWJ,eAAsB,YACpB,QACA,SACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI,mBAAmB;AACvB,QAAO,KAAK,YAAY,QAAQ,OAAO,cAAc;CAErD,MAAM,YAAY,QAAQ,QACvB,WACC,QAAQ,QAAQ,SAAS,OAAO,SAAS,KAAK,QAAQ,OAAO,YAAY,KAAK,QAAQ,CACnF,SAAS,EACf;AACD,KAAI,UAAU,SAAS,EACrB,OAAM,IAAI,MACR,oEAAoE,UACjE,KAAK,EAAE,WAAW,KAAK,CACvB,KAAK,IAAI,GACb;AAGH,KAAI;AACF,SAAO,KAAK,cAAc,QAAQ,OAAO,kBAAkB;AAC3D,QAAM,sBAAsB,QAAQ;GAAE,OAAO;GAAS;GAAQ,CAAC;AAC/D,SAAO,KAAK,uBAAuB,QAAQ,OAAO,WAAW;UACtD,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,+BAAgC,IAAc,UAAU;;AAGvE,QAAO,CAAC;;;;AC3EV,MAAMC,kBAAgB;;;;;;;AAQtB,eAAsB,gBACpB,QACA,SAQe;CACf,MAAM,EAAE,OAAO,WAAW,kBAAkB,OAAO,SAAS,gBAAgB;CAC5E,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;AAMtE,OAAM,UAAU,MAAM,WAAWA,gBAAc,EAAE,OAAO,SAAS;AAC/D,QAAM,mBAAmB,QAAQ,mBAAmB;GAClD,WAAW;IACT;IACA,WAAW,KAAK,KAAK,CAAC,MAAM,SAAS;KACnC;KACA,OAAO,KAAK;KACZ,MAAM,KAAK;KACX,cACE,KAAK,oBAAoB,KAAK,iBAAiB,SAAS,IACpD,KAAK,mBACL,KAAA;KAON,aAAa,KAAK;KAClB,SAAS,KAAK;KACd,QAAQ,KAAK;KACb,YAAY,KAAK;KAIlB,EAAE;IACH;IACD;GACD;GACD,CAAC;GACF;;;;;;;;;AAUJ,eAAsB,gBACpB,QACA,SAQe;CACf,MAAM,EAAE,OAAO,gBAAgB,kBAAkB,OAAO,SAAS,gBAAgB;CACjF,MAAM,iBAAiB,MAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;AAMtE,OAAM,UAAU,MAAM,gBAAgBA,gBAAc,EAAE,OAAO,SAAS;AACpE,QAAM,mBAAmB,QAAQ,mBAAmB;GAClD,WAAW;IACT;IACA,WAAW,KAAK,KAAK,UAAU;KAC7B,OAAO,KAAK;KACZ,MAAM,KAAK;KACX,cACE,KAAK,oBAAoB,KAAK,iBAAiB,SAAS,IACpD,KAAK,mBACL,KAAA;KAON,aAAa,KAAK;KAClB,SAAS,KAAK;KACd,QAAQ,KAAK;KACb,YAAY,KAAK;KAIlB,EAAE;IACH;IACD;GACD;GACD,CAAC;GACF;;;;;;;;;;AAWJ,eAAsB,cACpB,QACA,WACA,UAKI,EAAE,EACY;CAClB,MAAM,EAAE,kBAAkB,OAAO,SAAS,gBAAgB;CAC1D,IAAI,mBAAmB;AACvB,QAAO,KAAK,YAAY,UAAU,OAAO,iBAAiB;CAE1D,MAAM,YAAY,UAAU,QACzB,aACC,UAAU,QAAQ,SAAS,SAAS,UAAU,KAAK,SAAS,SAAS,SAAS,KAAK,KAAK,CACrF,SAAS,EACf;AAED,KAAI,UAAU,SAAS,EACrB,OAAM,IAAI,MACR,uEAAuE,UACpE,KAAK,EAAE,YAAY,MAAM,CACzB,KAAK,IAAI,GACb;AAGH,QAAO,KAAK,yBAAyB;CACrC,MAAM,CAAC,uBAAuB,6BAA6B,MAAM,QAAQ,IAAI,CAC3E,kBAAkB,QAAQ;EACxB;EACA,UAAU,EAAE,QAAQ,qBAAqB,MAAM;EAChD,CAAC,EACF,kBAAkB,QAAQ;EACxB;EACA,UAAU,EAAE,QAAQ,qBAAqB,aAAa;EACvD,CAAC,CACH,CAAC;CACF,MAAM,eAAe,CAAC,GAAG,uBAAuB,GAAG,0BAA0B;CAE7E,MAAM,yBAAyB,UAAU,KAAK,aAAa,CACzD,UACA,aAAa,MAAM,SAAS,SAAS,UAAU,KAAK,SAAS,SAAS,SAAS,KAAK,KAAK,EAAE,GAC5F,CAAC;CAEF,MAAM,eAAe,uBAClB,QAAQ,GAAG,cAAc,CAAC,SAAS,CACnC,KAAK,CAAC,UAAU,KAAsB;AACzC,KAAI;AACF,SAAO,KAAK,aAAa,aAAa,OAAO,qBAAqB;AAClE,QAAM,gBAAgB,QAAQ;GAAE,OAAO;GAAc;GAAiB;GAAQ,CAAC;AAC/E,SAAO,KAAK,uBAAuB,aAAa,OAAO,cAAc;UAC9D,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,kCAAmC,IAAc,UAAU;;CAG1E,MAAM,oBAAoB,uBAAuB,QAC9C,MAAoC,CAAC,CAAC,EAAE,GAC1C;AACD,KAAI;AACF,SAAO,KAAK,aAAa,kBAAkB,OAAO,iBAAiB;AACnE,QAAM,gBAAgB,QAAQ;GAAE,OAAO;GAAmB;GAAiB;GAAQ,CAAC;AACpF,SAAO,KAAK,yBAAyB,kBAAkB,OAAO,eAAe;UACtE,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,kCAAmC,IAAc,UAAU;;AAG1E,QAAO,KAAK,WAAW,UAAU,OAAO,eAAe;AAEvD,QAAO,CAAC;;;;ACnNV,MAAM,gBAAgB;;;;;;;AAQtB,SAAgB,yBAAyB,OAA2B;AAClE,KAAI,UAAU,iBACZ,QAAO,WAAW;AAEpB,KAAI,UAAU,gBACZ,QAAO,WAAW;AAEpB,QAAO,WAAW;;;;;;;;AASpB,eAAsB,eACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,cAAc,SAAS,gBAAgB;CACtD,MAAM,kBAAkB,MAAM,qBAAqB,QAAQ,EAAE,QAAQ,CAAC;AAEtE,OAAM,UAAU,MAAM,cAAc,cAAc,EAAE,OAAO,SAAS;AAClE,QAAM,mBAAmB,QAAQ,iBAAiB;GAChD,WAAW;IACT;IACA,UAAU,KAAK,KAAK,CAAC,QAAQ,eAAe;KAC1C,IAAI;KACJ,OAAO,OAAO;KAEd,GAAI,CAAC,WACD,EACE,MAAM,OAAO,QAAQ,yBAAyB,OAAO,MAAM,EAC5D,GACD,EAAE;KACN,oBAAoB,OAAO;KAC3B,iBAAiB,OAAO;KACxB,GAAI,OAAO,eAAe,OAAO,UAC7B,EACE,SAAS;MACP,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,aAAa,GAAG,EAAE;MACjE,GAAI,OAAO,UACP,EACE,SAAS,EACP,gBAAgB,OAAO,SACxB,EACF,GACD,EAAE;MACP,EACF,GACD,EAAE;KACP,EAAE;IACJ;GACD;GACD,CAAC;GACF;;;;;;;;;;AAWJ,eAAsB,aACpB,QACA,UACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI,mBAAmB;AACvB,QAAO,KAAK,YAAY,SAAS,OAAO,eAAe;CAEvD,MAAM,YAAY,SAAS,QACxB,WAAW,SAAS,QAAQ,QAAQ,OAAO,UAAU,IAAI,MAAM,CAAC,SAAS,EAC3E;AACD,KAAI,UAAU,SAAS,EACrB,OAAM,IAAI,MACR,qEAAqE,UAClE,KAAK,EAAE,YAAY,MAAM,CACzB,KAAK,IAAI,GACb;CAIH,MAAM,eAAe,MAAM,MADI,iBAAiB,QAAQ,EAAE,QAAQ,CAAC,GACrB,EAAE,YAAY,MAAM,eAAe;AAEjF,KAAI;AACF,SAAO,KAAK,cAAc,SAAS,OAAO,mBAAmB;AAC7D,QAAM,eAAe,QAAQ;GAC3B,OAAO,SAAS,KAAK,WAAW,CAAC,QAAQ,aAAa,OAAO,QAAQ,GAAG,CAAC;GACzE;GACD,CAAC;AACF,SAAO,KAAK,uBAAuB,SAAS,OAAO,YAAY;UACxD,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,gCAAiC,IAAc,UAAU;;AAGxE,QAAO,CAAC;;;;;;;;;;;;;;ACjHV,eAAsB,6BACpB,QACA,iBACA,aACA,qBACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,SAAS,gBAAgB;CAEjC,MAAM,YAAY,YAAY,QAC3B,SAAS,YAAY,QAAQ,UAAU,MAAM,UAAU,KAAK,MAAM,CAAC,SAAS,EAC9E;AACD,KAAI,UAAU,SAAS,EACrB,OAAM,IAAI,MACR,+EAA+E,CAC7E,GAAG,IAAI,IAAI,UAAU,KAAK,EAAE,YAAY,MAAM,CAAC,CAChD,CAAC,KAAK,KAAK,GACb;CAGH,MAAM,kBAAkB,MAAM,sBAAsB,EAAE,YAAY,MAAM,eAAe;CAEvF,MAAM,WAAW,YAAY,KAAK,SAAS;AAEzC,SAAO;GACL,IAFc,gBAAgB,KAAK,QAEtB;GACb,OAAO,KAAK;GACZ,GAAI,KAAK,QAAQ,KAAA,IAAY,EAAE,KAAK,KAAK,KAAK,GAAG,EAAE;GACnD,GAAI,KAAK,aAAa,KAAA,IAAY,EAAE,UAAU,KAAK,UAAU,GAAG,EAAE;GACnE;GACD;CAEF,MAAM,UAAU,IAAI,IAAI,SAAS,KAAK,SAAS,KAAK,GAAG,CAAC,QAAQ,OAAqB,CAAC,CAAC,GAAG,CAAC;CAC3F,MAAM,cAAc,oBAAoB,KAAK,EAAE,SAAS,GAAG,CAAC,QAAQ,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC;AAE5F,KAAI,YAAY,SAAS,GAAG;AAC1B,SAAO,KAAK,aAAa,YAAY,OAAO,kCAAkC;AAC9E,QAAM,mBAAmB,QAAQ,oCAAoC;GACnE,WAAW,EACT,OAAO;IACL;IACA,KAAK;IACN,EACF;GACD;GACD,CAAC;;AAGJ,KAAI,SAAS,SAAS,GAAG;AACvB,SAAO,KAAK,cAAc,SAAS,OAAO,kCAAkC;AAC5E,QAAM,mBAAmB,QAAQ,oCAAoC;GACnE,WAAW,EACT,OAAO;IACL;IACA,aAAa;IACd,EACF;GACD;GACD,CAAC;;;;;;;;;;;;;ACRN,eAAsB,kBACpB,QACA,eACA,UAKI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,aAAa,gBAAgB;CAC9C,IAAI,mBAAmB;AACvB,QAAO,KAAK,4BAA4B;CAExC,MAAM,kBAAkB,MAAM,qBAAqB,QAAQ,EAAE,QAAQ,CAAC;CAEtE,MAAM,EAAE,gCAAgC,gBAAgB;CAExD,MAAM,CAAC,YADe,CAAC,CAAC,kCAAkC,gBAAgB,KAAA,IACvC,MAAM,uBAAuB,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE;CAExF,IAAI;AACJ,KAAI,+BACF,iCAAgC,qCAC9B,UAAU,sBAAsB,EAAE,EAClC,+BACD;AAGH,KAAI;AACF,QAAM,mBAAmB,QAAQ,uBAAuB;GACtD,WAAW,EACT,OAAO;IACL;IACA,gCAAgC,cAAc;IAC9C,sBAAsB,cAAc;IACpC,wBAAwB,cAAc;IACtC,cAAc,cAAc;IAC5B,cAAc,cAAc;IAC5B,4BAA4B,cAAc;IAC1C,eAAe,cAAc;IAC7B,SAAS,cAAc;IACvB,0BAA0B,cAAc;IACxC,uBAAuB,cAAc;IACrC,cAAc,cAAc;IAC5B,oBAAoB,cAAc;IAClC,eAAe,cAAc;IAC7B,aAAa,cAAc;IAC3B,0BAA0B,cAAc;IACxC,0BAA0B,cAAc;IACxC,YAAY,cAAc;IAC1B,MAAM,cAAc;IACpB,yBAAyB,cAAc;IACvC,+BAA+B,cAAc;IAC7C,cAAc,cAAc;IAC5B,GAAI,gCAAgC,EAAE,+BAA+B,GAAG,EAAE;IAC1E,GAAI,gBAAgB,KAAA,IAAY,EAAE,aAAa,GAAG,EAAE;IACpD,GAAI,cAAc,QACd;KACE,cAAc,cAAc,MAAM;KAClC,iBAAiB,cAAc,MAAM;KACrC,YAAY,cAAc,MAAM;KACjC,GACD,EAAE;IACP,EACF;GACD;GACD,CAAC;AAEF,MAAI,gBAAgB,KAAA,EAClB,OAAM,6BACJ,QACA,iBACA,aACA,UAAU,eAAe,EAAE,EAC3B,EAAE,QAAQ,CACX;AAGH,SAAO,KAAK,sCAAsC;UAC3C,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,oCAAqC,IAAc,UAAU;;AAG5E,QAAO,CAAC;;;;ACpIV,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,gCACpB,QACA,UAGI,EAAE,EAC6B;CACnC,MAAM,EAAE,WAAW;CACnB,MAAM,WAAqC,EAAE;CAC7C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,yBAAyB,EAAE,YACzB,MAAM,mBAMP,QAAQ,2BAA2B;GACpC,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,WAAS,KAAK,GAAG,MAAM;AACvB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACzE9D,MAAa,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;AAsB1B,MAAa,gBAAgB,GAAG;;;;;;;;;ACIhC,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,gBACpB,QACA,UAGI,EAAE,EACa;CACnB,MAAM,EAAE,WAAW;CACnB,MAAM,UAAoB,EAAE;CAC5B,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,SAAS,EAAE,YACT,MAAM,mBAMP,QAAQ,SAAS;GAClB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,UAAQ,KAAK,GAAG,MAAM;AACtB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACnE7D,MAAa,mBAAiC,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCrD;AAEF,MAAa,kBAAgC,MAAM,GAAG;;;;;;;;;EASpD;AAEF,MAAa,yBAAuC,MAAM,GAAG;;;;;;EAM3D;;;ACoBF,MAAMC,eAAY;;;;;;;;AASlB,SAAgB,8BAA8B,UAAsC;AAClF,QAAO,SAAS,gBAAgB,SAAS,MAAM;;;;;;;;;AAUjD,eAAsB,wBACpB,QACA,UAKI,EAAE,EACyB;CAC/B,MAAM,EAAE,QAAQ,uBAAuB;CACvC,MAAM,UAAgC,EAAE;CACxC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,WAAW,EAAE,YACX,MAAM,mBAMP,QAAQ,kBAAkB;GAC3B,WAAW;IACT,OAAOA;IACP;IACA,UAAU,qBAAqB,EAAE,oBAAoB,GAAG,KAAA;IACzD;GACD;GACD,CAAC;AAEF,UAAQ,KAAK,GAAG,MAAM;AACtB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,QAAQ,MAAM,GAAG,MACtB,8BAA8B,EAAE,CAAC,cAAc,8BAA8B,EAAE,CAAC,CACjF;;;;;;;;;;AAWH,SAAgB,6BACd,WACA,eACoB;CACpB,MAAM,UAAU,UAAU,QACvB,aAAa,8BAA8B,SAAS,KAAK,cAC3D;AACD,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MACR,2CAA2C,cAAc,0EAE1D;AAEH,KAAI,QAAQ,SAAS,EACnB,OAAM,IAAI,MACR,4CAA4C,cAAc,qEAE3D;CAEH,MAAM,CAAC,SAAS;AAChB,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,2CAA2C,cAAc,GAAG;AAE9E,QAAO;;;;AC7KT,MAAa,gBAA8B,MAAM,GAAG;;;;;;;;;;;;;;;;EAgBlD;AAEF,MAAa,sBAAoC,MAAM,GAAG;;;;;;;;;EASxD;AAEF,MAAa,sBAAoC,MAAM,GAAG;;;;;;EAMxD;AAEF,MAAa,sBAAoC,MAAM,GAAG;;;;;;EAMxD;;;;;;;;;;ACTF,eAAsB,qBACpB,QACA,UAGI,EAAE,EACkB;CACxB,MAAM,EAAE,SAAS,gBAAgB;CACjC,MAAM,EAAE,qBAAqB,MAAM,mBAGhC,QAAQ,eAAe,EAAE,QAAQ,CAAC;AACrC,QAAO;;;;;;;;;AAUT,eAAsB,kBACpB,QACA,SAMsB;CACtB,MAAM,EAAE,OAAO,iBAAiB,SAAS,gBAAgB;CACzD,MAAM,EAAE,kBAAkB,MAAM,mBAM7B,QAAQ,qBAAqB;EAC9B,WAAW;GAAE,MAAM;GAAiB,aAAa;GAAM;EACvD;EACD,CAAC;AACF,QAAO,cAAc;;;;;;;;;AAUvB,SAAgB,8BACd,kBACA,iBACU;AACV,kBAAiB,SAAS,SAAS;AACjC,MAAI,CAAC,gBAAgB,MACnB,OAAM,IAAI,MAAM,6CAA6C,OAAO;GAEtE;AAEF,QAAO,OAAO,OAAO,gBAAgB,CAClC,QAAQ,SAAS,CAAC,iBAAiB,SAAS,KAAK,KAAK,CAAC,CACvD,KAAK,EAAE,SAAS,GAAG;;;;;;;;;AAUxB,SAAgB,8BACd,kBACA,iBACU;AACV,kBAAiB,SAAS,SAAS;AACjC,MAAI,CAAC,gBAAgB,MACnB,OAAM,IAAI,MAAM,6CAA6C,OAAO;GAEtE;AAEF,QAAO,OAAO,OAAO,gBAAgB,CAClC,QAAQ,SAAS,CAAC,iBAAiB,SAAS,KAAK,KAAK,CAAC,CACvD,KAAK,EAAE,WAAW,KAAK;;;;;;;;;;;AC/D5B,SAAgB,kCACd,UACQ;AACR,QAAO,KAAK,UAAU,EACpB,KAAK,SAAS,KAAK,aAAa,GAC7B,QAAQ,mBAAmB,QAAQ,mBACrC,EAAE,EACJ,CAAC;;;;;;;;;AAUJ,SAAgB,kCACd,kBACsC;AACtC,KAAI,CAAC,iBACH,QAAO,EAAE;AAGX,KAAI;EACF,MAAM,SAAkB,KAAK,MAAM,iBAAiB;AACpD,MAAI,CAAC,UAAU,OAAO,WAAW,SAC/B,QAAO,EAAE;EAGX,MAAM,aAAc,OAA6B;AACjD,MAAI,CAAC,MAAM,QAAQ,WAAW,CAC5B,QAAO,EAAE;EAGX,MAAM,WAAiD,EAAE;AACzD,OAAK,MAAM,SAAS,YAAY;AAC9B,OAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CAC7D;AAEF,QAAK,MAAM,CAAC,cAAc,kBAAkB,OAAO,QACjD,MACD,CACC,KAAI,OAAO,kBAAkB,UAC3B,UAAS,KAAK;IACZ,iBAAiB;IACjB,kBAAkB;IACnB,CAAC;;AAIR,SAAO;SACD;AACN,SAAO,EAAE;;;;;;;;;AAUb,SAAgB,+BACd,SACS;AACT,QAAO,QAAQ,QAAQ,kBAAkB;;;;;;;;;AAU3C,SAAgB,mCACd,SACA,SAIM;AACN,KAAI,CAAC,QAAQ,UAAU,OACrB,OAAM,IAAI,MACR,6BAA6B,QAAQ,KAAK,qIAG3C;CAGH,MAAM,OAAO,+BAA+B,QAAQ;CACpD,MAAM,kBAAkB,QAAQ,QAAQ,eAAe;CACvD,MAAM,iBAAiB,QAAQ,QAAQ,qBAAqB,OAAO;AAEnE,KAAI,SAAS,mBAAmB,gBAC9B,OAAM,IAAI,MACR,6BAA6B,QAAQ,KAAK,0KAG3C;AAGH,KAAI,CAAC,QAAQ,UAAU;AACrB,MAAI,KAEF;AAEF,MAAI,CAAC,QAAQ,eACX,OAAM,IAAI,MACR,6BAA6B,QAAQ,KAAK,qGAE3C;AAEH,MAAI,CAAC,QAAQ,qBACX,OAAM,IAAI,MACR,6BAA6B,QAAQ,KAAK,6DAC3C;;;;;;;;;;AAYP,SAAgB,2BACd,WACA,kBACoB;AACpB,KAAI,CAAC,iBACH;CAEF,MAAM,WAAW,UAAU,MAAM,cAAc,UAAU,OAAO,iBAAiB;AACjF,QAAO,WAAW,8BAA8B,SAAS,GAAG,KAAA;;;;;;;;;;AAW9D,eAAsB,4BACpB,QACA,QACA,UAKI,EAAE,EACY;CAClB,MAAM,EAAE,QAAQ,WAAW,OAAO;AAClC,SAAQ,KAAK,YAAY,OAAO,OAAO,gCAAgC;CAEvE,IAAI,mBAAmB;CAEvB,MAAM,UAAU,OAAO,MAAM,MAAM,+BAA+B,EAAE,CAAC;CACrE,MAAM,eAAe,OAAO,MAAM,MAAM,EAAE,eAAe;CACzD,MAAM,gBAAgB,OAAO,MAAM,MAAM,EAAE,qBAAqB;CAChE,MAAM,iBAAiB,OAAO,MAAM,MAAM,EAAE,qBAAqB,OAAO;CAExE,MAAM,CAAC,kBAAkB,SAAS,cAAc,UAAU,WAAW,gBACnE,MAAM,QAAQ,IAAI;EAChB,gCAAgC,QAAQ,EAAE,QAAQ,CAAC;EACnD,eAAe,gBAAgB,QAAQ,EAAE,QAAQ,CAAC,GAAI,EAAE;EACxD,gBAAgB,qBAAqB,QAAQ,EAAE,QAAQ,CAAC,GAAI,EAAE;EAE9D,iBAAiB,QAAQ,EAAE,QAAQ,CAAC;EACpC,iBAAiB,kBAAkB,QAAQ;GAAE;GAAU;GAAQ,CAAC,GAAG,EAAE;EACrE,UACI,wBAAwB,QAAQ;GAC9B;GACA,oBAAoB,mBAAmB;GACxC,CAAC,GACD,EAAE;EACR,CAAC;CAEJ,MAAM,gBAAgB,MAAM,kBAAkB,OAAO;CACrD,MAAM,eAAe,MAAM,SAAS,OAAO;CAC3C,MAAM,oBAAoB,MAAM,cAAc,OAAO;CACrD,MAAM,wBAAwB,MAAM,UAAU,eAAe;CAC7D,MAAM,kBAAkB,MAAM,WAAW,QAAQ;AAKjD,OAAM,UAAU,QAAQ,OAAO,YAAY;AACzC,MAAI;GACF,MAAM,kBAAkB,cAAc,QAAQ;AAC9C,sCAAmC,SAAS,EAAE,UAAU,QAAQ,gBAAgB,EAAE,CAAC;GAEnF,MAAM,iCAAiC,QAAQ,SAAS,KAAK,iBAAiB;IAC5E,MAAM,UAAU,sBAAsB,aAAa;AACnD,QAAI,CAAC,QACH,OAAM,IAAI,MACR,6CAA6C,aAAa,mBAC3D;AAEH,WAAO;KACL,WAAW,QAAQ;KACnB,eAAe,aAAa;KAC7B;KACD;GAEF,MAAM,gBAAyC;IAC7C,MAAM,QAAQ;IACd,GAAI,kBAAkB,EAAE,IAAI,gBAAgB,IAAI,GAAG,EAAE;IACrD,kBAAkB,kCAAkC,QAAQ,SAAS;IACrE;IACA,GAAI,QAAQ,iBAAiB,KAAA,IAAY,EAAE,UAAU,QAAQ,cAAc,GAAG,EAAE;IAChF,GAAI,QAAQ,6BAA6B,KAAA,IACrC,EAAE,sBAAsB,QAAQ,0BAA0B,GAC1D,EAAE;IACN,GAAI,QAAQ,iBAAiB,KAAA,IAAY,EAAE,UAAU,QAAQ,cAAc,GAAG,EAAE;IACjF;AAED,OAAI,+BAA+B,QAAQ,EAAE;IAC3C,MAAM,WAAW,6BAA6B,cAAc,QAAQ,kBAAmB;AACvF,kBAAc,mBAAmB,SAAS;AAG1C,QAAI,CAAC,iBAAiB;AACpB,mBAAc,WAAW,SAAS,OAAO;AACzC,SAAI,CAAC,SAAS,QACZ,OAAM,IAAI,MACR,iBAAiB,QAAQ,kBAAkB,iEACE,QAAQ,KAAK,IAC3D;AAEH,mBAAc,gBAAgB,SAAS,QAAQ;eACtC,QAAQ,sBAAsB;KAEvC,MAAM,UAAU,kBAAkB,QAAQ;AAC1C,SAAI,CAAC,QAEH,OAAM,IAAI,MACR,0CAA0C,QAAQ,uBACnD;AAEH,mBAAc,gBAAgB,QAAQ;;UAEnC;IAEL,IAAI;AACJ,QAAI,QAAQ,gBAAgB;KAC1B,MAAM,SAAS,aAAa,QAAQ;AACpC,SAAI,CAAC,OACH,OAAM,IAAI,MAAM,oCAAoC,QAAQ,iBAAiB;AAE/E,gBAAW,OAAO;;IAGpB,IAAI;AACJ,QAAI,QAAQ,sBAAsB;KAChC,MAAM,UAAU,kBAAkB,QAAQ;AAC1C,SAAI,CAAC,QACH,OAAM,IAAI,MACR,0CAA0C,QAAQ,uBACnD;AAEH,qBAAgB,QAAQ;;IAG1B,IAAI;AACJ,QAAI,QAAQ,qBAAqB,OAC/B,eAAc,QAAQ,oBAAoB,KAAK,UAAU;KACvD,MAAM,WAAW,gBAAgB;AACjC,SAAI,CAAC,SACH,OAAM,IAAI,MAAM,wCAAwC,QAAQ;AAElE,YAAO,SAAS;MAChB;AAGJ,QAAI,SACF,eAAc,WAAW;AAE3B,QAAI,cACF,eAAc,gBAAgB;AAEhC,QAAI,YACF,eAAc,cAAc;;AAIhC,SAAM,mBASH,QAAQ,2CAA2C;IACpD,WAAW,EAAE,OAAO,eAAe;IACnC;IACD,CAAC;AAEF,WAAQ,KAAK,iDAAiD,QAAQ,KAAK,IAAI;WACxE,KAAK;AACZ,sBAAmB;AACnB,WAAQ,MACN,4CAA4C,QAAQ,KAAK,OAAQ,IAAc,UAChF;;GAEH;AAEF,SAAQ,KAAK,WAAW,OAAO,OAAO,8BAA8B;AACpE,QAAO,CAAC;;;;;;;;;;;;AC7UV,eAAsB,wBACpB,QACA,SAMwE;CACxE,MAAM,EAAE,OAAO,mBAAmB,WAAW;CAQ7C,MAAM,EAAE,uCAAuC,MAAM,mBAMlD,QAAQ,wCAAwC;EACjD,WAAW,EAAE,OAAO;GAbpB,MAAM,kBAAkB;GACxB,SAAS,kBAAkB;GAC3B,aAAa,kBAAkB;GAWH,EAAE;EAC9B;EACD,CAAC;AACF,QAAO,mCAAmC;;;;;;;;AAS5C,eAAsB,yBACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,oBAAoB,WAAW;AAC9C,OAAM,mBAAmB,QAAQ,0CAA0C;EACzE,WAAW,EACT,OAAO,EACL,gCAAgC,mBAAmB,KAAK,CAAC,mBAAmB,SAAS;GACnF;GACA,aAAa,kBAAkB;GAE/B,YAAY,kBAAkB;GAC/B,EAAE,EACJ,EACF;EACD;EACD,CAAC;;;;;;;;;;AAWJ,eAAsB,uBACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAO,KAAK,YAAY,OAAO,OAAO,0BAA0B;CAEhE,IAAI,mBAAmB;CAIvB,MAAM,0BAEF,MAAM,MAJ+B,2BAA2B,QAAQ,EAAE,QAAQ,CAAC,GAIhD,EAAE,MAAM,cAAc,GAAG,KAAK,GAAG,UAAU;AAMlF,OAAM,UAJwB,OAAO,QAClC,UAAU,CAAC,wBAAwB,GAAG,MAAM,KAAK,GAAG,MAAM,WAGxB,EAAE,OAAO,sBAAsB;AAClE,MAAI;GACF,MAAM,uBAAuB,MAAM,wBAAwB,QAAQ;IACjE,OAAO;IACP;IACD,CAAC;AACF,2BAAwB,GAAG,qBAAqB,KAAK,GAAG,qBAAqB,aAC3E;AACF,UAAO,KAAK,2CAA2C,kBAAkB,KAAK,IAAI;WAC3E,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,sCAAsC,kBAAkB,KAAK,OAAQ,IAAc,UACpF;;GAEH;AAEF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,wBAAwB;AAC/D,QAAM,yBAAyB,QAAQ;GACrC,OAAO,OAAO,KAAK,UAAU,CAC3B,OACA,wBAAwB,GAAG,MAAM,KAAK,GAAG,MAAM,WAAY,GAC5D,CAAC;GACF;GACD,CAAC;AACF,SAAO,KAAK,wBAAwB,OAAO,OAAO,wBAAwB;UACnE,KAAK;AACZ,qBAAmB;AACnB,SAAO,MACL,mBAAmB,OAAO,OAAO,4BAA6B,IAAc,UAC7E;;AAGH,QAAO,CAAC;;;;ACtKV,MAAa,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;AA0BpC,MAAa,yBAAyB,GAAG;;;;;;;;;AAUzC,MAAa,wBAAwB,GAAG;;;;;;;;;;;ACjBxC,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,yBACpB,QACA,SAS4B;CAC5B,MAAM,EACJ,SAAS,aACT,UAAU,EAAE,gBACV;CACJ,MAAM,mBAAsC,EAAE;CAC9C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,kBAAkB,EAAE,YAClB,MAAM,mBAMP,QAAQ,mBAAmB;GAC5B,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAW;GAClD;GACD,CAAC;AACF,mBAAiB,KAAK,GAAG,MAAM;AAC/B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO;;;;ACnET,MAAa,iBAA+B,MAAM,GAAG;;;;;;;;EAQnD;;;ACCF,MAAM,gCAAgC;AAEtC,MAAM,oBAAoB,EAAE,KAAK;;CAE/B,IAAI,EAAE;;CAEN,MAAM,EAAE;;CAER,OAAO,EAAE;;CAET,MAAM,SAAS,eAAe;CAC/B,CAAC;AAKF,MAAMC,eAAY;AAElB,MAAa,6BAA6B,EAAE,KAAK,EAC/C,aAAa,EAAE,MAAM,kBAAkB,EACxC,CAAC;;;;;;;;;AAUF,eAAsB,sBACpB,QACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,SAAS,gBAAgB;CACjC,MAAM,EACJ,cAAc,EACZ,QAAQ,EAAE,gBAEV,MAAM,mBASP,QAAQ,gBAAgB,EAAE,QAAQ,CAAC;AAEtC,KAAI,WAAW,OAAO,GAAG,SAAS,8BAA8B,CAC9D,OAAM,IAAI,MACR,4BAA4B,8BAA8B,kCAC3D;;;;;;;;;;AAYL,eAAsB,2BACpB,QACA,QACA,SAW8B;CAC9B,MAAM,EACJ,UAAU,EAAE,aACZ,kBAAkB,OAClB,SAAS,gBACP;CACJ,MAAM,qBAA0C,EAAE;CAClD,IAAI,SAAS;CACb,IAAI,iBAAiB;AAErB,KAAI,CAAC,gBACH,OAAM,sBAAsB,QAAQ,EAAE,QAAQ,CAAC;AAGjD,IAAG;EAwBD,MAAM,EAAE,aAAa,UAAU,YAAY,4BAA4B,MAlBhD,mBACrB,6CAEE,OACG,KAGE,0BAA0B,EAC3B,MAAM;GACJ,OAAOA;GACP;GACA;GACD,EACF,CAAC,CACD,MAAM,EACX;GAAE;GAAQ,aAAa;GAAG,aAAa;GAAK,CAC7C,CAE+E;AAEhF,qBAAmB,KAAK,GAAG,MAAM;AAEjC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO;;;;ACzIT,MAAa,6BAA2C,MAAM,GAAG;;;;;;EAM/D;AAEF,MAAa,sBAAoC,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BxD;;;ACvBF,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,kCACpB,QACA,UAcI,EAAE,EACgC;CACtC,MAAM,EAAE,SAAS,aAAa,aAAa;CAC3C,MAAM,EAAE,WAAW,YAAY,iBAAiB,mBAAmB,YAAY,EAAE;CACjF,MAAM,qBAAqB,eAAe,YAAY,CAAC,UAAU,GAAG,KAAA;CACpE,MAAM,qBAAkD,EAAE;CAC1D,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,oBAAoB,EAAE,YACpB,MAAM,mBAMP,QAAQ,qBAAqB;GAC9B,WAAW;IACT,OAAOA;IACP;IACA,YAAY;IACZ,iBAAiB,kBAAkB,gBAAgB,aAAa,GAAG,KAAA;IACnE,gBAAgB,iBAAiB,eAAe,aAAa,GAAG,KAAA;IACjE;GACD;GACD,CAAC;AACF,qBAAmB,KAAK,GAAG,MAAM;AACjC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO;;;;ACtET,MAAa,uBAAqC,MAAM,GAAG;;;;;;EAMzD;AAEF,MAAa,oBAAkC,MAAM,GAAG;;;;;;;;;EAStD;AAEF,MAAa,oBAAkC,MAAM,GAAG;;;;;;EAMtD;;;;;;;;;;;ACQF,eAAsB,iCACpB,QACA,SAQwC;CACxC,MAAM,EAAE,OAAO,cAAc,OAAO,SAAS,gBAAgB;CAC7D,MAAM,EAAE,YAAY,EAAE,EAAE,cAAc,YAAY,EAAE,EAAE,cAAc,EAAE,KAAK;CAC3E,MAAM,iBAAiB,MAAM,oBAAoB,QAAQ,EAAE,QAAQ,CAAC;CACpE,MAAM,oBAAoB,MAAM,gBAAgB,OAAO;CAYvD,MAAM,qBAAqB,WAVC,KAAK;EAC/B,GAAG,QACD,UAAU,KAAK,aAAa,CAC1B,SAAS,qBACT,GAAG,SAAS,sBACb,CAAC,CACH;EACD,GAAG,QAAQ,UAAU,KAAK,aAAa,SAAS,iBAAiB,CAAC;EAClE,GAAG,YAAY,KAAK,EAAE,WAAW,KAAK;EACvC,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,EAEE,EACnB,eAAe,KAAK,EAAE,WAAW,KAAK,CACvC;AAED,KAAI,mBAAmB,SAAS,GAAG;AACjC,SAAO,KAAK,YAAY,mBAAmB,OAAO,qBAAqB;EACvE,MAAM,EAAE,uBAAuB,MAAM,mBAMlC,QAAQ,sBAAsB,EAAE,QAAQ,CAAC;EAC5C,MAAM,uBAAuB,mBAAmB,KAAK,EAAE,WAAW,KAAK;AACvE,QAAM,UAAU,oBAAoB,OAAO,eAAe;AACxD,UAAO,KAAK,uBAAuB,WAAW,KAAK;AACnD,OAAI;IACF,MAAM,EAAE,qBAAqB,MAAM,mBAMhC,QAAQ,mBAAmB;KAC5B,WAAW,EACT,OAAO;MACL,MAAM;MACN,MAAM,qBAAqB,SAAS,WAAY,GAAG,aAAa;MAChE;MACD,EACF;KACD;KACD,CAAC;AACF,WAAO,KAAK,sBAAsB,WAAW,GAAG;AAEhD,sBAAkB,cAAe,iBAAiB;YAC3C,KAAK;AACZ,WAAO,MAAM,gCAAgC,WAAW,MAAO,IAAc,UAAU;;IAEzF;;AAEJ,QAAO;;;;;;;;;;AC5FT,eAAsB,qBACpB,QACA,SAMe;CACf,MAAM,EAAE,IAAI,SAAS,gBAAgB;AACrC,OAAM,mBAAmB,QAAQ,wBAAwB;EACvD,WAAW,EAAE,mBAAmB,IAAI;EACpC;EACD,CAAC;;;;;;;;;;ACGJ,eAAsB,gBACpB,QACA,SAUe;CACf,MAAM,EAAE,OAAO,aAAa,eAAe,cAAc,OAAO,SAAS,gBAAgB;AACzF,OAAM,mBAAmB,QAAQ,qBAAqB;EACpD,WAAW,EACT,OAAO;GACL,IAAI;GACJ,OAAO,YAAY;GACnB,iCAAiC,YAAY;GAC7C,yBAAyB,YAAY;GACrC,SAAS,YAAY;GACrB,aAAa,eAAe,OAAO,YAAY,WAAW;GAC3D,EACF;EACD;EACD,CAAC;AAEF,KAAI,OAAO,YAAY,WAAW,UAChC,OAAM,mBAAmB,QAAQ,qBAAqB;EACpD,WAAW,EACT,OAAO;GACL,IAAI;GACJ,QAAQ,YAAY;GACpB;GACD,EACF;EACD;EACD,CAAC;;;;AC9DN,MAAa,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6C5B,MAAa,cAAc,GAAG;;;;;;;;;;AAW9B,MAAa,kBAAkB,GAAG;;;;;;;AAQlC,MAAa,kBAAkB,GAAG;;;;;;;;;ACiClC,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,kBACpB,QACA,UAQI,EAAE,EACe;CACrB,MAAM,EAAE,SAAS,aAAa,aAAa;CAC3C,MAAM,EAAE,UAAU,YAAY,EAAE;CAChC,MAAM,YAAwB,EAAE;CAChC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,WAAW,EAAE,YACX,MAAM,mBAMP,QAAQ,WAAW;GACpB,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAO;GAC9C;GACD,CAAC;AACF,YAAU,KAAK,GAAG,MAAM;AACxB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;;;;;AASjE,eAAsB,aACpB,QACA,SAUe;CACf,MAAM,EAAE,OAAO,UAAU,kBAAkB,oBAAoB,SAAS,gBAAgB;CAKxF,MAAM,oBAAmB,MAJH,kBAAkB,QAAQ;EAC9C,UAAU,EAAE,OAAO,SAAS,OAAO;EACnC;EACD,CAAC,EAC+B,MAAM,EAAE,YAAY,UAAU,SAAS,MAAM;CAE9E,MAAM,iBAAiB,SAAS,kBAAkB,KAAK,YAAY;EACjE,MAAM,WAAW,mBAAmB;AACpC,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,4CAA4C,UAAU;AAExE,SAAO,SAAS;GAChB;CAEF,MAAM,kBAAkB,SAAS;CACjC,MAAM,gBAAgB,SAAS,sBAAsB,OAAO,OAAO,cAAc;AACjF,KAAI,iBACF,OAAM,mBAAmB,QAAQ,iBAAiB;EAChD,WAAW,EACT,OAAO;GACL,IAAI,iBAAiB;GACrB,OAAO,SAAS;GAChB,KAAK,SAAS;GACd,SAAS,SAAS;GAClB,WAAW,SAAS;GACpB,kBAAkB,SAAS;GAC3B,oBACE,OAAO,SAAS,uBAAuB,WACnC,SAAS,mBAAmB,UAAU,GACtC,KAAA;GACN,yBAAyB,SAAS;GAClC,cAAc,SAAS;GACvB,YAAY,SAAS;GACrB;GACA,aAAa,SAAS,eAAe;GACrC,iBAAiB,kBAAkB,iBAAiB,iBAAkB,KAAK,KAAA;GAC3E,aAAa,SAAS,sBAAuB,KAAK,OAAO,iBAAiB,IAAK,GAAG;GAClF,GAAI,iBAAiB,SAAS,aAAa,SAAS,EAAE,GAAG,EAAE,SAAS,eAAe;GACpF,EACF;EACD;EACD,CAAC;UACO,gBACT,OAAM,mBAAmB,QAAQ,iBAAiB;EAChD,WAAW,EACT,OAAO;GACL,OAAO,SAAS;GAChB,KAAK,SAAS;GACd,MAAM,SAAS,QAAQ,aAAa;GACpC,SAAS,SAAS;GAClB,WAAW,SAAS;GACpB,kBAAkB,SAAS;GAC3B,oBACE,OAAO,SAAS,uBAAuB,WACnC,SAAS,mBAAmB,UAAU,GACtC,KAAA;GACN,yBAAyB,SAAS;GAClC,cAAc,SAAS;GACvB;GACA,YAAY,SAAS;GACrB,aAAa,SAAS,eAAe;GACrC,iBAAiB,iBAAiB,iBAAkB;GACpD,aAAa,SAAS,sBAAsB,KAAK,OAAO,iBAAiB,IAAK,GAAG;GACjF,SAAS;GACV,EACF;EACD;EACD,CAAC;;;;;;;;;;ACnMN,eAAsB,eACpB,QACA,SAYe;CACf,MAAM,EACJ,OAAO,YACP,oBACA,cACA,cAAc,OACd,SAAS,gBACP;AACJ,OAAM,mBAAmB,QAAQ,mBAAmB;EAClD,WAAW,EACT,OAAO;GACL,IAAI;GACJ,eAAe,WAAW;GAC1B,kBAAkB,WAAW;GAC7B,OAAO,WAAW;GAClB,aAAa,WAAW;GACxB,cAAc,WAAW;GACzB,oBAAoB,WAAW;GAC/B,cAAc,WAAW;GACzB,2BAA2B,WAAW;GACtC,yBAAyB,WAAW;GACpC,gBAAgB,WAAW,eACvB,WAAW,aAAa,KAAK,SAAS,mBAAmB,MAAO,GAAG,GACnE,KAAA;GACJ;GACD,EACF;EACD;EACD,CAAC;;;;ACjFJ,MAAa,6BAA6B,GAAG;;;;;;;AAQ7C,MAAa,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;ACuBlC,eAAsB,2BACpB,QACA,SASiB;CACjB,MAAM,EACJ,SAAS,aACT,UAAU,EAAE,iBACV;CASJ,MAAM,EAAE,SAAS,gBAAe,MART,mBAAmC,QAAQ,iBAAiB;EACjF,WAAW;GACT;GACA,MAAM;GACP;EACD;EACD,CAAC,EAEuC;AACzC,KAAI,eAAe,GAAG;AACpB,SAAO,MAAM,wDAAwD;AACrE,UAAQ,KAAK,EAAE;;AAGjB,QAAO,QAAQ;;;;AChDjB,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,6BACpB,QACA,UAGI,EAAE,EACmB;CACzB,MAAM,EAAE,WAAW;CACnB,MAAM,gBAAgC,EAAE;CACxC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,eAAe,EAAE,YACf,MAAM,mBAMP,QAAQ,yBAAyB;GAClC,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,gBAAc,KAAK,GAAG,MAAM;AAC5B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,cAAc,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACpDnE,MAAa,yBAAyB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;ACmCzC,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,6BACpB,QACA,UAGI,EAAE,EAC0B;CAChC,MAAM,EAAE,WAAW;CACnB,MAAM,uBAA8C,EAAE;CACtD,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,sBAAsB,EAAE,YACtB,MAAM,mBAMP,QAAQ,wBAAwB;GACjC,WAAW;IAAE,OAAOA;IAAW;IAAQ,OAAO,EAAE;IAAE,UAAU,EAAE;IAAE;GAChE;GACD,CAAC;EAEF,MAAM,cAAc,MAAM,KAAK,SAC7B,KAAK,UAAU,QAAQ,KAAK,kBAAkB,QAC1C;GAAE,GAAG;GAAM,OAAO,KAAK,iBAAiB;GAAO,GAC/C,KACL;AAED,uBAAqB,KAAK,GAAG,YAAY;AACzC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO;;;;AC9ET,MAAa,WAAW,GAAG;;;;;;;;;;;;;ACU3B,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,iBACpB,QACA,UAGI,EAAE,EACc;CACpB,MAAM,EAAE,WAAW;CACnB,MAAM,WAAsB,EAAE;CAC9B,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,UAAU,EAAE,YACV,MAAM,mBAMP,QAAQ,UAAU;GACnB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,WAAS,KAAK,GAAG,MAAM;AACvB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AACT,QAAO,SAAS,MAAM,GAAG,MAAM,EAAE,gBAAgB,cAAc,EAAE,gBAAgB,CAAC;;;;;;;;;AAiBpF,eAAsB,sBACpB,QACA,UAGI,EAAE,EAMN;CACA,MAAM,WAAW,MAAM,iBAAiB,QAAQ,QAAQ;AAexD,QAAO;EACL;EACA,gBAfqB,SAAS,QAC7B,KAAK,YAAY,OAAO,OAAO,KAAK,GAAG,QAAQ,kBAAkB,QAAQ,OAAO,CAAC,EAClF,EAAE,CAaY;EACd,+BAXoC,SAAS,QAC5C,KAAK,YACJ,OAAO,OAAO,KAAK,GAChB,QAAQ,kBAAkB,QAAQ,qBACpC,CAAC,EACJ,EAAE,CAM2B;EAC9B;;;;ACjGH,MAAa,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BrC,MAAa,kCAAkC,GAAG;;;;;;;;;;;;AAalD,MAAa,0BAA0B,GAAG;;;;;;;;;AAiB1C,MAAa,uCAAuC,GAAG;;;;;;;;;;;;;;;;;;AC1BvD,eAAsB,2BACpB,QACA,UAKI,EAAE,EACW;CACjB,MAAM,EAAE,QAAQ,UAAU,EAAE,WAAW,YAAY,iBAAiB,aAAa,EAAE,KAAK;CACxF,MAAM,EACJ,kBAAkB,EAAE,iBAClB,MAAM,mBAQP,QAAQ,oBAAoB;EAC7B,WAAW;GACT,OAAO;GACP,QAAQ;GACR,UAAU;IACR;IACA;IACA,QAAQ;IACR,eAAe;IAChB;GACF;EACD;EACD,CAAC;AAEF,QAAO;;AAGT,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,sBACpB,QACA,UASI,EAAE,EACsB;CAC5B,MAAM,EACJ,QACA,UAAU,EAAE,WAAW,YAAY,iBAAiB,aAAa,EAAE,EACnE,OACA,eACE;CACJ,MAAM,mBAAsC,EAAE;CAC9C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,kBAAkB,EAAE,YAClB,MAAM,mBAQP,QAAQ,oBAAoB;GAC7B,WAAW;IACT,OAAOA;IACP;IACA,UAAU;KACR;KACA;KACA,QAAQ;KACR,eAAe;KAChB;IACF;GACD;GACD,CAAC;AACF,mBAAiB,KAAK,GAAG,MAAM;AAE/B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;AAClC,eAAa,MAAM,OAAO;UACnB,mBAAmB,CAAC,SAAS,SAAS;AAE/C,QAAO;;;;;;;;;AAUT,eAAsB,qBACpB,QACA,SAW0B;CAC1B,MAAM,EACJ,QACA,UAAU,EAAE,WAAW,iBACrB;CACJ,MAAM,QAAQ,MAAM,sBAAsB,QAAQ;EAChD;EACA,UAAU;GAAE;GAAW;GAAY;EACpC,CAAC;AACF,KAAI,MAAM,WAAW,EACnB,OAAM,IAAI,MACR,iDAAiD,UAAU,cAAc,aAC1E;AAGH,QAAO,MAAM;;;;ACxKf,MAAa,gBAAgB,GAAG;;;;;;;;;;;;;;;AAgBhC,MAAa,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;ACqBrC,eAAsB,4BACpB,QACA,SAawB;CACxB,MAAM,EAAE,QAAQ,WAAW,KAAK,aAAa;CAC7C,MAAM,eAA8B,EAAE;CACtC,IAAI,SAAwB;CAE5B,IAAI,iBAAiB;AACrB,IAAG;EAeD,MAAM,EACJ,kBAAkB,EAAE,OAAO,eACzB,MAhBwC,mBAC1C,QACA,oBACA;GACE,WAAW;IACT,UAAU,EACR,GAAG,UACJ;IACD,OAAO;IACP,OAAO,UAAU,KAAA;IAClB;GACD;GACD,CACF;AAID,eAAa,KAAK,GAAG,MAAM;AAC3B,mBAAiB,SAAS;AAC1B,WAAS,SAAS;UACX;AAET,QAAO,aAAa,MAAM,GAAG,MAAM,EAAE,SAAS,cAAc,EAAE,SAAS,CAAC;;;;;;;;;;AC/D1E,SAAgB,sBAAsB,MAAsD;CAC1F,MAAM,aAAmC,EAAE;AAE3C,MAAK,KAAK,QAAQ;EAChB,IAAI,WAAW,WAAW,MAAM,QAAQ,IAAI,QAAQ,IAAI,aAAa,KAAK;AAE1E,MAAI,aAAa,KAAA,GAAW;AAC1B,cAAW;IACT,KAAK,IAAI,aAAa;IACtB,QAAQ,CAAC,IAAI,KAAK;IACnB;AACD,cAAW,KAAK,SAAS;QAEzB,UAAS,OAAO,KAAK,IAAI,KAAK;AAEhC,SAAO;GACP;AAEF,QAAO;;;;ACjCT,MAAM,iCAAiC,CACrC,GAAG,OAAO,OAAO,eAAe,EAChC,GAAG,OAAO,OAAO,0BAA0B,CAC5C;;;;;;;;AA0BD,eAAsB,WACpB,QACA,aAQA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,WAAW;CACnB,MAAM,EAAE,QAAQ,UAAU,cAAc,UAAU;AAClD,OAAM,mBAAmB,QAAQ,eAAe;EAC9C,WAAW,EACT,OAAO;GACL,IAAI;GACJ,wBAAwB,OAAO;GAC/B,sBAAsB,OAAO;GAC7B,gBAAgB,OAAO;GACvB,eAAe,OAAO;GACtB;GACA,YAAY,OAAO,kBACf,WAAW,gCAAgC,OAAO,gBAAgB,GAClE,OAAO;GACX,uBAAuB,OAAO;GAC/B,EACF;EACD;EACD,CAAC;;;;ACjEJ,MAAa,YAAY,GAAG;;;;;;;;;;;;;;;;AAiB5B,MAAa,kBAAkB,GAAG;;;;;;;;;ACMlC,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,kBACpB,QACA,UAQI,EAAE,EACe;CACrB,MAAM,EAAE,QAAQ,UAAU,EAAE,UAAU,EAAE,KAAK;CAC7C,MAAM,YAAwB,EAAE;CAChC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,WAAW,EAAE,YACX,MAAM,mBAMP,QAAQ,WAAW;GACpB,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAO;GAC9C;GACD,CAAC;AACF,YAAU,KAAK,GAAG,MAAM;AACxB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,UAAU,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;;;;;;AAUjE,eAAsB,aACpB,QACA,UACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,WAAW;AAOnB,KAAI,EAFqB,MAJH,kBAAkB,QAAQ;EAC9C;EACA,UAAU,EAAE,OAAO,SAAS,OAAO;EACpC,CAAC,EAC+B,MAAM,EAAE,YAAY,UAAU,SAAS,MAEnD,CACnB,OAAM,mBAAmB,QAAQ,iBAAiB;EAChD,WAAW,EAAE,OAAO,SAAS,OAAO;EACpC;EACD,CAAC;;;;;;;;;;ACnEN,SAAgB,uBAAuB,QAAoC;AACzE,QAAO;EACL,OAAO,MAAM;EACb,OAAO,OAAO;EACd,OAAO,SAAS,QAAQ;EACxB,sBAAsB,OAAO,WAAW;EACzC,CAAC,KAAK,KAAK;;;;;;;;AASd,SAAgB,sBAAsB,SAAuD;AAC3F,QAAO,CAAC,GAAI,WAAW,EAAE,CAAE,CAAC,MAAM,CAAC,KAAK,IAAI;;;;;;;;AAS9C,SAAgB,yBAAyB,OAAwC;AAC/E,QAAO,MAAM,oBAAoB,MAAM;;AAGzC,SAAS,eACP,cACA,eACS;AACT,QAAO,sBAAsB,aAAa,KAAK,sBAAsB,cAAc;;AAGrF,SAAS,sBACP,YACA,MAC+B;CAC/B,MAAM,CAAC,QAAQ;AACf,KAAI,WAAW,WAAW,KAAK,SAAS,KAAA,EACtC,QAAO;EAAE,MAAM;EAAS,QAAQ;EAAM;AAExC,KAAI,WAAW,WAAW,EACxB,QAAO,EAAE,MAAM,UAAU;AAE3B,QAAO,KAAK,WAAW;;AAGzB,SAAS,gBACP,YACA,OAC+B;AAC/B,QAAO,sBACL,WAAW,QAAQ,WAAW,OAAO,MAAM,mBAAmB,MAAM,MAAM,GACzE,YACC,sBACE,QAAQ,QAAQ,WAAW,OAAO,OAAO,SAAS,MAAM,eAAe,GACtE,aAAa;EACZ,MAAM,kBAAkB,MAAM;AAC9B,MAAI,gBACF,QAAO,sBACL,SAAS,QAAQ,WAAW,OAAO,SAAS,SAAS,gBAAgB,GACpE,cAAc,gBAAgB,WAAW,MAAM,CACjD;AAEH,SAAO,gBAAgB,UAAU,MAAM;GAE1C,CACJ;;AAGH,SAAS,gBACP,YACA,OAC+B;CAC/B,MAAM,UAAU,WAAW,QAAQ,WACjC,eAAe,MAAM,gBAAgB,OAAO,WAAW,CACxD;CACD,MAAM,CAAC,QAAQ;AACf,KAAI,QAAQ,WAAW,KAAK,SAAS,KAAA,EACnC,QAAO;EAAE,MAAM;EAAS,QAAQ;EAAM;AAExC,KAAI,QAAQ,WAAW,EACrB,QAAO,EAAE,MAAM,UAAU;AAS3B,KAAI,CAAC,MAAM,kBAAkB;EAE3B,MAAM,CAAC,gBADS,QAAQ,QAAQ,WAAW,OAAO,gBAAgB,KACpC;AAC9B,MAAI,iBAAiB,KAAA,EACnB,QAAO;GAAE,MAAM;GAAS,QAAQ;GAAc;;AAIlD,QAAO;EAAE,MAAM;EAAa,YAAY;EAAS;;;;;;;;;;;;;;;AAgBnD,SAAgB,2BACd,OACA,iBAC+B;CAC/B,MAAM,eAAe,MAAM;AAE3B,KAAI,cAAc;EAChB,MAAM,SAAS,gBAAgB,QAAQ,WAAW,OAAO,iBAAiB,aAAa;EACvF,MAAM,CAAC,cAAc;AACrB,MAAI,OAAO,WAAW,KAAK,eAAe,KAAA,EACxC,QAAO;GAAE,MAAM;GAAS,QAAQ;GAAY;AAE9C,MAAI,OAAO,WAAW,EACpB,QAAO,EAAE,MAAM,UAAU;AAE3B,SAAO,gBAAgB,QAAQ,MAAM;;AAGvC,QAAO,gBAAgB,iBAAiB,MAAM;;;;;;;;;;;;;;;;;;;AC7FhD,eAAsB,oBACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,WAAW;AACnB,SAAQ,KAAK,YAAY,OAAO,OAAO,uBAAuB;CAE9D,IAAI,mBAAmB;CAEvB,MAAM,gBAAgB,OAAO,MAAM,WAAW,OAAO,qBAAqB;CAC1E,MAAM,qBAAqB,OAAO,MAAM,WAAW,OAAO,mBAAmB,OAAO;CAEpF,MAAM,CAAC,gBAAgB,WAAW,MAAM,QAAQ,IAAI,CAClD,wBAAwB,QAAQ;EAC9B;EACA,oBAAoB,mBAAmB;EACxC,CAAC,EACF,gBAAgB,QAAQ,EAAE,QAAQ,CAAC,CACpC,CAAC;CAEF,MAAM,kBAAwC,CAAC,GAAG,eAAe;CAEjE,IAAI,eAA8B,EAAE;AACpC,KAAI,cACF,gBAAe,MAAM,qBAAqB,QAAQ,EAAE,QAAQ,CAAC;CAG/D,IAAI,gBAAgC,EAAE;AACtC,KAAI,mBACF,iBAAgB,MAAM,6BAA6B,QAAQ,EAAE,QAAQ,CAAC;CAGxE,MAAM,eAAe,MAAM,SAAS,OAAO;CAC3C,MAAM,oBAAoB,MAAM,cAAc,OAAO;CACrD,MAAM,qBAAqB,MAAM,eAAe,OAAO;AAEvD,OAAM,UAAU,QAAQ,OAAO,WAAW;EACxC,MAAM,QAAQ,yBAAyB,OAAO;AAC9C,MAAI;AAEF,OAAI,OAAO,QAAQ,OAAO,SAAS,mBAAmB,IACpD,OAAM,IAAI,MACR,oBAAoB,MAAM,cACpB,OAAO,KAAK,uJAEnB;GAGH,MAAM,aAAa,2BAA2B,QAAQ,gBAAgB;AACtE,OAAI,WAAW,SAAS,YACtB,OAAM,IAAI,MACR,UAAU,WAAW,WAAW,OAAO,+BACjC,MAAM,0JAEb;GAGH,MAAM,iBAAiB,WAAW,SAAS,UAAU,WAAW,SAAS,KAAA;AAEzE,OAAI,kBAAkB,eAAe,uBAAuB,mBAAmB,IAC7E,OAAM,IAAI,MACR,oBAAoB,MAAM,cACpB,eAAe,mBAAmB,uJAEzC;GAGH,MAAM,aAAa,OAAO;AAC1B,OAAI,cAAc,WAAW,SAAS,GAAG;AAGvC,QAAI,CAAC,WAAW,MAAM,EAAE,aAAa,WAAW,UAAU,CACxD,OAAM,IAAI,MACR,oBAAoB,MAAM,sGAE3B;IAEH,MAAM,iBAAiB,WAAW,QAAQ,EAAE,YAAY,SAAS,EAAE;AACnE,QAAI,eAAe,SAAS,EAC1B,OAAM,IAAI,MACR,oBAAoB,MAAM,kDACP,eACd,KAAK,EAAE,QAAQ,YAAY,GAAG,OAAO,GAAG,QAAQ,CAChD,KAAK,KAAK,CAAC,iDACjB;;GAIL,IAAI;AACJ,OAAI,OAAO,sBAAsB;IAC/B,MAAM,UAAU,kBAAkB,OAAO;AACzC,QAAI,CAAC,QACH,OAAM,IAAI,MAAM,0CAA0C,OAAO,uBAAuB;AAE1F,gBAAY,QAAQ;;AAGtB,OAAI,CAAC,aAAa,OAAO,gBACvB,OAAM,IAAI,MAAM,oCAAoC,OAAO,iBAAiB;GAG9E,IAAI;AACJ,OAAI,OAAO,sBAAsB,KAAA,EAC/B,mBAAkB,OAAO,kBAAkB,KAAK,SAAS;IACvD,MAAM,eAAe,mBAAmB;AACxC,QAAI,CAAC,aACH,OAAM,IAAI,MAAM,2CAA2C,OAAO;AAEpE,WAAO,aAAa;KACpB;GAGJ,IAAI;AACJ,OAAI,CAAC,gBAAgB;IAInB,MAAM,eAAe,OAAO;IAC5B,MAAM,EACJ,gBAAgB,EAAE,gBAAgB,cAChC,MAAM,mBAWP,QAAQ,iBAAiB;KAC1B,WAAW,EACT,OAAO;MACL,OAAO,OAAO;MACd,YAAY,OAAO;MACnB,GAAI,eAAe,EAAE,cAAc,GAAG,EAAE;MACxC,oBAAoB,mBAAmB;MACxC,EACF;KACD;KACD,CAAC;AACF,uBAAmB,QAAQ;AAG3B,oBAAgB,KAAK;KACnB,IAAI,QAAQ;KACZ,OAAO,EAAE,gBAAgB,OAAO,OAAO;KACvC,UAAU;KACV,aAAa;KACb,cAAc,QAAQ,gBAAgB,gBAAgB;KACtD,0BAA0B,yBAAyB;KACnD,oBAAoB,mBAAmB;KACvC,2BAA2B;KAC3B,YAAY,OAAO,kBAAkB,EAAE;KACvC,YAAY;KAEZ,QAAQ;MAAE,IAAI;MAAI,MAAM,OAAO;MAAgB;KAC/C,SAAS,YACL;MAAE,IAAI;MAAW,MAAM,OAAO;MAAgC,GAC9D;KACJ,6BAA6B;KAC9B,CAAC;AACF,YAAQ,KAAK,yCAAyC,MAAM,KAAK,QAAQ,GAAG,IAAI;SAEhF,oBAAmB,eAAe;AAoBpC,SAAM,mBAAmB,QAAQ,wBAAwB;IACvD,WAAW,EAAE,OAAO;KAjBpB;KACA,GAAI,OAAO,UAAU,KAAA,IAAY,EAAE,OAAO,OAAO,OAAO,GAAG,EAAE;KAC7D,GAAI,OAAO,aAAa,KAAA,IAAY,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;KACtE,GAAI,OAAO,gBAAgB,KAAA,IAAY,EAAE,aAAa,OAAO,aAAa,GAAG,EAAE;KAC/E,GAAI,OAAO,eAAe,KAAA,IAAY,EAAE,0BAA0B,OAAO,YAAY,GAAG,EAAE;KAC1F,GAAI,OAAO,qBAAqB,KAAA,IAAY,EAAE,cAAc,OAAO,kBAAkB,GAAG,EAAE;KAC1F,YAAY,OAAO;KACnB,GAAI,OAAO,oCAAoC,KAAA,IAC3C,EAAE,2BAA2B,OAAO,iCAAiC,GACrE,EAAE;KACN,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;KAClC,GAAI,OAAO,mBAAmB,KAAA,IAAY,EAAE,YAAY,OAAO,gBAAgB,GAAG,EAAE;KACpF,GAAI,OAAO,mBAAmB,KAAA,IAAY,EAAE,YAAY,OAAO,gBAAgB,GAAG,EAAE;KACpF,GAAI,oBAAoB,KAAA,IAAY,EAAE,iBAAiB,GAAG,EAAE;KAI3B,EAAE;IACnC;IACD,CAAC;AAKF,OAAI,gBAAgB;IAClB,MAAM,eAAe,gBAAgB,WAAW,WAAW,OAAO,OAAO,eAAe,GAAG;AAC3F,QAAI,gBAAgB,EAClB,iBAAgB,OAAO,cAAc,EAAE;;AAI3C,WAAQ,KAAK,wCAAwC,MAAM,KAAK,iBAAiB,IAAI;WAC9E,KAAK;AACZ,sBAAmB;AACnB,WAAQ,MAAM,mCAAmC,MAAM,OAAQ,IAAc,UAAU;;GAEzF;AAEF,SAAQ,KAAK,WAAW,OAAO,OAAO,qBAAqB;AAC3D,QAAO,CAAC;;;;AC/QV,MAAMC,eAAa;;;;;;;AAQnB,eAAsB,2BACpB,QACA,SAQe;CACf,MAAM,EAAE,UAAU,SAAS,SAAS,gBAAgB;AAGpD,OAAM,UAFS,MAAM,SAASA,aAER,EAAE,OAAO,eAAe;AAC5C,QAAM,mBAGH,QAAQ,4BAA4B;GACrC,WAAW;IAAE;IAAU;IAAY;GACnC;GACD,CAAC;GACF;;;;AC9CJ,MAAa,6BAA6B,GAAG;;;;;;;;;;;;;;;;;;;;AC6C7C,eAAsB,uBACpB,QACA,EAAE,aAAa,oBAAoB,EAAE,EAAE,GAAG,QAC1C,UAGI,EAAE,EACW;CACjB,MAAM,EAAE,WAAW;CACnB,MAAM,EACJ,wBAAwB,EAAE,gBACxB,MAAM,mBASP,QAAQ,4BAA4B;EACrC,WAAW,EACT,OAAO;GACL,GAAG;GACH,GAAG;GACH,mBAAmB,kBAAkB,KAAK,EAAE,SAAS,GAAG,YAAY;IAClE,GAAG;IACH,SAAS;IACV,EAAE;GACJ,EACF;EACD;EACD,CAAC;AACF,QAAO,UAAU;;;;AC3EnB,MAAa,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;AAqB9B,MAAa,oBAAoB,GAAG;;;;;;;;;;;;AAapC,MAAa,qBAAqB,GAAG;;;;;;;;;ACNrC,MAAMC,eAAY;;;;;;;;AASlB,eAAsB,mBACpB,QACA,UAKI,EAAE,EACgB;CACtB,MAAM,EAAE,QAAQ,WAAW,EAAE,KAAK;CAClC,MAAM,aAA0B,EAAE;CAClC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,YAAY,EAAE,YACZ,MAAM,mBAMP,QAAQ,aAAa;GACtB,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAU;GACjD;GACD,CAAC;AACF,aAAW,KAAK,GAAG,MAAM;AACzB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,WAAW,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACrEhE,MAAa,kBAAkB,GAAG;;;;;;;;;;;;;;;;AAiBlC,MAAa,wBAAwB,GAAG;;;;;;;;;;AAWxC,MAAa,yBAAyB,GAAG;;;;;;;;;ACVzC,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,uBACpB,QACA,UAGI,EAAE,EACoB;CAC1B,MAAM,EAAE,WAAW;CACnB,MAAM,iBAAkC,EAAE;CAC1C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,gBAAgB,EAAE,YAChB,MAAM,mBAMP,QAAQ,iBAAiB;GAC1B,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,iBAAe,KACb,GAAG,MAAM,KAAK,UAAU;GACtB,GAAG;GACH,YAAY,KAAK,MAAM,KAAK,WAAW;GACxC,EAAE,CACJ;AACD,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,eAAe,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AC9DpE,MAAa,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCzB,MAAa,eAAe,GAAG;;;;;;;;;;;AAY/B,MAAa,gBAAgB,GAAG;;;;;;;;;ACDhC,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,eACpB,QACA,UAUI,EAAE,EACY;CAClB,MAAM,EAAE,QAAQ,WAAW,EAAE,KAAK;CAClC,MAAM,SAAkB,EAAE;CAC1B,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,QAAQ,EAAE,YACR,MAAM,mBAMP,QAAQ,QAAQ;GACjB,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAU;GACjD;GACD,CAAC;AACF,SAAO,KAAK,GAAG,MAAM;AACrB,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,OAAO,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACjG5D,MAAa,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;ACaxC,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,4BACpB,QACA,UAGI,EAAE,EACyB;CAC/B,MAAM,EAAE,WAAW;CACnB,MAAM,sBAA4C,EAAE;CACpD,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,qBAAqB,EAAE,YACrB,MAAM,mBAMP,QAAQ,uBAAuB;GAChC,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,sBAAoB,KAAK,GAAG,MAAM;AAClC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,oBAAoB,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACxDzE,MAAa,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;ACmBjC,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,sBACpB,QACA,SAWyB;CACzB,MAAM,EAAE,QAAQ,aAAa;CAC7B,MAAM,gBAAgC,EAAE;CACxC,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,eAAe,EAAE,YACf,MAAM,mBAMP,QAAQ,gBAAgB;GACzB,WAAW;IAAE,OAAOA;IAAW;IAAQ;IAAU;GACjD;GACD,CAAC;AACF,gBAAc,KAAK,GAAG,MAAM;AAC5B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,cAAc,MAAM,GAAG,MAAM,EAAE,SAAS,cAAc,EAAE,SAAS,CAAC;;;;;;;;;;;;ACtC3E,eAAsB,gBACpB,QACA,SAMoD;CACpD,MAAM,EAAE,OAAO,WAAW,SAAS,gBAAgB;CAYnD,MAAM,EAAE,oBAAoB,MAAM,mBAM/B,QAAQ,mBAAmB;EAC5B,WAAW,EAAE,OAAA;GAjBb,MAAM,UAAU;GAChB,aAAa,UAAU;GACvB,QAAQ,UAAU;GAClB,MAAM,UAAU;GAChB,SAAS,UAAU;GACnB,gCAAgB,IAAI,MAAM;GAC1B,UAAU,EAAE;GAWM,EAAE;EACpB;EACD,CAAC;AACF,QAAO,gBAAgB;;;;;;;;AASzB,eAAsB,iBACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,YAAY,SAAS,gBAAgB;AACpD,OAAM,mBAAmB,QAAQ,oBAAoB;EACnD,WAAW,EACT,OAAO,EACL,YAAY,WAAW,KAAK,CAAC,WAAW,SAAS;GAC/C;GACA,MAAM,UAAU;GAChB,aAAa,UAAU;GACvB,QAAQ,UAAU;GAClB,MAAM,UAAU;GAChB,SAAS,UAAU;GACpB,EAAE,EACJ,EACF;EACD;EACD,CAAC;;;;;;;;;;AAWJ,eAAsB,eACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AACjC,QAAO,KAAK,YAAY,OAAO,OAAO,kBAAkB;CAExD,IAAI,mBAAmB;CAIvB,MAAM,kBAAkB,MAAM,MAFG,mBAAmB,QAAQ,EAAE,QAAQ,CAAC,EAErB,OAAO;AAMzD,OAAM,UAFgB,OAAO,QAAQ,UAAU,CAAC,gBAAgB,MAAM,MAEzC,EAAE,OAAO,cAAc;AAClD,MAAI;GACF,MAAM,eAAe,MAAM,gBAAgB,QAAQ;IAAE,OAAO;IAAW;IAAQ,CAAC;AAChF,mBAAgB,aAAa,QAAQ;AACrC,UAAO,KAAK,mCAAmC,UAAU,KAAK,IAAI;WAC3D,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,8BAA8B,UAAU,KAAK,OAAQ,IAAc,UAAU;;GAE5F;AAEF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,gBAAgB;AACvD,QAAM,iBAAiB,QAAQ;GAC7B,OAAO,OAAO,KAAK,UAAU,CAAC,OAAO,gBAAgB,MAAM,MAAO,GAAG,CAAC;GACtE;GACD,CAAC;AACF,SAAO,KAAK,wBAAwB,OAAO,OAAO,gBAAgB;UAC3D,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,mBAAmB,OAAO,OAAO,mBAAoB,IAAc,UAAU;;AAG5F,QAAO,CAAC;;;;;;;;;;;;AC3HV,eAAsB,oBACpB,QACA,SAM6C;CAC7C,MAAM,EAAE,OAAO,eAAe,SAAS,gBAAgB;CASvD,MAAM,EAAE,wBAAwB,MAAM,mBAMnC,QAAQ,uBAAuB;EAChC,WAAW,EAAE,OAAA;GAdb,MAAM,cAAc;GACpB,aAAa,cAAc;GAC3B,YAAY,cAAc;GAC1B,UAAU,EAAE;GAWM,EAAE;EACpB;EACD,CAAC;AACF,QAAO,oBAAoB;;;;;;;;AAS7B,eAAsB,qBACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,gBAAgB,SAAS,gBAAgB;AACxD,OAAM,mBAAmB,QAAQ,wBAAwB;EACvD,WAAW,EACT,OAAO,EACL,gBAAgB,eAAe,KAAK,CAAC,eAAe,SAAS;GAC3D;GACA,MAAM,cAAc;GACpB,aAAa,cAAc;GAC3B,YAAY,cAAc;GAC3B,EAAE,EACJ,EACF;EACD;EACD,CAAC;;;;;;;;;;AAWJ,eAAsB,mBACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AACjC,QAAO,KAAK,YAAY,OAAO,OAAO,sBAAsB;CAE5D,IAAI,mBAAmB;CAIvB,MAAM,sBAAsB,MAAM,MAFG,uBAAuB,QAAQ,EAAE,QAAQ,CAAC,EAErB,OAAO;AAMjE,OAAM,UAFoB,OAAO,QAAQ,UAAU,CAAC,oBAAoB,MAAM,MAE7C,EAAE,OAAO,kBAAkB;AAC1D,MAAI;GACF,MAAM,mBAAmB,MAAM,oBAAoB,QAAQ;IAAE,OAAO;IAAe;IAAQ,CAAC;AAC5F,uBAAoB,iBAAiB,QAAQ;AAC7C,UAAO,KAAK,uCAAuC,cAAc,KAAK,IAAI;WACnE,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,kCAAkC,cAAc,KAAK,OAAQ,IAAc,UAC5E;;GAEH;AAEF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,oBAAoB;AAC3D,QAAM,qBAAqB,QAAQ;GACjC,OAAO,OAAO,KAAK,UAAU,CAAC,OAAO,oBAAoB,MAAM,MAAO,GAAG,CAAC;GAC1E;GACD,CAAC;AACF,SAAO,KAAK,wBAAwB,OAAO,OAAO,oBAAoB;UAC/D,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,mBAAmB,OAAO,OAAO,uBAAwB,IAAc,UAAU;;AAGhG,QAAO,CAAC;;;;;;;;;;;;ACzGV,eAAsB,YACpB,QACA,SAMiD;CACjD,MAAM,EAAE,OAAO,OAAO,SAAS,gBAAgB;CAa/C,MAAM,EAAE,gBAAgB,MAAM,mBAM3B,QAAQ,cAAc;EACvB,WAAW,EAAE,OAAA;GAlBb,MAAM,MAAM;GACZ,aAAa,MAAM;GACnB,wBAAwB,MAAM;GAC9B,kBAAkB,MAAM;GACxB,aAAa,MAAM;GACnB,wBAAwB,MAAM,wBAAwB;GACtD,0BAA0B,MAAM,wBAAwB;GAYtC,EAAE;EACpB;EACD,CAAC;AACF,QAAO,YAAY;;;;;;;;AASrB,eAAsB,aACpB,QACA,SAMe;CACf,MAAM,EAAE,OAAO,QAAQ,SAAS,gBAAgB;AAChD,OAAM,mBAAmB,QAAQ,eAAe;EAC9C,WAAW,EACT,OAAO,EACL,QAAQ,OAAO,KAAK,CAAC,OAAO,SAAS;GACnC;GACA,MAAM,MAAM;GACZ,aAAa,MAAM;GACnB,wBAAwB,MAAM;GAC9B,kBAAkB,MAAM;GAEzB,EAAE,EACJ,EACF;EACD;EACD,CAAC;;;;;;;;;;AAWJ,eAAsB,WACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;AACjC,QAAO,KAAK,YAAY,OAAO,OAAO,aAAa;CAEnD,IAAI,mBAAmB;CAIvB,MAAM,cAAc,MAAM,MAFG,eAAe,QAAQ,EAAE,QAAQ,CAAC,EAErB,OAAO;AAMjD,OAAM,UAFY,OAAO,QAAQ,UAAU,CAAC,YAAY,MAAM,MAErC,EAAE,OAAO,UAAU;AAC1C,MAAI;GACF,MAAM,WAAW,MAAM,YAAY,QAAQ;IAAE,OAAO;IAAO;IAAQ,CAAC;AACpE,eAAY,SAAS,QAAQ;AAC7B,UAAO,KAAK,8BAA8B,MAAM,KAAK,IAAI;WAClD,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,yBAAyB,MAAM,KAAK,OAAQ,IAAc,UAAU;;GAEnF;AAEF,KAAI;AACF,SAAO,KAAK,aAAa,OAAO,OAAO,WAAW;AAClD,QAAM,aAAa,QAAQ;GACzB,OAAO,OAAO,KAAK,UAAU,CAAC,OAAO,YAAY,MAAM,MAAO,GAAG,CAAC;GAClE;GACD,CAAC;AACF,SAAO,KAAK,wBAAwB,OAAO,OAAO,WAAW;UACtD,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,mBAAmB,OAAO,OAAO,cAAe,IAAc,UAAU;;AAGvF,QAAO,CAAC;;;;ACpJV,MAAa,iCAAiC,GAAG;;;;;;;;;;;;;;;AAgBjD,MAAa,gCAAgC,GAAG;;;;;;;;;;AAWhD,MAAa,gCAAgC,GAAG;;;;;;;;;;;;;;;;ACThD,eAAsB,8BACpB,QACA,UAQI,EAAE,EAC2B;CACjC,MAAM,EAAE,QAAQ,WAAW,EAAE,KAAK;CAClC,MAAM,EACJ,6BAA6B,EAAE,YAC7B,MAAM,mBAMP,QAAQ,gCAAgC;EACzC,WAAW,EACT,UAAU,EACR,GAAG,UACJ,EACF;EACD;EACD,CAAC;AACF,QAAO;;;;AC/CT,MAAa,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDtC,MAAa,sBAAsB,GAAG;;;;;;;AAQtC,MAAa,sBAAsB,GAAG;;;;;;;;;AC4BtC,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,oBACpB,QACA,UAgBI,EAAE,EACiB;CACvB,MAAM,EAAE,QAAQ,WAAW,EAAE,KAAK;CAClC,MAAM,cAA4B,EAAE;CACpC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,mBAAmB,EAAE,YACnB,MAAM,mBAMP,QAAQ,qBAAqB;GAC9B,WAAW;IACT,OAAOA;IACP;IACA,UAAU;KACR,GAAG;KACH,GAAI,SAAS,eAAe,EAAE,cAAc,SAAS,aAAa,aAAa,EAAE,GAAG,EAAE;KACtF,GAAI,SAAS,aAAa,EAAE,YAAY,SAAS,WAAW,aAAa,EAAE,GAAG,EAAE;KACjF;IACF;GACD;GACD,CAAC;AACF,cAAY,KACV,GAAG,MAAM,KAAK,UAAU;GACtB,GAAG;GACH,IAAI,KAAK,IAAI;GACb,OAAO,KAAK,OAAO;GACnB,OAAO,KAAK,MAAM;GAClB,MAAM,KAAK,MAAM;GAClB,EAAE,CACJ;AACD,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO;;;;AChKT,MAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0IzC,MAAa,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YA4DlB,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFtC,MAAa,oCAAoC,GAAG;;;;;;;;;;;;ACkDpD,MAAMC,cAAY;;;;;;;AAQlB,eAAsB,oBACpB,QACA,UAGI,EAAE,EACiB;CACvB,MAAM,EAAE,WAAW;CACnB,MAAM,cAA4B,EAAE;CACpC,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,iBAAiB,EAAE,YACjB,MAAM,mBAMP,QAAQ,aAAa;GACtB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,cAAY,KAAK,GAAG,MAAM;AAC1B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,YAAY,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,MAAM,CAAC;;;;AC7WnE,MAAa,6BAA6B,EAAE,KAAK;CAC/C,8BAA8B,EAAE;CAChC,oBAAoB,EAAE,MAAM;EAC1B,EAAE,QAAQ,mBAAmB,UAAU;EACvC,EAAE,QAAQ,mBAAmB,aAAa;EAC1C,EAAE,QAAQ,mBAAmB,QAAQ;EACrC,EAAE,QAAQ,mBAAmB,WAAW;EACxC,EAAE,QAAQ,mBAAmB,SAAS;EACtC,EAAE,QAAQ,mBAAmB,eAAe;EAC7C,CAAC;CACF,oBAAoB,EAAE,MAAM,EAAE,OAAO;CACtC,CAAC;AAGF,MAAa,gCAAgC,EAAE,KAAK;CAClD,8BAA8B,EAAE;CAChC,oBAAoB,EAAE,MAAM,CAC1B,EAAE,QAAQ,mBAAmB,WAAW,EACxC,EAAE,QAAQ,mBAAmB,QAAQ,CACtC,CAAC;CACH,CAAC;;;;AAKF,MAAa,iBAAiB,EAAE,MAAM,CAAC,4BAA4B,8BAA8B,CAAC;AAalG,MAAa,uBAAsE,EAAE,UACnF,yBACC,SACC,EAAE,aAAa,CACb,EAAE,KAAK;;AAEL,eAAe,SAAS,cAAc,EACvC,CAAC,EACF,EAAE,QAAQ;;CAER,OAAO,EAAE,MAAM,eAAe;;CAE9B,aAAa,EAAE,MAAM,KAAK;CAC3B,CAAC,CACH,CAAC,CACL;AAED,MAAa,mBAAmB,EAAE,QAAQ;CACxC,QAAQ,SAAS,8BAA8B;CAC/C,MAAM;CACN,YAAY;CACb,CAAC;;;;;;;AAWF,SAAgB,4BAA4B,cAAwC;AAClF,QAAO,YAAY,kBAAkB,aAAa;;;;AC5EpD,MAAa,sBAAsB,EAAE,aAAa,CAChD,EAAE,QAAQ,EACR,gBAAgB,EAAE,QAAQ;CACxB,aAAa,EAAE;CACf,iBAAiB,EAAE;CACnB,oBAAoB,EAAE;CACvB,CAAC,EACH,CAAC,EACF,EAAE,KAAK;CACL,oBAAoB,EAAE,MAAM,EAAE,OAAO;CACrC,oBAAoB,SAAS,mBAAmB;CACjD,CAAC,CACH,CAAC;;;;;;;AAWF,SAAgB,yBAAyB,WAAwC;AAC/E,QAAO,YAAY,qBAAqB,UAAU;;;;;;;;;;;;ACMpD,eAAsB,2BACpB,QACA,SAMqD;CACrD,MAAM,EAAE,OAAO,sBAAsB,SAAS,gBAAgB;CAQ9D,MAAM,EAAE,+BAA+B,MAAM,mBAM1C,QAAQ,+BAA+B;EACxC,WAAW,EAAE,OAAA;GAbb,OAAO,qBAAqB;GAC5B,aAAa,qBAAqB,eAAe;GACjD,QAAQ,qBAAqB,UAAU;GACvC,aAAa,qBAAqB;GAUhB,EAAE;EACpB;EACD,CAAC;AACF,QAAO,2BAA2B;;;;;;;;AASpC,eAAsB,2BACpB,QACA,SASe;CACf,MAAM,EAAE,OAAO,sBAAsB,SAAS,gBAAgB;AAC9D,OAAM,mBAAmB,QAAQ,+BAA+B;EAC9D,WAAW,EACT,OAAO;GACL,IAAI,qBAAqB;GACzB,OAAO,qBAAqB;GAC5B,aAAa,qBAAqB;GAClC,QAAQ,qBAAqB;GAC7B,aAAa,qBAAqB;GACnC,EACF;EACD;EACD,CAAC;;;;;;;;;;AAWJ,eAAsB,0BACpB,QACA,QACA,UAGI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,gBAAgB;CACjC,IAAI,mBAAmB;AACvB,QAAO,KAAK,YAAY,OAAO,OAAO,8BAA8B;CAIpE,MAAM,oBAA6D,MACjE,MAH0C,8BAA8B,QAAQ,EAAE,QAAQ,CAAC,EAI3F,QACD;AAID,OAAM,UAFiB,OAAO,QAAQ,UAAU,CAAC,kBAAkB,MAAM,OAE3C,EAAE,OAAO,UAAU;AAC/C,MAAI;AACF,SAAM,2BAA2B,QAAQ;IAAE;IAAO;IAAQ,CAAC;AAC3D,UAAO,KAAK,gDAAgD,MAAM,MAAM,IAAI;WACrE,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,4CAA4C,MAAM,MAAM,OAAQ,IAAc,UAC/E;;GAEH;AAKF,OAAM,UAHsB,OACzB,KAAK,UAAU,CAAC,OAAO,kBAAkB,MAAM,QAAQ,GAAG,CAAC,CAC3D,QAAQ,MAAgD,CAAC,CAAC,EAAE,GAC5B,EAAE,OAAO,CAAC,OAAO,kBAAkB;AACpE,MAAI;AACF,SAAM,2BAA2B,QAAQ;IACvC,OAAO;KAAE,GAAG;KAAO,IAAI;KAAc;IACrC;IACD,CAAC;AACF,UAAO,KAAK,+CAA+C,MAAM,MAAM,IAAI;WACpE,KAAK;AACZ,sBAAmB;AACnB,UAAO,MACL,0CAA0C,MAAM,MAAM,OAAQ,IAAc,UAC7E;;GAEH;AAEF,QAAO,CAAC;;;;;;;;;;;;AC/FV,eAAsB,kBACpB,QACA,SAee;CACf,MAAM,EAAE,OAAO,aAAa,SAAS,aAAa,gCAAgC;AAUlF,OAAM,UADU,MAAM,aAAa,IACZ,EAAE,OAAO,kBAAkB;AAChD,QAAM,mBAAmB,QAAQ,qBAAqB;GACpD,WAAW,EACT,OAAO,cAAc,KAAK,gBAAgB;IACxC,OAAO,WAAW;IAClB,MAAM,WAAW;IACjB,kBAAkB,WAAW;IAC7B,SAAS,WAAW;IACpB,gCAAgC,WAAW;IAC3C,UAAU,WAAW;IACrB,OAAO,WAAW;IAClB,MAAM,WAAW;IACjB,qBAAqB,WAAW;IAChC,oBAAoB,WAAW;IAC/B,GAAI,WAAW,aACX,EAMC,GACD,EAAE;IACN,eAAe,WAAW,YAAY,KACnC,oBAAoB,4BAA4B,iBAAkB,GACpE;IACF,EAAE,EACJ;GACD;GACD,CAAC;GACF;;;;;;;;AASJ,eAAsB,iBACpB,QACA,SAae;CACf,MAAM,EAAE,OAAO,YAAY,SAAS,aAAa,sBAAsB,EAAE,KAAK;CAC9E,MAAM,gBAAgB,QAAwB;EAC5C,MAAM,WAAW,oBAAoB;AACrC,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,kBAAkB,IAAI,mBAAmB;AAE3D,SAAO,SAAS;;AAElB,OAAM,mBAAmB,QAAQ,qBAAqB;EACpD,WAAW,EACT,OAAO;GACL,KAAK,CAAC,WAAW,GAAG;GACpB,OAAO,WAAW;GAClB,kBAAkB,WAAW;GAC7B,SAAS,WAAW;GACpB,UAAU,WAAW;GACrB,gCAAgC,WAAW;GAC3C,OAAO,WAAW;GAClB,MAAM,WAAW;GACjB,qBAAqB,WAAW;GAChC,oBAAoB,WAAW;GAC/B,GAAI,WAAW,aACX,EACE,YAAY,WAAW,WAAW,KAAK,EAAE,KAAK,cAAc;IAC1D,gBAAgB,aAAa,IAAI;IACjC,qBAAqB;IACtB,EAAE,EACJ,GACD,EAAE;GACP,EACF;EACD;EACD,CAAC;;;;;;;;AASJ,SAAS,uBAAuB,EAC9B,OACA,eACoD;AACpD,QAAO,GAAG,MAAM,GAAG,YAChB,KAAK,MAAM,EAAE,MAAM,CACnB,MAAM,CACN,KAAK,IAAI;;;;;;;;AASd,SAAS,4BAA4B,EACnC,OACA,eACyD;AACzD,QAAO,GAAG,MAAM,GAAG,YAAY,MAAM,CAAC,KAAK,IAAI;;;;;;;;;;AAWjD,eAAsB,gBACpB,QACA,QACA,UAKI,EAAE,EACY;CAClB,MAAM,EAAE,SAAS,aAAa,gBAAgB,EAAE,KAAK;CACrD,IAAI,mBAAmB;AACvB,QAAO,KAAK,YAAY,OAAO,OAAO,kBAAkB;CAExD,MAAM,CAAC,qBAAqB,iCAAiC,MAAM,QAAQ,IAAI,CAC7E,oBAAoB,QAAQ,EAAE,QAAQ,CAAC,EACvC,8BAA8B,QAAQ,EAAE,QAAQ,CAAC,CAClD,CAAC;CAEF,MAAM,8BAAuE,MAC3E,+BACA,QACD;CACD,MAAM,oBAAoB,MAAM,qBAAqB,uBAAuB;CAG5E,MAAM,sBAAsB,MAAM,eAAe,OAAO;CACxD,MAAM,mBAAmB,MACvB,oBAAoB,QAAQ,MAAM,CAAC,CAAC,EAAE,gCAAgC,GACrE,EAAE,sCAAsC,gCAAgC,GAC1E;CAED,MAAM,qBAAqB,KAAK,OAAO,KAAK,UAAU,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC,QAC9E,oBAAoB,CAAC,4BAA4B,iBACnD;AACD,KAAI,mBAAmB,SAAS,GAAG;AACjC,SAAO,MACL,qCAAqC,mBAAmB,KACtD,SACD,CAAC,+BACH;AACD,SAAO;;CAGT,MAAM,iBAAiB,OAAO,QAC3B,UACC,CAAC,kBAAkB,4BAA4B,MAAM,KACrD,CAAC,iBAAiB,MAAM,gCAC3B;AAED,KAAI,eAAe,SAAS,EAC1B,KAAI;AACF,SAAO,KAAK,aAAa,eAAe,OAAO,kBAAkB;AACjE,QAAM,kBAAkB,QAAQ;GAC9B,OAAO;GACP;GACA;GACA;GACD,CAAC;AACF,SAAO,KAAK,yBAAyB,eAAe,OAAO,gBAAgB;UACpE,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,oCAAqC,IAAc,UAAU;;AAW9E,OAAM,UAPsB,OACzB,KAAK,UAAU,CACd,OACA,kBAAkB,4BAA4B,MAAM,GAAG,MACrD,iBAAiB,MAAM,iCAAkC,GAC5D,CAAC,CACD,QAAQ,MAAsC,CAAC,CAAC,EAAE,GAClB,EAAE,OAAO,CAAC,OAAO,kBAAkB;AACpE,MAAI;AACF,SAAM,iBAAiB,QAAQ;IAC7B,OAAO;KAAE,GAAG;KAAO,IAAI;KAAc;IACrC;IACA;IACD,CAAC;AACF,UAAO,KAAK,oCAAoC,MAAM,MAAM,IAAI;WACzD,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,+BAA+B,MAAM,MAAM,OAAQ,IAAc,UAAU;;GAE1F;AAEF,QAAO,CAAC;;;;AC1SV,MAAa,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoChC,MAAa,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCvC,MAAa,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvBtC,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,qBACpB,QACA,UAGI,EAAE,EACkB;CACxB,MAAM,EAAE,WAAW;CACnB,MAAM,eAA8B,EAAE;CACtC,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,cAAc,EAAE,YACd,MAAM,mBAMP,QAAQ,eAAe;GACxB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,eAAa,KAAK,GAAG,MAAM;AAC3B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,aAAa,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;ACrFlE,MAAa,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;AA0B/B,MAAa,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;AAqBtC,MAAa,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;ACrBpC,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,qBACpB,QACA,UAGI,EAAE,EACiB;CACvB,MAAM,EAAE,WAAW;CACnB,MAAM,eAA6B,EAAE;CACrC,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,cAAc,EAAE,YACd,MAAM,mBAMP,QAAQ,cAAc;GACvB,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,eAAa,KAAK,GAAG,MAAM;AAC3B,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,aAAa,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AClElE,MAAa,4BAA4B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC5C,MAAa,mCAAmC,GAAG;;;;;;;;;;;;;;;;;;;;;;;AAwBnD,MAAa,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;ACzBlD,MAAMC,cAAY;;;;;;;;AASlB,eAAsB,gCACpB,QACA,UAGI,EAAE,EAC6B;CACnC,MAAM,EAAE,WAAW;CACnB,MAAM,0BAAoD,EAAE;CAC5D,IAAI,SAAS;CAGb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,yBAAyB,EAAE,YACzB,MAAM,mBAMP,QAAQ,2BAA2B;GACpC,WAAW;IAAE,OAAOA;IAAW;IAAQ;GACvC;GACD,CAAC;AACF,0BAAwB,KAAK,GAAG,MAAM;AACtC,YAAUA;AACV,mBAAiB,MAAM,WAAWA;UAC3B;AAET,QAAO,wBAAwB,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;AC3D7E,MAAMC,eAAa;;;;;;;;;AAUnB,eAAsB,iBACpB,QACA,SAqBqB;CACrB,MAAM,EAAE,OAAO,SAAS,gBAAgB;CACxC,MAAM,EACJ,kBAAkB,EAAE,iBAClB,MAAM,mBAMP,QAAQ,mBAAmB;EAC5B,WAAW,EAAE,OAAO;EACpB;EACD,CAAC;AACF,QAAO,KAAK,oCAAoC,MAAM,KAAK,IAAI;AAC/D,QAAO;;;;;;;;;AAUT,eAAsB,mBACpB,QACA,SAuBuB;CACvB,MAAM,EAAE,OAAO,cAAc,SAAS,gBAAgB;CACtD,MAAM,EACJ,oBAAoB,EAAE,cAAc,0BAClC,MAAM,mBAMP,QAAQ,qBAAqB;EAC9B,WAAW,EACT,OAAO,EACL,cACD,EACF;EACD;EACD,CAAC;AACF,QAAO,KAAK,wBAAwB,aAAa,OAAO,gBAAgB;AACxE,QAAO;;;;;;;;;;AAWT,eAAsB,iBACpB,QACA,cACA,UAKI,EAAE,EAML;CACD,MAAM,EAAE,SAAS,aAAa,cAAc,OAAO;CACnD,IAAI,mBAAmB;CACvB,MAAM,QAAsB,EAAE;CAI9B,MAAM,mBAAmB,MAAM,MADR,qBAAqB,QAAQ,EAAE,QAAQ,CAAC,EACtB,OAAO;CAGhD,MAAM,4BAA4B,aAAa,KAAK,cAAc,CAChE,WACA,iBAAiB,UAAU,OAAO,GACnC,CAAC;CAGF,MAAM,kBAAkB,0BACrB,QAAQ,GAAG,cAAc,CAAC,SAAS,CACnC,KAAK,CAAC,eAAe,UAA6B;AACrD,KAAI;AACF,SAAO,KAAK,aAAa,gBAAgB,OAAO,uBAAuB;AACvE,QAAM,IACJ,iBACA,OAAO,SAAS;GACd,MAAM,UAAU,MAAM,iBAAiB,QAAQ;IAAE,OAAO;IAAM;IAAQ,CAAC;AACvE,SAAM,KAAK,QAAQ;KAErB,EACE,aACD,CACF;AACD,SAAO,KAAK,uBAAuB,gBAAgB,OAAO,gBAAgB;UACnE,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,oCAAqC,IAAc,UAAU;;CAI5E,MAAM,uBAAuB,0BAA0B,QACpD,MAAsC,CAAC,CAAC,EAAE,GAC5C;CACD,MAAM,SAAS,MAAM,sBAAsBA,aAAW;AACtD,QAAO,KAAK,aAAa,qBAAqB,OAAO,mBAAmB;AAExE,OAAM,UAAU,QAAQ,OAAO,UAAU;AACvC,MAAI;GACF,MAAM,eAAe,MAAM,mBAAmB,QAAQ;IACpD,OAAO,MAAM,KAAK,CAAC,OAAO,SAAS;KACjC,GAAG;KACH;KACD,EAAE;IACH;IACD,CAAC;AACF,SAAM,KAAK,GAAG,aAAa;AAC3B,UAAO,KAAK,yBAAyB,qBAAqB,OAAO,iBAAiB;WAC3E,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,oCAAqC,IAAc,UAAU;;AAG5E,SAAO,KAAK,WAAW,aAAa,OAAO,iBAAiB;GAC5D;AAGF,QAAO;EACL,cAAc;EACd,SAAS,CAAC;EACX;;;;ACrLH,MAAM,aAAa;;;;;;;;;AAUnB,eAAsB,6BACpB,QACA,SA+BiC;CACjC,MAAM,EAAE,OAAO,SAAS,gBAAgB;CACxC,MAAM,EACJ,8BAA8B,EAAE,6BAC9B,MAAM,mBAMP,QAAQ,iCAAiC;EAC1C,WAAW,EAAE,OAAO;EACpB;EACD,CAAC;AACF,QAAO,KAAK,kDAAkD,MAAM,KAAK,IAAI;AAC7E,QAAO;;;;;;;;;AAUT,eAAsB,8BACpB,QACA,SA+BmC;CACnC,MAAM,EAAE,OAAO,MAAM,SAAS,gBAAgB;CAC9C,MAAM,EACJ,+BAA+B,EAAE,8BAC/B,MAAM,mBAMP,QAAQ,kCAAkC;EAC3C,WAAW,EACT,OAAO,EACL,yBAAyB,MAC1B,EACF;EACD;EACD,CAAC;AACF,QAAO,KAAK,wBAAwB,KAAK,OAAO,6BAA6B;AAC7E,QAAO;;;;;;;;;;AAWT,eAAsB,4BACpB,QACA,yBACA,UAKI,EAAE,EAML;CACD,MAAM,EAAE,SAAS,aAAa,cAAc,OAAO;CACnD,IAAI,mBAAmB;CACvB,MAAM,OAAiC,EAAE;AACzC,QAAO,KAAK,uCAAuC;CAInD,MAAM,gCAAgC,MAAM,MADrB,gCAAgC,QAAQ,EAAE,QAAQ,CAAC,GACnB,EAAE,MAAM,sBAC7D,KAAK,UAAU;EAAE;EAAM;EAAiB,CAAC,CAC1C;CAGD,MAAM,uCAAuC,wBAAwB,KAAK,aAAa,CACrF,UACA,8BACE,KAAK,UAAU;EACb,MAAM,SAAS;EACf,iBAAiB,SAAS;EAC3B,CAAC,GACD,GACJ,CAAC;CAGF,MAAM,6BAA6B,qCAChC,QAAQ,GAAG,cAAc,CAAC,SAAS,CACnC,KAAK,CAAC,cAAc,SAAwC;AAC/D,KAAI;AACF,SAAO,KAAK,aAAa,2BAA2B,OAAO,oCAAoC;AAC/F,QAAM,IACJ,4BACA,OAAO,QAAQ;GACb,MAAM,SAAS,MAAM,6BAA6B,QAAQ;IAAE,OAAO;IAAK;IAAQ,CAAC;AACjF,QAAK,KAAK,OAAO;KAEnB,EACE,aACD,CACF;AACD,SAAO,KACL,uBAAuB,2BAA2B,OAAO,6BAC1D;UACM,KAAK;AACZ,qBAAmB;AACnB,SAAO,MAAM,iDAAkD,IAAc,UAAU;;CAIzF,MAAM,kCAAkC,qCAAqC,QAC1E,MAAkD,CAAC,CAAC,EAAE,GACxD;CACD,MAAM,SAAS,MAAM,iCAAiC,WAAW;AACjE,QAAO,KAAK,aAAa,gCAAgC,OAAO,gCAAgC;AAEhG,OAAM,UAAU,QAAQ,OAAO,UAAU;AACvC,MAAI;GACF,MAAM,cAAc,MAAM,8BAA8B,QAAQ;IAE9D,OAAO,MAAM,KAAK,CAAC,EAAE,iBAAiB,GAAG,SAAS,SAAS;KACzD,GAAG;KACH;KACD,EAAE;IACH;IACD,CAAC;AACF,QAAK,KAAK,GAAG,YAAY;AACzB,UAAO,KACL,yBAAyB,gCAAgC,OAAO,8BACjE;WACM,KAAK;AACZ,sBAAmB;AACnB,UAAO,MAAM,iDAAkD,IAAc,UAAU;;AAGzF,SAAO,KAAK,WAAW,wBAAwB,OAAO,8BAA8B;GACpF;AAGF,QAAO;EACL,yBAAyB;EACzB,SAAS,CAAC;EACX;;;;;;;;;;ACtPH,SAAgB,+BACd,OAC2B;AAC3B,QAAO;EACL,MAAM,MAAM;EACZ,SAAS;GACP,gBAAgB,MAAM,OAAO,EAAE;GAC/B,cAAc,MAAM,gBAAgB,EAAE;GACtC,GAAI,MAAM,2BAA2B,KAAA,IACjC,EAAE,wBAAwB,MAAM,wBAAwB,GACxD,EAAE;GACN,GAAI,MAAM,cAAc,KAAA,IAAY,EAAE,WAAW,MAAM,WAAW,GAAG,EAAE;GACxE;EACF;;;;;;;;;;;;;;;ACgBH,SAAgB,iBAAmC,OAA8B;AAC/E,QAAQ,IAAI,OAAO,OAAO,EAAE,MAAM,MAAM,CAAC,IAAiB,KAAA;;;;;;;;;;;;;;AAkD5D,SAAgB,uBACd,SACA,UAMoB;CACpB,MAAM,gBAA0B,EAAE;CAElC,MAAM,cAAc,iBAA2C,SAAS,cAAc;AAKtF,MAHE,gBAAgB,KAAA,IACZ,KAAA,IACA,OAAO,KAAK,YAAY,YAAY,SAAS,CAAC,SAAS,QAAQ,MAChD,QAAQ,KAC3B,eAAc,KAAK,OAAO;CAG5B,MAAM,iBAAiB,iBACrB,SAAS,qBACV;CACD,MAAM,gBAAgB,CAAC,GAAI,gBAAgB,gBAAgB,EAAE,CAAE,CAAC,MAAM;CACtE,MAAM,eAAe,CAAC,GAAG,QAAQ,QAAQ,aAAa,CAAC,MAAM;AAC7D,KAAI,KAAK,UAAU,cAAc,KAAK,KAAK,UAAU,aAAa,CAChE,eAAc,KAAK,eAAe;AAEpC,MACG,gBAAgB,0BAA0B,YAC1C,QAAQ,QAAQ,0BAA0B,OAE3C,eAAc,KAAK,yBAAyB;AAE9C,KAAI,gBAAgB,cAAc,QAAQ,QAAQ,UAChD,eAAc,KAAK,YAAY;CAEjC,MAAM,mBAAmB,OAAO,KAAK,gBAAgB,2BAA2B,EAAE,CAAC,CAAC,MAAM;CAC1F,MAAM,kBAAkB,OAAO,KAAK,QAAQ,QAAQ,eAAe,CAAC,MAAM;AAC1E,KAAI,KAAK,UAAU,iBAAiB,KAAK,KAAK,UAAU,gBAAgB,CACtE,eAAc,KAAK,MAAM;AAG3B,QAAO;EAAE,SAAS,cAAc,SAAS;EAAG;EAAe;;;;AClI7D,MAAa,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCnC,MAAa,yBAAyB,GAAG;;;;;;;;;;;;;;;;;;;AAoBzC,MAAa,oCAAoC,GAAG;;;;;;;;;;;;;;;;;;;;;;AAuBpD,MAAa,mCAAmC,GAAG;;;;;;;;;;AAWnD,MAAa,oBAAoB,GAAG;;;;;;;AAQpC,MAAa,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;AAqBtC,MAAa,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxGlD,MAAa,mCAAmC;;;;;;;;;;;;;AAwBhD,eAAsB,6BACpB,QACA,SAQiC;CACjC,MAAM,EAAE,OAAO,UAAU,SAAS,gBAAgB;CAClD,MAAM,EACJ,iBAAiB,EAAE,gBACjB,MAAM,mBAMP,QAAQ,kCAAkC;EAC3C,WAAW,EACT,OAAO,CACL;GACE,MAAM;GACN;GACA;GACD,CACF,EACF;EACD;EACD,CAAC;CACF,MAAM,CAAC,YAAY;AACnB,KAAI,CAAC,SACH,OAAM,IAAI,MAAM,wDAAwD,MAAM,IAAI;AAEpF,QAAO;;;;;;;;;;AAWT,eAAsB,eACpB,QACA,YACA,UAGI,EAAE,EACS;CACf,MAAM,EAAE,SAAS,gBAAgB;AACjC,OAAM,mBAAmB,QAAQ,mBAAmB;EAClD,WAAW,EACT,OAAO,EAAE,KAAK,CAAC,WAAW,EAAE,EAC7B;EACD;EACD,CAAC;;;;AC3CJ,MAAM,YAAY;;;;;;;;AASlB,eAAsB,wBACpB,QACA,UAQI,EAAE,EACqB;CAC3B,MAAM,EAAE,SAAS,aAAa,aAAa;CAC3C,MAAM,EAAE,SAAS,YAAY,EAAE;CAC/B,MAAM,kBAAoC,EAAE;CAC5C,IAAI,SAAS;CAEb,IAAI,iBAAiB;AACrB,IAAG;EACD,MAAM,EACJ,iBAAiB,EAAE,YACjB,MAAM,mBAMP,QAAQ,kBAAkB;GAC3B,WAAW;IAAE,OAAO;IAAW;IAAQ;IAAM;GAC7C;GACD,CAAC;AACF,kBAAgB,KAAK,GAAG,MAAM;AAC9B,YAAU;AACV,mBAAiB,MAAM,WAAW;UAC3B;AAET,QAAO,gBAAgB,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;ACpFrE,SAAgB,iCACd,SACA,UAMQ;CACR,MAAM,SAAS;CAIf,MAAM,SAAS,OAAO,UAAU,EAAE;CAClC,MAAM,mBAAoB,OAAO,eAAe,EAAE;AAClD,QAAO;EACL,GAAG;EACH,QAAQ;GACN,GAAG;GACH,UAAU;IACR,OAAO,SAAS;IAChB,aAAa;IACb,MAAM;IACN,GAAG;IACH,IAAI,SAAS;IACd;GACF;EACF;;;;;;;;;;;;;;;;;;;;;;;AChBH,SAAgB,yBACd,OACA,UACA,iBACoB;AACpB,KAAI,MAAM,YAAY,UAAU,YAAY,MAAM,aAAa,SAAS,SACtE,OAAM,IAAI,MACR,oBAAoB,MAAM,KAAK,yBAAyB,MAAM,SAAS,mCAC3C,SAAS,GAAG,+BAA+B,SAAS,SAAS,0IAG1F;AAEH,QAAO,MAAM,YAAY,UAAU,YAAY;;;;;;;;;AAUjD,eAAsB,uBACpB,QACA,SAAiB,aACA;CACjB,MAAM,EAAE,iBAAiB,MAAM,mBAS5B,QAAQ,cAAc,EAAE,QAAQ,CAAC;AACpC,KAAI,CAAC,aAAa,QAAQ,GACxB,OAAM,IAAI,MACR,sKAGD;AAEH,QAAO,aAAa,OAAO;;;;;;;;;;;;;;;;ACxD7B,SAAgB,8BACd,UACA,OAC4B;AAC5B,KAAI,MAAM,IAAI;EACZ,MAAM,QAAQ,SAAS,MAAM,EAAE,SAAS,OAAO,MAAM,GAAG;AACxD,MAAI,CAAC,MACH,OAAM,IAAI,MACR,oBAAoB,MAAM,KAAK,kBAAkB,MAAM,GAAG,0HAG3D;AAEH,SAAO;;CAGT,MAAM,UAAU,SAAS,QAAQ,EAAE,WAAW,SAAS,MAAM,KAAK;AAClE,KAAI,QAAQ,SAAS,EACnB,OAAM,IAAI,MACR,wCAAwC,MAAM,KAAK,UACxC,QAAQ,KAAK,EAAE,SAAS,GAAG,CAAC,KAAK,KAAK,CAAC,oFAEnD;AAEH,QAAO,QAAQ;;;;;;;;;;;;;;ACwCjB,SAAgB,0BACd,QACS;AACT,QAAO,CAAC,OAAO,SAAS,OAAO,YAAY;;;;;;;;;;;;;;;;;;;;;;;;AAyB7C,eAAsB,sBACpB,QACA,SAoBsC;CACtC,MAAM,EACJ,MACA,eACA,sBACA,SACA,UACA,aACA,SAAS,gBACP;CAEJ,IAAI;AAOJ,KAAI;AACF,aAAW,MAAM,mBAAoC,QAAQ,qBAAqB;GAChF,WAAW,EACT,OAAO;IACL;IACA,yBAAyB;IACzB,SAAS,OAAO,KAAK,KAAK,UAAU,QAAQ,EAAE,QAAQ,CAAC,SAAS,SAAS;IACzE;IACA;IACA,GAAI,aAAa,KAAA,IAAY,EAAE,UAAU,GAAG,EAAE;IAC9C,GAAI,gBAAgB,KAAA,IAAY,EAAE,aAAa,GAAG,EAAE;IACrD,EACF;GACD;GACD,CAAC;UACK,KAAK;EACZ,MAAM,UAAW,IAAc,WAAW;AAE1C,MACE,qCAAqC,KAAK,QAAQ,IAClD,qDAAqD,KAAK,QAAQ,CAElE,OAAM,IAAI,MACR,6KAEuB,UACxB;AAEH,QAAM;;CAGR,MAAM,EAAE,WAAW,SAAS;AAC5B,QAAO;EACL,QAAQ,0BAA0B,OAAO;EACzC;EACD;;;;;;;;;;;;;;;;;;;ACxJH,eAAsB,uBACpB,QACA,SACA,UAGI,EAAE,EAC6B;CACnC,MAAM,EAAE,qBAAqB;AAC7B,KAAI;AACF,SAAO,MAAM,OACV,KAAK,kBAAkB,EACtB,MAAM;GACJ,MAAM,QAAQ;GACd,SAAS,QAAQ;GACjB,GAAI,mBAAmB,EAAE,gBAAgB,EAAE,IAAI,kBAAkB,EAAE,GAAG,EAAE;GACzE,EACF,CAAC,CACD,MAAgC;UAC5B,KAAK;AAEZ,MADoB,KAAgD,UAAU,eAC3D,IACjB,OAAM,IAAI,MACR,sJAED;AAEH,QAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACiFV,eAAsB,mBACpB,QACA,SA8BmC;CACnC,MAAM,EACJ,OACA,QACA,iBACA,UAAU,aACV,UAAU,MACV,SAAS,OACT,QAAQ,OACR,cACA,SAAS,gBACP;CACJ,MAAM,OAA2B,MAAM,QAAQ,mBAAmB;CAElE,MAAM,WAAW,8BAA8B,aAAa,MAAM;CAElE,MAAM,oBAAoB,yBAAyB,OAAO,UAAU,gBAAgB;CACpF,MAAM,cAAc,+BAA+B,MAAM;CAGzD,IAAI,gBAA0B,EAAE;CAChC,IAAI,eAAe;AACnB,KAAI,UAAU;EACZ,MAAM,OAAO,uBAAuB,aAAa;GAC/C,eAAe,SAAS;GACxB,sBAAsB,SAAS;GAChC,CAAC;AACF,kBAAgB,KAAK;AACrB,MAAI,CAAC,KAAK,WAAW,CAAC,OAAO;GAK3B,MAAM,kBAA4B,EAAE;AACpC,OAAI,MAAM,SAAS,SAAS,KAC1B,iBAAgB,KAAK,OAAO;AAE9B,OAAI,MAAM,gBAAgB,KAAA,KAAa,MAAM,iBAAiB,SAAS,eAAe,IACpF,iBAAgB,KAAK,cAAc;AAErC,OAAI,gBAAgB,WAAW,GAAG;AAChC,WAAO,KAAK,4CAA4C,MAAM,KAAK,eAAe;AAClF,WAAO;KACL,SAAS;KACT,kBAAkB,SAAS;KAC3B,eAAe,EAAE;KACjB,UAAU;KACV,GAAI,SAAS,aAAa,EAAE,YAAY,SAAS,YAAY,GAAG,EAAE;KACnE;;AAEH,kBAAe;AACf,mBAAgB;;;AAIpB,KAAI,OACF,QAAO;EACL,SAAS,WAAW,iBAAiB;EACrC,GAAI,WAAW,EAAE,kBAAkB,SAAS,IAAI,GAAG,EAAE;EACrD;EACA,UAAU;EACX;AAMH,KAAI,YAAY,cAAc;AAC5B,SAAO,KACL,sBAAsB,cAAc,KAAK,KAAK,CAAC,yBACzC,MAAM,KAAK,yCAClB;AACD,QAAM,mBAAmB,QAAQ,mCAAmC;GAClE,WAAW,EACT,OAAO;IACL,IAAI,SAAS;IACb,MAAM,MAAM;IACZ,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;IAC9E,EACF;GACD;GACD,CAAC;AACF,SAAO;GACL,SAAS;GACT,kBAAkB,SAAS;GAC3B;GACA,UAAU;GACV,GAAI,SAAS,aAAa,EAAE,YAAY,SAAS,YAAY,GAAG,EAAE;GACnE;;AAGH,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,6EAA6E;CAK/F,MAAM,EAAE,eAAe,yBAAyB,MAAM,uBACpD,QACA,aACA,EAAE,kBAAkB,UAAU,IAAI,CACnC;CAQD,IAAI,aAAa,MAAM,cAAc,UAAU,cAAc,KAAA;CAC7D,IAAI,kBAAkB;AACtB,KAAI,SAAS,mBAAmB,OAAO,CAAC,YAAY,eAAe,KAAA,GAAW;EAC5E,MAAM,eAAe,qBAAsB,MAAM,uBAAuB,QAAQ,OAAO;AACvF,SAAO,KAAK,6DAA6D,MAAM,KAAK,MAAM;AAM1F,gBAAa,MALU,6BAA6B,QAAQ;GAC1D,OAAO,MAAM;GACb,UAAU;GACV;GACD,CAAC,EACoB;AACtB,oBAAkB;;CAOpB,IAAI;AACJ,KAAI,iBAAiB,KAAA,KAAa,aAAa,SAAS,GAAG;AACzD,SAAO,KACL,4BAA4B,MAAM,KAAK,iBACjC,aAAa,OAAO,UAAU,aAAa,WAAW,IAAI,KAAK,IAAI,MAC1E;AACD,gBAAc,EAAE;AAChB,OAAK,MAAM,EAAE,SAAS,iBAAiB,cAAc;GACnD,MAAM,MAAmC,MAAM,sBAAsB,QAAQ;IAC3E;IACA;IACA;IAGA,SACE,SAAS,mBAAmB,OAAO,eAAe,KAAA,IAC9C,iCAAiC,SAAS;KACxC,IAAI;KACJ,OAAO,MAAM;KACd,CAAC,GACF;IAEN,GAAI,SAAS,mBAAmB,WAAW,sBAAsB,KAAA,IAC7D,EAAE,UAAU,mBAAmB,GAC/B,EAAE;IACN,GAAI,SAAS,mBAAmB,OAAO,gBAAgB,KAAA,IAAY,EAAE,aAAa,GAAG,EAAE;IACvF;IACD,CAAC;AACF,eAAY,KAAK;IAAE,GAAG;IAAK,GAAI,gBAAgB,KAAA,IAAY,EAAE,aAAa,GAAG,EAAE;IAAG,CAAC;;AAErF,MAAI,YAAY,MAAM,EAAE,aAAa,CAAC,OAAO,EAAE;AAG7C,OAAI,mBAAmB,eAAe,KAAA,GAAW;AAC/C,WAAO,KACL,2CAA2C,WAAW,SAAS,MAAM,KAAK,kBAC3E;AACD,UAAM,eAAe,QAAQ,YAAY,EAAE,QAAQ,CAAC;AACpD,iBAAa,KAAA;;AAEf,UAAO;IACL,SAAS;IACT,GAAI,WAAW,EAAE,kBAAkB,SAAS,IAAI,GAAG,EAAE;IACrD;IACA,UAAU;IACV;IACA,GAAI,kBAAkB,EAAE,iBAAiB,GAAG,EAAE;IAC/C;;;AAKL,KAAI,CAAC,UAAU;EAGb,MAAM,iBACJ,SAAS,mBAAmB,UACvB,qBAAsB,MAAM,uBAAuB,QAAQ,OAAO,GACnE,KAAA;AAEN,MAAI,SAAS,mBAAmB,OAAO,CAAC,QACtC,QAAO,KACL,qDAAqD,MAAM,KAAK,4DAEjE;EAEH,IAAI;AAwBJ,MAAI;AACF,cAAW,MAAM,mBAAoC,QAAQ,wBAAwB;IACnF,WAAW,EACT,OAAO;KACL;KACA,GAAI,mBAAmB,KAAA,IAAY,EAAE,UAAU,gBAAgB,GAAG,EAAE;KACpE,GAAI,SAAS,mBAAmB,MAAM,EAAE,YAAY,GAAG,EAAE;KACzD,MAAM,MAAM;KACZ,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;KAC7E,GAAI,SAAS,mBAAmB,UAAU,EAAE,WAAW,SAAS,GAAG,EAAE;KACrE;KACA;KACD,EACF;IACD;IACD,CAAC;WACK,KAAK;AAGZ,OAAI,mBAAmB,eAAe,KAAA,GAAW;AAC/C,WAAO,KACL,2CAA2C,WAAW,SAChD,MAAM,KAAK,0CAClB;AACD,UAAM,eAAe,QAAQ,YAAY,EAAE,QAAQ,CAAC;;AAEtD,SAAM;;EAER,MAAM,EAAE,mBAAmB,SAAS;EACpC,MAAM,UAAU,eAAe,iBAAiB,eAAe;AAC/D,SAAO;GACL,SAAS;GACT,kBAAkB,eAAe;GACjC,GAAI,UAAU,EAAE,eAAe,QAAQ,eAAe,GAAG,EAAE;GAC3D;GACA,UAAU,SAAS,mBAAmB,MAAM,OAAO;GACnD,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;GACtC,GAAI,eAAe,KAAA,IAAY,EAAE,YAAY,GAAG,EAAE;GAClD,GAAI,kBAAkB,EAAE,iBAAiB,GAAG,EAAE;GAC/C;;CAIH,MAAM,EACJ,gCAAgC,EAAE,gBAAgB,cAChD,MAAM,mBAgBP,QAAQ,mCAAmC;EAC5C,WAAW,EACT,OAAO;GACL,IAAI,SAAS;GACb,MAAM,MAAM;GACZ,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E;GACA;GACD,EACF;EACD;EACD,CAAC;CAEF,MAAM,QAAQ,QAAQ;AACtB,KAAI,CAAC,MACH,OAAM,IAAI,MACR,+DAA+D,MAAM,KAAK,0BAC3E;AAGH,KAAI,QACF,OAAM,mBAAmB,QAAQ,iCAAiC;EAChE,WAAW,EACT,OAAO;GACL,kBAAkB,QAAQ;GAC1B,WAAW,MAAM;GAClB,EACF;EACD;EACD,CAAC;AAGJ,QAAO;EACL,SAAS;EACT,kBAAkB,QAAQ;EAC1B,eAAe,MAAM;EACrB;EACA,UAAU;EACV,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;EACtC,GAAI,eAAe,KAAA,IAAY,EAAE,YAAY,GAAG,EAAE;EACnD;;;;ACheH,SAAgB,gCACd,MACA,WAC2B;CAC3B,MAAM,mBAAmB,oBAAoB,KAAK;AAElD,QAAO;EACL;EACA,aAAa,iBAAiB;EAC9B,aAAa,iBAAiB,iBAAiB;EAChD"}