import { type ServiceResult } from '../models/service-result.js'; import { ProfilingDefinitionService, type CreateDefinitionInput } from '../services/ProfilingDefinitionService.js'; import { ProfilingSummaryService } from '../services/ProfilingSummaryService.js'; import { RecordTypeService } from '../services/RecordTypeService.js'; import { ObjectFilteringService } from '../services/ObjectFilteringService.js'; import { DefinitionFieldGenerationService } from '../services/DefinitionFieldGenerationService.js'; import type { YearRange } from '../models/year-range.js'; import type { DateLiteralRange } from '../models/date-literal.js'; import { DataAvailabilityService, type AvailabilityGrid } from '../services/DataAvailabilityService.js'; import type { ProfilingRestClient } from '../adapters/rest/profiling-rest-client.js'; export type { OutcomeFieldMapping, ExpressionLine, ExpressionBuilderJson, FilterSet, FilterJsonPayload, DateLiteralEntry, DefinitionMetadata, DefinitionMetadataContext, FieldGenerationOptions, } from '../services/DefinitionFieldGenerationService.js'; import type { DefinitionMetadata } from '../services/DefinitionFieldGenerationService.js'; /** * Options for the definition create operation. */ export type CreateOptions = { /** Target org username or alias */ targetOrg: string; /** Explicit object API names to create definitions for. Bypasses filtering when provided. */ objects?: string[]; /** Pattern for matching object API names. * for wildcard, plain string for substring match. */ pattern?: string; /** Filter objects by type: all, standard, or custom */ filter?: 'all' | 'standard' | 'custom'; /** Filter by namespace prefix */ namespace?: string; /** Profiling methodology: metadata, historical, comparative, recordtype, outcome, or full */ method?: 'metadata' | 'historical' | 'comparative' | 'recordtype' | 'outcome' | 'full'; /** Comparative year -- used when method is 'comparative', 'recordtype', or 'full' */ year?: number; /** Resolved year range for comparative definitions. When provided, replaces year + usePrior. */ yearRange?: YearRange; /** Object classification filter */ classification?: 'customer' | 'internal' | 'all'; /** Include only objects with records */ withRecords?: boolean; /** Include only objects without records */ withoutRecords?: boolean; /** Include only objects with active record types */ withRecordTypes?: boolean; /** Filter record types by activity status: active (default), all, or inactive */ recordTypeStatus?: 'active' | 'all' | 'inactive'; /** Category to assign to created definitions */ category?: string; /** Description to assign to created definitions */ description?: string; /** Minimum record count threshold -- objects with fewer records are excluded */ minRecords?: number; /** Maximum number of objects to create definitions for (safety cap) */ limit?: number; /** Progress callback for status updates */ onProgress?: (message: string) => void; /** Whether to exclude value frequency (CRc) from insight groups */ noValueFrequency?: boolean; /** When true, comparative definitions use unbounded SetB (all records before year). Default: year-over-year. */ usePrior?: boolean; /** * When true, comparative definitions use the "lifetime vs X" variant: SetA = all records (unfiltered), SetB = the * supplied secondary filter (--date-literal, --year, or --from/--to). Requires --method comparative AND a secondary * filter. Mutually exclusive with --use-prior, --depth, and non-comparative methods. */ lifetimePrimary?: boolean; /** Optional prefix prepended to each definition name (e.g., "TC-01") */ namePrefix?: string; /** Optional suffix appended to each definition name (e.g., "Run A") */ nameSuffix?: string; /** Verbatim time category override; takes precedence over all computed time category values. */ timeCategory?: string; /** Verbatim segment category override; takes precedence over all computed segment category values. */ segmentCategory?: string; /** Origin message appended to auto-generated descriptions. Empty string suppresses. */ origin?: string; /** Resolved date literal range. When provided, replaces year-based time scoping with date literal WHERE clauses. */ dateLiteralRange?: DateLiteralRange; /** Only create definitions for objects with successful metadata profiling results */ profiled?: boolean; /** Skip creation and return preview results only */ dryRun?: boolean; /** Skip the confirmation prompt (auto-confirm) */ noPrompt?: boolean; /** Callback fired after preview is built but before creation starts. Return false to cancel. */ onPreviewReady?: (preview: CreatePreview) => Promise; /** Optional logger for debug output */ logger?: Console; /** Target a specific record type by name (valid with --method recordtype, outcome, or full) */ recordType?: string; /** Callback fired after each definition is processed (created, skipped, or failed) */ onObjectComplete?: (result: { objectName: string; status: 'success' | 'skip' | 'fail' | 'preview'; message: string; current: number; total: number; type?: 'metadata' | 'historical' | 'comparative' | 'outcome'; definitionName?: string; definitionKey?: string; recordTypeName?: string; category?: string; timeCategory?: string; segmentCategory?: string; fieldCount?: number; }) => void; }; /** * Result of a definition create operation. */ export type CreateResult = { /** Number of definitions successfully created */ created: number; /** Number of definitions that failed to create */ failed: number; /** Number of objects skipped (already have definitions) */ skipped: number; /** Total number of objects processed */ total: number; /** Whether this was a dry run */ dryRun?: boolean; /** Whether the operation was cancelled by the user via confirmation prompt */ cancelled?: boolean; /** List of creation failures with details */ failures: Array<{ objectName: string; error: string; errorCode: string; }>; /** Per-definition results for progress feedback */ results: ObjectResult[]; /** Data availability grid showing per-object × per-year data presence */ availabilityGrid?: AvailabilityGrid; /** Record counts per object — populated for all creation paths so callers can display without re-fetching */ recordCounts?: Map; }; /** * Per-definition result for progress feedback. */ export type ObjectResult = { /** The object API name */ objectName: string; /** The object label */ objectLabel?: string; /** The definition name that was created or would be created */ definitionName: string; /** Outcome status */ status: 'success' | 'skip' | 'fail' | 'preview'; /** The profiling method type — 'outcome' added (CLI-3375) to honestly reflect outcome variants * in JSON output instead of misclassifying them as 'metadata'. */ type?: 'metadata' | 'historical' | 'comparative' | 'outcome'; /** The definition ID (only for successful creates) */ definitionId?: string; /** The definition key (only for successful creates) */ definitionKey?: string; /** Error message (only for failures) */ error?: string; /** Number of fields profiled (from ISV response) */ fieldCount?: number; /** Primary category */ category?: string; /** Time category */ timeCategory?: string; /** Segment category (record type name) */ segmentCategory?: string; }; /** * Preview data passed to onPreviewReady callback before creation begins. */ export type CreatePreview = { /** Definitions that will be created */ toCreate: CreateDefinitionInput[]; /** Definitions that will be skipped (already exist) */ toSkip: CreateDefinitionInput[]; /** Total candidate count */ total: number; /** Record counts per object (from Record Count API) */ recordCounts: Map; }; /** * Error codes specific to create operations. */ export declare const CreateErrorCodes: { /** Object filtering failed */ readonly FILTER_FAILED: "CREATE_FILTER_FAILED"; /** No objects found matching filters */ readonly NO_OBJECTS_FOUND: "CREATE_NO_OBJECTS_FOUND"; /** None of the specified --objects exist in the org */ readonly OBJECTS_NOT_FOUND: "CREATE_OBJECTS_NOT_FOUND"; /** All objects already have definitions */ readonly ALL_OBJECTS_SKIPPED: "CREATE_ALL_OBJECTS_SKIPPED"; /** Operation failed unexpectedly */ readonly OPERATION_FAILED: "CREATE_OPERATION_FAILED"; /** No objects have been profiled yet */ readonly NO_PROFILED_OBJECTS: "CREATE_NO_PROFILED_OBJECTS"; }; export declare class DefinitionCreateOperation { private readonly definitionService; private readonly filteringService; private readonly logger?; private readonly profilingSummaryService?; private readonly recordTypeService?; private readonly dataAvailabilityService?; private readonly restClient?; /** Exposes the date literal constant map for external consumers and tests. */ static readonly DATE_LITERAL_MAP: Record; /** The bullet separator used in definition names. */ static readonly NAME_SEPARATOR = " \u2022 "; /** * Creates a new DefinitionCreateOperation instance. * * @param definitionService - Service for creating and querying profiling definitions * @param filteringService - Service for filtering Salesforce objects * @param logger - Optional logger for debug output * @param profilingSummaryService - Optional service for querying profiled objects (required when profiled=true) * @param recordTypeService - Optional service for querying record types (required when method=recordtype) */ constructor(definitionService: ProfilingDefinitionService, filteringService: ObjectFilteringService, logger?: Console | undefined, profilingSummaryService?: ProfilingSummaryService | undefined, recordTypeService?: RecordTypeService | undefined, dataAvailabilityService?: DataAvailabilityService | undefined, restClient?: ProfilingRestClient | undefined); /** * Resolves the definition name from the method and context. * Single source of truth for definition naming across all definition types. * * @param label - Object label (e.g., 'Account', 'Lead') * @param method - ISV method (metadata, historical, comparative) * @param context - Optional context: timeLabel, year, recordTypeName, outcomeLabels, unbounded * @returns The formatted definition name */ static resolveDefinitionName(...args: Parameters): ReturnType; /** * Resolves all definition metadata (category, timeCategory, segmentCategory, description) * from the method and context. Single source of truth for definition classification. */ static resolveDefinitionMetadata(...args: Parameters): DefinitionMetadata; /** * Formats a human-readable message for a per-object result. */ static formatObjectMessage(result: ObjectResult): string; /** * Derives the time category display value from an input's method and year. */ static resolveTimeCategory(input?: CreateDefinitionInput): string | undefined; /** * Derives the segment category display value from an input. Prefers the * pre-resolved `input.segmentCategory` (populated by the field generation * service, which honors the --segment-category override and falls back to * method-derived defaults like 'N/A', 'Historical', or record type name). * Falls back to `input.recordTypeName` for back-compat with consumers that * build inputs without going through `resolveDefinitionMetadata`. */ static resolveSegmentCategory(input?: CreateDefinitionInput): string | undefined; /** * Maps Salesforce objects to their outcome boolean fields. * * @param objectName - Salesforce object API name (e.g., 'Opportunity', 'Case', 'Lead') * @returns Outcome field mapping, or undefined if the object has no outcome field */ static getOutcomeField(objectName: string): ReturnType; /** * Builds a single outcome definition input for a given object, outcome mapping, and optional time/RT context. * Consolidates the repeated context + input construction pattern used across buildOutcomeCandidateInputs * and buildInputsForRecordType. */ static buildOutcomeInput(...args: Parameters): CreateDefinitionInput; /** * Builds an ISV-compatible filterJson string for business process outcome comparisons. * SetA filters for the true outcome, SetB filters for the false outcome. * Produces the expression builder JSON schema that `fsc_expressionBuilder3` expects. * * @param fieldName - Boolean field API name (e.g., 'IsWon', 'IsClosed', 'IsConverted') * @param objectName - Salesforce object API name (e.g., 'Opportunity', 'Case', 'Lead') * @returns JSON string compatible with GlobalProfilingService.createProfilingDefinition() */ static buildOutcomeFilterJson(...args: Parameters): string; /** * Applies outcome pre-filtering for the REST path. For outcome method, only objects * with outcome fields (Opportunity, Case, Lead) are eligible. Returns a failure result * when no eligible objects remain; otherwise returns the filtered array. */ /** * Composes the `nameSuffix` value sent to the ISV `create-smart` REST endpoint, layering the * `No Value Frequency` disambiguator ahead of any user-supplied `--name-suffix`. The bullet * separator prefix (`• `) is required because the server joins suffix segments with a * plain space — this CLI carries the bullet so the final rendered name reads consistently * with locally-built names from `DefinitionFieldGenerationService.buildName`. * * CLI-3064: Without the disambiguator, value-frequency and no-value-frequency variants of * the same (object, method, time-segment) resolve to identical names on both client and * server; the second invocation is silently dropped by the name-keyed dedup gate. Layering * the disambiguator ahead of the user suffix preserves the user's `--name-suffix` semantics * while disambiguating the variant. * * @param userSuffix - User-supplied `--name-suffix` value (optional) * @param noValueFrequency - True when the request originates from `--no-value-frequency` * @returns Composed suffix to send as `SmartCreateRequest.options.nameSuffix`, or undefined when neither applies */ static composeRestNameSuffix(userSuffix: string | undefined, noValueFrequency: boolean | undefined): string | undefined; private static applyOutcomeFilter; /** * Invokes the onPreviewReady callback if provided and not in dry-run mode. * Returns a cancellation result if the user declines, or undefined to proceed. */ private static invokePreviewCallback; /** * Maps a server-side SmartCreateDefinitionResult to an ObjectResult. */ private static mapServerResult; /** Derives the definition type for a dry-run preview result from the requested method. */ private static derivePreviewType; /** Derives the definition category for a dry-run preview result from the requested method. */ private static derivePreviewCategory; /** * Normalizes a REST-path result for dry-run uniformity (CLI-1832). * * When options.dryRun is true and the server returned status='skipped' (because the * definition already exists), the JSON result's per-item status is remapped to 'preview' * so callers see uniform dry-run output across create and skip paths. This mirrors the * local-path buildDryRunResult behavior (see :936-937). Progress callbacks still fire * with status='skip' so human UX differentiates "already exists" from "would create". * * After normalization, type/category are back-filled from the method when undefined * (ISV create-smart omits classification fields per CLI-1756). 'full' is excluded * because it produces multiple definition types per object. */ private static normalizeDryRunResult; /** Fires the onObjectComplete progress callback for a single processed result. */ private static notifyObjectComplete; /** * Executes the definition create operation. * * @param options - Create operation options * @returns ServiceResult containing creation statistics */ execute(options: CreateOptions): Promise>; /** * Applies the --min-records threshold, returning the filtered list or an early failure result. */ private applyMinRecordsThreshold; /** * Builds record-type candidate inputs by querying the RecordTypeService for each object. * * @param cascade - Optional accumulator. Presence signals cascade-mode (`buildFullCandidateInputs`): * objects with no record types are pushed to `cascade.noRecordTypes` instead of warning per-object. * Absence is the standalone caller \u2014 per-object warnings fire as before. * Only the empty-result branch is routed; failed-query and `--recordtype X` no-match branches * remain per-object (a query failure is genuinely per-object actionable, and a user-specified * record-type narrowing justifies per-object diagnostics). */ private buildRecordTypeCandidateInputs; /** * Builds full candidate inputs by composing metadata + historical + comparative + recordtype methods. * Degrades gracefully to metadata + historical + comparative when RecordTypeService is absent. */ private buildFullCandidateInputs; /** * Builds candidate inputs for any method, delegating to the appropriate builder. */ private buildCandidateInputsForMethod; /** * Builds a dry run result without creating any definitions. */ private buildDryRunResult; /** * Executes sequential creation of definitions with per-item progress callbacks. */ private executeCreation; /** * Executes definition creation via the ISV REST API (create-smart endpoint). * Server handles naming, categorization, deduplication, and creation. * The CLI only provides objects, method, and options — no local business logic. */ private executeCreationViaRest; /** * Enriches classification fields (type, category, timeCategory, segmentCategory) on created * results in parallel. CLI-1756: ISV create-smart no longer returns these fields in its response. */ private enrichClassificationFields; /** * Resolves objects by explicit names, profiled set, or filtering criteria. */ private resolveObjects; /** * Resolves objects that have successful metadata profiling results. */ private resolveProfiledObjects; /** * Resolves objects by explicit API names. */ private resolveExplicitObjects; /** * Resolves objects by filtering criteria. */ private resolveFilteredObjects; /** * Filters objects by minimum record count threshold. */ private applyMinRecordsFilter; /** * Applies a safety cap to the number of definition inputs to process. */ private applyInputLimit; /** * Applies a safety cap to the number of SObjects sent to the ISV REST API. * Mirrors applyInputLimit for the REST path where objects are capped before the ISV call. */ private applyObjectLimit; /** * Fetches all existing profiling definitions using pagination. */ private fetchAllExistingDefinitions; }