import type { IdentifierMap } from './identifier-map.js'; import type { KeyRewrite } from './key-rewriter.js'; import type { ConversionNote } from './quonfig-target/report.js'; import type { CoercedSentinelSummary, DroppedOverrideSummary, DuplicateResolutionSummary, EnvironmentMapEntry, SkippedConfigSummary } from './source.js'; export type { EnvironmentMapEntry } from './source.js'; export interface MigrationReportCounts { configsMigrated: number; environmentsMapped: number; flagsMigrated: number; itemsSkipped: number; logLevelsMigrated: number; schemasMigrated: number; segmentsMigrated: number; } export interface CleanMapping { legacyKey: string; quonfigKey: string; } export interface LossyMapping { legacyKey: string; quonfigKey: string; reason: string; } export interface UnsupportedFeature { feature: string; note?: string; } export interface FollowUpChecklist { mustFixBeforeCutover: string[]; reviewPostCutover: string[]; } export interface MigrationReportData { cleanMappings: CleanMapping[]; /** * Rule values that translate() coerced from a sentinel like Launch's * empty-string "no value set yet" to the typed default. Null when nothing * was coerced. */ coercedSentinels?: CoercedSentinelSummary | null; /** * Structured conversion notes from a provider's `translate()` — re-bucketed * rollouts, dropped prerequisites, lossy individual-target conversions, etc. * (the LaunchDarkly `quonfig-target/report.ts` set). Rendered into the * "Users will be re-bucketed" + "Conversion notes" sections. Undefined or * empty when the source emitted none. */ conversionNotes?: ConversionNote[]; counts: MigrationReportCounts; /** * Override sections that were dropped during translate() because the env.id was not * present in the source's env map (e.g. archived/deleted Reforge envs). Null when * nothing was dropped. */ droppedOverrides?: DroppedOverrideSummary | null; dryRun: boolean; /** * Cross-type key collisions resolved by keeping the config side and deleting * the non-config type(s). Null when none were detected. Customers should * review each entry and clean up the source data to avoid the collision. */ duplicateResolutions?: DuplicateResolutionSummary | null; environmentMap: EnvironmentMapEntry[]; followUp: FollowUpChecklist; identifierMap: IdentifierMap; /** * qfg-6na9.3: source keys the migrator rewrote so the workspace is 100% * Policy-A conformant (bad charset, FS-floor, or collision). The renamed key * is what the customer's SDK code must now look up. Null/empty when every * source key already conformed (e.g. every LaunchDarkly import). */ keyRewrites?: KeyRewrite[] | null; lossyMappings: LossyMapping[]; /** * qfg-l8uz: source maintainerId → email pairs. Rendered as a sub-table on * `## Identifier map` so the reader sees who owned each dropped-maintainer * note, and consumed by the renderer to decorate the dropped-maintainer * rollup with emails. Optional/undefined when the source has no member * lookup (or it failed) — both the table and the decoration just no-op. */ maintainerMap?: null | Record; /** * Configs soft-skipped by translate() because the source data was invalid * (e.g. variant/valueType mismatch). Null when nothing was skipped. */ skippedConfigs?: SkippedConfigSummary | null; source: string; unsupportedFeatures: UnsupportedFeature[]; } /** * Folds conversion notes into a followUp checklist. De-duplicates by hint * string so a flag with two dropped prerequisites doesn't produce two * identical checkbox lines. */ export declare const deriveFollowUpFromConversionNotes: (base: FollowUpChecklist, notes: ConversionNote[] | undefined) => FollowUpChecklist; export declare const buildMigrationReport: (data: MigrationReportData) => string; /** * Path of the migration report. It lives inside the `.qf/` bookkeeping * directory (next to `import-state.json`), NOT the workspace root (qfg-a631). * * `qfg push` mirrors every non-dotfile on disk and the server's * config-path-allowlist only permits `configs/`, `feature-flags/`, etc. plus * `quonfig.json`/`README.md`. A root-level `MIGRATION_REPORT.md` got swept * into the push and rejected ("Path not allowed by push allow-list"). Keeping * it under the `.qf/` dotdir means the existing dotfile/dotdir skip in * `bare-path-diff.ts` excludes it — no server change needed. */ export declare const migrationReportPath: (outputDir: string) => string; /** * qfg-6na9.3: machine-readable `source -> quonfig` key map, next to the report * under `.qf/` (excluded from the push, like MIGRATION_REPORT.md). Lets a * customer script the SDK-lookup updates for renamed keys. */ export declare const keyMapPath: (outputDir: string) => string; export declare const writeMigrationReport: (outputDir: string, data: MigrationReportData) => string;