{"version":3,"sources":["/home/runner/work/lingo.dev/lingo.dev/packages/cli/build/cli.cjs","../src/cli/index.ts","../src/cli/cmd/auth.ts","../src/cli/utils/settings.ts","../src/cli/utils/errors.ts","../src/cli/utils/cloudflare-status.ts","../src/cli/utils/auth.ts","../src/cli/cmd/login.ts","../src/cli/utils/ui.ts","../src/cli/constants.ts","../src/cli/cmd/logout.ts","../src/cli/cmd/init.ts","../src/cli/utils/config.ts","../src/cli/utils/find-locale-paths.ts","../src/cli/utils/ensure-patterns.ts","../src/cli/utils/update-gitignore.ts","../src/cli/utils/init-ci-cd.ts","../src/cli/cmd/init/cursor.ts","../src/cli/cmd/show/index.ts","../src/cli/cmd/show/config.ts","../src/cli/cmd/show/locale.ts","../src/cli/cmd/show/files.ts","../src/cli/utils/buckets.ts","../src/cli/cmd/show/locked-keys.ts","../src/cli/cmd/show/_shared-key-command.ts","../src/cli/loaders/_utils.ts","../src/cli/loaders/json.ts","../src/cli/loaders/json5.ts","../src/cli/loaders/jsonc.ts","../src/cli/loaders/flat.ts","../src/cli/loaders/text-file.ts","../src/cli/loaders/yaml.ts","../src/cli/loaders/root-key.ts","../src/cli/loaders/flutter.ts","../src/cli/loaders/ail.ts","../src/cli/loaders/android.ts","../src/cli/loaders/csv.ts","../src/cli/loaders/html.ts","../src/cli/utils/element-extraction.ts","../src/cli/loaders/markdown.ts","../src/cli/loaders/markdoc.ts","../src/cli/loaders/mjml.ts","../src/cli/loaders/properties.ts","../src/cli/loaders/xcode-strings/tokenizer.ts","../src/cli/loaders/xcode-strings/escape.ts","../src/cli/loaders/xcode-strings/parser.ts","../src/cli/loaders/xcode-strings.ts","../src/cli/loaders/xcode-stringsdict.ts","../src/cli/loaders/xcode-xcstrings.ts","../src/cli/loaders/xcode-xcstrings-v2.ts","../src/cli/loaders/unlocalizable.ts","../../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/src/index.ts","../../../node_modules/.pnpm/@isaacs+brace-expansion@5.0.0/node_modules/@isaacs/brace-expansion/src/index.ts","../../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/src/assert-valid-pattern.ts","../../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/src/brace-expressions.ts","../../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/src/unescape.ts","../../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/src/ast.ts","../../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/src/escape.ts","../../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/src/index.ts","../src/cli/utils/key-matching.ts","../src/cli/loaders/formatters/prettier.ts","../src/cli/loaders/formatters/_base.ts","../src/cli/loaders/formatters/biome.ts","../src/cli/loaders/formatters/index.ts","../src/cli/loaders/po/index.ts","../src/cli/loaders/xliff.ts","../src/cli/loaders/xml.ts","../src/cli/loaders/srt.ts","../src/cli/loaders/dato/index.ts","../src/cli/loaders/dato/_base.ts","../src/cli/loaders/dato/filter.ts","../src/cli/loaders/dato/api.ts","../src/cli/loaders/dato/_utils.ts","../src/cli/loaders/dato/extract.ts","../src/cli/loaders/vtt.ts","../src/cli/loaders/variable/index.ts","../src/cli/loaders/sync.ts","../src/cli/utils/plutil-formatter.ts","../src/cli/loaders/plutil-json-loader.ts","../src/cli/loaders/php.ts","../src/cli/loaders/vue-json.ts","../src/cli/loaders/typescript/index.ts","../src/cli/loaders/typescript/cjs-interop.ts","../src/cli/loaders/inject-locale.ts","../src/cli/loaders/locked-keys.ts","../src/cli/loaders/mdx2/frontmatter-split.ts","../src/cli/utils/md5.ts","../src/cli/loaders/mdx2/code-placeholder.ts","../src/cli/loaders/mdx2/localizable-document.ts","../src/cli/loaders/mdx2/sections-split-2.ts","../src/cli/loaders/locked-patterns.ts","../src/cli/loaders/ignored-keys.ts","../src/cli/loaders/preserved-keys.ts","../src/cli/loaders/ejs.ts","../src/cli/loaders/twig.ts","../src/cli/loaders/ensure-key-order.ts","../src/cli/loaders/txt.ts","../src/cli/loaders/json-dictionary.ts","../src/cli/loaders/csv-per-locale.ts","../src/cli/loaders/index.ts","../src/cli/cmd/show/ignored-keys.ts","../src/cli/cmd/show/preserved-keys.ts","../src/cli/cmd/config/index.ts","../src/cli/cmd/config/set.ts","../src/cli/cmd/config/unset.ts","../src/cli/cmd/config/get.ts","../src/cli/cmd/i18n.ts","../src/cli/processor/index.ts","../src/cli/processor/lingo.ts","../src/cli/processor/basic.ts","../src/cli/utils/exp-backoff.ts","../src/cli/utils/observability.ts","../src/cli/utils/org-id.ts","../src/cli/utils/delta.ts","../src/cli/utils/fs.ts","../src/cli/utils/lockfile.ts","../src/cli/cmd/lockfile.ts","../src/cli/cmd/cleanup.ts","../src/cli/cmd/ci/index.ts","../src/cli/cmd/ci/flows/pull-request.ts","../src/cli/cmd/ci/flows/in-branch.ts","../src/cli/cmd/ci/flows/_base.ts","../src/cli/cmd/run/index.ts","../src/cli/cmd/run/setup.ts","../src/cli/cmd/run/_const.ts","../src/cli/localizer/lingodotdev.ts","../src/cli/localizer/explicit.ts","../src/utils/pseudo-localize.ts","../src/cli/localizer/pseudo.ts","../src/cli/localizer/index.ts","../src/cli/cmd/run/plan.ts","../src/cli/cmd/run/execute.ts","../src/cli/cmd/run/watch.ts","../src/cli/cmd/run/_types.ts","../src/cli/cmd/run/frozen.ts","../src/cli/cmd/run/exit-code.ts","../src/cli/cmd/run/_utils.ts","../src/cli/cmd/ci/platforms/bitbucket.ts","../src/cli/cmd/ci/platforms/_base.ts","../src/cli/cmd/ci/platforms/github.ts","../src/cli/cmd/ci/platforms/gitlab.ts","../src/cli/cmd/ci/platforms/index.ts","../src/cli/cmd/status.ts","../src/cli/utils/exit-gracefully.ts","../src/cli/cmd/may-the-fourth.ts","../package.json","../src/cli/cmd/purge.ts"],"names":["defaults","Command","InteractiveCommand","resolveLocaleCode","ext","confirm","path","InteractiveOption","value","defaultConfig","pkg","resolveOverriddenLocale","input","keyMatch","_","parseStringPromise","parse","childElements","getInnerHTML","YAML","FM_ATTR_PREFIX","DomHandler","serializeElement","Parser","i","glob","final","textContent","Builder","indent","jsonrepair","yamlEngine","match","LOCALIZABLE_ATTRIBUTES","html","stringify","dedent","chalk","inquirer","Ora","deltaProcessor","checksums","execSync","fileURLToPath","LingoDotDevEngine","createAnthropic","createOpenAI","createOpenRouter","createMistral","generateText","createOllama","createGoogleGenerativeAI","Listr","t","bucketTypeSchema","Z","localeCodeSchema","colors","vice"],"mappings":"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,QAAA;AACF,MAAA;AACe,MAAA;AACS,QAAA;AACJ,UAAA;AAClB,QAAA;AACa,QAAA;AACP,QAAA;AACiB,QAAA;AAChB,QAAA;AACT,MAAA;AACO,MAAA;AACT,IAAA;AAKW,IAAA;AACK,IAAA;AACY,IAAA;AACrB,IAAA;AACT,EAAA;AAEiC,EAAA;AACL,IAAA;AACH,IAAA;AAChB,IAAA;AACT,EAAA;AxD4lL8B;AACA;AwDzlLnB,EAAA;AAGS,IAAA;AAAyB,MAAA;AAEjB,IAAA;AACC,IAAA;AAKzB,IAAA;AAKa,IAAA;AACN,MAAA;AACT,IAAA;AAEoB,IAAA;AACK,IAAA;AACjB,MAAA;AACCqB,MAAAA;AACR,IAAA;AACH,EAAA;AAEW,EAAA;AACG,IAAA;AACd,EAAA;AxD+kL8B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AwD3kLV,EAAA;AAEQ,IAAA;AACP,IAAA;AAAoB,MAAA;AACvB,IAAA;AAEP,MAAA;AAIJ,MAAA;AACe,QAAA;AAIQ,QAAA;AACH,QAAA;AACZ,QAAA;AAED,MAAA;AAEE,MAAA;AACQ,MAAA;AACM,QAAA;AAMhB,UAAA;AAEe,UAAA;AACP,YAAA;AAGN,YAAA;AxD4jLc;AwD1jLF,cAAA;AAEA,cAAA;AAEA,cAAA;AxD0jLE,YAAA;AwDvjLD,YAAA;AAEX,YAAA;AACV,UAAA;AACF,QAAA;AACF,MAAA;AAGU,MAAA;AAGH,MAAA;AAGC,QAAA;AACR,MAAA;AACsB,MAAA;AACf,MAAA;AACLC,QAAAA;AACY,QAAA;AACa,QAAA;AACpB,QAAA;AxDkjL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AA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACa,UAAA;AACvB,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACiB,QAAA;AACM,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACgB,QAAA;AACO,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACiB,QAAA;AACjB,QAAA;AACiB,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACkB,QAAA;AAClB,QAAA;AACiB,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACkB,QAAA;AAClB,QAAA;AACiB,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACqB,QAAA;AACE,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACoB,QAAA;AACH,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACiB,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACiB,QAAA;AACM,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACe,QAAA;AACE,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACM,QAAA;AACvB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACuB,QAAA;AACA,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACA,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACA,QAAA;AACiB,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACA,QAAA;AACiB,QAAA;AACjB,QAAA;AACiB,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACM,QAAA;AACvB,QAAA;AACU,UAAA;AACG,UAAA;AACb,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACA,QAAA;AACiB,QAAA;AACjB,QAAA;AACiB,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACM,QAAA;AACvB,QAAA;AACU,UAAA;AACG,UAAA;AACb,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACiB,QAAA;AACA,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACiB,QAAA;AACO,QAAA;AACP,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACC,QAAA;AACtB,QAAA;AACiB,QAAA;AACjB,QAAA;AACoB,QAAA;AACH,QAAA;AACM,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACkB,QAAA;AACD,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACgB,QAAA;AACC,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACgB,QAAA;AACO,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACY,QAAA;AACA,QAAA;AACA,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACA,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACgB,QAAA;AACO,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACgB,QAAA;AACC,QAAA;AACA,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACA,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACoB,QAAA;AACH,QAAA;AACA,QAAA;AACjB,QAAA;AACuB,QAAA;AACC,QAAA;AACxB,QAAA;AACA,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACU,UAAA;AACR,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AACuB,QAAA;AACN,QAAA;AACjB,QAAA;AACiB,QAAA;AACM,QAAA;AACC,QAAA;AACxB,QAAA;AACA,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACiB,QAAA;AACM,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AACG,IAAA;AACI,MAAA;AACgB,QAAA;AACrB,QAAA;AACgB,QAAA;AACO,QAAA;AACC,QAAA;AACxB,QAAA;AACiB,QAAA;AACjB,QAAA;AACU,UAAA;AACR,UAAA;A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;AACP,MAAA;AACC,MAAA;AACC,MAAA;AACb,IAAA;AACwB,IAAA;AACb,MAAA;AACC,MAAA;AACC,MAAA;AACb,IAAA;AACkB,IAAA;AACP,MAAA;AACC,MAAA;AACC,MAAA;AACb,IAAA;AACF,EAAA;AACiB,EAAA;AACV,IAAA;AACI,MAAA;AACL,QAAA;AACF,MAAA;AACO,MAAA;AACL,QAAA;AACF,MAAA;AACQ,MAAA;AACN,QAAA;AACF,MAAA;AACY,MAAA;AACV,QAAA;AACF,MAAA;AACS,MAAA;AACP,QAAA;AACF,MAAA;AACgB,MAAA;AACd,QAAA;AACF,MAAA;AACa,MAAA;AACX,QAAA;AACF,MAAA;AACsB,MAAA;AACpB,QAAA;AACF,MAAA;AACgB,MAAA;AACd,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACI,IAAA;AACI,IAAA;AACf,EAAA;AACS,EAAA;AACP,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACW,EAAA;AACI,IAAA;AACN,IAAA;AACE,IAAA;AACI,IAAA;AACL,IAAA;AACM,IAAA;AACL,IAAA;AACX,EAAA;AACa,EAAA;AACH,EAAA;AACC,EAAA;AACK,EAAA;AACO,IAAA;AACH,IAAA;AACC,IAAA;AACD,IAAA;AACE,IAAA;AACH,IAAA;AACE,IAAA;AACH,IAAA;AACG,IAAA;AACK,IAAA;AACI,IAAA;AACT,IAAA;AACN,IAAA;AACQ,IAAA;AACG,IAAA;AACD,IAAA;AACF,IAAA;AACF,IAAA;AACC,IAAA;AACA,IAAA;AACpB,IAAA;AACA,IAAA;AACwB,IAAA;AACV,IAAA;AACR,IAAA;AACO,IAAA;AACJ,IAAA;AACG,IAAA;AACI,IAAA;AACF,IAAA;AACN,IAAA;AACK,IAAA;AACI,IAAA;AACL,IAAA;AACF,IAAA;AACF,IAAA;AACU,IAAA;AACJ,IAAA;AACF,IAAA;AACF,IAAA;AACH,IAAA;AACI,IAAA;AACQ,IAAA;AACT,IAAA;AACF,IAAA;AACU,IAAA;AACV,IAAA;AACW,IAAA;AACJ,IAAA;AACA,IAAA;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","file":"/home/runner/work/lingo.dev/lingo.dev/packages/cli/build/cli.cjs","sourcesContent":[null,"import dotenv from \"dotenv\";\ndotenv.config();\n\nimport { InteractiveCommand } from \"interactive-commander\";\nimport figlet from \"figlet\";\nimport { vice } from \"gradient-string\";\n\nimport authCmd from \"./cmd/auth\";\nimport loginCmd from \"./cmd/login\";\nimport logoutCmd from \"./cmd/logout\";\nimport initCmd from \"./cmd/init\";\nimport showCmd from \"./cmd/show\";\nimport configCmd from \"./cmd/config\";\nimport i18nCmd from \"./cmd/i18n\";\nimport lockfileCmd from \"./cmd/lockfile\";\nimport cleanupCmd from \"./cmd/cleanup\";\nimport ciCmd from \"./cmd/ci\";\nimport statusCmd from \"./cmd/status\";\nimport mayTheFourthCmd from \"./cmd/may-the-fourth\";\nimport packageJson from \"../../package.json\";\nimport run from \"./cmd/run\";\nimport purgeCmd from \"./cmd/purge\";\n\nexport default new InteractiveCommand()\n  .name(\"lingo.dev\")\n  .description(\"Lingo.dev CLI\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .addHelpText(\n    \"beforeAll\",\n    `\n${vice(\n  figlet.textSync(\"LINGO.DEV\", {\n    font: \"ANSI Shadow\",\n    horizontalLayout: \"default\",\n    verticalLayout: \"default\",\n  }),\n)}\n\n⚡️ AI-powered open-source CLI for web & mobile localization.\n\nStar the the repo :) https://github.com/LingoDotDev/lingo.dev\n`,\n  )\n  .version(`v${packageJson.version}`, \"-v, --version\", \"Show version\")\n  .addCommand(initCmd)\n  .interactive(\n    \"-y, --no-interactive\",\n    \"Run every command in non-interactive mode (no prompts); required when scripting\",\n  ) // all interactive commands above\n  .addCommand(i18nCmd)\n  .addCommand(authCmd)\n  .addCommand(loginCmd)\n  .addCommand(logoutCmd)\n  .addCommand(showCmd)\n  .addCommand(configCmd)\n  .addCommand(lockfileCmd)\n  .addCommand(cleanupCmd)\n  .addCommand(ciCmd)\n  .addCommand(statusCmd)\n  .addCommand(mayTheFourthCmd, { hidden: true })\n  .addCommand(run)\n  .addCommand(purgeCmd)\n  .exitOverride((err) => {\n    // Exit with code 0 when help or version is displayed\n    if (\n      err.code === \"commander.helpDisplayed\" ||\n      err.code === \"commander.version\" ||\n      err.code === \"commander.help\"\n    ) {\n      process.exit(0);\n    }\n    process.exit(1);\n  });\n","import { Command } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport { getSettings, saveSettings } from \"../utils/settings\";\nimport { createAuthenticator } from \"../utils/auth\";\n\nexport default new Command()\n  .command(\"auth\")\n  .description(\"Show current authentication status and user email\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  // Deprecated options, safe to remove after September 2025\n  .option(\n    \"--login\",\n    \"DEPRECATED: Shows deprecation warning and exits. Use `lingo.dev login` instead\",\n  )\n  .option(\n    \"--logout\",\n    \"DEPRECATED: Shows deprecation warning and exits. Use `lingo.dev logout` instead\",\n  )\n  .action(async (options) => {\n    try {\n      // Handle deprecated login option\n      if (options.login) {\n        Ora().warn(\n          \"⚠️  DEPRECATED: '--login' is deprecated. Please use 'lingo.dev login' instead.\",\n        );\n        process.exit(1);\n      }\n\n      // Handle deprecated logout option\n      if (options.logout) {\n        Ora().warn(\n          \"⚠️  DEPRECATED: '--logout' is deprecated. Please use 'lingo.dev logout' instead.\",\n        );\n        process.exit(1);\n      }\n\n      // Default behavior: show authentication status\n      const settings = await getSettings(undefined);\n      const authenticator = createAuthenticator({\n        apiUrl: settings.auth.apiUrl,\n        apiKey: settings.auth.apiKey!,\n      });\n      const auth = await authenticator.whoami();\n      if (!auth) {\n        Ora().warn(\"Not authenticated\");\n      } else {\n        Ora().succeed(`Authenticated as ${auth.email}`);\n      }\n    } catch (error: any) {\n      Ora().fail(error.message);\n      process.exit(1);\n    }\n  });\n","import os from \"os\";\nimport path from \"path\";\nimport _ from \"lodash\";\nimport Z from \"zod\";\nimport fs from \"fs\";\nimport Ini from \"ini\";\n\nexport type CliSettings = Z.infer<typeof SettingsSchema>;\n\nexport function getSettings(explicitApiKey: string | undefined): CliSettings {\n  const env = _loadEnv();\n  const systemFile = _loadSystemFile();\n  const defaults = _loadDefaults();\n\n  _legacyEnvVarWarning();\n\n  _envVarsInfo();\n\n  return {\n    auth: {\n      apiKey:\n        explicitApiKey ||\n        env.LINGO_API_KEY ||\n        env.LINGODOTDEV_API_KEY ||\n        systemFile.auth?.apiKey ||\n        systemFile.auth?.vnext?.apiKey ||\n        defaults.auth.apiKey,\n      apiUrl:\n        env.LINGO_API_URL ||\n        env.LINGODOTDEV_API_URL ||\n        systemFile.auth?.apiUrl ||\n        defaults.auth.apiUrl,\n      webUrl:\n        env.LINGODOTDEV_WEB_URL ||\n        systemFile.auth?.webUrl ||\n        defaults.auth.webUrl,\n    },\n    llm: {\n      openaiApiKey: env.OPENAI_API_KEY || systemFile.llm?.openaiApiKey,\n      anthropicApiKey: env.ANTHROPIC_API_KEY || systemFile.llm?.anthropicApiKey,\n      groqApiKey: env.GROQ_API_KEY || systemFile.llm?.groqApiKey,\n      googleApiKey: env.GOOGLE_API_KEY || systemFile.llm?.googleApiKey,\n      openrouterApiKey:\n        env.OPENROUTER_API_KEY || systemFile.llm?.openrouterApiKey,\n      mistralApiKey: env.MISTRAL_API_KEY || systemFile.llm?.mistralApiKey,\n    },\n  };\n}\n\nexport function saveSettings(settings: CliSettings): void {\n  _saveSystemFile(settings);\n}\n\nexport function loadSystemSettings() {\n  return _loadSystemFile();\n}\n\nconst flattenZodObject = (schema: Z.ZodObject<any>, prefix = \"\"): string[] => {\n  return Object.entries(schema.shape).flatMap(([key, value]) => {\n    const newPrefix = prefix ? `${prefix}.${key}` : key;\n    if (value instanceof Z.ZodObject) {\n      return flattenZodObject(value, newPrefix);\n    }\n    return [newPrefix];\n  });\n};\n\nconst SettingsSchema = Z.object({\n  auth: Z.object({\n    apiKey: Z.string(),\n    apiUrl: Z.string(),\n    webUrl: Z.string(),\n  }),\n  llm: Z.object({\n    openaiApiKey: Z.string().optional(),\n    anthropicApiKey: Z.string().optional(),\n    groqApiKey: Z.string().optional(),\n    googleApiKey: Z.string().optional(),\n    openrouterApiKey: Z.string().optional(),\n    mistralApiKey: Z.string().optional(),\n  }),\n});\n\nexport const SETTINGS_KEYS = flattenZodObject(\n  SettingsSchema,\n) as readonly string[];\n\n// Private\n\nfunction _loadDefaults(): CliSettings {\n  return {\n    auth: {\n      apiKey: \"\",\n      apiUrl: \"https://api.lingo.dev\",\n      webUrl: \"https://lingo.dev\",\n    },\n    llm: {},\n  };\n}\n\nfunction _loadEnv() {\n  return Z.looseObject({\n    LINGO_API_KEY: Z.string().optional(),\n    LINGO_API_URL: Z.string().optional(),\n    LINGODOTDEV_API_KEY: Z.string().optional(),\n    LINGODOTDEV_API_URL: Z.string().optional(),\n    LINGODOTDEV_WEB_URL: Z.string().optional(),\n    OPENAI_API_KEY: Z.string().optional(),\n    ANTHROPIC_API_KEY: Z.string().optional(),\n    GROQ_API_KEY: Z.string().optional(),\n    GOOGLE_API_KEY: Z.string().optional(),\n    OPENROUTER_API_KEY: Z.string().optional(),\n    MISTRAL_API_KEY: Z.string().optional(),\n  }).parse(process.env);\n}\n\nfunction _loadSystemFile() {\n  const settingsFilePath = _getSettingsFilePath();\n  const content = fs.existsSync(settingsFilePath)\n    ? fs.readFileSync(settingsFilePath, \"utf-8\")\n    : \"\";\n  const data = Ini.parse(content);\n\n  return Z.looseObject({\n    auth: Z.looseObject({\n      apiKey: Z.string().optional(),\n      apiUrl: Z.string().optional(),\n      webUrl: Z.string().optional(),\n      vnext: Z.object({\n        apiKey: Z.string().optional(),\n      }).optional(),\n    }).optional(),\n    llm: Z.looseObject({\n      openaiApiKey: Z.string().optional(),\n      anthropicApiKey: Z.string().optional(),\n      groqApiKey: Z.string().optional(),\n      googleApiKey: Z.string().optional(),\n      openrouterApiKey: Z.string().optional(),\n      mistralApiKey: Z.string().optional(),\n    }).optional(),\n  }).parse(data);\n}\n\nfunction _saveSystemFile(settings: CliSettings) {\n  const settingsFilePath = _getSettingsFilePath();\n  const content = Ini.stringify(settings);\n  fs.writeFileSync(settingsFilePath, content);\n}\n\nfunction _getSettingsFilePath(): string {\n  const settingsFile = \".lingodotdevrc\";\n  const homedir = os.homedir();\n  const settingsFilePath = path.join(homedir, settingsFile);\n  return settingsFilePath;\n}\n\nfunction _legacyEnvVarWarning() {\n  const env = _loadEnv();\n\n  if (env.REPLEXICA_API_KEY && !env.LINGO_API_KEY && !env.LINGODOTDEV_API_KEY) {\n    console.warn(\n      \"\\x1b[33m%s\\x1b[0m\",\n      `\n⚠️  WARNING: REPLEXICA_API_KEY env var is deprecated ⚠️\n===========================================================\n\nPlease use LINGO_API_KEY instead.\n===========================================================\n`,\n    );\n  }\n\n  if (env.LINGODOTDEV_API_KEY && !env.LINGO_API_KEY) {\n    console.warn(\n      \"\\x1b[33m%s\\x1b[0m\",\n      `\n⚠️  WARNING: LINGODOTDEV_API_KEY env var is deprecated ⚠️\n===========================================================\n\nPlease use LINGO_API_KEY instead.\n===========================================================\n`,\n    );\n  }\n\n  if (env.LINGODOTDEV_API_URL && !env.LINGO_API_URL) {\n    console.warn(\n      \"\\x1b[33m%s\\x1b[0m\",\n      `\n⚠️  WARNING: LINGODOTDEV_API_URL env var is deprecated ⚠️\n===========================================================\n\nPlease use LINGO_API_URL instead.\n===========================================================\n`,\n    );\n  }\n}\n\nfunction _envVarsInfo() {\n  const env = _loadEnv();\n  const systemFile = _loadSystemFile();\n\n  if (env.LINGODOTDEV_API_KEY && systemFile.auth?.apiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using LINGODOTDEV_API_KEY env var instead of credentials from user config`,\n    );\n  }\n  if (env.OPENAI_API_KEY && systemFile.llm?.openaiApiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using OPENAI_API_KEY env var instead of key from user config.`,\n    );\n  }\n  if (env.ANTHROPIC_API_KEY && systemFile.llm?.anthropicApiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using ANTHROPIC_API_KEY env var instead of key from user config`,\n    );\n  }\n  if (env.GROQ_API_KEY && systemFile.llm?.groqApiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using GROQ_API_KEY env var instead of key from user config`,\n    );\n  }\n  if (env.GOOGLE_API_KEY && systemFile.llm?.googleApiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using GOOGLE_API_KEY env var instead of key from user config`,\n    );\n  }\n  if (env.OPENROUTER_API_KEY && systemFile.llm?.openrouterApiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using OPENROUTER_API_KEY env var instead of key from user config`,\n    );\n  }\n  if (env.MISTRAL_API_KEY && systemFile.llm?.mistralApiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using MISTRAL_API_KEY env var instead of key from user config`,\n    );\n  }\n  if (env.LINGO_API_URL || env.LINGODOTDEV_API_URL) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using custom API URL: ${env.LINGO_API_URL || env.LINGODOTDEV_API_URL}`,\n    );\n  }\n  if (env.LINGODOTDEV_WEB_URL) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using LINGODOTDEV_WEB_URL: ${env.LINGODOTDEV_WEB_URL}`,\n    );\n  }\n  if (env.LINGO_API_KEY && systemFile.auth?.vnext?.apiKey) {\n    console.info(\n      \"\\x1b[36m%s\\x1b[0m\",\n      `ℹ️  Using LINGO_API_KEY env var instead of key from user config`,\n    );\n  }\n}\n","export const docLinks = {\n  i18nNotFound: \"https://lingo.dev/cli\",\n  bucketNotFound: \"https://lingo.dev/cli\",\n  authError: \"https://lingo.dev/cli\",\n  localeTargetNotFound: \"https://lingo.dev/cli\",\n  // corrected key (previously misspelled as \"lockFiletNotFound\")\n  lockFileNotFound: \"https://lingo.dev/cli\",\n  // legacy alias for backward compatibility\n  lockFiletNotFound: \"https://lingo.dev/cli\",\n  failedReplexicaEngine: \"https://lingo.dev/cli\",\n  placeHolderFailed: \"https://lingo.dev/cli\",\n  translationFailed: \"https://lingo.dev/cli\",\n  connectionFailed: \"https://lingo.dev/cli\",\n  invalidType: \"https://lingo.dev/cli\",\n  invalidPathPattern: \"https://lingo.dev/cli\",\n  // corrected key (previously misspelled as \"androidResouceError\")\n  androidResourceError: \"https://lingo.dev/cli\",\n  // legacy alias for backward compatibility\n  androidResouceError: \"https://lingo.dev/cli\",\n  invalidBucketType: \"https://lingo.dev/cli\",\n  invalidStringDict: \"https://lingo.dev/cli\",\n};\n\ntype DocLinkKeys = keyof typeof docLinks;\n\nexport class CLIError extends Error {\n  public readonly docUrl: string;\n  public readonly errorType: string = \"cli_error\";\n\n  constructor({ message, docUrl }: { message: string; docUrl: DocLinkKeys }) {\n    super(message);\n    this.docUrl = docLinks[docUrl];\n    this.message = `${this.message}\\n visit: ${this.docUrl}`;\n  }\n}\n\nexport class ConfigError extends CLIError {\n  public readonly errorType = \"config_error\";\n\n  constructor({ message, docUrl }: { message: string; docUrl: DocLinkKeys }) {\n    super({ message, docUrl });\n    this.name = \"ConfigError\";\n  }\n}\n\nexport class AuthenticationError extends CLIError {\n  public readonly errorType = \"auth_error\";\n\n  constructor({ message, docUrl }: { message: string; docUrl: DocLinkKeys }) {\n    super({ message, docUrl });\n    this.name = \"AuthenticationError\";\n  }\n}\n\nexport class ValidationError extends CLIError {\n  public readonly errorType = \"validation_error\";\n\n  constructor({ message, docUrl }: { message: string; docUrl: DocLinkKeys }) {\n    super({ message, docUrl });\n    this.name = \"ValidationError\";\n  }\n}\n\nexport class LocalizationError extends Error {\n  public readonly errorType = \"locale_error\";\n  public readonly bucket?: string;\n  public readonly sourceLocale?: string;\n  public readonly targetLocale?: string;\n  public readonly pathPattern?: string;\n\n  constructor(\n    message: string,\n    context?: {\n      bucket?: string;\n      sourceLocale?: string;\n      targetLocale?: string;\n      pathPattern?: string;\n    },\n  ) {\n    super(message);\n    this.name = \"LocalizationError\";\n    this.bucket = context?.bucket;\n    this.sourceLocale = context?.sourceLocale;\n    this.targetLocale = context?.targetLocale;\n    this.pathPattern = context?.pathPattern;\n  }\n}\n\nexport class BucketProcessingError extends Error {\n  public readonly errorType = \"bucket_error\";\n  public readonly bucket: string;\n\n  constructor(message: string, bucket: string) {\n    super(message);\n    this.name = \"BucketProcessingError\";\n    this.bucket = bucket;\n  }\n}\n\n// Type guard functions for robust error detection\nexport function isConfigError(error: any): error is ConfigError {\n  return error instanceof ConfigError || error.errorType === \"config_error\";\n}\n\nexport function isAuthenticationError(\n  error: any,\n): error is AuthenticationError {\n  return (\n    error instanceof AuthenticationError || error.errorType === \"auth_error\"\n  );\n}\n\nexport function isValidationError(error: any): error is ValidationError {\n  return (\n    error instanceof ValidationError || error.errorType === \"validation_error\"\n  );\n}\n\nexport function isLocalizationError(error: any): error is LocalizationError {\n  return (\n    error instanceof LocalizationError || error.errorType === \"locale_error\"\n  );\n}\n\nexport function isBucketProcessingError(\n  error: any,\n): error is BucketProcessingError {\n  return (\n    error instanceof BucketProcessingError || error.errorType === \"bucket_error\"\n  );\n}\n\nexport function getCLIErrorType(error: any): string {\n  if (isConfigError(error)) return \"config_error\";\n  if (isAuthenticationError(error)) return \"auth_error\";\n  if (isValidationError(error)) return \"validation_error\";\n  if (isLocalizationError(error)) return \"locale_error\";\n  if (isBucketProcessingError(error)) return \"bucket_error\";\n  if (error instanceof CLIError) return \"cli_error\";\n  return \"unknown_error\";\n}\n\n// Error detail interface for consistent tracking\nexport interface ErrorDetail {\n  type:\n    | \"bucket_error\"\n    | \"locale_error\"\n    | \"validation_error\"\n    | \"auth_error\"\n    | \"config_error\";\n  bucket?: string;\n  locale?: string;\n  pathPattern?: string;\n  message: string;\n  stack?: string;\n}\n\n// Utility to create previous error context for fatal errors\nexport function createPreviousErrorContext(errorDetails: ErrorDetail[]) {\n  if (errorDetails.length === 0) return undefined;\n\n  return {\n    count: errorDetails.length,\n    types: [...new Set(errorDetails.map((e) => e.type))],\n    buckets: [...new Set(errorDetails.map((e) => e.bucket).filter(Boolean))],\n  };\n}\n\n// Utility to create aggregated error analytics\nexport function aggregateErrorAnalytics(\n  errorDetails: ErrorDetail[],\n  buckets: any[],\n  targetLocales: string[],\n  i18nConfig: any,\n) {\n  if (errorDetails.length === 0) {\n    return {\n      errorCount: 0,\n      errorTypes: [],\n      errorsByBucket: {},\n      errorsByType: {},\n      firstError: undefined,\n      bucketCount: buckets.length,\n      localeCount: targetLocales.length,\n      i18nConfig: {\n        sourceLocale: i18nConfig.locale.source,\n        targetLocales: i18nConfig.locale.targets,\n        bucketTypes: Object.keys(i18nConfig.buckets),\n      },\n    };\n  }\n\n  const errorsByBucket = errorDetails.reduce(\n    (acc, error) => {\n      if (error.bucket) {\n        acc[error.bucket] = (acc[error.bucket] || 0) + 1;\n      }\n      return acc;\n    },\n    {} as Record<string, number>,\n  );\n\n  const errorsByType = errorDetails.reduce(\n    (acc, error) => {\n      acc[error.type] = (acc[error.type] || 0) + 1;\n      return acc;\n    },\n    {} as Record<string, number>,\n  );\n\n  return {\n    errorCount: errorDetails.length,\n    errorTypes: [...new Set(errorDetails.map((e) => e.type))],\n    errorsByBucket,\n    errorsByType,\n    firstError: {\n      type: errorDetails[0].type,\n      bucket: errorDetails[0].bucket,\n      locale: errorDetails[0].locale,\n      pathPattern: errorDetails[0].pathPattern,\n      message: errorDetails[0].message,\n    },\n    bucketCount: buckets.length,\n    localeCount: targetLocales.length,\n    i18nConfig: {\n      sourceLocale: i18nConfig.locale.source,\n      targetLocales: i18nConfig.locale.targets,\n      bucketTypes: Object.keys(i18nConfig.buckets),\n    },\n  };\n}\n","export interface CloudflareStatusResponse {\n  status: {\n    indicator: \"none\" | \"minor\" | \"major\" | \"critical\";\n    description: string;\n  };\n}\n\nexport async function checkCloudflareStatus(): Promise<CloudflareStatusResponse | null> {\n  try {\n    const response = await fetch(\n      \"https://www.cloudflarestatus.com/api/v2/status.json\",\n      {\n        signal: AbortSignal.timeout(5000),\n      },\n    );\n    if (response.ok) {\n      return await response.json();\n    }\n  } catch (error) {}\n  return null;\n}\n\nexport function formatCloudflareStatusMessage(\n  status: CloudflareStatusResponse,\n): string {\n  if (status.status.indicator === \"none\") {\n    return \"\";\n  }\n  return `Cloudflare is experiencing ${status.status.indicator} issues: ${status.status.description}. This may be affecting the API connection.`;\n}\n","import { CLIError } from \"./errors\";\nimport {\n  checkCloudflareStatus,\n  formatCloudflareStatusMessage,\n} from \"./cloudflare-status\";\n\nexport type AuthenticatorParams = {\n  apiUrl: string;\n  apiKey: string;\n};\n\nexport type AuthPayload = {\n  email: string;\n  id: string;\n};\n\nexport function createAuthenticator(params: AuthenticatorParams) {\n  return {\n    async whoami(): Promise<AuthPayload | null> {\n      try {\n        const res = await fetch(`${params.apiUrl}/users/me`, {\n          method: \"GET\",\n          headers: {\n            \"X-API-Key\": params.apiKey,\n            \"Content-Type\": \"application/json\",\n          },\n        });\n\n        if (res.ok) {\n          const payload = await res.json();\n          if (!payload?.email) {\n            return null;\n          }\n\n          return {\n            email: payload.email,\n            id: payload.id,\n          };\n        }\n\n        if (res.status >= 500 && res.status < 600) {\n          const originalErrorMessage = `Server error (${res.status}): ${res.statusText}. Please try again later.`;\n\n          const cloudflareStatus = await checkCloudflareStatus();\n\n          if (!cloudflareStatus) {\n            throw new CLIError({\n              message: originalErrorMessage,\n              docUrl: \"connectionFailed\",\n            });\n          }\n\n          if (cloudflareStatus.status.indicator !== \"none\") {\n            const cloudflareMessage =\n              formatCloudflareStatusMessage(cloudflareStatus);\n            throw new CLIError({\n              message: cloudflareMessage,\n              docUrl: \"connectionFailed\",\n            });\n          }\n\n          throw new CLIError({\n            message: originalErrorMessage,\n            docUrl: \"connectionFailed\",\n          });\n        }\n\n        return null;\n      } catch (error) {\n        if (error instanceof CLIError) {\n          throw error;\n        }\n\n        const isNetworkError =\n          error instanceof TypeError && error.message === \"fetch failed\";\n        if (isNetworkError) {\n          throw new CLIError({\n            message: `Failed to connect to the API at ${params.apiUrl}. Please check your connection and try again.`,\n            docUrl: \"connectionFailed\",\n          });\n        } else {\n          throw error;\n        }\n      }\n    },\n  };\n}\n","import { Command } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport express from \"express\";\nimport cors from \"cors\";\nimport open from \"open\";\nimport readline from \"readline/promises\";\nimport { getSettings, saveSettings } from \"../utils/settings\";\nimport {\n  renderClear,\n  renderSpacer,\n  renderBanner,\n  renderHero,\n} from \"../utils/ui\";\n\nexport default new Command()\n  .command(\"login\")\n  .description(\n    \"Open browser to authenticate with lingo.dev and save your API key\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async () => {\n    try {\n      await renderClear();\n      await renderSpacer();\n      await renderBanner();\n      await renderHero();\n      await renderSpacer();\n\n      const settings = await getSettings(undefined);\n      const apiKey = await login(settings.auth.webUrl);\n      settings.auth.apiKey = apiKey;\n      await saveSettings(settings);\n      Ora().succeed(\"Successfully logged in\");\n    } catch (error: any) {\n      Ora().fail(error.message);\n      process.exit(1);\n    }\n  });\n\nexport async function login(webAppUrl: string) {\n  await readline\n    .createInterface({\n      input: process.stdin,\n      output: process.stdout,\n    })\n    .question(\n      `\nPress Enter to open the browser for authentication.\n\n---\n\nHaving issues? Put LINGO_API_KEY in your .env file instead.\n    `.trim() + \"\\n\",\n    );\n\n  const spinner = Ora().start(\"Waiting for the API key\");\n  const apiKey = await waitForApiKey(async (port) => {\n    await open(`${webAppUrl}/app/cli?port=${port}`, { wait: false });\n  });\n  spinner.succeed(\"API key received\");\n\n  return apiKey;\n}\n\nasync function waitForApiKey(cb: (port: string) => void): Promise<string> {\n  const app = express();\n  app.use(express.json());\n  app.use(cors());\n\n  return new Promise((resolve) => {\n    const server = app.listen(0, async () => {\n      const port = (server.address() as any).port;\n      cb(port.toString());\n    });\n\n    app.post(\"/\", (req, res) => {\n      const apiKey = req.body.apiKey;\n      res.end();\n      server.close(() => {\n        resolve(apiKey);\n      });\n    });\n  });\n}\n","import chalk from \"chalk\";\nimport figlet from \"figlet\";\nimport { vice } from \"gradient-string\";\nimport readline from \"readline\";\nimport { colors } from \"../constants\";\nimport fs from \"fs\"; // <-- ADD THIS IMPORT\n\nfunction isCI(): boolean {\n  return Boolean(process.env.CI) || fs.existsSync(\"/.dockerenv\");\n}\n\nexport async function renderClear() {\n  process.stdout.write(\"\\x1b[2J\\x1b[H\");\n}\n\nexport async function renderSpacer() {\n  console.log(\" \");\n}\n\nexport async function renderBanner() {\n  console.log(\n    vice(\n      figlet.textSync(\"LINGO.DEV\", {\n        font: \"ANSI Shadow\",\n        horizontalLayout: \"default\",\n        verticalLayout: \"default\",\n      }),\n    ),\n  );\n}\n\nexport async function renderHero() {\n  console.log(\n    `⚡️ ${chalk.hex(colors.green)(\n      \"Lingo.dev\",\n    )} - open-source, AI-powered i18n CLI for web & mobile localization.`,\n  );\n  console.log(\"\");\n\n  const label1 = \"📚 Docs:\";\n  const label2 = \"⭐ Star the repo:\";\n  const label3 = \"🎮 Join Discord:\";\n  const maxLabelWidth = 17; // Approximate visual width accounting for emoji\n\n    // --- ADD THIS LOGIC ---\n  const isCIEnv = isCI(); // <-- USE THE LOCAL HELPER FUNCTION\n  const docsUrl = isCIEnv\n    ? \"https://lingo.dev/ci\"\n    : \"https://lingo.dev/cli\";\n  // ------------------------\n\n  console.log(\n    `${chalk.hex(colors.blue)(label1.padEnd(maxLabelWidth + 1))} ${chalk.hex(\n      colors.blue,\n    )(docsUrl)}`,\n  ); // Docs emoji seems narrower\n  console.log(\n    `${chalk.hex(colors.blue)(label2.padEnd(maxLabelWidth))} ${chalk.hex(\n      colors.blue,\n    )(\"https://lingo.dev/go/gh\")}`,\n  );\n  console.log(\n    `${chalk.hex(colors.blue)(label3.padEnd(maxLabelWidth + 1))} ${chalk.hex(\n      colors.blue,\n    )(\"https://lingo.dev/go/discord\")}`,\n  );\n}\n\nexport async function waitForUserPrompt(message: string): Promise<void> {\n  const rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout,\n  });\n\n  return new Promise((resolve) => {\n    rl.question(chalk.dim(`[${message}]\\n`), () => {\n      rl.close();\n      resolve();\n    });\n  });\n}\n\nexport async function pauseIfDebug(debug: boolean) {\n  if (debug) {\n    await waitForUserPrompt(\"Press Enter to continue...\");\n  }\n}\n\nexport async function renderSummary(results: Map<any, any>) {\n  console.log(chalk.hex(colors.green)(\"[Done]\"));\n\n  const skippedResults = Array.from(results.values()).filter(\n    (r) => r.status === \"skipped\",\n  );\n  const succeededResults = Array.from(results.values()).filter(\n    (r) => r.status === \"success\",\n  );\n  const failedResults = Array.from(results.values()).filter(\n    (r) => r.status === \"error\",\n  );\n\n  console.log(\n    `• ${chalk.hex(colors.yellow)(skippedResults.length)} from cache`,\n  );\n  console.log(\n    `• ${chalk.hex(colors.yellow)(succeededResults.length)} processed`,\n  );\n  console.log(`• ${chalk.hex(colors.yellow)(failedResults.length)} failed`);\n\n  // Show processed files\n  if (succeededResults.length > 0) {\n    console.log(chalk.hex(colors.green)(\"\\n[Processed Files]\"));\n    for (const result of succeededResults) {\n      const displayPath =\n        result.pathPattern?.replace(\"[locale]\", result.targetLocale) ||\n        \"unknown\";\n      console.log(\n        `  ✓ ${chalk.dim(displayPath)} ${chalk.hex(colors.yellow)(`(${result.sourceLocale} → ${result.targetLocale})`)}`,\n      );\n    }\n  }\n\n  // Show cached files\n  if (skippedResults.length > 0) {\n    console.log(chalk.hex(colors.blue)(\"\\n[Cached Files]\"));\n    for (const result of skippedResults) {\n      const displayPath =\n        result.pathPattern?.replace(\"[locale]\", result.targetLocale) ||\n        \"unknown\";\n      console.log(\n        `  ⚡ ${chalk.dim(displayPath)} ${chalk.hex(colors.yellow)(`(${result.sourceLocale} → ${result.targetLocale})`)}`,\n      );\n    }\n  }\n\n  // Show failed files\n  if (failedResults.length > 0) {\n    console.log(chalk.hex(colors.orange)(\"\\n[Failed Files]\"));\n    for (const result of failedResults) {\n      const displayPath =\n        result.pathPattern?.replace(\"[locale]\", result.targetLocale) ||\n        \"unknown\";\n      console.log(\n        `  ❌ ${chalk.dim(displayPath)} ${chalk.hex(colors.yellow)(`(${result.sourceLocale} → ${result.targetLocale})`)}`,\n      );\n      console.log(\n        `     ${chalk.hex(colors.white)(String(result.error?.message || \"Unknown error\"))}`,\n      );\n    }\n  }\n}\n","export const colors = {\n  orange: \"#ff6600\",\n  green: \"#6ae300\",\n  blue: \"#0090ff\",\n  yellow: \"#ffcc00\",\n  grey: \"#808080\",\n  red: \"#ff0000\",\n  white: \"#ffffff\",\n};\n","import { Command } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport { getSettings, saveSettings } from \"../utils/settings\";\nimport {\n  renderClear,\n  renderSpacer,\n  renderBanner,\n  renderHero,\n} from \"../utils/ui\";\n\nexport default new Command()\n  .command(\"logout\")\n  .description(\"Log out by removing saved authentication credentials\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async () => {\n    try {\n      await renderClear();\n      await renderSpacer();\n      await renderBanner();\n      await renderHero();\n      await renderSpacer();\n\n      const settings = await getSettings(undefined);\n      settings.auth.apiKey = \"\";\n      await saveSettings(settings);\n      Ora().succeed(\"Successfully logged out\");\n    } catch (error: any) {\n      Ora().fail(error.message);\n      process.exit(1);\n    }\n  });\n","import { InteractiveCommand, InteractiveOption } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport { getConfig, saveConfig } from \"../utils/config\";\nimport {\n  defaultConfig,\n  LocaleCode,\n  resolveLocaleCode,\n  bucketTypes,\n} from \"@lingo.dev/_spec\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport _ from \"lodash\";\nimport { checkbox, confirm, input } from \"@inquirer/prompts\";\nimport { login } from \"./login\";\nimport { getSettings, saveSettings } from \"../utils/settings\";\nimport { createAuthenticator } from \"../utils/auth\";\nimport findLocaleFiles from \"../utils/find-locale-paths\";\nimport { ensurePatterns } from \"../utils/ensure-patterns\";\nimport updateGitignore from \"../utils/update-gitignore\";\nimport initCICD from \"../utils/init-ci-cd\";\nimport open from \"open\";\nimport cursorInitCmd from \"./init/cursor\";\n\nconst openUrl = (path: string) => {\n  const settings = getSettings(undefined);\n  open(`${settings.auth.webUrl}${path}`, { wait: false });\n};\n\nconst throwHelpError = (option: string, value: string) => {\n  if (value === \"help\") {\n    openUrl(\"/go/call\");\n  }\n  throw new Error(\n    `Invalid ${option}: ${value}\\n\\nDo you need support for ${value} ${option}? Type \"help\" and we will.`,\n  );\n};\n\nexport default new InteractiveCommand()\n  .command(\"init\")\n  .description(\"Create i18n.json configuration file for a new project\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .addOption(\n    new InteractiveOption(\n      \"-f --force\",\n      \"Overwrite existing Lingo.dev configuration instead of aborting initialization (destructive operation)\",\n    )\n      .prompt(undefined)\n      .default(false),\n  )\n  .addOption(\n    new InteractiveOption(\n      \"-s --source <locale>\",\n      \"Primary language of your application that content will be translated from. Defaults to 'en'\",\n    )\n      .argParser((value) => {\n        try {\n          resolveLocaleCode(value as LocaleCode);\n        } catch (e) {\n          throwHelpError(\"locale\", value);\n        }\n        return value;\n      })\n      .default(\"en\"),\n  )\n  .addOption(\n    new InteractiveOption(\n      \"-t --targets <locale...>\",\n      \"Target languages to translate to. Accepts locale codes like 'es', 'fr', 'de-AT' separated by commas or spaces. Defaults to 'es'\",\n    )\n      .argParser((value) => {\n        const values = (\n          value.includes(\",\") ? value.split(\",\") : value.split(\" \")\n        ) as LocaleCode[];\n        values.forEach((value) => {\n          try {\n            resolveLocaleCode(value);\n          } catch (e) {\n            throwHelpError(\"locale\", value);\n          }\n        });\n        return values;\n      })\n      .default(\"es\"),\n  )\n  .addOption(\n    new InteractiveOption(\n      \"-b, --bucket <type>\",\n      \"File format for your translation files. Must match a supported type such as json, yaml, or android\",\n    )\n      .argParser((value) => {\n        if (!bucketTypes.includes(value as (typeof bucketTypes)[number])) {\n          throwHelpError(\"bucket format\", value);\n        }\n        return value;\n      })\n      .default(\"json\"),\n  )\n  .addOption(\n    new InteractiveOption(\n      \"-p, --paths [path...]\",\n      \"File paths containing translations when using --no-interactive mode. Specify paths with [locale] placeholder, separated by commas or spaces\",\n    )\n      .argParser((value) => {\n        if (!value || value.length === 0) return [];\n        const values = value.includes(\",\")\n          ? value.split(\",\")\n          : value.split(\" \");\n\n        for (const p of values) {\n          try {\n            const dirPath = path.dirname(p);\n            const stats = fs.statSync(dirPath);\n            if (!stats.isDirectory()) {\n              throw new Error(`${dirPath} is not a directory`);\n            }\n          } catch (err) {\n            throw new Error(`Invalid path: ${p}`);\n          }\n        }\n        return values;\n        \n      })\n      .prompt(undefined) // make non-interactive\n      .default([]),\n  )\n  .action(async (options) => {\n    const settings = getSettings(undefined);\n    const isInteractive = options.interactive;\n\n    const spinner = Ora().start(\"Initializing Lingo.dev project\");\n\n    let existingConfig = await getConfig(false);\n    if (existingConfig && !options.force) {\n      spinner.fail(\"Lingo.dev project already initialized\");\n      return process.exit(1);\n    }\n\n    const newConfig = _.cloneDeep(defaultConfig);\n\n    newConfig.locale.source = options.source;\n    newConfig.locale.targets = options.targets;\n\n    if (!isInteractive) {\n      newConfig.buckets = {\n        [options.bucket]: {\n          include: options.paths || [],\n        },\n      };\n    } else {\n      let selectedPatterns: string[] = [];\n      const localeFiles = findLocaleFiles(options.bucket);\n\n      if (!localeFiles) {\n        spinner.warn(\n          `Bucket type \"${options.bucket}\" does not supported automatic initialization. Add paths to \"i18n.json\" manually.`,\n        );\n        newConfig.buckets = {\n          [options.bucket]: {\n            include: options.paths || [],\n          },\n        };\n      } else {\n        const { patterns, defaultPatterns } = localeFiles;\n\n        if (patterns.length > 0) {\n          spinner.succeed(\"Found existing locale files:\");\n\n          selectedPatterns = await checkbox({\n            message: \"Select the paths to use\",\n            choices: patterns.map((value) => ({\n              value,\n            })),\n          });\n        } else {\n          spinner.succeed(\"No existing locale files found.\");\n        }\n\n        if (selectedPatterns.length === 0) {\n          const useDefault = await confirm({\n            message: `Use (and create) default path ${defaultPatterns.join(\n              \", \",\n            )}?`,\n          });\n          if (useDefault) {\n            ensurePatterns(defaultPatterns, options.source);\n            selectedPatterns = defaultPatterns;\n          }\n        }\n\n        if (selectedPatterns.length === 0) {\n          const customPaths = await input({\n            message: \"Enter paths to use\",\n          });\n          selectedPatterns = customPaths.includes(\",\")\n            ? customPaths.split(\",\")\n            : customPaths.split(\" \");\n        }\n\n        newConfig.buckets = {\n          [options.bucket]: {\n            include: selectedPatterns || [],\n          },\n        };\n      }\n    }\n\n    await saveConfig(newConfig);\n\n    spinner.succeed(\"Lingo.dev project initialized\");\n\n    if (isInteractive) {\n      await initCICD(spinner);\n\n      const openDocs = await confirm({\n        message: \"Would you like to see our docs?\",\n      });\n      if (openDocs) {\n        openUrl(\"/go/docs\");\n      }\n    }\n\n    const authenticator = createAuthenticator({\n      apiKey: settings.auth.apiKey,\n      apiUrl: settings.auth.apiUrl,\n    });\n    const auth = await authenticator.whoami();\n    if (!auth) {\n      if (isInteractive) {\n        const doAuth = await confirm({\n          message: \"It looks like you are not logged into the CLI. Login now?\",\n        });\n        if (doAuth) {\n          const apiKey = await login(settings.auth.webUrl);\n          settings.auth.apiKey = apiKey;\n          await saveSettings(settings);\n\n          const newAuthenticator = createAuthenticator({\n            apiKey: settings.auth.apiKey,\n            apiUrl: settings.auth.apiUrl,\n          });\n          const auth = await newAuthenticator.whoami();\n          if (auth) {\n            Ora().succeed(`Authenticated as ${auth?.email}`);\n          } else {\n            Ora().fail(\"Authentication failed.\");\n          }\n        }\n      } else {\n        Ora().warn(\n          \"You are not logged in. Run `npx lingo.dev@latest login` to login.\",\n        );\n      }\n    } else {\n      Ora().succeed(`Authenticated as ${auth.email}`);\n    }\n\n    updateGitignore();\n\n    if (!isInteractive) {\n      Ora().info(\"Please see https://lingo.dev/cli\");\n    }\n  })\n  .addCommand(cursorInitCmd);\n","import _ from \"lodash\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { I18nConfig, parseI18nConfig } from \"@lingo.dev/_spec\";\n\nexport function getConfig(resave = true): I18nConfig | null {\n  const configFilePath = _getConfigFilePath();\n\n  const configFileExists = fs.existsSync(configFilePath);\n  if (!configFileExists) {\n    return null;\n  }\n\n  const fileContents = fs.readFileSync(configFilePath, \"utf8\");\n  const rawConfig = JSON.parse(fileContents);\n\n  const result = parseI18nConfig(rawConfig);\n  const didConfigChange = !_.isEqual(rawConfig, result);\n\n  if (resave && didConfigChange) {\n    // Ensure the config is saved with the latest version / schema\n    saveConfig(result);\n  }\n\n  return result;\n}\n\nexport function saveConfig(config: I18nConfig) {\n  const configFilePath = _getConfigFilePath();\n\n  const serialized = JSON.stringify(config, null, 2);\n  fs.writeFileSync(configFilePath, serialized);\n\n  return config;\n}\n\n// Private\n\nfunction _getConfigFilePath() {\n  return path.join(process.cwd(), \"i18n.json\");\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport * as pkg from \"glob\";\nconst { glob } = pkg;\nimport _ from \"lodash\";\nimport { LocaleCode, resolveLocaleCode } from \"@lingo.dev/_spec\";\n\nexport default function findLocaleFiles(bucket: string) {\n  switch (bucket) {\n    case \"json\":\n      return findLocaleFilesWithExtension(\".json\");\n    case \"yaml\":\n      return findLocaleFilesWithExtension(\".yml\");\n    case \"flutter\":\n      return findLocaleFilesWithExtension(\".arb\");\n    case \"android\":\n      return findLocaleFilesWithExtension(\".xml\");\n    case \"markdown\":\n      return findLocaleFilesWithExtension(\".md\");\n    case \"php\":\n      return findLocaleFilesWithExtension(\".php\");\n    case \"po\":\n      return findLocaleFilesWithExtension(\".po\");\n    case \"xcode-xcstrings\":\n      return findLocaleFilesForFilename(\"Localizable.xcstrings\");\n    case \"xcode-strings\":\n      return findLocaleFilesForFilename(\"Localizable.strings\");\n    case \"xcode-stringsdict\":\n      return findLocaleFilesForFilename(\"Localizable.stringsdict\");\n    default:\n      return null;\n  }\n}\n\nfunction findLocaleFilesWithExtension(ext: string) {\n  const files = glob.sync(`**/*${ext}`, {\n    ignore: [\"node_modules/**\", \"package*.json\", \"i18n.json\", \"lingo.json\"],\n  });\n\n  const localeFilePattern = new RegExp(`\\/([a-z]{2}(-[A-Z]{2})?)${ext}$`);\n  const localeDirectoryPattern = new RegExp(\n    `\\/([a-z]{2}(-[A-Z]{2})?)\\/[^\\/]+${ext}$`,\n  );\n  const potentialLocaleFiles = files.filter(\n    (file: string) =>\n      localeFilePattern.test(file) || localeDirectoryPattern.test(file),\n  );\n\n  const potantialLocaleFilesAndPatterns = potentialLocaleFiles\n    .map((file: string) => {\n      const matchPotentialLocales = Array.from(\n        file.matchAll(\n          new RegExp(`\\/([a-z]{2}(-[A-Z]{2})?|[^\\/]+)(?=\\/|${ext})`, \"g\"),\n        ),\n      );\n      const potantialLocales = matchPotentialLocales.map((match) => match[1]);\n      return { file, potantialLocales };\n    })\n    .map(({ file, potantialLocales }) => {\n      for (const locale of potantialLocales) {\n        try {\n          resolveLocaleCode(locale as LocaleCode);\n          return { locale, file };\n        } catch (e) { }\n      }\n      return { file, locale: null };\n    })\n    .filter(({ locale }) => locale !== null);\n\n  const localeFilesAndPatterns = potantialLocaleFilesAndPatterns.map(\n    ({ file, locale }) => {\n      const pattern = file\n        .replaceAll(new RegExp(`/${locale}${ext}`, \"g\"), `/[locale]${ext}`)\n        .replaceAll(new RegExp(`/${locale}/`, \"g\"), `/[locale]/`)\n        .replaceAll(new RegExp(`/${locale}/`, \"g\"), `/[locale]/`); // for when there are 2 locales one after another\n      return { pattern, file };\n    },\n  );\n\n  const grouppedFilesAndPatterns = _.groupBy(localeFilesAndPatterns, \"pattern\");\n  const patterns = Object.keys(grouppedFilesAndPatterns);\n  const defaultPatterns = [`i18n/[locale]${ext}`];\n\n  if (patterns.length > 0) {\n    return { patterns, defaultPatterns };\n  }\n\n  return { patterns: [], defaultPatterns };\n}\n\nfunction findLocaleFilesForFilename(fileName: string) {\n  const pattern = fileName;\n  const localeFiles = glob.sync(`**/${fileName}`, {\n    ignore: [\"node_modules/**\", \"package*.json\", \"i18n.json\", \"lingo.json\"],\n  });\n\n  const localeFilesAndPatterns = localeFiles.map((file: string) => ({\n    file,\n    pattern: path.join(path.dirname(file), pattern),\n  }));\n  const grouppedFilesAndPatterns = _.groupBy(localeFilesAndPatterns, \"pattern\");\n  const patterns = Object.keys(grouppedFilesAndPatterns);\n  const defaultPatterns = [fileName];\n\n  if (patterns.length > 0) {\n    return { patterns, defaultPatterns };\n  }\n\n  return { patterns: [], defaultPatterns };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport _ from \"lodash\";\nimport { LocaleCode, resolveLocaleCode } from \"@lingo.dev/_spec\";\n\nexport function ensurePatterns(patterns: string[], source: string) {\n  if (patterns.length === 0) {\n    throw new Error(\"No patterns found\");\n  }\n\n  patterns.forEach((pattern) => {\n    const filePath = pattern.replace(\"[locale]\", source);\n    if (!fs.existsSync(filePath)) {\n      const defaultContent = getDefaultContent(path.extname(filePath), source);\n      fs.mkdirSync(path.dirname(filePath), { recursive: true });\n      fs.writeFileSync(filePath, defaultContent);\n    }\n  });\n}\n\nfunction getDefaultContent(ext: string, source: string) {\n  const defaultGreeting = \"Hello from Lingo.dev\";\n  switch (ext) {\n    case \".json\":\n    case \".arb\":\n      return `{\\n\\t\"greeting\": \"${defaultGreeting}\"\\n}`;\n    case \".yml\":\n      return `${source}:\\n\\tgreeting: \"${defaultGreeting}\"`;\n    case \".xml\":\n      return `<resources>\\n\\t<string name=\"greeting\">${defaultGreeting}</string>\\n</resources>`;\n    case \".md\":\n      return `# ${defaultGreeting}`;\n    case \".xcstrings\":\n      return `{\n  \"sourceLanguage\" : \"${source}\",\n  \"strings\" : {\n    \"${defaultGreeting}\" : {\n      \"extractionState\" : \"manual\",\n      \"localizations\" : {\n        \"${source}\" : {\n          \"stringUnit\" : {\n            \"state\" : \"translated\",\n            \"value\" : \"${defaultGreeting}\"\n          }\n        }\n      }\n    }\n  }\n}`;\n    case \".strings\":\n      return `\"greeting\" = \"${defaultGreeting}\";`;\n    case \".stringsdict\":\n      return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>key</key>\n  <dict>\n    <key>NSStringLocalizedFormatKey</key>\n    <string>%#@count@</string>\n    <key>count</key>\n    <dict>\n      <key>NSStringFormatSpecTypeKey</key>\n      <string>NSStringPluralRuleType</string>\n      <key>NSStringFormatValueTypeKey</key>\n      <string>d</string>\n      <key>zero</key>\n      <string>No items</string>\n      <key>one</key>\n      <string>One item</string>\n      <key>other</key>\n      <string>%d items</string>\n    </dict>\n  </dict>\n</dict>\n</plist>`;\n    default:\n      throw new Error(`Unsupported file extension: ${ext}`);\n  }\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport default function updateGitignore() {\n  const cacheFile = \"i18n.cache\";\n  const projectRoot = findCurrentProjectRoot();\n  if (!projectRoot) {\n    return;\n  }\n  const gitignorePath = path.join(projectRoot, \".gitignore\");\n  if (!fs.existsSync(gitignorePath)) {\n    return;\n  }\n\n  const gitignore = fs.readFileSync(gitignorePath, \"utf8\").split(\"\\n\");\n  const cacheIsIgnored = gitignore.includes(cacheFile);\n\n  if (!cacheIsIgnored) {\n    let content = \"\";\n\n    // Ensure there's a trailing newline\n    content = fs.readFileSync(gitignorePath, \"utf8\");\n    if (content !== \"\" && !content.endsWith(\"\\n\")) {\n      content += \"\\n\";\n    }\n\n    content += `${cacheFile}\\n`;\n    fs.writeFileSync(gitignorePath, content);\n  }\n}\n\nfunction findCurrentProjectRoot() {\n  let currentDir = process.cwd();\n  while (currentDir !== path.parse(currentDir).root) {\n    const gitDirPath = path.join(currentDir, \".git\");\n    if (fs.existsSync(gitDirPath) && fs.lstatSync(gitDirPath).isDirectory()) {\n      return currentDir;\n    }\n    currentDir = path.dirname(currentDir);\n  }\n  return null;\n}\n","import { checkbox, confirm } from \"@inquirer/prompts\";\nimport fs from \"fs\";\nimport { Ora } from \"ora\";\nimport path from \"path\";\n\ntype Platform = \"github\" | \"bitbucket\" | \"gitlab\";\n\nconst platforms: Platform[] = [\"github\", \"bitbucket\", \"gitlab\"];\n\nexport default async function initCICD(spinner: Ora) {\n  const initializers = getPlatformInitializers(spinner);\n\n  const init = await confirm({\n    message: \"Would you like to use Lingo.dev in your CI/CD?\",\n  });\n\n  if (!init) {\n    spinner.warn(\n      \"CI/CD not initialized. To set it up later, see docs: https://lingo.dev/ci\",\n    );\n    return;\n  }\n\n  const selectedPlatforms: Platform[] = await checkbox({\n    message: \"Please select CI/CD platform(s) you want to use:\",\n    choices: platforms.map((platform) => ({\n      name: initializers[platform].name,\n      value: platform,\n      checked: initializers[platform].isEnabled(),\n    })),\n  });\n\n  for (const platform of selectedPlatforms) {\n    await initializers[platform].init();\n  }\n}\n\nfunction getPlatformInitializers(spinner: Ora) {\n  return {\n    github: makeGithubInitializer(spinner),\n    bitbucket: makeBitbucketInitializer(spinner),\n    gitlab: makeGitlabInitializer(spinner),\n  };\n}\n\ntype PlatformConfig = {\n  name: string;\n  checkPath: string;\n  ciConfigPath: string;\n  ciConfigContent: string;\n};\n\nfunction makePlatformInitializer(config: PlatformConfig, spinner: Ora) {\n  return {\n    name: config.name,\n    isEnabled: () => {\n      const filePath = path.join(process.cwd(), config.checkPath);\n      return fs.existsSync(filePath);\n    },\n    init: async () => {\n      const filePath = path.join(process.cwd(), config.ciConfigPath);\n      const dirPath = path.dirname(filePath);\n      if (!fs.existsSync(dirPath)) {\n        fs.mkdirSync(dirPath, { recursive: true });\n      }\n      let canWrite = true;\n      if (fs.existsSync(filePath)) {\n        canWrite = await confirm({\n          message: `File ${filePath} already exists. Do you want to overwrite it?`,\n          default: false,\n        });\n      }\n      if (canWrite) {\n        fs.writeFileSync(filePath, config.ciConfigContent);\n        spinner.succeed(`CI/CD initialized for ${config.name}`);\n      } else {\n        spinner.warn(`CI/CD not initialized for ${config.name}`);\n      }\n    },\n  };\n}\n\nfunction makeGithubInitializer(spinner: Ora) {\n  return makePlatformInitializer(\n    {\n      name: \"GitHub Action\",\n      checkPath: \".github\",\n      ciConfigPath: \".github/workflows/i18n.yml\",\n      ciConfigContent: `name: Lingo.dev i18n\n\non:\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: write\n  pull-requests: write\n\njobs:\n  i18n:\n    name: Run i18n\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: lingodotdev/lingo.dev@main\n        with:\n          api-key: \\${{ secrets.LINGO_API_KEY }}\n`,\n    },\n    spinner,\n  );\n}\n\nfunction makeBitbucketInitializer(spinner: Ora) {\n  return makePlatformInitializer(\n    {\n      name: \"Bitbucket Pipeline\",\n      checkPath: \"bitbucket-pipelines.yml\",\n      ciConfigPath: \"bitbucket-pipelines.yml\",\n      ciConfigContent: `pipelines:\n  branches:\n    main:\n      - step:\n          name: Run i18n\n          script:\n            - pipe: lingodotdev/lingo.dev:main`,\n    },\n    spinner,\n  );\n}\n\nfunction makeGitlabInitializer(spinner: Ora) {\n  return makePlatformInitializer(\n    {\n      name: \"Gitlab CI\",\n      checkPath: \".gitlab-ci.yml\",\n      ciConfigPath: \".gitlab-ci.yml\",\n      ciConfigContent: `lingodotdev:\n  image: lingodotdev/ci-action:latest\n  script:\n    - echo \"Done\"\n`,\n    },\n    spinner,\n  );\n}\n","import { InteractiveCommand, InteractiveOption } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { confirm } from \"@inquirer/prompts\";\n\n// Get the directory of this file\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n// Access agents.md from package root (works in both dev and production)\n// Resolve from current file location: try both paths to handle dev and bundled environments\nconst AGENTS_MD = fs.existsSync(path.resolve(__dirname, \"../agents.md\"))\n  ? path.resolve(__dirname, \"../agents.md\")\n  : path.resolve(__dirname, \"../../../../agents.md\");\n// Create .cursorrules in user's current working directory\nconst CURSORRULES = path.resolve(process.cwd(), \".cursorrules\");\n\nexport default new InteractiveCommand()\n  .command(\"cursor\")\n  .description(\"Initialize .cursorrules with i18n-specific instructions for Cursor AI.\")\n  .addOption(\n    new InteractiveOption(\"-f, --force\", \"Overwrite .cursorrules without prompt.\")\n      .default(false)\n  )\n  .action(async (options) => {\n    const spinner = Ora();\n    // Read agents.md\n    let template: string;\n    try {\n      template = fs.readFileSync(AGENTS_MD, \"utf-8\");\n    } catch (err) {\n      spinner.fail(\"Template file agents.md not found. Please reinstall the package.\");\n      return process.exit(1);\n    }\n    // Check for existing .cursorrules\n    const exists = fs.existsSync(CURSORRULES);\n    let shouldWrite;\n    if (exists && !options.force) {\n      shouldWrite = await confirm({\n        message: \".cursorrules already exists. Overwrite?\",\n      });\n      if (!shouldWrite) {\n        spinner.info(\"Skipped: .cursorrules left unchanged.\");\n        return;\n      }\n    }\n    try {\n      fs.writeFileSync(CURSORRULES, template);\n      spinner.succeed(\"Created .cursorrules\");\n      spinner.info(\n        \".cursorrules has been created with i18n-specific instructions for Cursor AI.\",\n      );\n    } catch (err) {\n      spinner.fail(`Failed to write .cursorrules: ${err}`);\n      process.exit(1);\n    }\n  });\n","import { Command } from \"interactive-commander\";\nimport _ from \"lodash\";\nimport configCmd from \"./config\";\nimport localeCmd from \"./locale\";\nimport filesCmd from \"./files\";\nimport lockedKeysCmd from \"./locked-keys\";\nimport ignoredKeysCmd from \"./ignored-keys\";\nimport preservedKeysCmd from \"./preserved-keys\";\n\nexport default new Command()\n  .command(\"show\")\n  .description(\"Display configuration, locales, and file paths\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .addCommand(configCmd)\n  .addCommand(localeCmd)\n  .addCommand(filesCmd)\n  .addCommand(lockedKeysCmd)\n  .addCommand(ignoredKeysCmd)\n  .addCommand(preservedKeysCmd);\n","import { Command } from \"interactive-commander\";\nimport _ from \"lodash\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { defaultConfig } from \"@lingo.dev/_spec\";\n\nexport default new Command()\n  .command(\"config\")\n  .description(\"Print effective i18n.json after merging with defaults\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (options) => {\n    const fileConfig = loadReplexicaFileConfig();\n    const config = _.merge({}, defaultConfig, fileConfig);\n\n    console.log(JSON.stringify(config, null, 2));\n  });\n\nfunction loadReplexicaFileConfig(): any {\n  const replexicaConfigPath = path.resolve(process.cwd(), \"i18n.json\");\n  const fileExists = fs.existsSync(replexicaConfigPath);\n  if (!fileExists) {\n    return undefined;\n  }\n\n  const fileContent = fs.readFileSync(replexicaConfigPath, \"utf-8\");\n  const replexicaFileConfig = JSON.parse(fileContent);\n  return replexicaFileConfig;\n}\n","import { Command } from \"interactive-commander\";\nimport _ from \"lodash\";\nimport Z from \"zod\";\nimport Ora from \"ora\";\nimport { localeCodes } from \"@lingo.dev/_spec\";\nimport { CLIError } from \"../../utils/errors\";\n\nexport default new Command()\n  .command(\"locale\")\n  .description(\"List supported locale codes\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  // argument can be equal either \"sources\" or \"targets\"\n  .argument(\n    \"<type>\",\n    'Type of locales to show: \"sources\" or \"targets\" - both show the full supported locale list',\n  )\n  .action(async (type) => {\n    const ora = Ora();\n    try {\n      switch (type) {\n        default:\n          throw new CLIError({\n            message: `Invalid type: ${type}`,\n            docUrl: \"invalidType\",\n          });\n        case \"sources\":\n          localeCodes.forEach((locale) => console.log(locale));\n          break;\n        case \"targets\":\n          localeCodes.forEach((locale) => console.log(locale));\n          break;\n      }\n    } catch (error: any) {\n      ora.fail(error.message);\n      process.exit(1);\n    }\n  });\n","import { Command } from \"interactive-commander\";\nimport _ from \"lodash\";\nimport Ora from \"ora\";\nimport { getConfig } from \"../../utils/config\";\nimport { CLIError } from \"../../utils/errors\";\nimport { getBuckets } from \"../../utils/buckets\";\nimport { resolveOverriddenLocale } from \"@lingo.dev/_spec\";\n\nexport default new Command()\n  .command(\"files\")\n  .description(\n    \"Expand each bucket's path pattern into concrete source and target file paths\",\n  )\n  .option(\n    \"--source\",\n    \"Only list the source locale variant for each path pattern\",\n  )\n  .option(\n    \"--target\",\n    \"Only list the target locale variants for each configured locale\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (type) => {\n    const ora = Ora();\n    try {\n      try {\n        const i18nConfig = await getConfig();\n\n        if (!i18nConfig) {\n          throw new CLIError({\n            message:\n              \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n            docUrl: \"i18nNotFound\",\n          });\n        }\n\n        const buckets = getBuckets(i18nConfig);\n        for (const bucket of buckets) {\n          for (const bucketConfig of bucket.paths) {\n            const sourceLocale = resolveOverriddenLocale(\n              i18nConfig.locale.source,\n              bucketConfig.delimiter,\n            );\n            const sourcePath = bucketConfig.pathPattern.replace(\n              /\\[locale\\]/g,\n              sourceLocale,\n            );\n            const targetPaths = i18nConfig.locale.targets.map(\n              (_targetLocale) => {\n                const targetLocale = resolveOverriddenLocale(\n                  _targetLocale,\n                  bucketConfig.delimiter,\n                );\n                return bucketConfig.pathPattern.replace(\n                  /\\[locale\\]/g,\n                  targetLocale,\n                );\n              },\n            );\n\n            const result: string[] = [];\n            if (!type.source && !type.target) {\n              result.push(sourcePath, ...targetPaths);\n            } else if (type.source) {\n              result.push(sourcePath);\n            } else if (type.target) {\n              result.push(...targetPaths);\n            }\n\n            result.forEach((path) => {\n              console.log(path);\n            });\n          }\n        }\n      } catch (error: any) {\n        throw new CLIError({\n          message: `Failed to expand placeholdered globs: ${error.message}`,\n          docUrl: \"placeHolderFailed\",\n        });\n      }\n    } catch (error: any) {\n      ora.fail(error.message);\n      process.exit(1);\n    }\n  });\n","import _ from \"lodash\";\nimport path from \"path\";\nimport * as pkg from \"glob\";\nconst { glob } = pkg;\nimport { CLIError } from \"./errors\";\nimport {\n  I18nConfig,\n  resolveOverriddenLocale,\n  BucketItem,\n  LocaleDelimiter,\n} from \"@lingo.dev/_spec\";\nimport { bucketTypeSchema } from \"@lingo.dev/_spec\";\nimport Z from \"zod\";\n\n// Track bucket types we've already warned about for misplaced `keyColumn`,\n// so the warning fires only once per CLI invocation (getBuckets is called\n// from multiple command stages).\nconst warnedKeyColumnTypes = new Set<string>();\n\ntype BucketConfig = {\n  type: Z.infer<typeof bucketTypeSchema>;\n  paths: Array<{ pathPattern: string; delimiter?: LocaleDelimiter }>;\n  injectLocale?: string[];\n  lockedKeys?: string[];\n  lockedPatterns?: string[];\n  ignoredKeys?: string[];\n  preservedKeys?: string[];\n  localizableKeys?: string[];\n  keyColumn?: string;\n};\n\nexport function getBuckets(i18nConfig: I18nConfig) {\n  const result = Object.entries(i18nConfig.buckets).map(\n    ([bucketType, bucketEntry]) => {\n      const includeItems = bucketEntry.include.map((item) =>\n        resolveBucketItem(item),\n      );\n      const excludeItems = bucketEntry.exclude?.map((item) =>\n        resolveBucketItem(item),\n      );\n      const config: BucketConfig = {\n        type: bucketType as Z.infer<typeof bucketTypeSchema>,\n        paths: extractPathPatterns(\n          i18nConfig.locale.source,\n          includeItems,\n          excludeItems,\n        ),\n      };\n      if (bucketEntry.injectLocale) {\n        config.injectLocale = bucketEntry.injectLocale;\n      }\n      if (bucketEntry.lockedKeys) {\n        config.lockedKeys = bucketEntry.lockedKeys;\n      }\n      if (bucketEntry.lockedPatterns) {\n        config.lockedPatterns = bucketEntry.lockedPatterns;\n      }\n      if (bucketEntry.ignoredKeys) {\n        config.ignoredKeys = bucketEntry.ignoredKeys;\n      }\n      if (bucketEntry.preservedKeys) {\n        config.preservedKeys = bucketEntry.preservedKeys;\n      }\n      if (bucketEntry.localizableKeys) {\n        config.localizableKeys = bucketEntry.localizableKeys;\n      }\n      if (bucketEntry.keyColumn) {\n        if (bucketType !== \"csv\") {\n          if (!warnedKeyColumnTypes.has(bucketType)) {\n            warnedKeyColumnTypes.add(bucketType);\n            console.warn(\n              `Warning: \"keyColumn\" is only supported on \"csv\" buckets, but was set on \"${bucketType}\". ` +\n                `The setting will be ignored. Remove it from this bucket's config to silence this warning.`,\n            );\n          }\n        } else {\n          config.keyColumn = bucketEntry.keyColumn;\n        }\n      }\n      return config;\n    },\n  );\n\n  return result;\n}\n\nfunction extractPathPatterns(\n  sourceLocale: string,\n  include: BucketItem[],\n  exclude?: BucketItem[],\n) {\n  const includedPatterns = include.flatMap((pattern) =>\n    expandPlaceholderedGlob(\n      pattern.path,\n      resolveOverriddenLocale(sourceLocale, pattern.delimiter),\n    ).map((pathPattern) => ({\n      pathPattern,\n      delimiter: pattern.delimiter,\n    })),\n  );\n  const excludedPatterns = exclude?.flatMap((pattern) =>\n    expandPlaceholderedGlob(\n      pattern.path,\n      resolveOverriddenLocale(sourceLocale, pattern.delimiter),\n    ).map((pathPattern) => ({\n      pathPattern,\n      delimiter: pattern.delimiter,\n    })),\n  );\n  const result = _.differenceBy(\n    includedPatterns,\n    excludedPatterns ?? [],\n    (item) => item.pathPattern,\n  );\n  return result;\n}\n\n// Windows path normalization helper function\nfunction normalizePath(filepath: string): string {\n  const normalized = path.normalize(filepath);\n  // Ensure case consistency on Windows\n  return process.platform === \"win32\" ? normalized.toLowerCase() : normalized;\n}\n\n// Path expansion\nfunction expandPlaceholderedGlob(\n  _pathPattern: string,\n  sourceLocale: string,\n): string[] {\n  const absolutePathPattern = path.resolve(_pathPattern);\n  const pathPattern = normalizePath(\n    path.relative(process.cwd(), absolutePathPattern),\n  );\n  if (pathPattern.startsWith(\"..\")) {\n    throw new CLIError({\n      message: `Invalid path pattern: ${pathPattern}. Path pattern must be within the current working directory.`,\n      docUrl: \"invalidPathPattern\",\n    });\n  }\n\n  // Throw error if pathPattern contains \"**\" – we don't support recursive path patterns\n  if (pathPattern.includes(\"**\")) {\n    throw new CLIError({\n      message: `Invalid path pattern: ${pathPattern}. Recursive path patterns are not supported.`,\n      docUrl: \"invalidPathPattern\",\n    });\n  }\n\n  // Break down path pattern into parts\n  const pathPatternChunks = pathPattern.split(path.sep);\n  // Find the index of the segment containing \"[locale]\"\n  const localeSegmentIndexes = pathPatternChunks.reduce(\n    (indexes, segment, index) => {\n      if (segment.includes(\"[locale]\")) {\n        indexes.push(index);\n      }\n      return indexes;\n    },\n    [] as number[],\n  );\n  // substitute [locale] in pathPattern with sourceLocale\n  const sourcePathPattern = pathPattern.replaceAll(/\\[locale\\]/g, sourceLocale);\n  // Convert to Unix-style for Windows compatibility\n  const unixStylePattern = sourcePathPattern.replace(/\\\\/g, \"/\");\n\n  // get all files that match the sourcePathPattern\n  const sourcePaths = glob\n    .sync(unixStylePattern, {\n      follow: true,\n      withFileTypes: true,\n      windowsPathsNoEscape: true, // Windows path support\n    })\n    .filter((file) => file.isFile() || file.isSymbolicLink())\n    .map((file) => file.fullpath())\n    .map((fullpath) => normalizePath(path.relative(process.cwd(), fullpath)));\n\n  // transform each source file path back to [locale] placeholder paths\n  const placeholderedPaths = sourcePaths.map((sourcePath) => {\n    // Normalize path returned by glob for platform compatibility\n    const normalizedSourcePath = normalizePath(\n      sourcePath.replace(/\\//g, path.sep),\n    );\n    const sourcePathChunks = normalizedSourcePath.split(path.sep);\n    localeSegmentIndexes.forEach((localeSegmentIndex) => {\n      // Find the position of the \"[locale]\" placeholder within the segment\n      const pathPatternChunk = pathPatternChunks[localeSegmentIndex];\n      const sourcePathChunk = sourcePathChunks[localeSegmentIndex];\n      const regexp = new RegExp(\n        \"(\" +\n          pathPatternChunk\n            .replaceAll(\".\", \"\\\\.\")\n            .replaceAll(\"*\", \".*\")\n            .replace(\"[locale]\", `)${sourceLocale}(`) +\n          \")\",\n      );\n      const match = sourcePathChunk.match(regexp);\n      if (match) {\n        const [, prefix, suffix] = match;\n        const placeholderedSegment = prefix + \"[locale]\" + suffix;\n        sourcePathChunks[localeSegmentIndex] = placeholderedSegment;\n      }\n    });\n    const placeholderedPath = sourcePathChunks.join(path.sep);\n    return placeholderedPath;\n  });\n  // return the placeholdered paths\n  return placeholderedPaths;\n}\n\nfunction resolveBucketItem(bucketItem: string | BucketItem): BucketItem {\n  if (typeof bucketItem === \"string\") {\n    return { path: bucketItem, delimiter: null };\n  }\n  return bucketItem;\n}\n","import { Command } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport { getConfig } from \"../../utils/config\";\nimport { CLIError } from \"../../utils/errors\";\nimport { getBuckets } from \"../../utils/buckets\";\nimport { executeKeyCommand } from \"./_shared-key-command\";\n\nexport default new Command()\n  .command(\"locked-keys\")\n  .description(\n    \"Show which key-value pairs in source files match lockedKeys patterns\",\n  )\n  .option(\"--bucket <name>\", \"Only show locked keys for a specific bucket\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (options) => {\n    const ora = Ora();\n    try {\n      const i18nConfig = await getConfig();\n\n      if (!i18nConfig) {\n        throw new CLIError({\n          message:\n            \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n          docUrl: \"i18nNotFound\",\n        });\n      }\n\n      const buckets = getBuckets(i18nConfig);\n\n      await executeKeyCommand(i18nConfig, buckets, options, {\n        filterType: \"lockedKeys\",\n        displayName: \"locked\",\n      });\n    } catch (error: any) {\n      ora.fail(error.message);\n      process.exit(1);\n    }\n  });\n","import { resolveOverriddenLocale, I18nConfig } from \"@lingo.dev/_spec\";\nimport createBucketLoader from \"../../loaders\";\nimport {\n  matchesKeyPattern,\n  formatDisplayValue,\n} from \"../../utils/key-matching\";\n\nexport type KeyFilterType = \"lockedKeys\" | \"ignoredKeys\" | \"preservedKeys\";\n\nexport interface KeyCommandOptions {\n  bucket?: string;\n}\n\nexport interface KeyCommandConfig {\n  filterType: KeyFilterType;\n  displayName: string; // e.g., \"locked\", \"ignored\"\n}\n\nexport async function executeKeyCommand(\n  i18nConfig: I18nConfig,\n  buckets: any[],\n  options: KeyCommandOptions,\n  config: KeyCommandConfig,\n): Promise<void> {\n  let hasAnyKeys = false;\n\n  for (const bucket of buckets) {\n    // Filter by bucket name if specified\n    if (options.bucket && bucket.type !== options.bucket) {\n      continue;\n    }\n\n    // Skip buckets without the specified key patterns\n    const keyPatterns = bucket[config.filterType];\n    if (!keyPatterns || keyPatterns.length === 0) {\n      continue;\n    }\n\n    hasAnyKeys = true;\n\n    console.log(`\\nBucket: ${bucket.type}`);\n    console.log(\n      `${capitalize(config.displayName)} key patterns: ${keyPatterns.join(\", \")}`,\n    );\n\n    for (const bucketConfig of bucket.paths) {\n      const sourceLocale = resolveOverriddenLocale(\n        i18nConfig.locale.source,\n        bucketConfig.delimiter,\n      );\n      const sourcePath = bucketConfig.pathPattern.replace(\n        /\\[locale\\]/g,\n        sourceLocale,\n      );\n\n      try {\n        // Create a loader to read the source file\n        const loader = createBucketLoader(\n          bucket.type,\n          bucketConfig.pathPattern,\n          {\n            defaultLocale: sourceLocale,\n            injectLocale: bucket.injectLocale,\n            keyColumn: bucket.keyColumn,\n          },\n          [], // Don't apply any filtering when reading\n          [],\n          [],\n        );\n        loader.setDefaultLocale(sourceLocale);\n\n        // Read the source file content\n        const data = await loader.pull(sourceLocale);\n\n        if (!data || Object.keys(data).length === 0) {\n          continue;\n        }\n\n        // Filter keys that match the patterns\n        const matchedEntries = Object.entries(data).filter(([key]) =>\n          matchesKeyPattern(key, keyPatterns),\n        );\n\n        if (matchedEntries.length > 0) {\n          console.log(`\\nMatches in ${sourcePath}:`);\n          for (const [key, value] of matchedEntries) {\n            const displayValue = formatDisplayValue(value);\n            console.log(`  - ${key}: ${displayValue}`);\n          }\n          console.log(\n            `Total: ${matchedEntries.length} ${config.displayName} key(s)`,\n          );\n        }\n      } catch (error: any) {\n        console.error(`  Error reading ${sourcePath}: ${error.message}`);\n      }\n    }\n  }\n\n  if (!hasAnyKeys) {\n    if (options.bucket) {\n      console.log(\n        `No ${config.displayName} keys configured for bucket: ${options.bucket}`,\n      );\n    } else {\n      console.log(`No ${config.displayName} keys configured in any bucket.`);\n    }\n  }\n}\n\nfunction capitalize(str: string): string {\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n","import { ILoader, ILoaderDefinition } from \"./_types\";\n\nexport function composeLoaders(\n  ...loaders: ILoader<any, any, any>[]\n): ILoader<any, any> {\n  return {\n    init: async () => {\n      for (const loader of loaders) {\n        await loader.init?.();\n      }\n    },\n    setDefaultLocale(locale: string) {\n      for (const loader of loaders) {\n        loader.setDefaultLocale?.(locale);\n      }\n      return this;\n    },\n    pull: async (locale, input) => {\n      let result: any = input;\n      for (let i = 0; i < loaders.length; i++) {\n        result = await loaders[i].pull(locale, result);\n      }\n      return result;\n    },\n    push: async (locale, data) => {\n      let result: any = data;\n      for (let i = loaders.length - 1; i >= 0; i--) {\n        result = await loaders[i].push(locale, result);\n      }\n      return result;\n    },\n    pullHints: async (originalInput?) => {\n      let result: any = originalInput;\n      for (let i = 0; i < loaders.length; i++) {\n        const subResult = await loaders[i].pullHints?.(result);\n        if (subResult) {\n          result = subResult;\n        }\n      }\n      return result;\n    },\n  };\n}\n\nexport function createLoader<I, O, C>(\n  lDefinition: ILoaderDefinition<I, O, C>,\n): ILoader<I, O, C> {\n  const state = {\n    defaultLocale: undefined as string | undefined,\n    originalInput: undefined as I | undefined | null,\n    // Store pullInput and pullOutput per-locale to avoid race conditions\n    // when multiple locales are processed concurrently\n    pullInputByLocale: new Map<string, I | null>(),\n    pullOutputByLocale: new Map<string, O | null>(),\n    initCtx: undefined as C | undefined,\n  };\n  return {\n    async init() {\n      if (state.initCtx) {\n        return state.initCtx;\n      }\n      state.initCtx = await lDefinition.init?.();\n      return state.initCtx as C;\n    },\n    setDefaultLocale(locale) {\n      if (state.defaultLocale) {\n        throw new Error(\"Default locale already set\");\n      }\n      state.defaultLocale = locale;\n      return this;\n    },\n    async pullHints(originalInput?: I) {\n      return lDefinition.pullHints?.(originalInput || state.originalInput!);\n    },\n    async pull(locale, input) {\n      if (!state.defaultLocale) {\n        throw new Error(\"Default locale not set\");\n      }\n      if (state.originalInput === undefined && locale !== state.defaultLocale) {\n        throw new Error(\"The first pull must be for the default locale\");\n      }\n      if (locale === state.defaultLocale) {\n        state.originalInput = input || null;\n      }\n\n      state.pullInputByLocale.set(locale, input || null);\n      const result = await lDefinition.pull(\n        locale,\n        input,\n        state.initCtx!,\n        state.defaultLocale,\n        state.originalInput!,\n      );\n      state.pullOutputByLocale.set(locale, result);\n\n      return result;\n    },\n    async push(locale, data) {\n      if (!state.defaultLocale) {\n        throw new Error(\"Default locale not set\");\n      }\n      if (state.originalInput === undefined) {\n        throw new Error(\"Cannot push data without pulling first\");\n      }\n\n      // Use locale-specific pullInput/pullOutput if available,\n      // otherwise fall back to the default locale's values for backward compatibility\n      // (some loaders push for locales that were never explicitly pulled)\n      const pullInput =\n        state.pullInputByLocale.get(locale) ??\n        state.pullInputByLocale.get(state.defaultLocale) ??\n        null;\n      const pullOutput =\n        state.pullOutputByLocale.get(locale) ??\n        state.pullOutputByLocale.get(state.defaultLocale) ??\n        null;\n\n      const pushResult = await lDefinition.push(\n        locale,\n        data,\n        state.originalInput,\n        state.defaultLocale,\n        pullInput!,\n        pullOutput!,\n      );\n      return pushResult;\n    },\n  };\n}\n","import { jsonrepair } from \"jsonrepair\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createJsonLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      const jsonString = input || \"{}\";\n      let result: Record<string, any>;\n      try {\n        result = JSON.parse(jsonString);\n      } catch (error) {\n        result = JSON.parse(jsonrepair(jsonString));\n      }\n      return result;\n    },\n    push: async (locale, data) => {\n      const serializedData = JSON.stringify(data, null, 2);\n      return serializedData;\n    },\n  });\n}\n","import JSON5 from \"json5\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createJson5Loader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      const json5String = input || \"{}\";\n      return JSON5.parse(json5String);\n    },\n    push: async (locale, data) => {\n      const serializedData = JSON5.stringify(data, null, 2);\n      return serializedData;\n    },\n  });\n}\n","import { parse, ParseError } from \"jsonc-parser\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\ninterface CommentInfo {\n  hint?: string;\n  [key: string]: any;\n}\n\nfunction extractCommentsFromJsonc(jsoncString: string): Record<string, any> {\n  const lines = jsoncString.split(\"\\n\");\n  const comments: Record<string, any> = {};\n\n  // Parse to validate structure\n  const errors: ParseError[] = [];\n  const result = parse(jsoncString, errors, {\n    allowTrailingComma: true,\n    disallowComments: false,\n    allowEmptyContent: true,\n  });\n\n  if (errors.length > 0) {\n    return {};\n  }\n\n  // Track nesting context with array indices\n  const contextStack: Array<{ key: string; isArray: boolean; arrayIndex?: number }> = [];\n  let arrayObjectCount: Record<number, number> = {}; // Track object count per array depth\n\n  for (let i = 0; i < lines.length; i++) {\n    const line = lines[i];\n    const trimmedLine = line.trim();\n\n    if (!trimmedLine) continue;\n\n    // Handle different comment types\n    const commentData = extractCommentFromLine(line, lines, i);\n    if (commentData.hint) {\n      let keyInfo;\n\n      if (commentData.isInline) {\n        // For inline comments, extract key from the same line\n        const keyMatch = line.match(/^\\s*[\"']?([^\"':,\\s]+)[\"']?\\s*:/);\n        if (keyMatch) {\n          const key = keyMatch[1];\n          const path = contextStack.map((ctx) => ctx.arrayIndex !== undefined ? String(ctx.arrayIndex) : ctx.key).filter(Boolean);\n          keyInfo = { key, path };\n        }\n      } else {\n        // For standalone comments, find the next key\n        keyInfo = findAssociatedKey(lines, commentData.lineIndex, contextStack, arrayObjectCount);\n      }\n\n      if (keyInfo && keyInfo.key) {\n        setCommentAtPath(comments, keyInfo.path, keyInfo.key, commentData.hint);\n      }\n\n      // Skip processed lines for multi-line comments\n      i = commentData.endIndex;\n      continue;\n    }\n\n    // Update context for object/array nesting\n    updateContext(contextStack, line, result, arrayObjectCount);\n  }\n\n  return comments;\n}\n\nfunction extractCommentFromLine(\n  line: string,\n  lines: string[],\n  lineIndex: number,\n): {\n  hint: string | null;\n  lineIndex: number;\n  endIndex: number;\n  isInline: boolean;\n} {\n  const trimmed = line.trim();\n\n  // Single-line comment (standalone)\n  if (trimmed.startsWith(\"//\")) {\n    const hint = trimmed.replace(/^\\/\\/\\s*/, \"\").trim();\n    return { hint, lineIndex, endIndex: lineIndex, isInline: false };\n  }\n\n  // Block comment (standalone or multi-line)\n  if (trimmed.startsWith(\"/*\")) {\n    const blockResult = extractBlockComment(lines, lineIndex);\n    return { ...blockResult, isInline: false };\n  }\n\n  // Inline comments (after JSON content)\n  // Handle single-line inline comments\n  const singleInlineMatch = line.match(/^(.+?)\\s*\\/\\/\\s*(.+)$/);\n  if (singleInlineMatch && singleInlineMatch[1].includes(\":\")) {\n    const hint = singleInlineMatch[2].trim();\n    return { hint, lineIndex, endIndex: lineIndex, isInline: true };\n  }\n\n  // Handle block inline comments\n  const blockInlineMatch = line.match(/^(.+?)\\s*\\/\\*\\s*(.*?)\\s*\\*\\/.*$/);\n  if (blockInlineMatch && blockInlineMatch[1].includes(\":\")) {\n    const hint = blockInlineMatch[2].trim();\n    return { hint, lineIndex, endIndex: lineIndex, isInline: true };\n  }\n\n  return { hint: null, lineIndex, endIndex: lineIndex, isInline: false };\n}\n\nfunction extractBlockComment(\n  lines: string[],\n  startIndex: number,\n): { hint: string | null; lineIndex: number; endIndex: number } {\n  const startLine = lines[startIndex];\n\n  // Single-line block comment\n  const singleMatch = startLine.match(/\\/\\*\\s*(.*?)\\s*\\*\\//);\n  if (singleMatch) {\n    return {\n      hint: singleMatch[1].trim(),\n      lineIndex: startIndex,\n      endIndex: startIndex,\n    };\n  }\n\n  // Multi-line block comment\n  const commentParts: string[] = [];\n  let endIndex = startIndex;\n\n  // Extract content from first line\n  const firstContent = startLine.replace(/.*?\\/\\*\\s*/, \"\").trim();\n  if (firstContent && !firstContent.includes(\"*/\")) {\n    commentParts.push(firstContent);\n  }\n\n  // Process subsequent lines\n  for (let i = startIndex + 1; i < lines.length; i++) {\n    const line = lines[i];\n    endIndex = i;\n\n    if (line.includes(\"*/\")) {\n      const lastContent = line\n        .replace(/\\*\\/.*$/, \"\")\n        .replace(/^\\s*\\*?\\s*/, \"\")\n        .trim();\n      if (lastContent) {\n        commentParts.push(lastContent);\n      }\n      break;\n    } else {\n      const content = line.replace(/^\\s*\\*?\\s*/, \"\").trim();\n      if (content) {\n        commentParts.push(content);\n      }\n    }\n  }\n\n  return {\n    hint: commentParts.join(\" \").trim() || null,\n    lineIndex: startIndex,\n    endIndex,\n  };\n}\n\nfunction findAssociatedKey(\n  lines: string[],\n  commentLineIndex: number,\n  contextStack: Array<{ key: string; isArray: boolean; arrayIndex?: number }>,\n  arrayObjectCount: Record<number, number>,\n): { key: string | null; path: string[] } {\n  // Look for the next key after the comment\n  for (let i = commentLineIndex + 1; i < lines.length; i++) {\n    const line = lines[i].trim();\n\n    if (\n      !line ||\n      line.startsWith(\"//\") ||\n      line.startsWith(\"/*\")\n    ) {\n      continue;\n    }\n\n    // Check if we're about to enter an array object\n    if (line === \"{\" && contextStack.length > 0) {\n      const parent = contextStack[contextStack.length - 1];\n      if (parent.isArray) {\n        // Get the current array index from arrayObjectCount\n        const depth = contextStack.length - 1;\n        const arrayIndex = arrayObjectCount[depth] || 0;\n\n        // Continue looking for the key inside this object\n        for (let j = i + 1; j < lines.length; j++) {\n          const innerLine = lines[j].trim();\n          if (!innerLine || innerLine.startsWith(\"//\") || innerLine.startsWith(\"/*\")) continue;\n\n          const keyMatch = innerLine.match(/^\\s*[\"']?([^\"':,\\s]+)[\"']?\\s*:/);\n          if (keyMatch) {\n            const key = keyMatch[1];\n            const path = contextStack\n              .map((ctx) => ctx.arrayIndex !== undefined ? String(ctx.arrayIndex) : ctx.key)\n              .filter(Boolean);\n            path.push(String(arrayIndex));\n            return { key, path };\n          }\n\n          if (innerLine === \"}\") break;\n        }\n      }\n    }\n\n    if (line === \"{\" || line === \"}\") {\n      continue;\n    }\n\n    // Extract key from line\n    const keyMatch = line.match(/^\\s*[\"']?([^\"':,\\s]+)[\"']?\\s*:/);\n    if (keyMatch) {\n      const key = keyMatch[1];\n      const path = contextStack\n        .map((ctx) => ctx.arrayIndex !== undefined ? String(ctx.arrayIndex) : ctx.key)\n        .filter(Boolean);\n      return { key, path };\n    }\n  }\n\n  return { key: null, path: [] };\n}\n\nfunction updateContext(\n  contextStack: Array<{ key: string; isArray: boolean; arrayIndex?: number }>,\n  line: string,\n  parsedJson: any,\n  arrayObjectCount: Record<number, number>,\n): void {\n  const trimmed = line.trim();\n\n  // Track opening of arrays\n  const arrayMatch = line.match(/^\\s*[\"']?([^\"':,\\s]+)[\"']?\\s*:\\s*\\[/);\n  if (arrayMatch) {\n    const depth = contextStack.length;\n    arrayObjectCount[depth] = 0; // Initialize counter for this array\n    contextStack.push({ key: arrayMatch[1], isArray: true });\n    return;\n  }\n\n  // Track opening of objects\n  const openBraces = (line.match(/\\{/g) || []).length;\n  const closeBraces = (line.match(/\\}/g) || []).length;\n\n  if (openBraces > closeBraces) {\n    // Extract the key that's opening this object\n    const keyMatch = line.match(/^\\s*[\"']?([^\"':,\\s]+)[\"']?\\s*:\\s*\\{/);\n    if (keyMatch) {\n      contextStack.push({ key: keyMatch[1], isArray: false });\n    } else if (trimmed === '{' && contextStack.length > 0) {\n      // This is an object within an array\n      const parent = contextStack[contextStack.length - 1];\n      if (parent.isArray) {\n        const depth = contextStack.length - 1;\n        const arrayIndex = arrayObjectCount[depth] || 0;\n        contextStack.push({ key: '', isArray: false, arrayIndex });\n        arrayObjectCount[depth]++;\n      }\n    }\n  }\n\n  // Track closing of objects and arrays\n  const openBrackets = (line.match(/\\[/g) || []).length;\n  const closeBrackets = (line.match(/\\]/g) || []).length;\n\n  if (closeBraces > openBraces) {\n    for (let i = 0; i < closeBraces - openBraces; i++) {\n      contextStack.pop();\n    }\n  }\n\n  if (closeBrackets > openBrackets) {\n    for (let i = 0; i < closeBrackets - openBrackets; i++) {\n      const popped = contextStack.pop();\n      if (popped?.isArray) {\n        const depth = contextStack.length;\n        delete arrayObjectCount[depth]; // Clean up counter\n      }\n    }\n  }\n}\n\nfunction setCommentAtPath(\n  comments: Record<string, any>,\n  path: string[],\n  key: string,\n  hint: string,\n): void {\n  let current = comments;\n\n  // Navigate to the correct nested location\n  for (const pathKey of path) {\n    if (!current[pathKey]) {\n      current[pathKey] = {};\n    }\n    current = current[pathKey];\n  }\n\n  // Set the hint for the key\n  if (!current[key]) {\n    current[key] = {};\n  }\n\n  if (typeof current[key] === \"object\" && current[key] !== null) {\n    current[key].hint = hint;\n  } else {\n    current[key] = { hint };\n  }\n}\n\nexport default function createJsoncLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      const jsoncString = input || \"{}\";\n      const errors: ParseError[] = [];\n      const result = parse(jsoncString, errors, {\n        allowTrailingComma: true,\n        disallowComments: false,\n        allowEmptyContent: true,\n      });\n\n      if (errors.length > 0) {\n        throw new Error(`Failed to parse JSONC: ${errors[0].error}`);\n      }\n\n      return result || {};\n    },\n    push: async (locale, data) => {\n      // JSONC parser's stringify preserves formatting but doesn't add comments\n      // We'll use standard JSON.stringify with pretty formatting for output\n      const serializedData = JSON.stringify(data, null, 2);\n      return serializedData;\n    },\n    pullHints: async (input) => {\n      if (!input || typeof input !== \"string\") {\n        return {};\n      }\n\n      try {\n        return extractCommentsFromJsonc(input);\n      } catch (error) {\n        console.warn(\"Failed to extract comments from JSONC:\", error);\n        return {};\n      }\n    },\n  });\n}\n","import { flatten, unflatten } from \"flat\";\nimport { ILoader } from \"./_types\";\nimport { composeLoaders, createLoader } from \"./_utils\";\nimport _ from \"lodash\";\n\nexport const OBJECT_NUMERIC_KEY_PREFIX = \"__lingodotdev__obj__\";\n\n/**\n * Options for configuring the flat loader behavior\n */\nexport interface FlatLoaderOptions {\n  /**\n   * Optional predicate to determine if an object should be preserved (not flattened)\n   * Use this to prevent flattening of special objects like ICU plurals\n   */\n  shouldPreserveObject?: (value: any) => boolean;\n}\n\n/**\n * Creates a flat loader that flattens nested objects into dot-notation keys\n *\n * @param options - Configuration options for the loader\n * @param options.shouldPreserveObject - Predicate to identify objects that should not be flattened\n */\nexport default function createFlatLoader(options?: FlatLoaderOptions) {\n  const composedLoader = composeLoaders(\n    createDenormalizeLoader(options),\n    createNormalizeLoader(),\n  );\n\n  return {\n    ...composedLoader,\n    pullHints: async (input: Record<string, any>) => {\n      if (!input || typeof input !== \"object\") {\n        return {};\n      }\n      return flattenHints(input);\n    },\n  };\n}\n\ntype DenormalizeResult = {\n  denormalized: Record<string, string>;\n  keysMap: Record<string, string>;\n};\n\nfunction createDenormalizeLoader(\n  options?: FlatLoaderOptions,\n): ILoader<Record<string, any>, DenormalizeResult> {\n  return createLoader({\n    pull: async (locale, input) => {\n      const inputDenormalized = denormalizeObjectKeys(input || {});\n\n      // First pass: extract preserved objects before flattening (if predicate provided)\n      const preservedObjects: Record<string, any> = {};\n      const nonPreservedInput: Record<string, any> = {};\n\n      for (const [key, value] of Object.entries(inputDenormalized)) {\n        if (options?.shouldPreserveObject?.(value)) {\n          preservedObjects[key] = value;\n        } else {\n          nonPreservedInput[key] = value;\n        }\n      }\n\n      // Flatten only non-preserved objects\n      const flattened: Record<string, string> = flatten(nonPreservedInput, {\n        delimiter: \"/\",\n        transformKey(key) {\n          return encodeURIComponent(String(key));\n        },\n      });\n\n      // Merge preserved objects back (they stay as objects, not flattened)\n      // BUT: encode their keys too!\n      const denormalized: Record<string, any> = { ...flattened };\n\n      for (const [key, value] of Object.entries(preservedObjects)) {\n        const encodedKey = encodeURIComponent(String(key));\n        denormalized[encodedKey] = value;\n      }\n\n      const keysMap = buildDenormalizedKeysMap(denormalized);\n      return { denormalized, keysMap };\n    },\n    push: async (locale, { denormalized }) => {\n      const normalized = normalizeObjectKeys(denormalized);\n      return normalized;\n    },\n  });\n}\n\nfunction createNormalizeLoader(): ILoader<\n  DenormalizeResult,\n  Record<string, string>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      const normalized = normalizeObjectKeys(input.denormalized);\n      return normalized;\n    },\n    push: async (locale, data, originalInput) => {\n      const keysMap = originalInput?.keysMap ?? {};\n      const input = mapDenormalizedKeys(data, keysMap);\n      const denormalized: Record<string, any> = unflatten(input, {\n        delimiter: \"/\",\n        transformKey(key) {\n          return decodeURIComponent(String(key));\n        },\n      });\n      return { denormalized, keysMap: keysMap || {} };\n    },\n  });\n}\n\nexport function buildDenormalizedKeysMap(obj: Record<string, string>) {\n  if (!obj) return {};\n\n  return Object.keys(obj).reduce(\n    (acc, key) => {\n      if (key) {\n        const normalizedKey = `${key}`.replace(OBJECT_NUMERIC_KEY_PREFIX, \"\");\n        acc[normalizedKey] = key;\n      }\n      return acc;\n    },\n    {} as Record<string, string>,\n  );\n}\n\nexport function mapDenormalizedKeys(\n  obj: Record<string, any>,\n  denormalizedKeysMap: Record<string, string>,\n) {\n  return Object.keys(obj).reduce(\n    (acc, key) => {\n      const denormalizedKey = denormalizedKeysMap[key] ?? key;\n      acc[denormalizedKey] = obj[key];\n      return acc;\n    },\n    {} as Record<string, string>,\n  );\n}\n\nexport function denormalizeObjectKeys(\n  obj: Record<string, any>,\n): Record<string, any> {\n  if (_.isObject(obj) && !_.isArray(obj)) {\n    return _.transform(\n      obj,\n      (result, value, key) => {\n        const newKey = !isNaN(Number(key))\n          ? `${OBJECT_NUMERIC_KEY_PREFIX}${key}`\n          : key;\n        result[newKey] =\n          _.isObject(value) && !_.isDate(value)\n            ? denormalizeObjectKeys(value)\n            : value;\n      },\n      {} as Record<string, any>,\n    );\n  } else {\n    return obj;\n  }\n}\n\nexport function normalizeObjectKeys(\n  obj: Record<string, any>,\n): Record<string, any> {\n  if (_.isObject(obj) && !_.isArray(obj)) {\n    return _.transform(\n      obj,\n      (result, value, key) => {\n        const newKey = `${key}`.replace(OBJECT_NUMERIC_KEY_PREFIX, \"\");\n        result[newKey] =\n          _.isObject(value) && !_.isDate(value)\n            ? normalizeObjectKeys(value)\n            : value;\n      },\n      {} as Record<string, any>,\n    );\n  } else {\n    return obj;\n  }\n}\n\nfunction flattenHints(\n  obj: Record<string, any>,\n  parentHints: string[] = [],\n  parentPath: string = \"\",\n): Record<string, string[]> {\n  const result: Record<string, string[]> = {};\n\n  for (const [key, _value] of Object.entries(obj)) {\n    if (_.isObject(_value) && !_.isArray(_value)) {\n      const value = _value as Record<string, any>;\n      const currentHints = [...parentHints];\n      const currentPath = parentPath ? `${parentPath}/${key}` : key;\n\n      // Add this level's hint if it exists\n      if (value.hint && typeof value.hint === \"string\") {\n        currentHints.push(value.hint);\n      }\n\n      // Process nested objects (excluding the hint property)\n      const nestedObj = _.omit(value, \"hint\");\n\n      // If this is a leaf node (no nested objects), add to result\n      if (Object.keys(nestedObj).length === 0) {\n        if (currentHints.length > 0) {\n          result[currentPath] = currentHints;\n        }\n      } else {\n        // Recursively process nested objects\n        const nestedComments = flattenHints(\n          nestedObj,\n          currentHints,\n          currentPath,\n        );\n        Object.assign(result, nestedComments);\n      }\n    }\n  }\n\n  return result;\n}\n","import fs from \"fs/promises\";\nimport path from \"path\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createTextFileLoader(\n  pathPattern: string,\n): ILoader<void, string> {\n  return createLoader({\n    async pull(locale) {\n      const result = await readFileForLocale(pathPattern, locale);\n      const trimmedResult = result.trim();\n      return trimmedResult;\n    },\n    async push(locale, data, _, originalLocale) {\n      const draftPath = pathPattern.replaceAll(\"[locale]\", locale);\n      const finalPath = path.resolve(draftPath);\n\n      // Create parent directories if needed\n      const dirPath = path.dirname(finalPath);\n      await fs.mkdir(dirPath, { recursive: true });\n\n      const trimmedPayload = data.trim();\n\n      // Add trailing new line if needed\n      const trailingNewLine = await getTrailingNewLine(\n        pathPattern,\n        locale,\n        originalLocale,\n      );\n      let finalPayload = trimmedPayload + trailingNewLine;\n\n      await fs.writeFile(finalPath, finalPayload, {\n        encoding: \"utf-8\",\n        flag: \"w\",\n      });\n    },\n  });\n}\n\nasync function readFileForLocale(pathPattern: string, locale: string) {\n  const draftPath = pathPattern.replaceAll(\"[locale]\", locale);\n  const finalPath = path.resolve(draftPath);\n  const exists = await fs\n    .access(finalPath)\n    .then(() => true)\n    .catch(() => false);\n  if (!exists) {\n    return \"\";\n  }\n  return fs.readFile(finalPath, \"utf-8\");\n}\n\nasync function getTrailingNewLine(\n  pathPattern: string,\n  locale: string,\n  originalLocale: string,\n) {\n  let templateData = await readFileForLocale(pathPattern, locale);\n  if (!templateData) {\n    templateData = await readFileForLocale(pathPattern, originalLocale);\n  }\n\n  if (templateData?.match(/[\\r\\n]$/)) {\n    const ending = templateData?.includes(\"\\r\\n\")\n      ? \"\\r\\n\"\n      : templateData?.includes(\"\\r\")\n        ? \"\\r\"\n        : \"\\n\";\n    return ending;\n  }\n  return \"\";\n}\n","import YAML, { ToStringOptions } from \"yaml\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\ninterface QuotingMetadata {\n  keys: Map<string, string>;\n  values: Map<string, string>;\n}\n\nexport default function createYamlLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      return YAML.parse(input) || {};\n    },\n    async push(locale, payload, originalInput) {\n      // If no original input, use simple stringify\n      if (!originalInput || !originalInput.trim()) {\n        return YAML.stringify(payload, {\n          lineWidth: -1,\n          defaultKeyType: \"PLAIN\",\n          defaultStringType: \"PLAIN\",\n        });\n      }\n\n      try {\n        // Parse source and extract quoting metadata\n        const sourceDoc = YAML.parseDocument(originalInput);\n\n        // Create output document - let the library handle smart quoting\n        const outputDoc = YAML.parseDocument(\n          YAML.stringify(payload, {\n            lineWidth: -1,\n            defaultKeyType: \"PLAIN\",\n          }),\n        );\n\n        // Detect if this is yaml-root-key format by comparing structures\n        const isRootKeyFormat = detectRootKeyFormat(sourceDoc, outputDoc);\n\n        // Extract and apply metadata with root-key awareness\n        const metadata = extractQuotingMetadata(sourceDoc, isRootKeyFormat);\n        applyQuotingMetadata(outputDoc, metadata, isRootKeyFormat);\n\n        return outputDoc.toString({ lineWidth: -1 });\n      } catch (error) {\n        console.warn(\"Failed to preserve YAML formatting:\", error);\n        // Fallback to current behavior\n        return YAML.stringify(payload, {\n          lineWidth: -1,\n          defaultKeyType: getKeyType(originalInput),\n          defaultStringType: getStringType(originalInput),\n        });\n      }\n    },\n  });\n}\n\n// Detect if this is yaml-root-key format by comparing source and output structures\nfunction detectRootKeyFormat(\n  sourceDoc: YAML.Document,\n  outputDoc: YAML.Document,\n): boolean {\n  const sourceRoot = sourceDoc.contents;\n  const outputRoot = outputDoc.contents;\n\n  // Both must be maps with single root key\n  if (!isYAMLMap(sourceRoot) || !isYAMLMap(outputRoot)) {\n    return false;\n  }\n\n  const sourceMap = sourceRoot as any;\n  const outputMap = outputRoot as any;\n\n  if (\n    !sourceMap.items ||\n    sourceMap.items.length !== 1 ||\n    !outputMap.items ||\n    outputMap.items.length !== 1\n  ) {\n    return false;\n  }\n\n  const sourceRootKey = getKeyValue(sourceMap.items[0].key);\n  const outputRootKey = getKeyValue(outputMap.items[0].key);\n\n  // If both have single root keys that are DIFFERENT strings, it's yaml-root-key format\n  // (e.g., source has \"en:\", output has \"es:\")\n  if (\n    sourceRootKey !== outputRootKey &&\n    typeof sourceRootKey === \"string\" &&\n    typeof outputRootKey === \"string\"\n  ) {\n    return true;\n  }\n\n  return false;\n}\n\n// Extract quoting metadata from source document\nfunction extractQuotingMetadata(\n  doc: YAML.Document,\n  skipRootKey: boolean,\n): QuotingMetadata {\n  const metadata: QuotingMetadata = {\n    keys: new Map<string, string>(),\n    values: new Map<string, string>(),\n  };\n  const root = doc.contents;\n  if (!root) return metadata;\n\n  let startNode: any = root;\n\n  // If yaml-root-key format, skip the locale root key\n  if (skipRootKey && isYAMLMap(root)) {\n    const rootMap = root as any;\n    if (rootMap.items && rootMap.items.length === 1) {\n      startNode = rootMap.items[0].value;\n    }\n  }\n\n  walkAndExtract(startNode, [], metadata);\n  return metadata;\n}\n\n// Walk AST and extract quoting information\nfunction walkAndExtract(\n  node: any,\n  path: string[],\n  metadata: QuotingMetadata,\n): void {\n  if (isScalar(node)) {\n    // Store non-PLAIN value quoting types\n    if (node.type && node.type !== \"PLAIN\") {\n      metadata.values.set(path.join(\".\"), node.type);\n    }\n  } else if (isYAMLMap(node)) {\n    if (node.items && Array.isArray(node.items)) {\n      for (const pair of node.items) {\n        if (pair && pair.key) {\n          const key = getKeyValue(pair.key);\n          if (key !== null && key !== undefined) {\n            const keyPath = [...path, String(key)].join(\".\");\n\n            // Store non-PLAIN key quoting types\n            if (pair.key.type && pair.key.type !== \"PLAIN\") {\n              metadata.keys.set(keyPath, pair.key.type);\n            }\n\n            // Continue walking values\n            if (pair.value) {\n              walkAndExtract(pair.value, [...path, String(key)], metadata);\n            }\n          }\n        }\n      }\n    }\n  } else if (isYAMLSeq(node)) {\n    if (node.items && Array.isArray(node.items)) {\n      for (let i = 0; i < node.items.length; i++) {\n        if (node.items[i]) {\n          walkAndExtract(node.items[i], [...path, String(i)], metadata);\n        }\n      }\n    }\n  }\n}\n\n// Apply quoting metadata to output document\nfunction applyQuotingMetadata(\n  doc: YAML.Document,\n  metadata: QuotingMetadata,\n  skipRootKey: boolean,\n): void {\n  const root = doc.contents;\n  if (!root) return;\n\n  let startNode: any = root;\n\n  // If yaml-root-key format, skip the locale root key\n  if (skipRootKey && isYAMLMap(root)) {\n    const rootMap = root as any;\n    if (rootMap.items && rootMap.items.length === 1) {\n      startNode = rootMap.items[0].value;\n    }\n  }\n\n  walkAndApply(startNode, [], metadata);\n}\n\n// Walk AST and apply quoting information\nfunction walkAndApply(\n  node: any,\n  path: string[],\n  metadata: QuotingMetadata,\n): void {\n  if (isScalar(node)) {\n    // Apply value quoting\n    const pathKey = path.join(\".\");\n    const quoteType = metadata.values.get(pathKey);\n    if (quoteType) {\n      node.type = quoteType;\n    }\n  } else if (isYAMLMap(node)) {\n    if (node.items && Array.isArray(node.items)) {\n      for (const pair of node.items) {\n        if (pair && pair.key) {\n          const key = getKeyValue(pair.key);\n          if (key !== null && key !== undefined) {\n            const keyPath = [...path, String(key)].join(\".\");\n\n            // Apply key quoting\n            const keyQuoteType = metadata.keys.get(keyPath);\n            if (keyQuoteType) {\n              pair.key.type = keyQuoteType;\n            }\n\n            // Continue walking values\n            if (pair.value) {\n              walkAndApply(pair.value, [...path, String(key)], metadata);\n            }\n          }\n        }\n      }\n    }\n  } else if (isYAMLSeq(node)) {\n    if (node.items && Array.isArray(node.items)) {\n      for (let i = 0; i < node.items.length; i++) {\n        if (node.items[i]) {\n          walkAndApply(node.items[i], [...path, String(i)], metadata);\n        }\n      }\n    }\n  }\n}\n\n// Type guards using YAML library's built-in functions\nfunction isScalar(node: any): boolean {\n  return YAML.isScalar(node);\n}\n\nfunction isYAMLMap(node: any): boolean {\n  return YAML.isMap(node);\n}\n\nfunction isYAMLSeq(node: any): boolean {\n  return YAML.isSeq(node);\n}\n\nfunction getKeyValue(key: any): string | number | null {\n  if (key === null || key === undefined) {\n    return null;\n  }\n  // Scalar key\n  if (typeof key === \"object\" && \"value\" in key) {\n    return key.value;\n  }\n  // Already a primitive\n  if (typeof key === \"string\" || typeof key === \"number\") {\n    return key;\n  }\n  return null;\n}\n\n// check if the yaml keys are using double quotes or single quotes\nfunction getKeyType(\n  yamlString: string | null,\n): ToStringOptions[\"defaultKeyType\"] {\n  if (yamlString) {\n    const lines = yamlString.split(\"\\n\");\n    const hasDoubleQuotes = lines.find((line) => {\n      return line.trim().startsWith('\"') && line.trim().match('\":');\n    });\n    if (hasDoubleQuotes) {\n      return \"QUOTE_DOUBLE\";\n    }\n  }\n  return \"PLAIN\";\n}\n\n// check if the yaml string values are using double quotes or single quotes\nfunction getStringType(\n  yamlString: string | null,\n): ToStringOptions[\"defaultStringType\"] {\n  if (yamlString) {\n    const lines = yamlString.split(\"\\n\");\n\n    // Check if the file uses literal block scalars (|, |-, |+)\n    const hasLiteralBlockScalar = lines.find((line) => {\n      const trimmedLine = line.trim();\n      return trimmedLine.match(/:\\s*\\|[-+]?\\s*$/);\n    });\n\n    // If literal block scalars are used, always use PLAIN to preserve them\n    if (hasLiteralBlockScalar) {\n      return \"PLAIN\";\n    }\n\n    // Otherwise, check for double quotes on string values\n    const hasDoubleQuotes = lines.find((line) => {\n      const trimmedLine = line.trim();\n      return (\n        (trimmedLine.startsWith('\"') || trimmedLine.match(/:\\s*\"/)) &&\n        (trimmedLine.endsWith('\"') || trimmedLine.endsWith('\",'))\n      );\n    });\n    if (hasDoubleQuotes) {\n      return \"QUOTE_DOUBLE\";\n    }\n  }\n  return \"PLAIN\";\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createRootKeyLoader(\n  replaceAll = false,\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    async pull(locale, input) {\n      const result = input[locale];\n      return result;\n    },\n    async push(locale, data, originalInput) {\n      const result = {\n        ...(replaceAll ? {} : originalInput),\n        [locale]: data,\n      };\n      return result;\n    },\n  });\n}\n","import _ from \"lodash\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createFlutterLoader(): ILoader<\n  Record<string, any>,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      // skip all metadata (keys starting with @)\n      const result = _.pickBy(input, (value, key) => !_isMetadataKey(key));\n      return result;\n    },\n    async push(locale, data, originalInput) {\n      // find all metadata keys in originalInput\n      const metadata = _.pickBy(originalInput, (value, key) =>\n        _isMetadataKey(key),\n      );\n      const result = _.merge({}, metadata, { \"@@locale\": locale }, data);\n      return result;\n    },\n  });\n}\n\nfunction _isMetadataKey(key: string) {\n  return key.startsWith(\"@\");\n}\n","import { parseStringPromise, Builder } from \"xml2js\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\n// Advanced Installer AIL (localization dictionary) file format\n// Structure:\n// <DICTIONARY type=\"multilanguage\">\n//   <ENTRY id=\"Control.Text.WelcomeDlg#Title\">\n//     <STRING lang=\"en\" value=\"Welcome\"/>\n//     <STRING lang=\"de\" value=\"Willkommen\"/>\n//   </ENTRY>\n// </DICTIONARY>\n\ninterface StringNode {\n  $: {\n    lang: string;\n    value: string;\n  };\n}\n\ninterface EntryNode {\n  $?: {\n    id: string;\n  };\n  STRING?: StringNode[];\n}\n\ninterface DictionaryNode {\n  $?: Record<string, string>; // Preserve attributes like type=\"multilanguage\", ignore=\"...\"\n  ENTRY?: EntryNode[];\n}\n\ninterface ParsedAIL {\n  DICTIONARY: DictionaryNode;\n}\n\nexport default function createAilLoader(): ILoader<\n  string,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const result: Record<string, string> = {};\n\n      if (!input || !input.trim()) {\n        return result;\n      }\n\n      try {\n        // Parse AIL XML\n        const parsed = (await parseStringPromise(input, {\n          explicitArray: true, // Always use arrays for consistency\n          mergeAttrs: false, // Keep attributes separate in $\n          trim: true,\n          explicitRoot: true,\n        })) as ParsedAIL;\n\n        const dictionary = parsed.DICTIONARY;\n        if (!dictionary) {\n          return result;\n        }\n\n        const entries = dictionary.ENTRY || [];\n\n        // Extract entries for source locale\n        for (const entry of entries) {\n          const id = entry.$?.id;\n          if (!id) {\n            // Skip entries without id\n            continue;\n          }\n\n          const strings = entry.STRING || [];\n\n          // Find STRING element matching source locale\n          const sourceString = strings.find(\n            (s) => s.$?.lang === locale\n          );\n\n          if (sourceString?.$.value) {\n            result[id] = sourceString.$.value;\n          }\n        }\n\n        return result;\n      } catch (error) {\n        console.error(\"Failed to parse AIL file:\", error);\n        return result;\n      }\n    },\n\n    async push(locale, data, originalInput) {\n      if (!originalInput || !originalInput.trim()) {\n        // Create new AIL file from scratch\n        const dictionary: DictionaryNode = {\n          $: { type: \"multilanguage\" },\n          ENTRY: Object.entries(data).map(([id, value]) => ({\n            $: { id },\n            STRING: [\n              {\n                $: { lang: locale, value },\n              },\n            ],\n          })),\n        };\n\n        const builder = new Builder({\n          xmldec: { version: \"1.0\", encoding: \"UTF-8\" },\n          headless: false,\n        });\n\n        return builder.buildObject({ DICTIONARY: dictionary });\n      }\n\n      try {\n        // Parse original AIL XML\n        const parsed = (await parseStringPromise(originalInput, {\n          explicitArray: true,\n          mergeAttrs: false,\n          trim: true,\n          explicitRoot: true,\n        })) as ParsedAIL;\n\n        const dictionary = parsed.DICTIONARY;\n        if (!dictionary) {\n          throw new Error(\"No DICTIONARY root element found\");\n        }\n\n        const entries = dictionary.ENTRY || [];\n\n        // Update or add translations for target locale\n        for (const [id, value] of Object.entries(data)) {\n          // Find existing entry by id\n          let entry = entries.find((e) => e.$?.id === id);\n\n          if (!entry) {\n            // Create new entry if doesn't exist\n            entry = {\n              $: { id },\n              STRING: [],\n            };\n            entries.push(entry);\n          }\n\n          // Ensure STRING array exists\n          if (!entry.STRING) {\n            entry.STRING = [];\n          }\n\n          // Find or create STRING element for target locale\n          let targetString = entry.STRING.find(\n            (s) => s.$?.lang === locale\n          );\n\n          if (targetString) {\n            // Update existing translation\n            targetString.$.value = value;\n          } else {\n            // Add new translation\n            entry.STRING.push({\n              $: { lang: locale, value },\n            });\n          }\n        }\n\n        // Update ENTRY array in dictionary\n        dictionary.ENTRY = entries;\n\n        // Build XML output\n        const builder = new Builder({\n          xmldec: { version: \"1.0\", encoding: \"UTF-8\" },\n          headless: false,\n        });\n\n        return builder.buildObject({ DICTIONARY: dictionary });\n      } catch (error) {\n        console.error(\"Failed to build AIL file:\", error);\n        throw error;\n      }\n    },\n  });\n}\n","import { createRequire } from \"node:module\";\nimport { parseStringPromise, type XmlDeclarationAttributes } from \"xml2js\";\nimport { ILoader } from \"./_types\";\nimport { CLIError } from \"../utils/errors\";\nimport { createLoader } from \"./_utils\";\n\ninterface SaxParser {\n  onopentag: (node: {\n    name: string;\n    attributes: Record<string, string>;\n  }) => void;\n  onclosetag: (name: string) => void;\n  ontext: (text: string) => void;\n  oncdata: (cdata: string) => void;\n  write(data: string): SaxParser;\n  close(): SaxParser;\n}\n\ninterface SaxModule {\n  parser(\n    strict: boolean,\n    options?: { trim?: boolean; normalize?: boolean; lowercase?: boolean },\n  ): SaxParser;\n}\n\nconst require = createRequire(import.meta.url);\nconst sax: SaxModule = require(\"sax\") as SaxModule;\n\nconst defaultAndroidResourcesXml = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>`;\n\ntype AndroidResourceType =\n  | \"string\"\n  | \"string-array\"\n  | \"plurals\"\n  | \"bool\"\n  | \"integer\";\n\ntype PrimitiveValue = boolean | number | string;\n\ntype ContentSegment =\n  | { kind: \"text\"; value: string }\n  | { kind: \"cdata\"; value: string };\n\ninterface TextualMeta {\n  segments: ContentSegment[];\n  hasCdata: boolean;\n}\n\ninterface ArrayItemMeta extends TextualMeta {\n  quantity?: string;\n}\n\ninterface StringResourceNode {\n  type: \"string\";\n  name: string;\n  translatable: boolean;\n  node: any;\n  meta: TextualMeta;\n}\n\ninterface StringArrayItemNode {\n  node: any;\n  meta: TextualMeta;\n}\n\ninterface StringArrayResourceNode {\n  type: \"string-array\";\n  name: string;\n  translatable: boolean;\n  node: any;\n  items: StringArrayItemNode[];\n}\n\ninterface PluralsItemNode {\n  node: any;\n  quantity: string;\n  meta: TextualMeta;\n}\n\ninterface PluralsResourceNode {\n  type: \"plurals\";\n  name: string;\n  translatable: boolean;\n  node: any;\n  items: PluralsItemNode[];\n}\n\ninterface BoolResourceNode {\n  type: \"bool\";\n  name: string;\n  translatable: boolean;\n  node: any;\n  meta: TextualMeta;\n}\n\ninterface IntegerResourceNode {\n  type: \"integer\";\n  name: string;\n  translatable: boolean;\n  node: any;\n  meta: TextualMeta;\n}\n\ntype AndroidResourceNode =\n  | StringResourceNode\n  | StringArrayResourceNode\n  | PluralsResourceNode\n  | BoolResourceNode\n  | IntegerResourceNode;\n\ninterface AndroidDocument {\n  resources: any;\n  resourceNodes: AndroidResourceNode[];\n}\n\ninterface XmlDeclarationOptions {\n  xmldec?: XmlDeclarationAttributes;\n  headless: boolean;\n}\n\nexport default function createAndroidLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      try {\n        if (!input) {\n          return {};\n        }\n\n        const document = await parseAndroidDocument(input);\n        return buildPullResult(document);\n      } catch (error) {\n        console.error(\"Error parsing Android resource file:\", error);\n        throw new CLIError({\n          message: \"Failed to parse Android resource file\",\n          docUrl: \"androidResourceError\",\n        });\n      }\n    },\n    async push(\n      locale,\n      payload,\n      originalInput,\n      originalLocale,\n      pullInput,\n      pullOutput,\n    ) {\n      try {\n        const selectedBase = selectBaseXml(\n          locale,\n          originalLocale,\n          pullInput,\n          originalInput,\n        );\n\n        const existingDocument = await parseAndroidDocument(selectedBase);\n        const sourceDocument = await parseAndroidDocument(originalInput);\n        const translatedDocument = buildTranslatedDocument(\n          payload,\n          existingDocument,\n          sourceDocument,\n        );\n\n        const referenceXml =\n          selectedBase || originalInput || defaultAndroidResourcesXml;\n        const declaration = resolveXmlDeclaration(referenceXml);\n\n        return buildAndroidXml(translatedDocument, declaration);\n      } catch (error) {\n        console.error(\"Error generating Android resource file:\", error);\n        throw new CLIError({\n          message: \"Failed to generate Android resource file\",\n          docUrl: \"androidResourceError\",\n        });\n      }\n    },\n  });\n}\n\nfunction resolveXmlDeclaration(xml: string | null): XmlDeclarationOptions {\n  if (!xml) {\n    const xmldec: XmlDeclarationAttributes = {\n      version: \"1.0\",\n      encoding: \"utf-8\",\n    };\n    return {\n      xmldec,\n      headless: false,\n    };\n  }\n\n  const match = xml.match(\n    /<\\?xml\\s+version=\"([^\"]+)\"(?:\\s+encoding=\"([^\"]+)\")?\\s*\\?>/,\n  );\n  if (match) {\n    const version = match[1] && match[1].trim().length > 0 ? match[1] : \"1.0\";\n    const encoding =\n      match[2] && match[2].trim().length > 0 ? match[2] : undefined;\n    const xmldec: XmlDeclarationAttributes = encoding\n      ? { version, encoding }\n      : { version };\n    return {\n      xmldec,\n      headless: false,\n    };\n  }\n\n  return { headless: true };\n}\n\nasync function parseAndroidDocument(\n  input?: string | null,\n): Promise<AndroidDocument> {\n  const xmlToParse =\n    input && input.trim().length > 0 ? input : defaultAndroidResourcesXml;\n\n  const parsed = await parseStringPromise(xmlToParse, {\n    explicitArray: true,\n    explicitChildren: true,\n    preserveChildrenOrder: true,\n    charsAsChildren: true,\n    includeWhiteChars: true,\n    mergeAttrs: false,\n    normalize: false,\n    normalizeTags: false,\n    trim: false,\n    attrkey: \"$\",\n    charkey: \"_\",\n    childkey: \"$$\",\n  });\n\n  if (!parsed || !parsed.resources) {\n    return {\n      resources: { $$: [] },\n      resourceNodes: [],\n    };\n  }\n\n  const resourcesNode = parsed.resources;\n  resourcesNode[\"#name\"] = resourcesNode[\"#name\"] ?? \"resources\";\n  resourcesNode.$$ = resourcesNode.$$ ?? [];\n\n  const metadata = extractResourceMetadata(xmlToParse);\n\n  const resourceNodes: AndroidResourceNode[] = [];\n  let metaIndex = 0;\n\n  for (const child of resourcesNode.$$ as any[]) {\n    const elementName = child?.[\"#name\"];\n    if (!isResourceElementName(elementName)) {\n      continue;\n    }\n\n    const meta = metadata[metaIndex++];\n    if (!meta || meta.type !== elementName) {\n      continue;\n    }\n\n    const name = child?.$?.name ?? meta.name;\n    if (!name) {\n      continue;\n    }\n\n    const translatable =\n      (child?.$?.translatable ?? \"\").toLowerCase() !== \"false\";\n\n    switch (meta.type) {\n      case \"string\": {\n        resourceNodes.push({\n          type: \"string\",\n          name,\n          translatable,\n          node: child,\n          meta: cloneTextMeta(meta.meta),\n        });\n        break;\n      }\n      case \"string-array\": {\n        const itemNodes = (child?.item ?? []) as any[];\n        const items: StringArrayItemNode[] = [];\n        const templateItems = meta.items;\n\n        for (\n          let i = 0;\n          i < Math.max(itemNodes.length, templateItems.length);\n          i++\n        ) {\n          const nodeItem = itemNodes[i];\n          const templateItem =\n            templateItems[i] ?? templateItems[templateItems.length - 1];\n          if (!nodeItem) {\n            continue;\n          }\n          items.push({\n            node: nodeItem,\n            meta: cloneTextMeta(templateItem.meta),\n          });\n        }\n\n        resourceNodes.push({\n          type: \"string-array\",\n          name,\n          translatable,\n          node: child,\n          items,\n        });\n        break;\n      }\n      case \"plurals\": {\n        const itemNodes = (child?.item ?? []) as any[];\n        const templateItems = meta.items;\n        const items: PluralsItemNode[] = [];\n\n        for (const templateItem of templateItems) {\n          const quantity = templateItem.quantity;\n          if (!quantity) {\n            continue;\n          }\n          const nodeItem = itemNodes.find(\n            (item: any) => item?.$?.quantity === quantity,\n          );\n          if (!nodeItem) {\n            continue;\n          }\n          items.push({\n            node: nodeItem,\n            quantity,\n            meta: cloneTextMeta(templateItem.meta),\n          });\n        }\n\n        resourceNodes.push({\n          type: \"plurals\",\n          name,\n          translatable,\n          node: child,\n          items,\n        });\n        break;\n      }\n      case \"bool\": {\n        resourceNodes.push({\n          type: \"bool\",\n          name,\n          translatable,\n          node: child,\n          meta: cloneTextMeta(meta.meta),\n        });\n        break;\n      }\n      case \"integer\": {\n        resourceNodes.push({\n          type: \"integer\",\n          name,\n          translatable,\n          node: child,\n          meta: cloneTextMeta(meta.meta),\n        });\n        break;\n      }\n    }\n  }\n\n  return { resources: resourcesNode, resourceNodes };\n}\n\nfunction buildPullResult(document: AndroidDocument): Record<string, any> {\n  const result: Record<string, any> = {};\n\n  for (const resource of document.resourceNodes) {\n    if (!isTranslatable(resource)) {\n      continue;\n    }\n\n    switch (resource.type) {\n      case \"string\": {\n        result[resource.name] = decodeAndroidText(\n          segmentsToString(resource.meta.segments),\n        );\n        break;\n      }\n      case \"string-array\": {\n        result[resource.name] = resource.items.map((item) =>\n          decodeAndroidText(segmentsToString(item.meta.segments)),\n        );\n        break;\n      }\n      case \"plurals\": {\n        const pluralMap: Record<string, string> = {};\n        for (const item of resource.items) {\n          pluralMap[item.quantity] = decodeAndroidText(\n            segmentsToString(item.meta.segments),\n          );\n        }\n        result[resource.name] = pluralMap;\n        break;\n      }\n      case \"bool\": {\n        const value = segmentsToString(resource.meta.segments).trim();\n        result[resource.name] = value === \"true\";\n        break;\n      }\n      case \"integer\": {\n        const value = parseInt(\n          segmentsToString(resource.meta.segments).trim(),\n          10,\n        );\n        result[resource.name] = Number.isNaN(value) ? 0 : value;\n        break;\n      }\n    }\n  }\n\n  return result;\n}\n\nfunction isTranslatable(resource: AndroidResourceNode): boolean {\n  return resource.translatable;\n}\n\nfunction buildTranslatedDocument(\n  payload: Record<string, any>,\n  existingDocument: AndroidDocument,\n  sourceDocument: AndroidDocument,\n): AndroidDocument {\n  const templateDocument = sourceDocument;\n  const finalDocument = cloneDocumentStructure(templateDocument);\n\n  const templateMap = createResourceMap(templateDocument);\n  const existingMap = createResourceMap(existingDocument);\n  const payloadEntries = payload ?? {};\n  const finalMap = createResourceMap(finalDocument);\n\n  for (const resource of finalDocument.resourceNodes) {\n    if (!resource.translatable) {\n      continue;\n    }\n\n    const templateResource = templateMap.get(resource.name);\n    let translationValue: any;\n\n    if (\n      Object.prototype.hasOwnProperty.call(payloadEntries, resource.name) &&\n      payloadEntries[resource.name] !== undefined &&\n      payloadEntries[resource.name] !== null\n    ) {\n      translationValue = payloadEntries[resource.name];\n    } else if (existingMap.has(resource.name)) {\n      translationValue = extractValueFromResource(\n        existingMap.get(resource.name)!,\n      );\n    } else {\n      translationValue = extractValueFromResource(templateResource ?? resource);\n    }\n\n    updateResourceNode(resource, translationValue, templateResource);\n  }\n\n  for (const resource of existingDocument.resourceNodes) {\n    if (finalMap.has(resource.name)) {\n      continue;\n    }\n    if (!isTranslatable(resource)) {\n      continue;\n    }\n    const cloned = cloneResourceNode(resource);\n    appendResourceNode(finalDocument, cloned);\n    finalMap.set(cloned.name, cloned);\n  }\n\n  for (const [name, value] of Object.entries(payloadEntries)) {\n    if (finalMap.has(name)) {\n      continue;\n    }\n    try {\n      const inferred = createResourceNodeFromValue(name, value);\n      appendResourceNode(finalDocument, inferred);\n      finalMap.set(name, inferred);\n    } catch (error) {\n      if (error instanceof CLIError) {\n        throw error;\n      }\n    }\n  }\n\n  return finalDocument;\n}\n\nfunction buildAndroidXml(\n  document: AndroidDocument,\n  declaration: XmlDeclarationOptions,\n): string {\n  const xmlBody = serializeElement(document.resources);\n\n  if (declaration.headless) {\n    return xmlBody;\n  }\n\n  if (declaration.xmldec) {\n    const { version, encoding } = declaration.xmldec;\n    const encodingPart = encoding ? ` encoding=\"${encoding}\"` : \"\";\n    return `<?xml version=\"${version}\"${encodingPart}?>\\n${xmlBody}`;\n  }\n\n  return `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n${xmlBody}`;\n}\n\nfunction selectBaseXml(\n  locale: string,\n  originalLocale: string,\n  pullInput: string | null,\n  originalInput: string | null,\n): string | null {\n  if (locale === originalLocale) {\n    return pullInput ?? originalInput;\n  }\n  return pullInput ?? originalInput;\n}\n\nfunction updateResourceNode(\n  target: AndroidResourceNode,\n  rawValue: any,\n  template: AndroidResourceNode | undefined,\n): void {\n  switch (target.type) {\n    case \"string\": {\n      const value = asString(rawValue, target.name);\n      const templateMeta =\n        template && template.type === \"string\" ? template.meta : target.meta;\n      const useCdata = templateMeta.hasCdata;\n      setTextualNodeContent(target.node, value, useCdata);\n      target.meta = makeTextMeta([\n        { kind: useCdata ? \"cdata\" : \"text\", value },\n      ]);\n      break;\n    }\n    case \"string-array\": {\n      const values = asStringArray(rawValue, target.name);\n      const templateItems =\n        template && template.type === \"string-array\"\n          ? template.items\n          : target.items;\n      const maxLength = Math.max(target.items.length, templateItems.length);\n      for (let index = 0; index < maxLength; index++) {\n        const targetItem = target.items[index];\n        const templateItem =\n          templateItems[index] ??\n          templateItems[templateItems.length - 1] ??\n          target.items[index];\n        if (!targetItem || !templateItem) {\n          continue;\n        }\n        const translation =\n          index < values.length\n            ? values[index]\n            : segmentsToString(templateItem.meta.segments);\n        const useCdata = templateItem.meta.hasCdata;\n        setTextualNodeContent(targetItem.node, translation, useCdata);\n        targetItem.meta = makeTextMeta([\n          { kind: useCdata ? \"cdata\" : \"text\", value: translation },\n        ]);\n      }\n      break;\n    }\n    case \"plurals\": {\n      const pluralValues = asPluralMap(rawValue, target.name);\n      const templateItems =\n        template && template.type === \"plurals\" ? template.items : target.items;\n      const templateMap = new Map(\n        templateItems.map((item) => [item.quantity, item]),\n      );\n      for (const item of target.items) {\n        const templateItem =\n          templateMap.get(item.quantity) ?? templateMap.values().next().value;\n        const fallback = templateItem\n          ? segmentsToString(templateItem.meta.segments)\n          : segmentsToString(item.meta.segments);\n        const translation =\n          typeof pluralValues[item.quantity] === \"string\"\n            ? pluralValues[item.quantity]\n            : fallback;\n        const useCdata = templateItem\n          ? templateItem.meta.hasCdata\n          : item.meta.hasCdata;\n        setTextualNodeContent(item.node, translation, useCdata);\n        item.meta = makeTextMeta([\n          { kind: useCdata ? \"cdata\" : \"text\", value: translation },\n        ]);\n      }\n      break;\n    }\n    case \"bool\": {\n      const boolValue = asBoolean(rawValue, target.name);\n      const strValue = boolValue ? \"true\" : \"false\";\n      setTextualNodeContent(target.node, strValue, false);\n      target.meta = makeTextMeta([{ kind: \"text\", value: strValue }]);\n      break;\n    }\n    case \"integer\": {\n      const intValue = asInteger(rawValue, target.name);\n      const strValue = intValue.toString();\n      setTextualNodeContent(target.node, strValue, false);\n      target.meta = makeTextMeta([{ kind: \"text\", value: strValue }]);\n      break;\n    }\n  }\n}\n\nfunction appendResourceNode(\n  document: AndroidDocument,\n  resourceNode: AndroidResourceNode,\n): void {\n  document.resources.$$ = document.resources.$$ ?? [];\n  const children = document.resources.$$ as any[];\n\n  if (\n    children.length === 0 ||\n    (children[children.length - 1][\"#name\"] !== \"__text__\" &&\n      children[children.length - 1][\"#name\"] !== \"__comment__\")\n  ) {\n    children.push({ \"#name\": \"__text__\", _: \"\\n    \" });\n  }\n\n  children.push(resourceNode.node);\n  children.push({ \"#name\": \"__text__\", _: \"\\n\" });\n  document.resourceNodes.push(resourceNode);\n}\n\nfunction setTextualNodeContent(\n  node: any,\n  value: string,\n  useCdata: boolean,\n): void {\n  // CDATA needs apostrophe escaping but not XML entity escaping\n  const escapedValue = useCdata\n    ? escapeApostrophesOnly(value)\n    : escapeAndroidString(value);\n  node._ = escapedValue;\n\n  // Replace entire children array to avoid duplicating inline HTML elements\n  // When inline HTML exists (e.g., <b>text</b>), xml2js creates element nodes\n  // in node.$$ that would otherwise be serialized alongside the escaped text\n  node.$$ = [\n    { \"#name\": useCdata ? \"__cdata\" : \"__text__\", _: escapedValue }\n  ];\n}\n\nfunction buildResourceNameMap(\n  document: AndroidDocument,\n): Map<string, AndroidResourceNode> {\n  const map = new Map<string, AndroidResourceNode>();\n  for (const node of document.resourceNodes) {\n    if (!map.has(node.name)) {\n      map.set(node.name, node);\n    }\n  }\n  return map;\n}\n\nfunction createResourceMap(\n  document: AndroidDocument,\n): Map<string, AndroidResourceNode> {\n  return buildResourceNameMap(document);\n}\n\nfunction cloneResourceNode(resource: AndroidResourceNode): AndroidResourceNode {\n  switch (resource.type) {\n    case \"string\": {\n      const nodeClone = deepClone(resource.node);\n      return {\n        type: \"string\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node: nodeClone,\n        meta: cloneTextMeta(resource.meta),\n      };\n    }\n    case \"string-array\": {\n      const nodeClone = deepClone(resource.node);\n      const itemNodes = (nodeClone.item ?? []) as any[];\n      const items: StringArrayItemNode[] = itemNodes.map((itemNode, index) => {\n        const templateMeta =\n          resource.items[index]?.meta ??\n          resource.items[resource.items.length - 1]?.meta ??\n          makeTextMeta([]);\n        return {\n          node: itemNode,\n          meta: cloneTextMeta(templateMeta),\n        };\n      });\n      return {\n        type: \"string-array\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node: nodeClone,\n        items,\n      };\n    }\n    case \"plurals\": {\n      const nodeClone = deepClone(resource.node);\n      const itemNodes = (nodeClone.item ?? []) as any[];\n      const items: PluralsItemNode[] = [];\n      for (const templateItem of resource.items) {\n        const cloneNode = itemNodes.find(\n          (item: any) => item?.$?.quantity === templateItem.quantity,\n        );\n        if (!cloneNode) {\n          continue;\n        }\n        items.push({\n          node: cloneNode,\n          quantity: templateItem.quantity,\n          meta: cloneTextMeta(templateItem.meta),\n        });\n      }\n      return {\n        type: \"plurals\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node: nodeClone,\n        items,\n      };\n    }\n    case \"bool\": {\n      const nodeClone = deepClone(resource.node);\n      return {\n        type: \"bool\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node: nodeClone,\n        meta: cloneTextMeta(resource.meta),\n      };\n    }\n    case \"integer\": {\n      const nodeClone = deepClone(resource.node);\n      return {\n        type: \"integer\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node: nodeClone,\n        meta: cloneTextMeta(resource.meta),\n      };\n    }\n  }\n}\n\nfunction cloneTextMeta(meta: TextualMeta): TextualMeta {\n  return {\n    hasCdata: meta.hasCdata,\n    segments: meta.segments.map((segment) => ({ ...segment })),\n  };\n}\n\nfunction asString(value: any, name: string): string {\n  if (typeof value === \"string\") {\n    return value;\n  }\n  throw new CLIError({\n    message: `Expected string value for resource \"${name}\"`,\n    docUrl: \"androidResourceError\",\n  });\n}\n\nfunction asStringArray(value: any, name: string): string[] {\n  if (Array.isArray(value) && value.every((item) => typeof item === \"string\")) {\n    return value;\n  }\n  throw new CLIError({\n    message: `Expected array of strings for resource \"${name}\"`,\n    docUrl: \"androidResourceError\",\n  });\n}\n\nfunction asPluralMap(value: any, name: string): Record<string, string> {\n  if (value && typeof value === \"object\" && !Array.isArray(value)) {\n    const result: Record<string, string> = {};\n    for (const [quantity, pluralValue] of Object.entries(value)) {\n      if (typeof pluralValue !== \"string\") {\n        throw new CLIError({\n          message: `Expected plural item \"${quantity}\" of \"${name}\" to be a string`,\n          docUrl: \"androidResourceError\",\n        });\n      }\n      result[quantity] = pluralValue;\n    }\n    return result;\n  }\n  throw new CLIError({\n    message: `Expected object value for plurals resource \"${name}\"`,\n    docUrl: \"androidResourceError\",\n  });\n}\n\nfunction asBoolean(value: any, name: string): boolean {\n  if (typeof value === \"boolean\") {\n    return value;\n  }\n  if (typeof value === \"string\") {\n    if (value === \"true\" || value === \"false\") {\n      return value === \"true\";\n    }\n  }\n  throw new CLIError({\n    message: `Expected boolean value for resource \"${name}\"`,\n    docUrl: \"androidResourceError\",\n  });\n}\n\nfunction asInteger(value: any, name: string): number {\n  if (typeof value === \"number\" && Number.isInteger(value)) {\n    return value;\n  }\n  throw new CLIError({\n    message: `Expected number value for resource \"${name}\"`,\n    docUrl: \"androidResourceError\",\n  });\n}\n\nfunction escapeAndroidString(value: string): string {\n  return value\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/(?<!\\\\)'/g, \"\\\\'\");\n}\n\nfunction escapeApostrophesOnly(value: string): string {\n  // Even inside CDATA, apostrophes must be escaped for Android AAPT\n  return value.replace(/(?<!\\\\)'/g, \"\\\\'\");\n}\n\nfunction segmentsToString(segments: ContentSegment[]): string {\n  return segments.map((segment) => segment.value).join(\"\");\n}\n\nfunction makeTextMeta(segments: ContentSegment[]): TextualMeta {\n  return {\n    segments,\n    hasCdata: segments.some((segment) => segment.kind === \"cdata\"),\n  };\n}\n\nfunction createResourceNodeFromValue(\n  name: string,\n  value: any,\n): AndroidResourceNode {\n  const inferredType = inferTypeFromValue(value);\n\n  switch (inferredType) {\n    case \"string\": {\n      const stringValue = asString(value, name);\n      const escaped = escapeAndroidString(stringValue);\n      const node = {\n        \"#name\": \"string\",\n        $: { name },\n        _: escaped,\n        $$: [{ \"#name\": \"__text__\", _: escaped }],\n      };\n      return {\n        type: \"string\",\n        name,\n        translatable: true,\n        node,\n        meta: makeTextMeta([{ kind: \"text\", value: stringValue }]),\n      };\n    }\n    case \"string-array\": {\n      const items = asStringArray(value, name);\n      const node = {\n        \"#name\": \"string-array\",\n        $: { name },\n        $$: [] as any[],\n        item: [] as any[],\n      };\n      const itemNodes: StringArrayItemNode[] = [];\n      for (const itemValue of items) {\n        const escaped = escapeAndroidString(itemValue);\n        const itemNode = {\n          \"#name\": \"item\",\n          _: escaped,\n          $$: [{ \"#name\": \"__text__\", _: escaped }],\n        };\n        node.$$!.push(itemNode);\n        node.item!.push(itemNode);\n        itemNodes.push({\n          node: itemNode,\n          meta: makeTextMeta([{ kind: \"text\", value: itemValue }]),\n        });\n      }\n      return {\n        type: \"string-array\",\n        name,\n        translatable: true,\n        node,\n        items: itemNodes,\n      };\n    }\n    case \"plurals\": {\n      const pluralMap = asPluralMap(value, name);\n      const node = {\n        \"#name\": \"plurals\",\n        $: { name },\n        $$: [] as any[],\n        item: [] as any[],\n      };\n      const items: PluralsItemNode[] = [];\n      for (const [quantity, pluralValue] of Object.entries(pluralMap)) {\n        const escaped = escapeAndroidString(pluralValue);\n        const itemNode = {\n          \"#name\": \"item\",\n          $: { quantity },\n          _: escaped,\n          $$: [{ \"#name\": \"__text__\", _: escaped }],\n        };\n        node.$$!.push(itemNode);\n        node.item!.push(itemNode);\n        items.push({\n          node: itemNode,\n          quantity,\n          meta: makeTextMeta([{ kind: \"text\", value: pluralValue }]),\n        });\n      }\n      return {\n        type: \"plurals\",\n        name,\n        translatable: true,\n        node,\n        items,\n      };\n    }\n    case \"bool\": {\n      const boolValue = asBoolean(value, name);\n      const textValue = boolValue ? \"true\" : \"false\";\n      const node = {\n        \"#name\": \"bool\",\n        $: { name },\n        _: textValue,\n        $$: [{ \"#name\": \"__text__\", _: textValue }],\n      };\n      return {\n        type: \"bool\",\n        name,\n        translatable: true,\n        node,\n        meta: makeTextMeta([{ kind: \"text\", value: textValue }]),\n      };\n    }\n    case \"integer\": {\n      const intValue = asInteger(value, name);\n      const textValue = intValue.toString();\n      const node = {\n        \"#name\": \"integer\",\n        $: { name },\n        _: textValue,\n        $$: [{ \"#name\": \"__text__\", _: textValue }],\n      };\n      return {\n        type: \"integer\",\n        name,\n        translatable: true,\n        node,\n        meta: makeTextMeta([{ kind: \"text\", value: textValue }]),\n      };\n    }\n  }\n}\n\nfunction cloneDocumentStructure(document: AndroidDocument): AndroidDocument {\n  // Filter first - only keep translatable resources\n  const translatableResources = document.resourceNodes.filter(isTranslatable);\n\n  const resourcesClone = deepClone(document.resources);\n  const lookup = buildResourceLookup(resourcesClone);\n  const resourceNodes: AndroidResourceNode[] = [];\n\n  for (const resource of translatableResources) {\n    const cloned = cloneResourceNodeFromLookup(resource, lookup);\n    resourceNodes.push(cloned);\n  }\n\n  // Clean up XML structure - only keep translatable resource nodes\n  if (resourcesClone.$$ && Array.isArray(resourcesClone.$$)) {\n    const includedKeys = new Set(\n      resourceNodes.map((r) => resourceLookupKey(r.type, r.name)),\n    );\n\n    // Filter out non-translatable resources\n    let filtered = resourcesClone.$$.filter((child: any) => {\n      const elementName = child?.[\"#name\"];\n      const name = child?.$?.name;\n      if (!isResourceElementName(elementName) || !name) {\n        return true; // Keep whitespace, comments, etc.\n      }\n      return includedKeys.has(resourceLookupKey(elementName, name));\n    });\n\n    // Remove consecutive whitespace nodes (fixes extra blank lines)\n    const cleaned: any[] = [];\n    let lastWasWhitespace = false;\n\n    for (const child of filtered) {\n      const isWhitespace =\n        child?.[\"#name\"] === \"__text__\" && (!child._ || child._.trim() === \"\");\n\n      if (isWhitespace) {\n        if (!lastWasWhitespace) {\n          cleaned.push(child);\n          lastWasWhitespace = true;\n        }\n        // Skip consecutive whitespace\n      } else {\n        cleaned.push(child);\n        lastWasWhitespace = false;\n      }\n    }\n\n    resourcesClone.$$ = cleaned;\n  }\n\n  return {\n    resources: resourcesClone,\n    resourceNodes,\n  };\n}\n\nfunction buildResourceLookup(resources: any): Map<string, any[]> {\n  const lookup = new Map<string, any[]>();\n  const children = Array.isArray(resources.$$) ? resources.$$ : [];\n  for (const child of children) {\n    const type = child?.[\"#name\"];\n    const name = child?.$?.name;\n    if (!type || !name || !isResourceElementName(type)) {\n      continue;\n    }\n    const key = resourceLookupKey(type, name);\n    if (!lookup.has(key)) {\n      lookup.set(key, []);\n    }\n    lookup.get(key)!.push(child);\n  }\n  return lookup;\n}\n\nfunction cloneResourceNodeFromLookup(\n  resource: AndroidResourceNode,\n  lookup: Map<string, any[]>,\n): AndroidResourceNode {\n  const node = takeResourceNode(lookup, resource.type, resource.name);\n  if (!node) {\n    return cloneResourceNode(resource);\n  }\n\n  switch (resource.type) {\n    case \"string\": {\n      return {\n        type: \"string\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node,\n        meta: cloneTextMeta(resource.meta),\n      };\n    }\n    case \"string-array\": {\n      const childItems = (Array.isArray(node.$$) ? node.$$ : []).filter(\n        (child: any) => child?.[\"#name\"] === \"item\",\n      );\n      node.item = childItems;\n      if (childItems.length < resource.items.length) {\n        return cloneResourceNode(resource);\n      }\n      const items: StringArrayItemNode[] = resource.items.map((item, index) => {\n        const nodeItem = childItems[index];\n        if (!nodeItem) {\n          return {\n            node: deepClone(item.node),\n            meta: cloneTextMeta(item.meta),\n          };\n        }\n        return {\n          node: nodeItem,\n          meta: cloneTextMeta(item.meta),\n        };\n      });\n      return {\n        type: \"string-array\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node,\n        items,\n      };\n    }\n    case \"plurals\": {\n      const childItems = (Array.isArray(node.$$) ? node.$$ : []).filter(\n        (child: any) => child?.[\"#name\"] === \"item\",\n      );\n      node.item = childItems;\n      const itemMap = new Map<string, any>();\n      for (const item of childItems) {\n        if (item?.$?.quantity) {\n          itemMap.set(item.$.quantity, item);\n        }\n      }\n      const items: PluralsItemNode[] = [];\n      for (const templateItem of resource.items) {\n        const nodeItem = itemMap.get(templateItem.quantity);\n        if (!nodeItem) {\n          return cloneResourceNode(resource);\n        }\n        items.push({\n          node: nodeItem,\n          quantity: templateItem.quantity,\n          meta: cloneTextMeta(templateItem.meta),\n        });\n      }\n      return {\n        type: \"plurals\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node,\n        items,\n      };\n    }\n    case \"bool\": {\n      return {\n        type: \"bool\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node,\n        meta: cloneTextMeta(resource.meta),\n      };\n    }\n    case \"integer\": {\n      return {\n        type: \"integer\",\n        name: resource.name,\n        translatable: resource.translatable,\n        node,\n        meta: cloneTextMeta(resource.meta),\n      };\n    }\n  }\n}\n\nfunction takeResourceNode(\n  lookup: Map<string, any[]>,\n  type: AndroidResourceType,\n  name: string,\n): any | undefined {\n  const key = resourceLookupKey(type, name);\n  const list = lookup.get(key);\n  if (!list || list.length === 0) {\n    return undefined;\n  }\n  return list.shift();\n}\n\nfunction resourceLookupKey(type: string, name: string): string {\n  return `${type}:${name}`;\n}\n\nfunction extractValueFromResource(resource: AndroidResourceNode): any {\n  switch (resource.type) {\n    case \"string\":\n      return decodeAndroidText(segmentsToString(resource.meta.segments));\n    case \"string-array\":\n      return resource.items.map((item) =>\n        decodeAndroidText(segmentsToString(item.meta.segments)),\n      );\n    case \"plurals\": {\n      const result: Record<string, string> = {};\n      for (const item of resource.items) {\n        result[item.quantity] = decodeAndroidText(\n          segmentsToString(item.meta.segments),\n        );\n      }\n      return result;\n    }\n    case \"bool\": {\n      const value = segmentsToString(resource.meta.segments).trim();\n      return value === \"true\";\n    }\n    case \"integer\": {\n      const value = parseInt(\n        segmentsToString(resource.meta.segments).trim(),\n        10,\n      );\n      return Number.isNaN(value) ? 0 : value;\n    }\n  }\n}\n\nfunction inferTypeFromValue(value: any): AndroidResourceType {\n  if (typeof value === \"string\") {\n    return \"string\";\n  }\n  if (Array.isArray(value)) {\n    return \"string-array\";\n  }\n  if (value && typeof value === \"object\") {\n    return \"plurals\";\n  }\n  if (typeof value === \"boolean\") {\n    return \"bool\";\n  }\n  if (typeof value === \"number\" && Number.isInteger(value)) {\n    return \"integer\";\n  }\n  throw new CLIError({\n    message: \"Unable to infer Android resource type from payload\",\n    docUrl: \"androidResourceError\",\n  });\n}\n\nfunction extractResourceMetadata(xml: string) {\n  interface StackEntry {\n    name: string;\n    rawName: string;\n    attributes: Record<string, string>;\n    segments: ContentSegment[];\n    items: Array<{ quantity?: string; meta: TextualMeta }>;\n  }\n\n  interface StringMeta {\n    type: \"string\";\n    name: string;\n    translatable: boolean;\n    meta: TextualMeta;\n  }\n\n  interface StringArrayMeta {\n    type: \"string-array\";\n    name: string;\n    translatable: boolean;\n    items: Array<{ meta: TextualMeta }>;\n  }\n\n  interface PluralsMeta {\n    type: \"plurals\";\n    name: string;\n    translatable: boolean;\n    items: Array<{ quantity: string; meta: TextualMeta }>;\n  }\n\n  interface BoolMeta {\n    type: \"bool\";\n    name: string;\n    translatable: boolean;\n    meta: TextualMeta;\n  }\n\n  interface IntegerMeta {\n    type: \"integer\";\n    name: string;\n    translatable: boolean;\n    meta: TextualMeta;\n  }\n\n  type ResourceMeta =\n    | StringMeta\n    | StringArrayMeta\n    | PluralsMeta\n    | BoolMeta\n    | IntegerMeta;\n\n  const parser = sax.parser(true, {\n    trim: false,\n    normalize: false,\n    lowercase: false,\n  });\n\n  const stack: StackEntry[] = [];\n  const result: ResourceMeta[] = [];\n\n  parser.onopentag = (node) => {\n    const lowerName = node.name.toLowerCase();\n    const attributes: Record<string, string> = {};\n    for (const [key, value] of Object.entries(node.attributes ?? {})) {\n      attributes[key.toLowerCase()] = String(value);\n    }\n    stack.push({\n      name: lowerName,\n      rawName: node.name,\n      attributes,\n      segments: [],\n      items: [],\n    });\n\n    if (\n      lowerName !== \"resources\" &&\n      lowerName !== \"item\" &&\n      !isResourceElementName(lowerName)\n    ) {\n      const attrString = Object.entries(node.attributes ?? {})\n        .map(\n          ([key, value]) => ` ${key}=\"${escapeAttributeValue(String(value))}\"`,\n        )\n        .join(\"\");\n      appendSegmentToNearestResource(stack, {\n        kind: \"text\",\n        value: `<${node.name}${attrString}>`,\n      });\n    }\n  };\n\n  parser.ontext = (text) => {\n    if (!text) {\n      return;\n    }\n    appendSegmentToNearestResource(stack, { kind: \"text\", value: text });\n  };\n\n  parser.oncdata = (cdata) => {\n    appendSegmentToNearestResource(stack, { kind: \"cdata\", value: cdata });\n  };\n\n  parser.onclosetag = () => {\n    const entry = stack.pop();\n    if (!entry) {\n      return;\n    }\n\n    const parent = stack[stack.length - 1];\n\n    if (entry.name === \"item\" && parent) {\n      const meta = makeTextMeta(entry.segments);\n      parent.items.push({\n        quantity: entry.attributes.quantity,\n        meta,\n      });\n      return;\n    }\n\n    if (\n      entry.name !== \"resources\" &&\n      entry.name !== \"item\" &&\n      !isResourceElementName(entry.name)\n    ) {\n      appendSegmentToNearestResource(stack, {\n        kind: \"text\",\n        value: `</${entry.rawName}>`,\n      });\n      return;\n    }\n\n    if (!isResourceElementName(entry.name)) {\n      return;\n    }\n\n    const name = entry.attributes.name;\n    if (!name) {\n      return;\n    }\n\n    const translatable =\n      (entry.attributes.translatable ?? \"\").toLowerCase() !== \"false\";\n\n    switch (entry.name) {\n      case \"string\": {\n        result.push({\n          type: \"string\",\n          name,\n          translatable,\n          meta: makeTextMeta(entry.segments),\n        });\n        break;\n      }\n      case \"string-array\": {\n        result.push({\n          type: \"string-array\",\n          name,\n          translatable,\n          items: entry.items.map((item) => ({\n            meta: cloneTextMeta(item.meta),\n          })),\n        });\n        break;\n      }\n      case \"plurals\": {\n        const items: Array<{ quantity: string; meta: TextualMeta }> = [];\n        for (const item of entry.items) {\n          if (!item.quantity) {\n            continue;\n          }\n          items.push({\n            quantity: item.quantity,\n            meta: cloneTextMeta(item.meta),\n          });\n        }\n        result.push({\n          type: \"plurals\",\n          name,\n          translatable,\n          items,\n        });\n        break;\n      }\n      case \"bool\": {\n        result.push({\n          type: \"bool\",\n          name,\n          translatable,\n          meta: makeTextMeta(entry.segments),\n        });\n        break;\n      }\n      case \"integer\": {\n        result.push({\n          type: \"integer\",\n          name,\n          translatable,\n          meta: makeTextMeta(entry.segments),\n        });\n        break;\n      }\n    }\n  };\n\n  parser.write(xml).close();\n\n  return result;\n}\n\nfunction appendSegmentToNearestResource(\n  stack: Array<{\n    name: string;\n    segments: ContentSegment[];\n    attributes: Record<string, string>;\n  }>,\n  segment: ContentSegment,\n) {\n  for (let index = stack.length - 1; index >= 0; index--) {\n    const entry = stack[index];\n    if (\n      entry.name === \"string\" ||\n      entry.name === \"item\" ||\n      entry.name === \"bool\" ||\n      entry.name === \"integer\"\n    ) {\n      entry.segments.push(segment);\n      return;\n    }\n  }\n}\n\nfunction isResourceElementName(\n  value: string | undefined,\n): value is AndroidResourceType {\n  return (\n    value === \"string\" ||\n    value === \"string-array\" ||\n    value === \"plurals\" ||\n    value === \"bool\" ||\n    value === \"integer\"\n  );\n}\n\nfunction deepClone<T>(value: T): T {\n  return value === undefined ? value : JSON.parse(JSON.stringify(value));\n}\n\nfunction serializeElement(node: any): string {\n  if (!node) {\n    return \"\";\n  }\n\n  const name = node[\"#name\"] ?? \"resources\";\n\n  if (name === \"__text__\") {\n    return node._ ?? \"\";\n  }\n\n  if (name === \"__cdata\") {\n    return `<![CDATA[${node._ ?? \"\"}]]>`;\n  }\n\n  if (name === \"__comment__\") {\n    return `<!--${node._ ?? \"\"}-->`;\n  }\n\n  const attributes = node.$ ?? {};\n  const attrString = Object.entries(attributes)\n    .map(([key, value]) => ` ${key}=\"${escapeAttributeValue(String(value))}\"`)\n    .join(\"\");\n\n  const children = Array.isArray(node.$$) ? node.$$ : [];\n\n  if (children.length === 0) {\n    const textContent = node._ ?? \"\";\n    return `<${name}${attrString}>${textContent}</${name}>`;\n  }\n\n  const childContent = children.map(serializeElement).join(\"\");\n  return `<${name}${attrString}>${childContent}</${name}>`;\n}\n\nfunction escapeAttributeValue(value: string): string {\n  return value\n    .replace(/&/g, \"&amp;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/'/g, \"&apos;\");\n}\n\nfunction decodeAndroidText(value: string): string {\n  return value.replace(/\\\\'/g, \"'\");\n}\n","import { parse } from \"csv-parse/sync\";\nimport { stringify } from \"csv-stringify/sync\";\nimport _ from \"lodash\";\nimport { ILoader } from \"./_types\";\nimport { composeLoaders, createLoader } from \"./_utils\";\n\n/**\n * Tries to detect the key column name from a csvString.\n *\n * Current logic: get first cell > 'KEY' fallback if empty\n */\nexport function detectKeyColumnName(csvString: string) {\n  const row: string[] | undefined = parse(csvString)[0];\n  const firstColumn = row?.[0]?.trim();\n  return firstColumn || \"KEY\";\n}\n\nexport type CsvLoaderOptions = {\n  /**\n   * Name of the column to use as the unique row identifier.\n   * If omitted, defaults to the first column in the CSV header.\n   */\n  keyColumn?: string;\n};\n\nexport default function createCsvLoader(options?: CsvLoaderOptions) {\n  return composeLoaders(_createCsvLoader(options), createPullOutputCleaner());\n}\n\ntype InternalTransferState = {\n  keyColumnName: string;\n  inputParsed: Record<string, any>[];\n  items: Record<string, string>;\n};\n\nfunction _createCsvLoader(\n  options?: CsvLoaderOptions,\n): ILoader<string, InternalTransferState> {\n  // Validation runs once per loader instance. The loader is created per file\n  // path, and the same file is pulled once per locale (source + each target) —\n  // re-validating on every pull would do the same O(N) work N+1 times for\n  // identical content, with no possibility of catching new issues.\n  let validated = false;\n\n  return createLoader({\n    async pull(locale, input) {\n      const firstNonEmptyLine =\n        input.split(/\\r?\\n/).find((line) => line.trim().length > 0) ?? \"\";\n      const keyColumnName =\n        options?.keyColumn ?? detectKeyColumnName(firstNonEmptyLine);\n      const inputParsed = parse(input, {\n        columns: true,\n        skip_empty_lines: true,\n        relax_column_count_less: true,\n      }) as Record<string, any>[];\n\n      if (!validated) {\n        if (options?.keyColumn) {\n          const [headerRow = []] = parse(input, {\n            to_line: 1,\n            skip_empty_lines: true,\n          }) as string[][];\n          const availableColumns = headerRow.map((col) => String(col));\n          if (!availableColumns.includes(options.keyColumn)) {\n            throw new Error(\n              `CSV key column \"${options.keyColumn}\" is not present in the file. ` +\n                `Available columns: ${availableColumns.join(\", \")}. ` +\n                `Either rename a column to \"${options.keyColumn}\" or update the \"keyColumn\" setting in your bucket config.`,\n            );\n          }\n        }\n\n        assertUniqueKeys(inputParsed, keyColumnName);\n        validated = true;\n      }\n\n      const items: Record<string, string> = {};\n\n      // Assign keys that already have translation so AI doesn't re-generate it.\n      _.forEach(inputParsed, (row) => {\n        const key = row[keyColumnName];\n        if (key && row[locale] && row[locale].trim() !== \"\") {\n          items[key] = row[locale];\n        }\n      });\n\n      return {\n        inputParsed,\n        keyColumnName,\n        items,\n      };\n    },\n    async push(locale, { inputParsed, keyColumnName, items }) {\n      const columns =\n        inputParsed.length > 0\n          ? Object.keys(inputParsed[0])\n          : [keyColumnName, locale];\n      if (!columns.includes(locale)) {\n        columns.push(locale);\n      }\n\n      const updatedRows = inputParsed.map((row) => ({\n        ...row,\n        [locale]: items[row[keyColumnName]] || row[locale] || \"\",\n      }));\n      const existingKeys = new Set(\n        inputParsed.map((row) => row[keyColumnName]),\n      );\n\n      Object.entries(items).forEach(([key, value]) => {\n        if (!existingKeys.has(key)) {\n          const newRow: Record<string, string> = {\n            [keyColumnName]: key,\n            ...Object.fromEntries(columns.map((column) => [column, \"\"])),\n          };\n          newRow[locale] = value;\n          updatedRows.push(newRow);\n        }\n      });\n\n      return stringify(updatedRows, {\n        header: true,\n        columns,\n      });\n    },\n  });\n}\n\n/**\n * Validates that the key column has unique values across all rows.\n * Without this check, rows with repeated key values silently overwrite\n * each other when building the translation map, causing most source rows\n * to be dropped and the last row's translation to be broadcast everywhere.\n */\nfunction assertUniqueKeys(\n  rows: Record<string, any>[],\n  keyColumnName: string,\n): void {\n  const seen = new Set<string>();\n  const duplicates = new Set<string>();\n  for (const row of rows) {\n    const key = row[keyColumnName];\n    if (key === undefined || key === \"\") continue;\n    if (seen.has(key)) {\n      duplicates.add(key);\n    } else {\n      seen.add(key);\n    }\n  }\n  if (duplicates.size === 0) return;\n\n  const preview = [...duplicates].slice(0, 3).join(\", \");\n  const more = duplicates.size > 3 ? `, and ${duplicates.size - 3} more` : \"\";\n  throw new Error(\n    `CSV column \"${keyColumnName}\" has duplicate values (${preview}${more}). ` +\n      `Lingo uses this column as a unique row identifier, so duplicates would cause rows to silently overwrite each other. ` +\n      `Fix: make the first column unique (add an \"id\" column or move your source-language column first), ` +\n      `or set \"keyColumn\" in this bucket's config to point at a column with unique values.`,\n  );\n}\n\n/**\n * This is a simple extra loader that is used to clean the data written to lockfile\n */\nfunction createPullOutputCleaner(): ILoader<\n  InternalTransferState,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(_locale, input) {\n      return input.items;\n    },\n    async push(_locale, data, _oI, _oL, pullInput) {\n      return { ...pullInput!, items: data };\n    },\n  });\n}\n","import * as htmlparser2 from \"htmlparser2\";\nimport { DomHandler, Element } from \"domhandler\";\nimport * as domutils from \"domutils\";\nimport * as DomSerializer from \"dom-serializer\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport {\n  createElementExtractor,\n  BASE_LOCALIZABLE_ATTRIBUTES,\n} from \"../utils/element-extraction\";\n\nexport default function createHtmlLoader(): ILoader<\n  string,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const result: Record<string, string> = {};\n\n      // Parse HTML with htmlparser2 (preserves structure, no foster parenting)\n      const handler = new DomHandler();\n      const parser = new htmlparser2.Parser(handler, {\n        lowerCaseTags: false,\n        lowerCaseAttributeNames: false,\n      });\n      parser.write(input);\n      parser.end();\n\n      const dom = handler.dom;\n\n      // Get innerHTML equivalent (serialize children)\n      function getInnerHTML(element: Element): string {\n        return element.children\n          .map((child) =>\n            DomSerializer.default(child, { encodeEntities: false }),\n          )\n          .join(\"\");\n      }\n\n      // Extract localizable attributes from element\n      function extractAttributes(element: Element, path: string): void {\n        const tagName = element.name.toLowerCase();\n        const attrs = BASE_LOCALIZABLE_ATTRIBUTES[tagName];\n        if (!attrs) return;\n\n        for (const attr of attrs) {\n          const value = element.attribs?.[attr];\n          if (value && value.trim()) {\n            result[`${path}#${attr}`] = value.trim();\n          }\n        }\n      }\n\n      // Recursively extract translation units from element tree\n      const extractFromElement = createElementExtractor(\n        { getInnerHTML, extractAttributes },\n        result,\n      );\n\n      // Find head and body elements\n      const html = domutils.findOne(\n        (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"html\",\n        dom,\n        true,\n      ) as Element | null;\n\n      if (html) {\n        const head = domutils.findOne(\n          (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"head\",\n          html.children,\n          true,\n        ) as Element | null;\n\n        const body = domutils.findOne(\n          (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"body\",\n          html.children,\n          true,\n        ) as Element | null;\n\n        // Process head children\n        if (head) {\n          let headIndex = 0;\n          const headChildren = head.children.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n          for (const child of headChildren) {\n            extractFromElement(child, [\"head\", headIndex++]);\n          }\n        }\n\n        // Process body children\n        if (body) {\n          let bodyIndex = 0;\n          const bodyChildren = body.children.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n          for (const child of bodyChildren) {\n            extractFromElement(child, [\"body\", bodyIndex++]);\n          }\n        }\n      } else {\n        // Handle HTML fragments (no <html> element) - process root elements directly\n        let rootIndex = 0;\n        const rootElements = dom.filter(\n          (child): child is Element => child.type === \"tag\",\n        );\n        for (const child of rootElements) {\n          extractFromElement(child, [rootIndex++]);\n        }\n      }\n\n      return result;\n    },\n\n    async push(locale, data, originalInput) {\n      // Parse original HTML\n      const handler = new DomHandler();\n      const parser = new htmlparser2.Parser(handler, {\n        lowerCaseTags: false,\n        lowerCaseAttributeNames: false,\n      });\n      parser.write(\n        originalInput ??\n          \"<!DOCTYPE html><html><head></head><body></body></html>\",\n      );\n      parser.end();\n\n      const dom = handler.dom;\n\n      // Find HTML element and set lang attribute\n      const html = domutils.findOne(\n        (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"html\",\n        dom,\n        true,\n      ) as Element | null;\n\n      if (html) {\n        html.attribs = html.attribs || {};\n        html.attribs.lang = locale;\n      }\n\n      // Helper to traverse child elements by numeric indices\n      function traverseByIndices(\n        element: Element | null,\n        indices: string[],\n      ): Element | null {\n        let current = element;\n\n        for (const indexStr of indices) {\n          if (!current) return null;\n\n          const index = parseInt(indexStr, 10);\n          const children: Element[] = current.children.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n\n          if (index >= children.length) {\n            return null; // Path doesn't exist\n          }\n\n          current = children[index];\n        }\n\n        return current;\n      }\n\n      // Resolve path to element in the DOM\n      function resolvePathToElement(path: string): Element | null {\n        const parts = path.split(\"/\");\n        const [rootTag, ...indices] = parts;\n\n        let current: Element | null = null;\n\n        if (html) {\n          // Full HTML document with <html>, <head>, <body>\n          // Find head or body\n          if (rootTag === \"head\") {\n            current = domutils.findOne(\n              (elem) =>\n                elem.type === \"tag\" && elem.name.toLowerCase() === \"head\",\n              html.children,\n              true,\n            ) as Element | null;\n          } else if (rootTag === \"body\") {\n            current = domutils.findOne(\n              (elem) =>\n                elem.type === \"tag\" && elem.name.toLowerCase() === \"body\",\n              html.children,\n              true,\n            ) as Element | null;\n          }\n\n          if (!current) return null;\n\n          // Traverse by indices\n          return traverseByIndices(current, indices);\n        } else {\n          // HTML fragment - no <html> element\n          // Path is just numeric indices from root\n          const rootElements = dom.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n\n          // First part is the root index\n          const rootIndex = parseInt(rootTag, 10);\n          if (rootIndex >= rootElements.length) {\n            return null;\n          }\n\n          current = rootElements[rootIndex];\n\n          // Traverse remaining indices\n          return traverseByIndices(current, indices);\n        }\n      }\n\n      // Apply translations\n      for (const [path, value] of Object.entries(data)) {\n        const [nodePath, attribute] = path.split(\"#\");\n\n        const element = resolvePathToElement(nodePath);\n        if (!element) {\n          console.warn(`Path not found in original HTML: ${nodePath}`);\n          continue;\n        }\n\n        if (attribute) {\n          // Set attribute\n          element.attribs = element.attribs || {};\n          element.attribs[attribute] = value;\n        } else {\n          // Set innerHTML (parse value as HTML and replace children)\n          if (value) {\n            const valueHandler = new DomHandler();\n            const valueParser = new htmlparser2.Parser(valueHandler);\n            valueParser.write(value);\n            valueParser.end();\n\n            element.children = valueHandler.dom;\n          } else {\n            // If value is empty/null, clear children\n            element.children = [];\n          }\n        }\n      }\n\n      // Serialize back to HTML\n      return DomSerializer.default(dom, { encodeEntities: false });\n    },\n  });\n}\n","import { Element } from \"domhandler\";\nimport * as domutils from \"domutils\";\n\n/**\n * SVG tags that contain translatable content.\n * All other SVG elements should be excluded from translation.\n *\n * @see https://www.w3.org/TR/SVG/text.html\n */\nexport const SVG_TRANSLATABLE_TAGS = new Set([\n  \"title\",\n  \"text\",\n  \"desc\",\n]) as ReadonlySet<string>;\n\n/**\n * Based on WHATWG HTML spec: https://html.spec.whatwg.org/multipage/indices.html\n * Phrasing content = inline elements that should be preserved within text\n */\nexport const PHRASING_ELEMENTS = new Set([\n  // Text-level semantics\n  \"a\",\n  \"abbr\",\n  \"b\",\n  \"bdi\",\n  \"bdo\",\n  \"br\",\n  \"cite\",\n  \"code\",\n  \"data\",\n  \"dfn\",\n  \"em\",\n  \"i\",\n  \"kbd\",\n  \"mark\",\n  \"q\",\n  \"ruby\",\n  \"s\",\n  \"samp\",\n  \"small\",\n  \"span\",\n  \"strong\",\n  \"sub\",\n  \"sup\",\n  \"time\",\n  \"u\",\n  \"var\",\n  \"wbr\",\n  // Media\n  \"audio\",\n  \"img\",\n  \"video\",\n  \"picture\",\n  // Interactive\n  \"button\",\n  \"input\",\n  \"label\",\n  \"select\",\n  \"textarea\",\n  // Embedded\n  \"canvas\",\n  \"iframe\",\n  \"object\",\n  \"svg\",\n  \"math\",\n  // Other\n  \"del\",\n  \"ins\",\n  \"map\",\n  \"area\",\n]) as ReadonlySet<string>;\n\n/**\n * Block elements create translation boundaries.\n */\nexport const BLOCK_ELEMENTS = new Set([\n  \"div\",\n  \"p\",\n  \"h1\",\n  \"h2\",\n  \"h3\",\n  \"h4\",\n  \"h5\",\n  \"h6\",\n  \"ul\",\n  \"ol\",\n  \"li\",\n  \"dl\",\n  \"dt\",\n  \"dd\",\n  \"blockquote\",\n  \"pre\",\n  \"article\",\n  \"aside\",\n  \"nav\",\n  \"section\",\n  \"header\",\n  \"footer\",\n  \"main\",\n  \"figure\",\n  \"figcaption\",\n  \"table\",\n  \"thead\",\n  \"tbody\",\n  \"tfoot\",\n  \"tr\",\n  \"td\",\n  \"th\",\n  \"caption\",\n  \"form\",\n  \"fieldset\",\n  \"legend\",\n  \"details\",\n  \"summary\",\n  \"address\",\n  \"hr\",\n  \"search\",\n  \"dialog\",\n  \"noscript\",\n  \"title\",\n]) as ReadonlySet<string>;\n\n/**\n * Tags whose content should never be translated.\n */\nexport const UNLOCALIZABLE_TAGS = new Set([\n  \"script\",\n  \"style\",\n]) as ReadonlySet<string>;\n\n/**\n * Base localizable attributes for HTML elements.\n * Loaders can extend this with additional attributes.\n */\nexport const BASE_LOCALIZABLE_ATTRIBUTES: Record<string, string[]> = {\n  meta: [\"content\"],\n  img: [\"alt\", \"title\"],\n  input: [\"placeholder\", \"title\"],\n  textarea: [\"placeholder\", \"title\"],\n  a: [\"title\"],\n  abbr: [\"title\"],\n  button: [\"title\"],\n  link: [\"title\"],\n};\n\n/**\n * Check if element is inside an unlocalizable tag.\n */\nexport function isInsideUnlocalizableTag(\n  element: Element,\n  unlocalizableTags: ReadonlySet<string> = UNLOCALIZABLE_TAGS,\n): boolean {\n  let current = element.parent;\n  while (current && current.type === \"tag\") {\n    if (unlocalizableTags.has((current as Element).name.toLowerCase())) {\n      return true;\n    }\n    current = current.parent;\n  }\n  return false;\n}\n\n/**\n * Check if element contains any translatable text (not just whitespace).\n */\nexport function hasTranslatableContent(element: Element): boolean {\n  const text = domutils.textContent(element);\n  return text.trim().length > 0;\n}\n\n/**\n * Check if element is a \"leaf\" block (contains text with inline elements, not nested blocks).\n */\nexport function isLeafBlock(\n  element: Element,\n  blockElements: ReadonlySet<string> = BLOCK_ELEMENTS,\n): boolean {\n  const childElements = element.children.filter(\n    (child): child is Element => child.type === \"tag\",\n  );\n  for (const child of childElements) {\n    if (blockElements.has(child.name.toLowerCase())) {\n      return false;\n    }\n  }\n  return hasTranslatableContent(element);\n}\n\n/**\n * Strategy for handling SVG elements during extraction.\n */\nexport type SvgExtractionStrategy =\n  | \"extract\" // Element is translatable - extract its content\n  | \"skip-recurse\" // Element is not translatable - skip but recurse into children\n  | \"process-normal\"; // Element is not in SVG context - use normal extraction\n\n/**\n * Check if an element is nested inside an SVG element by traversing up the DOM tree.\n *\n * @param element - The DOM element to check\n * @returns True if any ancestor is an <svg> tag, false otherwise\n *\n * @example\n * // <svg><text>Hello</text></svg>\n * isInsideSvg(textElement) // returns true\n */\nfunction isInsideSvg(element: Element): boolean {\n  let current = element.parent;\n  while (current && current.type === \"tag\") {\n    if (current.name.toLowerCase() === \"svg\") {\n      return true;\n    }\n    current = current.parent;\n  }\n  return false;\n}\n\n/**\n * Check if an element contains an SVG descendant (recursive).\n * Used to determine if we should recurse into an element rather than extracting it as a whole.\n *\n * @param element - The DOM element to check\n * @returns True if any descendant is an <svg> tag, false otherwise\n */\nfunction containsSvgDescendant(element: Element): boolean {\n  const childElements = element.children.filter(\n    (child): child is Element => child.type === \"tag\",\n  );\n  for (const child of childElements) {\n    if (child.name.toLowerCase() === \"svg\") {\n      return true;\n    }\n    if (containsSvgDescendant(child)) {\n      return true;\n    }\n  }\n  return false;\n}\n\n/**\n * Determines the extraction strategy for an SVG element.\n * Consolidates all SVG-specific logic into a single decision function.\n *\n * @param element - The DOM element to evaluate\n * @returns The extraction strategy to apply:\n *   - \"extract\": Element is translatable (title/text/desc inside SVG) - extract content\n *   - \"skip-recurse\": Element is non-translatable SVG element - skip but recurse into children\n *   - \"process-normal\": Element is not in SVG context - use normal extraction logic\n *\n * @example\n * // <svg><title>Chart</title><rect/></svg>\n * getSvgExtractionStrategy(titleElement) // returns \"extract\"\n * getSvgExtractionStrategy(rectElement)  // returns \"skip-recurse\"\n * getSvgExtractionStrategy(svgElement)   // returns \"skip-recurse\"\n */\nexport function getSvgExtractionStrategy(\n  element: Element,\n): SvgExtractionStrategy {\n  const tagName = element.name.toLowerCase();\n\n  // SVG element itself should be skipped but children should be processed\n  if (tagName === \"svg\") {\n    return \"skip-recurse\";\n  }\n\n  // Check if element is inside an SVG context\n  const inSvg = isInsideSvg(element);\n  if (!inSvg) {\n    return \"process-normal\";\n  }\n\n  // Inside SVG - only extract translatable tags, skip others\n  return SVG_TRANSLATABLE_TAGS.has(tagName) ? \"extract\" : \"skip-recurse\";\n}\n\n/**\n * Context-specific functions for extraction.\n * Only includes functions that differ between loaders.\n */\nexport interface ExtractionContext {\n  /**\n   * Get the innerHTML of an element (serialized children).\n   * This may include format-specific processing (e.g., Twig restoration).\n   */\n  getInnerHTML: (element: Element) => string;\n\n  /**\n   * Extract localizable attributes from element.\n   * This may include format-specific processing (e.g., Twig restoration).\n   */\n  extractAttributes: (element: Element, path: string) => void;\n}\n\n/**\n * Creates a reusable extraction function that can be shared across loaders.\n * Uses the shared BLOCK_ELEMENTS, PHRASING_ELEMENTS, and UNLOCALIZABLE_TAGS constants.\n *\n * @param context - Loader-specific context functions (getInnerHTML, extractAttributes)\n * @param result - Output object to store extracted content\n * @returns A function that recursively extracts translatable content from the DOM\n *\n * @example\n * const extractFromElement = createElementExtractor(context, result);\n * extractFromElement(rootElement, [\"body\", 0]);\n */\nexport function createElementExtractor(\n  context: ExtractionContext,\n  result: Record<string, string>,\n) {\n  /**\n   * Recursively extracts translation units from element tree.\n   * Handles SVG filtering, block/phrasing elements, and attributes.\n   */\n  function extractFromElement(\n    element: Element,\n    pathParts: (string | number)[],\n  ): void {\n    const path = pathParts.join(\"/\");\n    const tagName = element.name.toLowerCase();\n\n    // Skip if inside unlocalizable tag\n    if (isInsideUnlocalizableTag(element)) {\n      return;\n    }\n\n    // Extract localizable attributes\n    context.extractAttributes(element, path);\n\n    // Handle SVG elements using strategy pattern\n    const svgStrategy = getSvgExtractionStrategy(element);\n\n    if (svgStrategy === \"extract\") {\n      // SVG translatable element (<title>, <text>, <desc>)\n      const content = context.getInnerHTML(element).trim();\n      if (content) {\n        result[path] = content;\n      }\n      return;\n    }\n\n    if (svgStrategy === \"skip-recurse\") {\n      // SVG non-translatable element - skip but recurse into children\n      let childIndex = 0;\n      const childElements = element.children.filter(\n        (child): child is Element => child.type === \"tag\",\n      );\n      for (const child of childElements) {\n        extractFromElement(child, [...pathParts, childIndex++]);\n      }\n      return;\n    }\n\n    // svgStrategy === \"process-normal\" - use normal extraction logic\n\n    // If this is a leaf block element (contains text but no nested blocks), extract it\n    // But NOT if it contains SVG - in that case, recurse to handle SVG separately\n    if (\n      BLOCK_ELEMENTS.has(tagName) &&\n      isLeafBlock(element) &&\n      !containsSvgDescendant(element)\n    ) {\n      const content = context.getInnerHTML(element).trim();\n      if (content) {\n        result[path] = content;\n      }\n      // Don't recurse into children - innerHTML captures everything\n      return;\n    }\n\n    // If this is a standalone phrasing element with text content, extract it\n    // But NOT if it contains SVG - in that case, recurse to handle SVG separately\n    if (\n      PHRASING_ELEMENTS.has(tagName) &&\n      hasTranslatableContent(element) &&\n      !containsSvgDescendant(element)\n    ) {\n      const content = context.getInnerHTML(element).trim();\n      if (content) {\n        result[path] = content;\n      }\n      // Don't recurse - innerHTML captures everything\n      return;\n    }\n\n    // For structural/container elements, recurse into children\n    let childIndex = 0;\n    const childElements = element.children.filter(\n      (child): child is Element => child.type === \"tag\",\n    );\n    for (const child of childElements) {\n      extractFromElement(child, [...pathParts, childIndex++]);\n    }\n  }\n\n  return extractFromElement;\n}\n","import matter from \"gray-matter\";\nimport YAML from \"yaml\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nconst SECTION_REGEX =\n  /^(#{1,6}\\s.*$|[-=*]{3,}$|!\\[.*\\]\\(.*\\)$|\\[.*\\]\\(.*\\)$)/gm;\nconst MD_SECTION_PREFIX = \"md-section-\";\nconst FM_ATTR_PREFIX = \"fm-attr-\";\n\nconst yamlEngine = {\n  parse: (str: string) => YAML.parse(str),\n  stringify: (obj: any) => YAML.stringify(obj, { defaultStringType: \"PLAIN\" }),\n};\n\nexport default function createMarkdownLoader(): ILoader<\n  string,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const { data: frontmatter, content } = matter(input, {\n        engines: {\n          yaml: yamlEngine,\n        },\n      });\n\n      const sections = content\n        .split(SECTION_REGEX)\n        .map((section) => section?.trim() ?? \"\")\n        .filter(Boolean);\n\n      return {\n        ...Object.fromEntries(\n          sections\n            .map((section, index) => [`${MD_SECTION_PREFIX}${index}`, section])\n            .filter(([, section]) => Boolean(section)),\n        ),\n        ...Object.fromEntries(\n          Object.entries(frontmatter).map(([key, value]) => [\n            `${FM_ATTR_PREFIX}${key}`,\n            value,\n          ]),\n        ),\n      };\n    },\n    async push(locale, data: Record<string, string>) {\n      const frontmatter = Object.fromEntries(\n        Object.entries(data)\n          .filter(([key]) => key.startsWith(FM_ATTR_PREFIX))\n          .map(([key, value]) => [key.replace(FM_ATTR_PREFIX, \"\"), value]),\n      );\n\n      let content = Object.entries(data)\n        .filter(([key]) => key.startsWith(MD_SECTION_PREFIX))\n        .sort(\n          ([a], [b]) => Number(a.split(\"-\").pop()) - Number(b.split(\"-\").pop()),\n        )\n        .map(([, value]) => value?.trim() ?? \"\")\n        .filter(Boolean)\n        .join(\"\\n\\n\");\n\n      if (Object.keys(frontmatter).length > 0) {\n        content = `\\n${content}`;\n      }\n\n      return matter.stringify(content, frontmatter, {\n        engines: {\n          yaml: yamlEngine,\n        },\n      });\n    },\n  });\n}\n","import Markdoc from \"@markdoc/markdoc\";\nimport YAML from \"yaml\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\ntype MarkdocNode = {\n  $$mdtype?: string;\n  type: string;\n  tag?: string;\n  attributes?: Record<string, any>;\n  children?: MarkdocNode[];\n  [key: string]: any;\n};\n\ntype NodeCounter = {\n  [nodeType: string]: number;\n};\n\ntype NodePathMap = {\n  [semanticKey: string]: string; // maps semantic key to AST path\n};\n\nconst FM_ATTR_PREFIX = \"fm-attr-\";\n\nexport default function createMarkdocLoader(): ILoader<\n  string,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const ast = Markdoc.parse(input) as unknown as MarkdocNode;\n      const result: Record<string, string> = {};\n      const counters: NodeCounter = {};\n\n      // Traverse the AST and extract text content with semantic keys\n      traverseAndExtract(ast, \"\", result, counters);\n\n      // Extract frontmatter if present\n      if (ast.attributes?.frontmatter) {\n        const frontmatter = YAML.parse(ast.attributes.frontmatter);\n        Object.entries(frontmatter).forEach(([key, value]) => {\n          if (typeof value === \"string\") {\n            result[`${FM_ATTR_PREFIX}${key}`] = value;\n          }\n        });\n      }\n\n      return result;\n    },\n\n    async push(locale, data, originalInput) {\n      if (!originalInput) {\n        throw new Error(\"Original input is required for push\");\n      }\n\n      const ast = Markdoc.parse(originalInput) as unknown as MarkdocNode;\n      const counters: NodeCounter = {};\n      const pathMap: NodePathMap = {};\n\n      // Build path map from semantic keys to AST paths\n      buildPathMap(ast, \"\", counters, pathMap);\n\n      // Extract frontmatter from data\n      const frontmatterEntries = Object.entries(data)\n        .filter(([key]) => key.startsWith(FM_ATTR_PREFIX))\n        .map(([key, value]) => [key.replace(FM_ATTR_PREFIX, \"\"), value]);\n\n      // Update frontmatter in AST if present\n      if (frontmatterEntries.length > 0 && ast.attributes) {\n        const frontmatter = Object.fromEntries(frontmatterEntries);\n        ast.attributes.frontmatter = YAML.stringify(frontmatter, {\n          defaultStringType: \"PLAIN\",\n        }).trim();\n      }\n\n      // Filter out frontmatter keys from translation data\n      const contentData = Object.fromEntries(\n        Object.entries(data).filter(([key]) => !key.startsWith(FM_ATTR_PREFIX)),\n      );\n\n      // Apply translations using the path map\n      applyTranslations(ast, \"\", contentData, pathMap);\n\n      // Format back to string\n      return Markdoc.format(ast);\n    },\n  });\n}\n\nfunction getSemanticNodeType(node: MarkdocNode): string | null {\n  // For custom tags, use the tag name instead of \"tag\"\n  if (node.type === \"tag\") return node.tag || \"tag\";\n  return node.type;\n}\n\nfunction traverseAndExtract(\n  node: MarkdocNode,\n  path: string,\n  result: Record<string, string>,\n  counters: NodeCounter,\n  parentType?: string,\n) {\n  if (!node || typeof node !== \"object\") {\n    return;\n  }\n\n  // Determine the semantic type for this node\n  let semanticType = parentType;\n  const nodeSemanticType = getSemanticNodeType(node);\n\n  // Use node's own semantic type for structural elements\n  if (\n    nodeSemanticType &&\n    ![\"text\", \"strong\", \"em\", \"inline\", \"link\"].includes(nodeSemanticType)\n  ) {\n    semanticType = nodeSemanticType;\n  }\n\n  // If this is a text node, extract its content only if it's a string\n  // Skip interpolation nodes (where content is a Variable or Function object)\n  if (node.type === \"text\" && node.attributes?.content) {\n    const content = node.attributes.content;\n\n    // Only extract if content is a string (not interpolation)\n    if (typeof content === \"string\" && content.trim()) {\n      if (semanticType) {\n        const index = counters[semanticType] || 0;\n        counters[semanticType] = index + 1;\n        const semanticKey = `${semanticType}-${index}`;\n        result[semanticKey] = content;\n      }\n    }\n  }\n\n  // If the node has children, traverse them\n  if (Array.isArray(node.children)) {\n    node.children.forEach((child, index) => {\n      const childPath = path\n        ? `${path}/children/${index}`\n        : `children/${index}`;\n      traverseAndExtract(child, childPath, result, counters, semanticType);\n    });\n  }\n}\n\nfunction buildPathMap(\n  node: MarkdocNode,\n  path: string,\n  counters: NodeCounter,\n  pathMap: NodePathMap,\n  parentType?: string,\n) {\n  if (!node || typeof node !== \"object\") {\n    return;\n  }\n\n  // Determine the semantic type for this node\n  let semanticType = parentType;\n  const nodeSemanticType = getSemanticNodeType(node);\n\n  // Use node's own semantic type for structural elements\n  if (\n    nodeSemanticType &&\n    ![\"text\", \"strong\", \"em\", \"inline\", \"link\"].includes(nodeSemanticType)\n  ) {\n    semanticType = nodeSemanticType;\n  }\n\n  // Build the map from semantic keys to AST paths\n  if (node.type === \"text\" && node.attributes?.content) {\n    const content = node.attributes.content;\n\n    if (typeof content === \"string\" && content.trim()) {\n      if (semanticType) {\n        const index = counters[semanticType] || 0;\n        counters[semanticType] = index + 1;\n        const semanticKey = `${semanticType}-${index}`;\n        const contentPath = path\n          ? `${path}/attributes/content`\n          : \"attributes/content\";\n        pathMap[semanticKey] = contentPath;\n      }\n    }\n  }\n\n  // Recursively build map for children\n  if (Array.isArray(node.children)) {\n    node.children.forEach((child, index) => {\n      const childPath = path\n        ? `${path}/children/${index}`\n        : `children/${index}`;\n      buildPathMap(child, childPath, counters, pathMap, semanticType);\n    });\n  }\n}\n\nfunction applyTranslations(\n  node: MarkdocNode,\n  path: string,\n  data: Record<string, string>,\n  pathMap: NodePathMap,\n) {\n  if (!node || typeof node !== \"object\") {\n    return;\n  }\n\n  // Check if we have a translation for this node's text content\n  // Only apply translations to string content (not interpolation)\n  if (node.type === \"text\" && node.attributes?.content) {\n    const content = node.attributes.content;\n\n    // Only apply translation if content is currently a string\n    if (typeof content === \"string\") {\n      const contentPath = path\n        ? `${path}/attributes/content`\n        : \"attributes/content\";\n\n      // Find the semantic key for this path\n      const semanticKey = Object.keys(pathMap).find(\n        (key) => pathMap[key] === contentPath,\n      );\n\n      if (semanticKey && data[semanticKey] !== undefined) {\n        node.attributes.content = data[semanticKey];\n      }\n    }\n    // If content is an object (Variable/Function), leave it unchanged\n  }\n\n  // Recursively apply translations to children\n  if (Array.isArray(node.children)) {\n    node.children.forEach((child, index) => {\n      const childPath = path\n        ? `${path}/children/${index}`\n        : `children/${index}`;\n      applyTranslations(child, childPath, data, pathMap);\n    });\n  }\n}\n","import { parseStringPromise, Builder } from \"xml2js\";\nimport * as htmlparser2 from \"htmlparser2\";\nimport { DomHandler } from \"domhandler\";\nimport * as DomSerializer from \"dom-serializer\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nconst LOCALIZABLE_COMPONENTS = [\n  \"mj-text\",\n  \"mj-button\",\n  \"mj-title\",\n  \"mj-preview\",\n  \"mj-navbar-link\",\n  \"mj-accordion-title\",\n  \"mj-accordion-text\",\n  \"p\",\n  \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\",\n  \"li\",\n];\n\nconst LOCALIZABLE_ATTRIBUTES: Record<string, string[]> = {\n  \"mj-image\": [\"alt\", \"title\"],\n  \"mj-button\": [\"title\", \"aria-label\"],\n  \"mj-social-element\": [\"title\", \"alt\"],\n  \"img\": [\"alt\", \"title\"],\n  \"a\": [\"title\", \"aria-label\"],\n};\n\nexport default function createMjmlLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const result: Record<string, any> = {};\n\n      // Handle empty input\n      if (!input || input.trim() === \"\") {\n        return result;\n      }\n\n      try {\n        const parsed = await parseStringPromise(input, {\n          explicitArray: true,\n          explicitChildren: true,\n          preserveChildrenOrder: true,\n          charsAsChildren: true,\n          includeWhiteChars: true,\n          mergeAttrs: false,\n          trim: false,\n          attrkey: \"$\",\n          charkey: \"_\",\n          childkey: \"$$\",\n        });\n\n        if (!parsed || typeof parsed !== \"object\") {\n          console.error(\"Failed to parse MJML: invalid parsed structure\");\n          return result;\n        }\n\n        const rootKey = Object.keys(parsed).find(key => !key.startsWith(\"_\") && !key.startsWith(\"$\"));\n        const rootNode = rootKey ? parsed[rootKey] : parsed;\n        const rootPath = rootNode[\"#name\"] || rootKey || \"\";\n\n        traverse(rootNode, (node, path, componentName) => {\n          if (typeof node !== \"object\") return;\n\n          const localizableAttrs = LOCALIZABLE_ATTRIBUTES[componentName];\n          if (localizableAttrs && node.$) {\n            localizableAttrs.forEach((attr) => {\n              const attrValue = node.$[attr];\n              if (attrValue) {\n                result[`${path}#${attr}`] = attrValue;\n              }\n            });\n          }\n\n          if (LOCALIZABLE_COMPONENTS.includes(componentName)) {\n            const innerHTML = getInnerHTML(node);\n            if (innerHTML) {\n              result[path] = innerHTML;\n              return \"SKIP_CHILDREN\";\n            }\n          }\n\n          return undefined;\n        }, rootPath);\n      } catch (error) {\n        console.error(\"Failed to parse MJML:\", error);\n      }\n\n      return result;\n    },\n\n    async push(locale, data, originalInput) {\n      // Handle empty input\n      if (!originalInput || originalInput.trim() === \"\") {\n        return originalInput || \"\";\n      }\n\n      try {\n        const parsed = await parseStringPromise(originalInput, {\n          explicitArray: true,\n          explicitChildren: true,\n          preserveChildrenOrder: true,\n          charsAsChildren: true,\n          includeWhiteChars: true,\n          mergeAttrs: false,\n          trim: false,\n          attrkey: \"$\",\n          charkey: \"_\",\n          childkey: \"$$\",\n        });\n\n        if (!parsed || typeof parsed !== \"object\") {\n          console.error(\"Failed to parse MJML for push: invalid parsed structure\");\n          return originalInput || \"\";\n        }\n\n        const rootKey = Object.keys(parsed).find(key => !key.startsWith(\"_\") && !key.startsWith(\"$\"));\n        const rootNode = rootKey ? parsed[rootKey] : parsed;\n        const rootPath = rootNode[\"#name\"] || rootKey || \"\";\n\n        traverse(rootNode, (node, path, componentName) => {\n          if (typeof node !== \"object\") return;\n\n          const localizableAttrs = LOCALIZABLE_ATTRIBUTES[componentName];\n          if (localizableAttrs && node.$) {\n            localizableAttrs.forEach((attr) => {\n              const attrKey = `${path}#${attr}`;\n              if (data[attrKey] !== undefined) {\n                node.$[attr] = data[attrKey];\n              }\n            });\n          }\n\n          if (LOCALIZABLE_COMPONENTS.includes(componentName) && data[path]) {\n            setInnerHTML(node, data[path]);\n            return \"SKIP_CHILDREN\";\n          }\n\n          return undefined;\n        }, rootPath);\n\n        return serializeMjml(parsed);\n      } catch (error) {\n        console.error(\"Failed to build MJML:\", error);\n        return \"\";\n      }\n    },\n  });\n}\n\nfunction traverse(\n  node: any,\n  visitor: (node: any, path: string, componentName: string) => string | undefined,\n  path: string = \"\",\n) {\n  if (!node || typeof node !== \"object\") {\n    return;\n  }\n\n  const children = node.$$;\n  if (!Array.isArray(children)) {\n    return;\n  }\n\n  const elementCounts = new Map<string, number>();\n\n  children.forEach((child: any) => {\n    const elementName = child[\"#name\"];\n\n    if (!elementName || elementName.startsWith(\"__\")) {\n      return;\n    }\n\n    const currentIndex = elementCounts.get(elementName) || 0;\n    elementCounts.set(elementName, currentIndex + 1);\n\n    const currentPath = path\n      ? `${path}/${elementName}/${currentIndex}`\n      : `${elementName}/${currentIndex}`;\n\n    const result = visitor(child, currentPath, elementName);\n\n    if (result !== \"SKIP_CHILDREN\") {\n      traverse(child, visitor, currentPath);\n    }\n  });\n}\n\nfunction getInnerHTML(node: any): string | null {\n  if (!node.$$ || !Array.isArray(node.$$)) {\n    return null;\n  }\n\n  let html = \"\";\n  node.$$.forEach((child: any) => {\n    html += serializeXmlNode(child);\n  });\n\n  return html.trim() || null;\n}\n\nfunction setInnerHTML(node: any, htmlContent: string): void {\n  const handler = new DomHandler();\n  const parser = new htmlparser2.Parser(handler);\n  parser.write(htmlContent);\n  parser.end();\n\n  const newChildren: any[] = [];\n\n  for (const domNode of handler.dom) {\n    const xmlNode = convertDomToXmlNode(domNode);\n    if (xmlNode) {\n      newChildren.push(xmlNode);\n    }\n  }\n\n  node.$$ = newChildren;\n  node._ = htmlContent;\n}\n\nfunction serializeXmlNode(node: any): string {\n  const name = node[\"#name\"];\n\n  if (name === \"__text__\") {\n    return node._ || \"\";\n  }\n\n  if (name === \"__cdata\") {\n    return `<![CDATA[${node._ || \"\"}]]>`;\n  }\n\n  if (!name || name.startsWith(\"__\")) {\n    return \"\";\n  }\n\n  const attrs = node.$ || {};\n  const attrString = Object.entries(attrs)\n    .map(([key, value]) => ` ${key}=\"${escapeAttributeValue(String(value))}\"`)\n    .join(\"\");\n\n  const children = node.$$ || [];\n  if (children.length === 0) {\n    const textContent = node._ || \"\";\n    if (textContent) {\n      return `<${name}${attrString}>${textContent}</${name}>`;\n    }\n    return `<${name}${attrString} />`;\n  }\n\n  const childContent = children.map(serializeXmlNode).join(\"\");\n  return `<${name}${attrString}>${childContent}</${name}>`;\n}\n\nfunction convertDomToXmlNode(domNode: any): any {\n  if (domNode.type === \"text\") {\n    return {\n      \"#name\": \"__text__\",\n      \"_\": domNode.data,\n    };\n  }\n\n  if (domNode.type === \"tag\") {\n    const xmlNode: any = {\n      \"#name\": domNode.name,\n      \"$\": domNode.attribs || {},\n      \"$$\": [],\n    };\n\n    if (domNode.children && domNode.children.length > 0) {\n      for (const child of domNode.children) {\n        const xmlChild = convertDomToXmlNode(child);\n        if (xmlChild) {\n          xmlNode.$$.push(xmlChild);\n        }\n      }\n    }\n\n    return xmlNode;\n  }\n\n  return null;\n}\n\nfunction serializeMjml(parsed: any): string {\n  const rootKey = Object.keys(parsed).find(key => !key.startsWith(\"_\") && !key.startsWith(\"$\"));\n  const rootNode = rootKey ? parsed[rootKey] : parsed;\n\n  const body = serializeElement(rootNode);\n\n  // Don't add XML declaration - xml2js already preserves it in the parsed object\n  // or it will be added by the consumer if needed\n  return body;\n}\n\nfunction serializeElement(node: any, indent: string = \"\"): string {\n  if (!node) {\n    return \"\";\n  }\n\n  const name = node[\"#name\"] ?? \"mjml\";\n\n  if (name === \"__text__\") {\n    return node._ ?? \"\";\n  }\n\n  if (name === \"__cdata\") {\n    return `<![CDATA[${node._ ?? \"\"}]]>`;\n  }\n\n  if (name === \"__comment__\") {\n    return `<!--${node._ ?? \"\"}-->`;\n  }\n\n  const attributes = node.$ ?? {};\n  const attrString = Object.entries(attributes)\n    .map(([key, value]) => ` ${key}=\"${escapeAttributeValue(String(value))}\"`)\n    .join(\"\");\n\n  const children = Array.isArray(node.$$) ? node.$$ : [];\n\n  if (children.length === 0) {\n    const textContent = node._ ?? \"\";\n    if (textContent) {\n      return `${indent}<${name}${attrString}>${textContent}</${name}>`;\n    }\n    return `${indent}<${name}${attrString} />`;\n  }\n\n  const childContent = children.map((child: any) => serializeElement(child, indent)).join(\"\");\n  return `${indent}<${name}${attrString}>${childContent}</${name}>`;\n}\n\nfunction escapeAttributeValue(value: string): string {\n  return value\n    .replace(/&/g, \"&amp;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/'/g, \"&apos;\");\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createPropertiesLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, text) {\n      const result: Record<string, string> = {};\n      const lines = text.split(\"\\n\");\n\n      for (const line of lines) {\n        const trimmed = line.trim();\n\n        // Skip empty lines and comments\n        if (isSkippableLine(trimmed)) {\n          continue;\n        }\n\n        const { key, value } = parsePropertyLine(trimmed);\n        if (key) {\n          result[key] = value;\n        }\n      }\n\n      return result;\n    },\n    async push(locale, payload) {\n      const result = Object.entries(payload)\n        .filter(([_, value]) => value != null)\n        .map(([key, value]) => `${key}=${value}`)\n        .join(\"\\n\");\n\n      return result;\n    },\n  });\n}\n\nfunction isSkippableLine(line: string): boolean {\n  return !line || line.startsWith(\"#\");\n}\n\nfunction parsePropertyLine(line: string): { key: string; value: string } {\n  const [key, ...valueParts] = line.split(\"=\");\n  return {\n    key: key?.trim() || \"\",\n    value: valueParts.join(\"=\").trim(),\n  };\n}\n","import { Token, TokenType, Position } from \"./types\";\n\nexport class Tokenizer {\n  private input: string;\n  private pos: number;\n  private line: number;\n  private column: number;\n\n  constructor(input: string) {\n    this.input = input;\n    this.pos = 0;\n    this.line = 1;\n    this.column = 1;\n  }\n\n  tokenize(): Token[] {\n    const tokens: Token[] = [];\n\n    while (this.pos < this.input.length) {\n      const char = this.current();\n\n      // Skip whitespace\n      if (this.isWhitespace(char)) {\n        this.advance();\n        continue;\n      }\n\n      // Handle comments\n      if (char === \"/\" && this.peek() === \"/\") {\n        tokens.push(this.scanSingleLineComment());\n        continue;\n      }\n\n      if (char === \"/\" && this.peek() === \"*\") {\n        tokens.push(this.scanMultiLineComment());\n        continue;\n      }\n\n      // Handle strings\n      if (char === '\"') {\n        tokens.push(this.scanString());\n        continue;\n      }\n\n      // Handle operators\n      if (char === \"=\") {\n        tokens.push(this.makeToken(TokenType.EQUALS, \"=\"));\n        this.advance();\n        continue;\n      }\n\n      if (char === \";\") {\n        tokens.push(this.makeToken(TokenType.SEMICOLON, \";\"));\n        this.advance();\n        continue;\n      }\n\n      // Unexpected character - skip it\n      // (More forgiving than throwing error)\n      this.advance();\n    }\n\n    tokens.push(this.makeToken(TokenType.EOF, \"\"));\n    return tokens;\n  }\n\n  private scanString(): Token {\n    const start = this.getPosition();\n    let value = \"\";\n\n    this.advance(); // Skip opening \"\n\n    while (this.pos < this.input.length) {\n      const char = this.current();\n\n      if (char === \"\\\\\") {\n        // Escape sequence - preserve both \\ and next char\n        this.advance();\n        if (this.pos < this.input.length) {\n          const nextChar = this.current();\n          value += \"\\\\\" + nextChar;\n          this.advance();\n        }\n        continue;\n      }\n\n      if (char === '\"') {\n        // End of string\n        this.advance(); // Skip closing \"\n        return {\n          type: TokenType.STRING,\n          value,\n          ...start,\n        };\n      }\n\n      // Regular character (including actual newlines)\n      value += char;\n      this.advance();\n    }\n\n    // Unterminated string - return what we have\n    return {\n      type: TokenType.STRING,\n      value,\n      ...start,\n    };\n  }\n\n  private scanSingleLineComment(): Token {\n    const start = this.getPosition();\n    let value = \"\";\n\n    this.advance(); // Skip first '/'\n    this.advance(); // Skip second '/'\n\n    while (this.pos < this.input.length && this.current() !== \"\\n\") {\n      value += this.current();\n      this.advance();\n    }\n\n    return {\n      type: TokenType.COMMENT_SINGLE,\n      value,\n      ...start,\n    };\n  }\n\n  private scanMultiLineComment(): Token {\n    const start = this.getPosition();\n    let value = \"\";\n\n    this.advance(); // Skip '/'\n    this.advance(); // Skip '*'\n\n    while (this.pos < this.input.length) {\n      if (this.current() === \"*\" && this.peek() === \"/\") {\n        this.advance(); // Skip '*'\n        this.advance(); // Skip '/'\n        return {\n          type: TokenType.COMMENT_MULTI,\n          value,\n          ...start,\n        };\n      }\n\n      value += this.current();\n      this.advance();\n    }\n\n    // Unterminated comment - return what we have\n    return {\n      type: TokenType.COMMENT_MULTI,\n      value,\n      ...start,\n    };\n  }\n\n  private current(): string {\n    return this.input[this.pos];\n  }\n\n  private peek(): string | null {\n    if (this.pos + 1 < this.input.length) {\n      return this.input[this.pos + 1];\n    }\n    return null;\n  }\n\n  private advance(): void {\n    if (this.pos < this.input.length) {\n      if (this.current() === \"\\n\") {\n        this.line++;\n        this.column = 1;\n      } else {\n        this.column++;\n      }\n      this.pos++;\n    }\n  }\n\n  private isWhitespace(char: string): boolean {\n    return char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\";\n  }\n\n  private getPosition(): Position {\n    return {\n      line: this.line,\n      column: this.column,\n    };\n  }\n\n  private makeToken(type: TokenType, value: string): Token {\n    return {\n      type,\n      value,\n      ...this.getPosition(),\n    };\n  }\n}\n","/**\n * Unescape a string value from .strings file format\n * Handles: \\\", \\\\, \\n, \\t, etc.\n */\nexport function unescapeString(raw: string): string {\n  let result = \"\";\n  let i = 0;\n\n  while (i < raw.length) {\n    if (raw[i] === \"\\\\\" && i + 1 < raw.length) {\n      const nextChar = raw[i + 1];\n      switch (nextChar) {\n        case '\"':\n          result += '\"';\n          i += 2;\n          break;\n        case \"\\\\\":\n          result += \"\\\\\";\n          i += 2;\n          break;\n        case \"n\":\n          result += \"\\n\";\n          i += 2;\n          break;\n        case \"t\":\n          result += \"\\t\";\n          i += 2;\n          break;\n        case \"r\":\n          result += \"\\r\";\n          i += 2;\n          break;\n        default:\n          // Unknown escape - keep as-is\n          result += raw[i];\n          i++;\n          break;\n      }\n    } else {\n      result += raw[i];\n      i++;\n    }\n  }\n\n  return result;\n}\n\n/**\n * Escape a string value for .strings file format\n * Escapes: \\, \", newlines to \\n\n */\nexport function escapeString(str: string): string {\n  if (str == null) {\n    return \"\";\n  }\n\n  let result = \"\";\n\n  for (let i = 0; i < str.length; i++) {\n    const char = str[i];\n    switch (char) {\n      case \"\\\\\":\n        result += \"\\\\\\\\\";\n        break;\n      case '\"':\n        result += '\\\\\"';\n        break;\n      case \"\\n\":\n        result += \"\\\\n\";\n        break;\n      case \"\\r\":\n        result += \"\\\\r\";\n        break;\n      case \"\\t\":\n        result += \"\\\\t\";\n        break;\n      default:\n        result += char;\n        break;\n    }\n  }\n\n  return result;\n}\n","import { Token, TokenType } from \"./types\";\nimport { unescapeString } from \"./escape\";\n\nexport class Parser {\n  private tokens: Token[];\n  private pos: number;\n\n  constructor(tokens: Token[]) {\n    this.tokens = tokens;\n    this.pos = 0;\n  }\n\n  parse(): Record<string, string> {\n    const result: Record<string, string> = {};\n\n    while (this.pos < this.tokens.length) {\n      const token = this.current();\n\n      // Skip comments\n      if (\n        token.type === TokenType.COMMENT_SINGLE ||\n        token.type === TokenType.COMMENT_MULTI\n      ) {\n        this.advance();\n        continue;\n      }\n\n      // End of file\n      if (token.type === TokenType.EOF) {\n        break;\n      }\n\n      // Expect entry: STRING \"=\" STRING \";\"\n      if (token.type === TokenType.STRING) {\n        const entry = this.parseEntry();\n        if (entry) {\n          result[entry.key] = entry.value;\n        }\n        continue;\n      }\n\n      // Skip unexpected tokens gracefully\n      this.advance();\n    }\n\n    return result;\n  }\n\n  private parseEntry(): { key: string; value: string } | null {\n    // Current token should be STRING (key)\n    const keyToken = this.current();\n    if (keyToken.type !== TokenType.STRING) {\n      return null;\n    }\n    const key = keyToken.value;\n    this.advance();\n\n    // Expect '='\n    if (!this.expect(TokenType.EQUALS)) {\n      // Missing '=' - skip this entry\n      return null;\n    }\n\n    // Expect STRING (value)\n    const valueToken = this.current();\n    if (valueToken.type !== TokenType.STRING) {\n      // Missing value - skip this entry\n      return null;\n    }\n    const rawValue = valueToken.value;\n    this.advance();\n\n    // Expect ';'\n    if (!this.expect(TokenType.SEMICOLON)) {\n      // Missing ';' - but still process the entry\n      // (more forgiving)\n    }\n\n    // Unescape the value\n    const value = unescapeString(rawValue);\n\n    return { key, value };\n  }\n\n  private current(): Token {\n    return this.tokens[this.pos];\n  }\n\n  private advance(): void {\n    if (this.pos < this.tokens.length) {\n      this.pos++;\n    }\n  }\n\n  private expect(type: TokenType): boolean {\n    if (this.current()?.type === type) {\n      this.advance();\n      return true;\n    }\n    return false;\n  }\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { Tokenizer } from \"./xcode-strings/tokenizer\";\nimport { Parser } from \"./xcode-strings/parser\";\nimport { escapeString } from \"./xcode-strings/escape\";\n\nexport default function createXcodeStringsLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      // Tokenize the input\n      const tokenizer = new Tokenizer(input);\n      const tokens = tokenizer.tokenize();\n\n      // Parse tokens into key-value pairs\n      const parser = new Parser(tokens);\n      const result = parser.parse();\n\n      return result;\n    },\n\n    async push(locale, payload) {\n      const lines = Object.entries(payload)\n        .filter(([_, value]) => value != null)\n        .map(([key, value]) => {\n          const escapedValue = escapeString(value);\n          return `\"${key}\" = \"${escapedValue}\";`;\n        });\n\n      return lines.join(\"\\n\");\n    },\n  });\n}\n","import plist from \"plist\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { CLIError } from \"../utils/errors\";\n\nconst emptyData = [\n  '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n  '<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">',\n  '<plist version=\"1.0\">',\n  \"<dict/>\",\n  \"</plist>\",\n].join(\"\\n\");\n\nexport default function createXcodeStringsdictLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      try {\n        const parsed = plist.parse(input || emptyData);\n        if (typeof parsed !== \"object\" || parsed === null) {\n          throw new CLIError({\n            message: \"Invalid .stringsdict format\",\n            docUrl: \"invalidStringDict\",\n          });\n        }\n        return parsed as Record<string, any>;\n      } catch (error: any) {\n        throw new CLIError({\n          message: `Invalid .stringsdict format: ${error.message}`,\n          docUrl: \"invalidStringDict\",\n        });\n      }\n    },\n    async push(locale, payload) {\n      const plistContent = plist.build(payload);\n      return plistContent;\n    },\n  });\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport _ from \"lodash\";\n\nexport default function createXcodeXcstringsLoader(\n  defaultLocale: string,\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    async pull(locale, input, initCtx) {\n      const resultData: Record<string, any> = {};\n      const isSourceLanguage = locale === defaultLocale;\n\n      for (const [translationKey, _translationEntity] of Object.entries(\n        (input as any).strings,\n      )) {\n        const rootTranslationEntity = _translationEntity as any;\n\n        if (rootTranslationEntity.shouldTranslate === false) {\n          continue;\n        }\n\n        const langTranslationEntity =\n          rootTranslationEntity?.localizations?.[locale];\n\n        if (langTranslationEntity) {\n          if (\"stringUnit\" in langTranslationEntity) {\n            resultData[translationKey] = langTranslationEntity.stringUnit.value;\n          } else if (\"stringSet\" in langTranslationEntity) {\n            const values = langTranslationEntity.stringSet.values;\n            if (Array.isArray(values) && values.length > 0) {\n              resultData[translationKey] = values;\n            }\n          } else if (\"variations\" in langTranslationEntity) {\n            if (\"plural\" in langTranslationEntity.variations) {\n              resultData[translationKey] = {};\n              const pluralForms = langTranslationEntity.variations.plural;\n              for (const form in pluralForms) {\n                if (pluralForms[form]?.stringUnit?.value) {\n                  resultData[translationKey][form] =\n                    pluralForms[form].stringUnit.value;\n                }\n              }\n            }\n          }\n        } else if (isSourceLanguage) {\n          resultData[translationKey] = translationKey;\n        }\n      }\n\n      return resultData;\n    },\n    async push(locale, payload, originalInput) {\n      const langDataToMerge: any = {};\n      langDataToMerge.strings = {};\n\n      const input = _.cloneDeep(originalInput) || {\n        sourceLanguage: locale,\n        strings: {},\n      };\n\n      for (const [key, value] of Object.entries(payload)) {\n        if (value === null || value === undefined) {\n          continue;\n        }\n\n        const hasDoNotTranslateFlag =\n          originalInput &&\n          (originalInput as any).strings &&\n          (originalInput as any).strings[key] &&\n          (originalInput as any).strings[key].shouldTranslate === false;\n\n        if (typeof value === \"string\") {\n          langDataToMerge.strings[key] = {\n            extractionState: originalInput?.strings?.[key]?.extractionState,\n            localizations: {\n              [locale]: {\n                stringUnit: {\n                  state: \"translated\",\n                  value,\n                },\n              },\n            },\n          };\n\n          if (hasDoNotTranslateFlag) {\n            langDataToMerge.strings[key].shouldTranslate = false;\n          }\n        } else if (Array.isArray(value)) {\n          langDataToMerge.strings[key] = {\n            extractionState: originalInput?.strings?.[key]?.extractionState,\n            localizations: {\n              [locale]: {\n                stringSet: {\n                  state: \"translated\",\n                  values: value,\n                },\n              },\n            },\n          };\n\n          if (hasDoNotTranslateFlag) {\n            langDataToMerge.strings[key].shouldTranslate = false;\n          }\n        } else {\n          const updatedVariations: any = {};\n\n          for (const form in value) {\n            updatedVariations[form] = {\n              stringUnit: {\n                state: \"translated\",\n                value: value[form],\n              },\n            };\n          }\n\n          langDataToMerge.strings[key] = {\n            extractionState: \"manual\",\n            localizations: {\n              [locale]: {\n                variations: {\n                  plural: updatedVariations,\n                },\n              },\n            },\n          };\n\n          if (hasDoNotTranslateFlag) {\n            langDataToMerge.strings[key].shouldTranslate = false;\n          }\n        }\n      }\n\n      const originalInputWithoutLocale = originalInput\n        ? _removeLocale(originalInput, locale)\n        : {};\n\n      const result = _.merge({}, originalInputWithoutLocale, langDataToMerge);\n      return result;\n    },\n    async pullHints(originalInput) {\n      if (!originalInput || !originalInput.strings) {\n        return {};\n      }\n\n      const hints: Record<string, any> = {};\n\n      for (const [translationKey, translationEntity] of Object.entries(\n        originalInput.strings,\n      )) {\n        const entity = translationEntity as any;\n\n        // Extract comment field if it exists\n        if (entity.comment && typeof entity.comment === \"string\") {\n          hints[translationKey] = { hint: entity.comment };\n        }\n\n        // For plural forms, we might want to include the base comment for all variants\n        if (entity.localizations) {\n          for (const [locale, localization] of Object.entries(\n            entity.localizations,\n          )) {\n            if ((localization as any).variations?.plural) {\n              const pluralForms = (localization as any).variations.plural;\n              for (const form in pluralForms) {\n                const pluralKey = `${translationKey}/${form}`;\n                if (entity.comment && typeof entity.comment === \"string\") {\n                  hints[pluralKey] = { hint: entity.comment };\n                }\n              }\n            }\n          }\n        }\n      }\n\n      return hints;\n    },\n  });\n}\n\nexport function _removeLocale(input: Record<string, any>, locale: string) {\n  const { strings } = input;\n  const newStrings = _.cloneDeep(strings);\n  for (const [key, value] of Object.entries(newStrings)) {\n    if ((value as any).localizations?.[locale]) {\n      delete (value as any).localizations[locale];\n    }\n  }\n  return { ...input, strings: newStrings };\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport _ from \"lodash\";\n\n/**\n * CLDR plural categories\n */\nconst CLDR_PLURAL_CATEGORIES = new Set([\n  \"zero\",\n  \"one\",\n  \"two\",\n  \"few\",\n  \"many\",\n  \"other\",\n]);\n\n/**\n * Get CLDR plural categories used by a locale\n * @param locale - The locale to check (e.g., \"en\", \"ru\", \"zh\")\n * @returns Array of plural category names used by this locale\n */\nfunction getRequiredPluralCategories(locale: string): string[] {\n  try {\n    const pluralRules = new Intl.PluralRules(locale);\n    const categories = pluralRules.resolvedOptions().pluralCategories;\n    if (!categories || categories.length === 0) {\n      return [\"other\"];\n    }\n    return categories;\n  } catch (error) {\n    // Fallback for unsupported locales - 'other' is the only universally required form\n    return [\"other\"];\n  }\n}\n\n/**\n * Check if a plural form is valid for a given locale\n * Always allows exact match forms (=0, =1, =2)\n * @param form - The plural form to check (e.g., \"one\", \"few\", \"=0\")\n * @param locale - The target locale\n * @returns true if the form should be kept\n */\nfunction isValidPluralForm(form: string, locale: string): boolean {\n  // Always allow exact match forms (=0, =1, =2, etc.)\n  if (form.startsWith(\"=\")) return true;\n\n  // Check if form is a required CLDR category for this locale\n  const requiredCategories = getRequiredPluralCategories(locale);\n  return requiredCategories.includes(form);\n}\n\n/**\n * Build ICU MessageFormat string from xcstrings plural forms\n * Converts optional CLDR forms to exact match syntax for better backend understanding\n * @param forms - Plural forms from xcstrings\n * @param sourceLocale - Source language locale to determine required vs optional forms\n * @returns ICU MessageFormat string\n */\nfunction buildIcuPluralString(\n  forms: Record<string, string>,\n  sourceLocale: string,\n): string {\n  const requiredCategories = new Set(getRequiredPluralCategories(sourceLocale));\n\n  const parts = Object.entries(forms).map(([form, text]) => {\n    // Convert optional CLDR forms to exact match syntax\n    let normalizedForm = form;\n    if (!requiredCategories.has(form)) {\n      if (form === \"zero\") normalizedForm = \"=0\";\n      else if (form === \"one\") normalizedForm = \"=1\";\n      else if (form === \"two\") normalizedForm = \"=2\";\n    }\n    return `${normalizedForm} {${text}}`;\n  });\n\n  return `{count, plural, ${parts.join(\" \")}}`;\n}\n\nfunction parseIcuPluralString(\n  icuString: string,\n  locale: string,\n): Record<string, string> {\n  const pluralMatch = icuString.match(/\\{[\\w]+,\\s*plural,\\s*(.+)\\}$/);\n  if (!pluralMatch) {\n    throw new Error(`Invalid ICU plural format: ${icuString}`);\n  }\n\n  const formsText = pluralMatch[1];\n  const forms: Record<string, string> = {};\n  const exactMatches = new Set<string>(); // Track which forms came from exact matches\n\n  let i = 0;\n  while (i < formsText.length) {\n    // Skip whitespace\n    while (i < formsText.length && /\\s/.test(formsText[i])) {\n      i++;\n    }\n\n    if (i >= formsText.length) break;\n\n    // Read form name (e.g., \"one\", \"other\", \"=0\")\n    let formName = \"\";\n    if (formsText[i] === \"=\") {\n      // Exact match like =0, =1\n      formName += formsText[i];\n      i++;\n      while (i < formsText.length && /\\d/.test(formsText[i])) {\n        formName += formsText[i];\n        i++;\n      }\n    } else {\n      // CLDR keyword like \"one\", \"other\"\n      while (i < formsText.length && /\\w/.test(formsText[i])) {\n        formName += formsText[i];\n        i++;\n      }\n    }\n\n    if (!formName) break;\n\n    // Convert exact match syntax back to CLDR category names\n    if (formName === \"=0\") {\n      formName = \"zero\";\n      exactMatches.add(\"zero\");\n    } else if (formName === \"=1\") {\n      formName = \"one\";\n      exactMatches.add(\"one\");\n    } else if (formName === \"=2\") {\n      formName = \"two\";\n      exactMatches.add(\"two\");\n    }\n\n    // Skip whitespace and find opening brace\n    while (i < formsText.length && /\\s/.test(formsText[i])) {\n      i++;\n    }\n\n    if (i >= formsText.length || formsText[i] !== \"{\") {\n      throw new Error(`Expected '{' after form name '${formName}'`);\n    }\n\n    // Find matching closing brace\n    i++; // skip opening brace\n    let braceCount = 1;\n    let formText = \"\";\n\n    while (i < formsText.length && braceCount > 0) {\n      if (formsText[i] === \"{\") {\n        braceCount++;\n        formText += formsText[i];\n      } else if (formsText[i] === \"}\") {\n        braceCount--;\n        if (braceCount > 0) {\n          formText += formsText[i];\n        }\n      } else {\n        formText += formsText[i];\n      }\n      i++;\n    }\n\n    if (braceCount !== 0) {\n      throw new Error(\n        `Unclosed brace for form '${formName}' in ICU: ${icuString}`,\n      );\n    }\n\n    forms[formName] = formText;\n  }\n\n  const filteredForms: Record<string, string> = {};\n  for (const [form, text] of Object.entries(forms)) {\n    if (exactMatches.has(form) || isValidPluralForm(form, locale)) {\n      filteredForms[form] = text;\n    }\n  }\n\n  return filteredForms;\n}\n\n/**\n * Check if a value looks like an ICU plural string\n */\nfunction isIcuPluralString(value: any): boolean {\n  return typeof value === \"string\" && /^\\{[\\w]+,\\s*plural,\\s*.+\\}$/.test(value);\n}\n\nexport default function createXcodeXcstringsV2Loader(\n  defaultLocale: string,\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    async pull(locale, input, initCtx) {\n      const resultData: Record<string, any> = {};\n      const isSourceLanguage = locale === defaultLocale;\n\n      for (const [translationKey, _translationEntity] of Object.entries(\n        (input as any).strings,\n      )) {\n        const rootTranslationEntity = _translationEntity as any;\n\n        if (rootTranslationEntity.shouldTranslate === false) {\n          continue;\n        }\n\n        const langTranslationEntity =\n          rootTranslationEntity?.localizations?.[locale];\n\n        if (langTranslationEntity) {\n          if (!resultData[translationKey]) {\n            resultData[translationKey] = {};\n          }\n\n          if (\"stringUnit\" in langTranslationEntity) {\n            resultData[translationKey].stringUnit =\n              langTranslationEntity.stringUnit.value;\n\n            if (\"substitutions\" in langTranslationEntity) {\n              resultData[translationKey].substitutions = {};\n\n              for (const [subName, subData] of Object.entries(\n                langTranslationEntity.substitutions as any,\n              )) {\n                const pluralForms = (subData as any).variations?.plural;\n                if (pluralForms) {\n                  const forms: Record<string, string> = {};\n                  for (const [form, formData] of Object.entries(pluralForms)) {\n                    forms[form] = (formData as any).stringUnit.value;\n                  }\n\n                  const icuString = buildIcuPluralString(forms, locale);\n                  resultData[translationKey].substitutions[subName] = {\n                    variations: {\n                      plural: icuString,\n                    },\n                  };\n                }\n              }\n            }\n          } else if (\"stringSet\" in langTranslationEntity) {\n            const values = langTranslationEntity.stringSet.values;\n            if (Array.isArray(values) && values.length > 0) {\n              resultData[translationKey].stringSet = values;\n            }\n          } else if (\"variations\" in langTranslationEntity) {\n            if (\"plural\" in langTranslationEntity.variations) {\n              const pluralForms = langTranslationEntity.variations.plural;\n\n              const forms: Record<string, string> = {};\n              for (const [form, formData] of Object.entries(pluralForms)) {\n                if ((formData as any)?.stringUnit?.value) {\n                  forms[form] = (formData as any).stringUnit.value;\n                }\n              }\n\n              const icuString = buildIcuPluralString(forms, locale);\n              resultData[translationKey].variations = {\n                plural: icuString,\n              };\n            }\n          }\n        } else if (isSourceLanguage) {\n          if (!resultData[translationKey]) {\n            resultData[translationKey] = {};\n          }\n          resultData[translationKey].stringUnit = translationKey;\n        }\n      }\n\n      return resultData;\n    },\n\n    async push(locale, payload, originalInput) {\n      const langDataToMerge: any = {};\n      langDataToMerge.strings = {};\n\n      const input = _.cloneDeep(originalInput) || {\n        sourceLanguage: locale,\n        strings: {},\n      };\n\n      for (const [baseKey, keyData] of Object.entries(payload)) {\n        if (!keyData || typeof keyData !== \"object\") {\n          continue;\n        }\n\n        const hasDoNotTranslateFlag =\n          originalInput &&\n          (originalInput as any).strings &&\n          (originalInput as any).strings[baseKey] &&\n          (originalInput as any).strings[baseKey].shouldTranslate === false;\n\n        const localizationData: any = {};\n\n        if (\"stringUnit\" in keyData) {\n          localizationData.stringUnit = {\n            state: \"translated\",\n            value: keyData.stringUnit,\n          };\n        }\n\n        if (\"substitutions\" in keyData && keyData.substitutions) {\n          const subs: any = {};\n\n          for (const [subName, subData] of Object.entries(\n            keyData.substitutions as any,\n          )) {\n            const pluralValue = (subData as any)?.variations?.plural;\n\n            if (pluralValue && isIcuPluralString(pluralValue)) {\n              try {\n                const pluralForms = parseIcuPluralString(pluralValue, locale);\n                const pluralOut: any = {};\n                for (const [form, text] of Object.entries(pluralForms)) {\n                  pluralOut[form] = {\n                    stringUnit: {\n                      state: \"translated\",\n                      value: text,\n                    },\n                  };\n                }\n\n                const sourceLocale =\n                  (originalInput as any)?.sourceLanguage || \"en\";\n                const origFormatSpec =\n                  (originalInput as any)?.strings?.[baseKey]?.localizations?.[\n                    sourceLocale\n                  ]?.substitutions?.[subName]?.formatSpecifier || subName;\n\n                subs[subName] = {\n                  formatSpecifier: origFormatSpec,\n                  variations: {\n                    plural: pluralOut,\n                  },\n                };\n              } catch (error) {\n                throw new Error(\n                  `Failed to write substitution plural translation for key \"${baseKey}/substitutions/${subName}\" (locale: ${locale}).\\n` +\n                    `${error instanceof Error ? error.message : String(error)}`,\n                );\n              }\n            }\n          }\n\n          if (Object.keys(subs).length > 0) {\n            localizationData.substitutions = subs;\n          }\n        }\n\n        if (\"stringSet\" in keyData && Array.isArray(keyData.stringSet)) {\n          localizationData.stringSet = {\n            state: \"translated\",\n            values: keyData.stringSet,\n          };\n        }\n\n        if (\"variations\" in keyData && keyData.variations?.plural) {\n          const pluralValue = keyData.variations.plural;\n\n          if (isIcuPluralString(pluralValue)) {\n            try {\n              const pluralForms = parseIcuPluralString(pluralValue, locale);\n              const pluralOut: any = {};\n              for (const [form, text] of Object.entries(pluralForms)) {\n                pluralOut[form] = {\n                  stringUnit: {\n                    state: \"translated\",\n                    value: text,\n                  },\n                };\n              }\n\n              localizationData.variations = {\n                plural: pluralOut,\n              };\n            } catch (error) {\n              throw new Error(\n                `Failed to write plural translation for key \"${baseKey}\" (locale: ${locale}).\\n` +\n                  `${error instanceof Error ? error.message : String(error)}`,\n              );\n            }\n          }\n        }\n\n        if (Object.keys(localizationData).length > 0) {\n          langDataToMerge.strings[baseKey] = {\n            extractionState: originalInput?.strings?.[baseKey]?.extractionState,\n            localizations: {\n              [locale]: localizationData,\n            },\n          };\n\n          if (hasDoNotTranslateFlag) {\n            langDataToMerge.strings[baseKey].shouldTranslate = false;\n          }\n        }\n      }\n\n      return _.merge(input, langDataToMerge);\n    },\n\n    async pullHints(input) {\n      const hints: Record<string, any> = {};\n\n      for (const [translationKey, _translationEntity] of Object.entries(\n        (input as any).strings || {},\n      )) {\n        const rootTranslationEntity = _translationEntity as any;\n        if (\n          rootTranslationEntity.comment &&\n          typeof rootTranslationEntity.comment === \"string\"\n        ) {\n          hints[translationKey] = { hint: rootTranslationEntity.comment };\n        }\n      }\n\n      return hints;\n    },\n  });\n}\n","import _ from \"lodash\";\nimport _isUrl from \"is-url\";\nimport { isValid, parseISO } from \"date-fns\";\n\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { matchesKeyPattern } from \"../utils/key-matching\";\n\nexport default function createUnlocalizableLoader(\n  returnUnlocalizedKeys: boolean = false,\n  localizableKeys: string[] = [],\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    async pull(locale, input) {\n      const unlocalizableKeys = _getUnlocalizableKeys(input, localizableKeys);\n\n      const result = _.omitBy(input, (_, key) =>\n        unlocalizableKeys.includes(key),\n      );\n\n      if (returnUnlocalizedKeys) {\n        result.unlocalizable = _.omitBy(\n          input,\n          (_, key) => !unlocalizableKeys.includes(key),\n        );\n      }\n\n      return result;\n    },\n    async push(locale, data, originalInput) {\n      const unlocalizableKeys = _getUnlocalizableKeys(\n        originalInput,\n        localizableKeys,\n      );\n\n      const result = _.merge(\n        {},\n        data,\n        _.omitBy(originalInput, (_, key) => !unlocalizableKeys.includes(key)),\n      );\n\n      return result;\n    },\n  });\n}\n\nfunction _isSystemId(v: string) {\n  return /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z0-9]{22}$/.test(v);\n}\n\nfunction _isIsoDate(v: string) {\n  return isValid(parseISO(v));\n}\n\nfunction _getUnlocalizableKeys(\n  input?: Record<string, any> | null,\n  localizableKeys: string[] = [],\n) {\n  const rules = {\n    isEmpty: (v: any) => _.isEmpty(v),\n    isNumber: (v: any) => typeof v === \"number\" || /^[0-9]+$/.test(v),\n    isBoolean: (v: any) => _.isBoolean(v),\n    isIsoDate: (v: any) => _.isString(v) && _isIsoDate(v),\n    isSystemId: (v: any) => _.isString(v) && _isSystemId(v),\n    isUrl: (v: any) => _.isString(v) && _isUrl(v),\n  };\n\n  if (!input) {\n    return [];\n  }\n\n  return Object.entries(input)\n    .filter(([key, value]) => {\n      if (\n        localizableKeys.length > 0 &&\n        matchesKeyPattern(key, localizableKeys) &&\n        value !== undefined\n      ) {\n        return false;\n      }\n      for (const [ruleName, rule] of Object.entries(rules)) {\n        if (rule(value)) {\n          return true;\n        }\n      }\n      return false;\n    })\n    .map(([key, _]) => key);\n}\n","export const balanced = (\n  a: string | RegExp,\n  b: string | RegExp,\n  str: string,\n) => {\n  const ma = a instanceof RegExp ? maybeMatch(a, str) : a\n  const mb = b instanceof RegExp ? maybeMatch(b, str) : b\n\n  const r = ma !== null && mb != null && range(ma, mb, str)\n\n  return (\n    r && {\n      start: r[0],\n      end: r[1],\n      pre: str.slice(0, r[0]),\n      body: str.slice(r[0] + ma.length, r[1]),\n      post: str.slice(r[1] + mb.length),\n    }\n  )\n}\n\nconst maybeMatch = (reg: RegExp, str: string) => {\n  const m = str.match(reg)\n  return m ? m[0] : null\n}\n\nexport const range = (\n  a: string,\n  b: string,\n  str: string,\n): undefined | [number, number] => {\n  let begs: number[],\n    beg: number | undefined,\n    left: number,\n    right: number | undefined = undefined,\n    result: undefined | [number, number]\n  let ai = str.indexOf(a)\n  let bi = str.indexOf(b, ai + 1)\n  let i = ai\n\n  if (ai >= 0 && bi > 0) {\n    if (a === b) {\n      return [ai, bi]\n    }\n    begs = []\n    left = str.length\n\n    while (i >= 0 && !result) {\n      if (i === ai) {\n        begs.push(i)\n        ai = str.indexOf(a, i + 1)\n      } else if (begs.length === 1) {\n        const r = begs.pop()\n        if (r !== undefined) result = [r, bi]\n      } else {\n        beg = begs.pop()\n        if (beg !== undefined && beg < left) {\n          left = beg\n          right = bi\n        }\n\n        bi = str.indexOf(b, i + 1)\n      }\n\n      i = ai < bi && ai >= 0 ? ai : bi\n    }\n\n    if (begs.length && right !== undefined) {\n      result = [left, right]\n    }\n  }\n\n  return result\n}\n","import { balanced } from '@isaacs/balanced-match'\n\nconst escSlash = '\\0SLASH' + Math.random() + '\\0'\nconst escOpen = '\\0OPEN' + Math.random() + '\\0'\nconst escClose = '\\0CLOSE' + Math.random() + '\\0'\nconst escComma = '\\0COMMA' + Math.random() + '\\0'\nconst escPeriod = '\\0PERIOD' + Math.random() + '\\0'\nconst escSlashPattern = new RegExp(escSlash, 'g')\nconst escOpenPattern = new RegExp(escOpen, 'g')\nconst escClosePattern = new RegExp(escClose, 'g')\nconst escCommaPattern = new RegExp(escComma, 'g')\nconst escPeriodPattern = new RegExp(escPeriod, 'g')\nconst slashPattern = /\\\\\\\\/g\nconst openPattern = /\\\\{/g\nconst closePattern = /\\\\}/g\nconst commaPattern = /\\\\,/g\nconst periodPattern = /\\\\./g\n\nfunction numeric(str: string) {\n  return !isNaN(str as any) ? parseInt(str, 10) : str.charCodeAt(0)\n}\n\nfunction escapeBraces(str: string) {\n  return str\n    .replace(slashPattern, escSlash)\n    .replace(openPattern, escOpen)\n    .replace(closePattern, escClose)\n    .replace(commaPattern, escComma)\n    .replace(periodPattern, escPeriod)\n}\n\nfunction unescapeBraces(str: string) {\n  return str\n    .replace(escSlashPattern, '\\\\')\n    .replace(escOpenPattern, '{')\n    .replace(escClosePattern, '}')\n    .replace(escCommaPattern, ',')\n    .replace(escPeriodPattern, '.')\n}\n\n/**\n * Basically just str.split(\",\"), but handling cases\n * where we have nested braced sections, which should be\n * treated as individual members, like {a,{b,c},d}\n */\nfunction parseCommaParts(str: string) {\n  if (!str) {\n    return ['']\n  }\n\n  const parts: string[] = []\n  const m = balanced('{', '}', str)\n\n  if (!m) {\n    return str.split(',')\n  }\n\n  const { pre, body, post } = m\n  const p = pre.split(',')\n\n  p[p.length - 1] += '{' + body + '}'\n  const postParts = parseCommaParts(post)\n  if (post.length) {\n    ;(p[p.length - 1] as string) += postParts.shift()\n    p.push.apply(p, postParts)\n  }\n\n  parts.push.apply(parts, p)\n\n  return parts\n}\n\nexport function expand(str: string) {\n  if (!str) {\n    return []\n  }\n\n  // I don't know why Bash 4.3 does this, but it does.\n  // Anything starting with {} will have the first two bytes preserved\n  // but *only* at the top level, so {},a}b will not expand to anything,\n  // but a{},b}c will be expanded to [a}c,abc].\n  // One could argue that this is a bug in Bash, but since the goal of\n  // this module is to match Bash's rules, we escape a leading {}\n  if (str.slice(0, 2) === '{}') {\n    str = '\\\\{\\\\}' + str.slice(2)\n  }\n\n  return expand_(escapeBraces(str), true).map(unescapeBraces)\n}\n\nfunction embrace(str: string) {\n  return '{' + str + '}'\n}\n\nfunction isPadded(el: string) {\n  return /^-?0\\d/.test(el)\n}\n\nfunction lte(i: number, y: number) {\n  return i <= y\n}\n\nfunction gte(i: number, y: number) {\n  return i >= y\n}\n\nfunction expand_(str: string, isTop?: boolean): string[] {\n  /** @type {string[]} */\n  const expansions: string[] = []\n\n  const m = balanced('{', '}', str)\n  if (!m) return [str]\n\n  // no need to expand pre, since it is guaranteed to be free of brace-sets\n  const pre = m.pre\n  const post: string[] = m.post.length ? expand_(m.post, false) : ['']\n\n  if (/\\$$/.test(m.pre)) {\n    for (let k = 0; k < post.length; k++) {\n      const expansion = pre + '{' + m.body + '}' + post[k]\n      expansions.push(expansion)\n    }\n  } else {\n    const isNumericSequence = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/.test(m.body)\n    const isAlphaSequence = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/.test(m.body)\n    const isSequence = isNumericSequence || isAlphaSequence\n    const isOptions = m.body.indexOf(',') >= 0\n    if (!isSequence && !isOptions) {\n      // {a},b}\n      if (m.post.match(/,(?!,).*\\}/)) {\n        str = m.pre + '{' + m.body + escClose + m.post\n        return expand_(str)\n      }\n      return [str]\n    }\n\n    let n: string[]\n    if (isSequence) {\n      n = m.body.split(/\\.\\./)\n    } else {\n      n = parseCommaParts(m.body)\n      if (n.length === 1 && n[0] !== undefined) {\n        // x{{a,b}}y ==> x{a}y x{b}y\n        n = expand_(n[0], false).map(embrace)\n        //XXX is this necessary? Can't seem to hit it in tests.\n        /* c8 ignore start */\n        if (n.length === 1) {\n          return post.map(p => m.pre + n[0] + p)\n        }\n        /* c8 ignore stop */\n      }\n    }\n\n    // at this point, n is the parts, and we know it's not a comma set\n    // with a single entry.\n    let N: string[]\n\n    if (isSequence && n[0] !== undefined && n[1] !== undefined) {\n      const x = numeric(n[0])\n      const y = numeric(n[1])\n      const width = Math.max(n[0].length, n[1].length)\n      let incr =\n        n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1\n      let test = lte\n      const reverse = y < x\n      if (reverse) {\n        incr *= -1\n        test = gte\n      }\n      const pad = n.some(isPadded)\n\n      N = []\n\n      for (let i = x; test(i, y); i += incr) {\n        let c\n        if (isAlphaSequence) {\n          c = String.fromCharCode(i)\n          if (c === '\\\\') {\n            c = ''\n          }\n        } else {\n          c = String(i)\n          if (pad) {\n            const need = width - c.length\n            if (need > 0) {\n              const z = new Array(need + 1).join('0')\n              if (i < 0) {\n                c = '-' + z + c.slice(1)\n              } else {\n                c = z + c\n              }\n            }\n          }\n        }\n        N.push(c)\n      }\n    } else {\n      N = []\n\n      for (let j = 0; j < n.length; j++) {\n        N.push.apply(N, expand_(n[j] as string, false))\n      }\n    }\n\n    for (let j = 0; j < N.length; j++) {\n      for (let k = 0; k < post.length; k++) {\n        const expansion = pre + N[j] + post[k]\n        if (!isTop || isSequence || expansion) {\n          expansions.push(expansion)\n        }\n      }\n    }\n  }\n\n  return expansions\n}\n","const MAX_PATTERN_LENGTH = 1024 * 64\nexport const assertValidPattern: (pattern: any) => void = (\n  pattern: any,\n): asserts pattern is string => {\n  if (typeof pattern !== 'string') {\n    throw new TypeError('invalid pattern')\n  }\n\n  if (pattern.length > MAX_PATTERN_LENGTH) {\n    throw new TypeError('pattern is too long')\n  }\n}\n","// translate the various posix character classes into unicode properties\n// this works across all unicode locales\n\n// { <posix class>: [<translation>, /u flag required, negated]\nconst posixClasses: { [k: string]: [e: string, u: boolean, n?: boolean] } = {\n  '[:alnum:]': ['\\\\p{L}\\\\p{Nl}\\\\p{Nd}', true],\n  '[:alpha:]': ['\\\\p{L}\\\\p{Nl}', true],\n  '[:ascii:]': ['\\\\x' + '00-\\\\x' + '7f', false],\n  '[:blank:]': ['\\\\p{Zs}\\\\t', true],\n  '[:cntrl:]': ['\\\\p{Cc}', true],\n  '[:digit:]': ['\\\\p{Nd}', true],\n  '[:graph:]': ['\\\\p{Z}\\\\p{C}', true, true],\n  '[:lower:]': ['\\\\p{Ll}', true],\n  '[:print:]': ['\\\\p{C}', true],\n  '[:punct:]': ['\\\\p{P}', true],\n  '[:space:]': ['\\\\p{Z}\\\\t\\\\r\\\\n\\\\v\\\\f', true],\n  '[:upper:]': ['\\\\p{Lu}', true],\n  '[:word:]': ['\\\\p{L}\\\\p{Nl}\\\\p{Nd}\\\\p{Pc}', true],\n  '[:xdigit:]': ['A-Fa-f0-9', false],\n}\n\n// only need to escape a few things inside of brace expressions\n// escapes: [ \\ ] -\nconst braceEscape = (s: string) => s.replace(/[[\\]\\\\-]/g, '\\\\$&')\n// escape all regexp magic characters\nconst regexpEscape = (s: string) =>\n  s.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&')\n\n// everything has already been escaped, we just have to join\nconst rangesToString = (ranges: string[]): string => ranges.join('')\n\nexport type ParseClassResult = [\n  src: string,\n  uFlag: boolean,\n  consumed: number,\n  hasMagic: boolean,\n]\n\n// takes a glob string at a posix brace expression, and returns\n// an equivalent regular expression source, and boolean indicating\n// whether the /u flag needs to be applied, and the number of chars\n// consumed to parse the character class.\n// This also removes out of order ranges, and returns ($.) if the\n// entire class just no good.\nexport const parseClass = (\n  glob: string,\n  position: number,\n): ParseClassResult => {\n  const pos = position\n  /* c8 ignore start */\n  if (glob.charAt(pos) !== '[') {\n    throw new Error('not in a brace expression')\n  }\n  /* c8 ignore stop */\n  const ranges: string[] = []\n  const negs: string[] = []\n\n  let i = pos + 1\n  let sawStart = false\n  let uflag = false\n  let escaping = false\n  let negate = false\n  let endPos = pos\n  let rangeStart = ''\n  WHILE: while (i < glob.length) {\n    const c = glob.charAt(i)\n    if ((c === '!' || c === '^') && i === pos + 1) {\n      negate = true\n      i++\n      continue\n    }\n\n    if (c === ']' && sawStart && !escaping) {\n      endPos = i + 1\n      break\n    }\n\n    sawStart = true\n    if (c === '\\\\') {\n      if (!escaping) {\n        escaping = true\n        i++\n        continue\n      }\n      // escaped \\ char, fall through and treat like normal char\n    }\n    if (c === '[' && !escaping) {\n      // either a posix class, a collation equivalent, or just a [\n      for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) {\n        if (glob.startsWith(cls, i)) {\n          // invalid, [a-[] is fine, but not [a-[:alpha]]\n          if (rangeStart) {\n            return ['$.', false, glob.length - pos, true]\n          }\n          i += cls.length\n          if (neg) negs.push(unip)\n          else ranges.push(unip)\n          uflag = uflag || u\n          continue WHILE\n        }\n      }\n    }\n\n    // now it's just a normal character, effectively\n    escaping = false\n    if (rangeStart) {\n      // throw this range away if it's not valid, but others\n      // can still match.\n      if (c > rangeStart) {\n        ranges.push(braceEscape(rangeStart) + '-' + braceEscape(c))\n      } else if (c === rangeStart) {\n        ranges.push(braceEscape(c))\n      }\n      rangeStart = ''\n      i++\n      continue\n    }\n\n    // now might be the start of a range.\n    // can be either c-d or c-] or c<more...>] or c] at this point\n    if (glob.startsWith('-]', i + 1)) {\n      ranges.push(braceEscape(c + '-'))\n      i += 2\n      continue\n    }\n    if (glob.startsWith('-', i + 1)) {\n      rangeStart = c\n      i += 2\n      continue\n    }\n\n    // not the start of a range, just a single character\n    ranges.push(braceEscape(c))\n    i++\n  }\n\n  if (endPos < i) {\n    // didn't see the end of the class, not a valid class,\n    // but might still be valid as a literal match.\n    return ['', false, 0, false]\n  }\n\n  // if we got no ranges and no negates, then we have a range that\n  // cannot possibly match anything, and that poisons the whole glob\n  if (!ranges.length && !negs.length) {\n    return ['$.', false, glob.length - pos, true]\n  }\n\n  // if we got one positive range, and it's a single character, then that's\n  // not actually a magic pattern, it's just that one literal character.\n  // we should not treat that as \"magic\", we should just return the literal\n  // character. [_] is a perfectly valid way to escape glob magic chars.\n  if (\n    negs.length === 0 &&\n    ranges.length === 1 &&\n    /^\\\\?.$/.test(ranges[0]) &&\n    !negate\n  ) {\n    const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0]\n    return [regexpEscape(r), false, endPos - pos, false]\n  }\n\n  const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']'\n  const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']'\n  const comb =\n    ranges.length && negs.length\n      ? '(' + sranges + '|' + snegs + ')'\n      : ranges.length\n        ? sranges\n        : snegs\n\n  return [comb, uflag, endPos - pos, true]\n}\n","import { MinimatchOptions } from './index.js'\n\n/**\n * Un-escape a string that has been escaped with {@link escape}.\n *\n * If the {@link MinimatchOptions.windowsPathsNoEscape} option is used, then\n * square-bracket escapes are removed, but not backslash escapes.\n *\n * For example, it will turn the string `'[*]'` into `*`, but it will not\n * turn `'\\\\*'` into `'*'`, because `\\` is a path separator in\n * `windowsPathsNoEscape` mode.\n *\n * When `windowsPathsNoEscape` is not set, then both square-bracket escapes and\n * backslash escapes are removed.\n *\n * Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped\n * or unescaped.\n *\n * When `magicalBraces` is not set, escapes of braces (`{` and `}`) will not be\n * unescaped.\n */\n\nexport const unescape = (\n  s: string,\n  {\n    windowsPathsNoEscape = false,\n    magicalBraces = true,\n  }: Pick<MinimatchOptions, 'windowsPathsNoEscape' | 'magicalBraces'> = {},\n) => {\n  if (magicalBraces) {\n    return windowsPathsNoEscape\n      ? s.replace(/\\[([^\\/\\\\])\\]/g, '$1')\n      : s\n          .replace(/((?!\\\\).|^)\\[([^\\/\\\\])\\]/g, '$1$2')\n          .replace(/\\\\([^\\/])/g, '$1')\n  }\n  return windowsPathsNoEscape\n    ? s.replace(/\\[([^\\/\\\\{}])\\]/g, '$1')\n    : s\n        .replace(/((?!\\\\).|^)\\[([^\\/\\\\{}])\\]/g, '$1$2')\n        .replace(/\\\\([^\\/{}])/g, '$1')\n}\n","// parse a single path portion\n\nimport { parseClass } from './brace-expressions.js'\nimport { MinimatchOptions, MMRegExp } from './index.js'\nimport { unescape } from './unescape.js'\n\n// classes [] are handled by the parseClass method\n// for positive extglobs, we sub-parse the contents, and combine,\n// with the appropriate regexp close.\n// for negative extglobs, we sub-parse the contents, but then\n// have to include the rest of the pattern, then the parent, etc.,\n// as the thing that cannot be because RegExp negative lookaheads\n// are different from globs.\n//\n// So for example:\n// a@(i|w!(x|y)z|j)b => ^a(i|w((!?(x|y)zb).*)z|j)b$\n//   1   2 3   4 5 6      1   2    3   46      5 6\n//\n// Assembling the extglob requires not just the negated patterns themselves,\n// but also anything following the negative patterns up to the boundary\n// of the current pattern, plus anything following in the parent pattern.\n//\n//\n// So, first, we parse the string into an AST of extglobs, without turning\n// anything into regexps yet.\n//\n// ['a', {@ [['i'], ['w', {!['x', 'y']}, 'z'], ['j']]}, 'b']\n//\n// Then, for all the negative extglobs, we append whatever comes after in\n// each parent as their tail\n//\n// ['a', {@ [['i'], ['w', {!['x', 'y'], 'z', 'b'}, 'z'], ['j']]}, 'b']\n//\n// Lastly, we turn each of these pieces into a regexp, and join\n//\n//                                 v----- .* because there's more following,\n//                                 v    v  otherwise, .+ because it must be\n//                                 v    v  *something* there.\n// ['^a', {@ ['i', 'w(?:(!?(?:x|y).*zb$).*)z', 'j' ]}, 'b$']\n//   copy what follows into here--^^^^^\n// ['^a', '(?:i|w(?:(?!(?:x|y).*zb$).*)z|j)', 'b$']\n// ['^a(?:i|w(?:(?!(?:x|y).*zb$).*)z|j)b$']\n\nexport type ExtglobType = '!' | '?' | '+' | '*' | '@'\nconst types = new Set<ExtglobType>(['!', '?', '+', '*', '@'])\nconst isExtglobType = (c: string): c is ExtglobType =>\n  types.has(c as ExtglobType)\n\n// Patterns that get prepended to bind to the start of either the\n// entire string, or just a single path portion, to prevent dots\n// and/or traversal patterns, when needed.\n// Exts don't need the ^ or / bit, because the root binds that already.\nconst startNoTraversal = '(?!(?:^|/)\\\\.\\\\.?(?:$|/))'\nconst startNoDot = '(?!\\\\.)'\n\n// characters that indicate a start of pattern needs the \"no dots\" bit,\n// because a dot *might* be matched. ( is not in the list, because in\n// the case of a child extglob, it will handle the prevention itself.\nconst addPatternStart = new Set(['[', '.'])\n// cases where traversal is A-OK, no dot prevention needed\nconst justDots = new Set(['..', '.'])\nconst reSpecials = new Set('().*{}+?[]^$\\\\!')\nconst regExpEscape = (s: string) =>\n  s.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&')\n\n// any single thing other than /\nconst qmark = '[^/]'\n\n// * => any number of characters\nconst star = qmark + '*?'\n// use + when we need to ensure that *something* matches, because the * is\n// the only thing in the path portion.\nconst starNoEmpty = qmark + '+?'\n\n// remove the \\ chars that we added if we end up doing a nonmagic compare\n// const deslash = (s: string) => s.replace(/\\\\(.)/g, '$1')\n\nexport class AST {\n  type: ExtglobType | null\n  readonly #root: AST\n\n  #hasMagic?: boolean\n  #uflag: boolean = false\n  #parts: (string | AST)[] = []\n  readonly #parent?: AST\n  readonly #parentIndex: number\n  #negs: AST[]\n  #filledNegs: boolean = false\n  #options: MinimatchOptions\n  #toString?: string\n  // set to true if it's an extglob with no children\n  // (which really means one child of '')\n  #emptyExt: boolean = false\n\n  constructor(\n    type: ExtglobType | null,\n    parent?: AST,\n    options: MinimatchOptions = {},\n  ) {\n    this.type = type\n    // extglobs are inherently magical\n    if (type) this.#hasMagic = true\n    this.#parent = parent\n    this.#root = this.#parent ? this.#parent.#root : this\n    this.#options = this.#root === this ? options : this.#root.#options\n    this.#negs = this.#root === this ? [] : this.#root.#negs\n    if (type === '!' && !this.#root.#filledNegs) this.#negs.push(this)\n    this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0\n  }\n\n  get hasMagic(): boolean | undefined {\n    /* c8 ignore start */\n    if (this.#hasMagic !== undefined) return this.#hasMagic\n    /* c8 ignore stop */\n    for (const p of this.#parts) {\n      if (typeof p === 'string') continue\n      if (p.type || p.hasMagic) return (this.#hasMagic = true)\n    }\n    // note: will be undefined until we generate the regexp src and find out\n    return this.#hasMagic\n  }\n\n  // reconstructs the pattern\n  toString(): string {\n    if (this.#toString !== undefined) return this.#toString\n    if (!this.type) {\n      return (this.#toString = this.#parts.map(p => String(p)).join(''))\n    } else {\n      return (this.#toString =\n        this.type + '(' + this.#parts.map(p => String(p)).join('|') + ')')\n    }\n  }\n\n  #fillNegs() {\n    /* c8 ignore start */\n    if (this !== this.#root) throw new Error('should only call on root')\n    if (this.#filledNegs) return this\n    /* c8 ignore stop */\n\n    // call toString() once to fill this out\n    this.toString()\n    this.#filledNegs = true\n    let n: AST | undefined\n    while ((n = this.#negs.pop())) {\n      if (n.type !== '!') continue\n      // walk up the tree, appending everthing that comes AFTER parentIndex\n      let p: AST | undefined = n\n      let pp = p.#parent\n      while (pp) {\n        for (\n          let i = p.#parentIndex + 1;\n          !pp.type && i < pp.#parts.length;\n          i++\n        ) {\n          for (const part of n.#parts) {\n            /* c8 ignore start */\n            if (typeof part === 'string') {\n              throw new Error('string part in extglob AST??')\n            }\n            /* c8 ignore stop */\n            part.copyIn(pp.#parts[i])\n          }\n        }\n        p = pp\n        pp = p.#parent\n      }\n    }\n    return this\n  }\n\n  push(...parts: (string | AST)[]) {\n    for (const p of parts) {\n      if (p === '') continue\n      /* c8 ignore start */\n      if (typeof p !== 'string' && !(p instanceof AST && p.#parent === this)) {\n        throw new Error('invalid part: ' + p)\n      }\n      /* c8 ignore stop */\n      this.#parts.push(p)\n    }\n  }\n\n  toJSON() {\n    const ret: any[] =\n      this.type === null\n        ? this.#parts.slice().map(p => (typeof p === 'string' ? p : p.toJSON()))\n        : [this.type, ...this.#parts.map(p => (p as AST).toJSON())]\n    if (this.isStart() && !this.type) ret.unshift([])\n    if (\n      this.isEnd() &&\n      (this === this.#root ||\n        (this.#root.#filledNegs && this.#parent?.type === '!'))\n    ) {\n      ret.push({})\n    }\n    return ret\n  }\n\n  isStart(): boolean {\n    if (this.#root === this) return true\n    // if (this.type) return !!this.#parent?.isStart()\n    if (!this.#parent?.isStart()) return false\n    if (this.#parentIndex === 0) return true\n    // if everything AHEAD of this is a negation, then it's still the \"start\"\n    const p = this.#parent\n    for (let i = 0; i < this.#parentIndex; i++) {\n      const pp = p.#parts[i]\n      if (!(pp instanceof AST && pp.type === '!')) {\n        return false\n      }\n    }\n    return true\n  }\n\n  isEnd(): boolean {\n    if (this.#root === this) return true\n    if (this.#parent?.type === '!') return true\n    if (!this.#parent?.isEnd()) return false\n    if (!this.type) return this.#parent?.isEnd()\n    // if not root, it'll always have a parent\n    /* c8 ignore start */\n    const pl = this.#parent ? this.#parent.#parts.length : 0\n    /* c8 ignore stop */\n    return this.#parentIndex === pl - 1\n  }\n\n  copyIn(part: AST | string) {\n    if (typeof part === 'string') this.push(part)\n    else this.push(part.clone(this))\n  }\n\n  clone(parent: AST) {\n    const c = new AST(this.type, parent)\n    for (const p of this.#parts) {\n      c.copyIn(p)\n    }\n    return c\n  }\n\n  static #parseAST(\n    str: string,\n    ast: AST,\n    pos: number,\n    opt: MinimatchOptions,\n  ): number {\n    let escaping = false\n    let inBrace = false\n    let braceStart = -1\n    let braceNeg = false\n    if (ast.type === null) {\n      // outside of a extglob, append until we find a start\n      let i = pos\n      let acc = ''\n      while (i < str.length) {\n        const c = str.charAt(i++)\n        // still accumulate escapes at this point, but we do ignore\n        // starts that are escaped\n        if (escaping || c === '\\\\') {\n          escaping = !escaping\n          acc += c\n          continue\n        }\n\n        if (inBrace) {\n          if (i === braceStart + 1) {\n            if (c === '^' || c === '!') {\n              braceNeg = true\n            }\n          } else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {\n            inBrace = false\n          }\n          acc += c\n          continue\n        } else if (c === '[') {\n          inBrace = true\n          braceStart = i\n          braceNeg = false\n          acc += c\n          continue\n        }\n\n        if (!opt.noext && isExtglobType(c) && str.charAt(i) === '(') {\n          ast.push(acc)\n          acc = ''\n          const ext = new AST(c, ast)\n          i = AST.#parseAST(str, ext, i, opt)\n          ast.push(ext)\n          continue\n        }\n        acc += c\n      }\n      ast.push(acc)\n      return i\n    }\n\n    // some kind of extglob, pos is at the (\n    // find the next | or )\n    let i = pos + 1\n    let part = new AST(null, ast)\n    const parts: AST[] = []\n    let acc = ''\n    while (i < str.length) {\n      const c = str.charAt(i++)\n      // still accumulate escapes at this point, but we do ignore\n      // starts that are escaped\n      if (escaping || c === '\\\\') {\n        escaping = !escaping\n        acc += c\n        continue\n      }\n\n      if (inBrace) {\n        if (i === braceStart + 1) {\n          if (c === '^' || c === '!') {\n            braceNeg = true\n          }\n        } else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {\n          inBrace = false\n        }\n        acc += c\n        continue\n      } else if (c === '[') {\n        inBrace = true\n        braceStart = i\n        braceNeg = false\n        acc += c\n        continue\n      }\n\n      if (isExtglobType(c) && str.charAt(i) === '(') {\n        part.push(acc)\n        acc = ''\n        const ext = new AST(c, part)\n        part.push(ext)\n        i = AST.#parseAST(str, ext, i, opt)\n        continue\n      }\n      if (c === '|') {\n        part.push(acc)\n        acc = ''\n        parts.push(part)\n        part = new AST(null, ast)\n        continue\n      }\n      if (c === ')') {\n        if (acc === '' && ast.#parts.length === 0) {\n          ast.#emptyExt = true\n        }\n        part.push(acc)\n        acc = ''\n        ast.push(...parts, part)\n        return i\n      }\n      acc += c\n    }\n\n    // unfinished extglob\n    // if we got here, it was a malformed extglob! not an extglob, but\n    // maybe something else in there.\n    ast.type = null\n    ast.#hasMagic = undefined\n    ast.#parts = [str.substring(pos - 1)]\n    return i\n  }\n\n  static fromGlob(pattern: string, options: MinimatchOptions = {}) {\n    const ast = new AST(null, undefined, options)\n    AST.#parseAST(pattern, ast, 0, options)\n    return ast\n  }\n\n  // returns the regular expression if there's magic, or the unescaped\n  // string if not.\n  toMMPattern(): MMRegExp | string {\n    // should only be called on root\n    /* c8 ignore start */\n    if (this !== this.#root) return this.#root.toMMPattern()\n    /* c8 ignore stop */\n    const glob = this.toString()\n    const [re, body, hasMagic, uflag] = this.toRegExpSource()\n    // if we're in nocase mode, and not nocaseMagicOnly, then we do\n    // still need a regular expression if we have to case-insensitively\n    // match capital/lowercase characters.\n    const anyMagic =\n      hasMagic ||\n      this.#hasMagic ||\n      (this.#options.nocase &&\n        !this.#options.nocaseMagicOnly &&\n        glob.toUpperCase() !== glob.toLowerCase())\n    if (!anyMagic) {\n      return body\n    }\n\n    const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '')\n    return Object.assign(new RegExp(`^${re}$`, flags), {\n      _src: re,\n      _glob: glob,\n    })\n  }\n\n  get options() {\n    return this.#options\n  }\n\n  // returns the string match, the regexp source, whether there's magic\n  // in the regexp (so a regular expression is required) and whether or\n  // not the uflag is needed for the regular expression (for posix classes)\n  // TODO: instead of injecting the start/end at this point, just return\n  // the BODY of the regexp, along with the start/end portions suitable\n  // for binding the start/end in either a joined full-path makeRe context\n  // (where we bind to (^|/), or a standalone matchPart context (where\n  // we bind to ^, and not /).  Otherwise slashes get duped!\n  //\n  // In part-matching mode, the start is:\n  // - if not isStart: nothing\n  // - if traversal possible, but not allowed: ^(?!\\.\\.?$)\n  // - if dots allowed or not possible: ^\n  // - if dots possible and not allowed: ^(?!\\.)\n  // end is:\n  // - if not isEnd(): nothing\n  // - else: $\n  //\n  // In full-path matching mode, we put the slash at the START of the\n  // pattern, so start is:\n  // - if first pattern: same as part-matching mode\n  // - if not isStart(): nothing\n  // - if traversal possible, but not allowed: /(?!\\.\\.?(?:$|/))\n  // - if dots allowed or not possible: /\n  // - if dots possible and not allowed: /(?!\\.)\n  // end is:\n  // - if last pattern, same as part-matching mode\n  // - else nothing\n  //\n  // Always put the (?:$|/) on negated tails, though, because that has to be\n  // there to bind the end of the negated pattern portion, and it's easier to\n  // just stick it in now rather than try to inject it later in the middle of\n  // the pattern.\n  //\n  // We can just always return the same end, and leave it up to the caller\n  // to know whether it's going to be used joined or in parts.\n  // And, if the start is adjusted slightly, can do the same there:\n  // - if not isStart: nothing\n  // - if traversal possible, but not allowed: (?:/|^)(?!\\.\\.?$)\n  // - if dots allowed or not possible: (?:/|^)\n  // - if dots possible and not allowed: (?:/|^)(?!\\.)\n  //\n  // But it's better to have a simpler binding without a conditional, for\n  // performance, so probably better to return both start options.\n  //\n  // Then the caller just ignores the end if it's not the first pattern,\n  // and the start always gets applied.\n  //\n  // But that's always going to be $ if it's the ending pattern, or nothing,\n  // so the caller can just attach $ at the end of the pattern when building.\n  //\n  // So the todo is:\n  // - better detect what kind of start is needed\n  // - return both flavors of starting pattern\n  // - attach $ at the end of the pattern when creating the actual RegExp\n  //\n  // Ah, but wait, no, that all only applies to the root when the first pattern\n  // is not an extglob. If the first pattern IS an extglob, then we need all\n  // that dot prevention biz to live in the extglob portions, because eg\n  // +(*|.x*) can match .xy but not .yx.\n  //\n  // So, return the two flavors if it's #root and the first child is not an\n  // AST, otherwise leave it to the child AST to handle it, and there,\n  // use the (?:^|/) style of start binding.\n  //\n  // Even simplified further:\n  // - Since the start for a join is eg /(?!\\.) and the start for a part\n  // is ^(?!\\.), we can just prepend (?!\\.) to the pattern (either root\n  // or start or whatever) and prepend ^ or / at the Regexp construction.\n  toRegExpSource(\n    allowDot?: boolean,\n  ): [re: string, body: string, hasMagic: boolean, uflag: boolean] {\n    const dot = allowDot ?? !!this.#options.dot\n    if (this.#root === this) this.#fillNegs()\n    if (!this.type) {\n      const noEmpty =\n        this.isStart() &&\n        this.isEnd() &&\n        !this.#parts.some(s => typeof s !== 'string')\n      const src = this.#parts\n        .map(p => {\n          const [re, _, hasMagic, uflag] =\n            typeof p === 'string'\n              ? AST.#parseGlob(p, this.#hasMagic, noEmpty)\n              : p.toRegExpSource(allowDot)\n          this.#hasMagic = this.#hasMagic || hasMagic\n          this.#uflag = this.#uflag || uflag\n          return re\n        })\n        .join('')\n\n      let start = ''\n      if (this.isStart()) {\n        if (typeof this.#parts[0] === 'string') {\n          // this is the string that will match the start of the pattern,\n          // so we need to protect against dots and such.\n\n          // '.' and '..' cannot match unless the pattern is that exactly,\n          // even if it starts with . or dot:true is set.\n          const dotTravAllowed =\n            this.#parts.length === 1 && justDots.has(this.#parts[0])\n          if (!dotTravAllowed) {\n            const aps = addPatternStart\n            // check if we have a possibility of matching . or ..,\n            // and prevent that.\n            const needNoTrav =\n              // dots are allowed, and the pattern starts with [ or .\n              (dot && aps.has(src.charAt(0))) ||\n              // the pattern starts with \\., and then [ or .\n              (src.startsWith('\\\\.') && aps.has(src.charAt(2))) ||\n              // the pattern starts with \\.\\., and then [ or .\n              (src.startsWith('\\\\.\\\\.') && aps.has(src.charAt(4)))\n            // no need to prevent dots if it can't match a dot, or if a\n            // sub-pattern will be preventing it anyway.\n            const needNoDot = !dot && !allowDot && aps.has(src.charAt(0))\n\n            start = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : ''\n          }\n        }\n      }\n\n      // append the \"end of path portion\" pattern to negation tails\n      let end = ''\n      if (\n        this.isEnd() &&\n        this.#root.#filledNegs &&\n        this.#parent?.type === '!'\n      ) {\n        end = '(?:$|\\\\/)'\n      }\n      const final = start + src + end\n      return [\n        final,\n        unescape(src),\n        (this.#hasMagic = !!this.#hasMagic),\n        this.#uflag,\n      ]\n    }\n\n    // We need to calculate the body *twice* if it's a repeat pattern\n    // at the start, once in nodot mode, then again in dot mode, so a\n    // pattern like *(?) can match 'x.y'\n\n    const repeated = this.type === '*' || this.type === '+'\n    // some kind of extglob\n    const start = this.type === '!' ? '(?:(?!(?:' : '(?:'\n    let body = this.#partsToRegExp(dot)\n\n    if (this.isStart() && this.isEnd() && !body && this.type !== '!') {\n      // invalid extglob, has to at least be *something* present, if it's\n      // the entire path portion.\n      const s = this.toString()\n      this.#parts = [s]\n      this.type = null\n      this.#hasMagic = undefined\n      return [s, unescape(this.toString()), false, false]\n    }\n\n    // XXX abstract out this map method\n    let bodyDotAllowed =\n      !repeated || allowDot || dot || !startNoDot\n        ? ''\n        : this.#partsToRegExp(true)\n    if (bodyDotAllowed === body) {\n      bodyDotAllowed = ''\n    }\n    if (bodyDotAllowed) {\n      body = `(?:${body})(?:${bodyDotAllowed})*?`\n    }\n\n    // an empty !() is exactly equivalent to a starNoEmpty\n    let final = ''\n    if (this.type === '!' && this.#emptyExt) {\n      final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty\n    } else {\n      const close =\n        this.type === '!'\n          ? // !() must match something,but !(x) can match ''\n            '))' +\n            (this.isStart() && !dot && !allowDot ? startNoDot : '') +\n            star +\n            ')'\n          : this.type === '@'\n            ? ')'\n            : this.type === '?'\n              ? ')?'\n              : this.type === '+' && bodyDotAllowed\n                ? ')'\n                : this.type === '*' && bodyDotAllowed\n                  ? `)?`\n                  : `)${this.type}`\n      final = start + body + close\n    }\n    return [\n      final,\n      unescape(body),\n      (this.#hasMagic = !!this.#hasMagic),\n      this.#uflag,\n    ]\n  }\n\n  #partsToRegExp(dot: boolean) {\n    return this.#parts\n      .map(p => {\n        // extglob ASTs should only contain parent ASTs\n        /* c8 ignore start */\n        if (typeof p === 'string') {\n          throw new Error('string type in extglob ast??')\n        }\n        /* c8 ignore stop */\n        // can ignore hasMagic, because extglobs are already always magic\n        const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot)\n        this.#uflag = this.#uflag || uflag\n        return re\n      })\n      .filter(p => !(this.isStart() && this.isEnd()) || !!p)\n      .join('|')\n  }\n\n  static #parseGlob(\n    glob: string,\n    hasMagic: boolean | undefined,\n    noEmpty: boolean = false,\n  ): [re: string, body: string, hasMagic: boolean, uflag: boolean] {\n    let escaping = false\n    let re = ''\n    let uflag = false\n    for (let i = 0; i < glob.length; i++) {\n      const c = glob.charAt(i)\n      if (escaping) {\n        escaping = false\n        re += (reSpecials.has(c) ? '\\\\' : '') + c\n        continue\n      }\n      if (c === '\\\\') {\n        if (i === glob.length - 1) {\n          re += '\\\\\\\\'\n        } else {\n          escaping = true\n        }\n        continue\n      }\n      if (c === '[') {\n        const [src, needUflag, consumed, magic] = parseClass(glob, i)\n        if (consumed) {\n          re += src\n          uflag = uflag || needUflag\n          i += consumed - 1\n          hasMagic = hasMagic || magic\n          continue\n        }\n      }\n      if (c === '*') {\n        re += noEmpty && glob === '*' ? starNoEmpty : star\n        hasMagic = true\n        continue\n      }\n      if (c === '?') {\n        re += qmark\n        hasMagic = true\n        continue\n      }\n      re += regExpEscape(c)\n    }\n    return [re, unescape(glob), !!hasMagic, uflag]\n  }\n}\n","import { MinimatchOptions } from './index.js'\n\n/**\n * Escape all magic characters in a glob pattern.\n *\n * If the {@link MinimatchOptions.windowsPathsNoEscape}\n * option is used, then characters are escaped by wrapping in `[]`, because\n * a magic character wrapped in a character class can only be satisfied by\n * that exact character.  In this mode, `\\` is _not_ escaped, because it is\n * not interpreted as a magic character, but instead as a path separator.\n *\n * If the {@link MinimatchOptions.magicalBraces} option is used,\n * then braces (`{` and `}`) will be escaped.\n */\nexport const escape = (\n  s: string,\n  {\n    windowsPathsNoEscape = false,\n    magicalBraces = false,\n  }: Pick<MinimatchOptions, 'windowsPathsNoEscape' | 'magicalBraces'> = {},\n) => {\n  // don't need to escape +@! because we escape the parens\n  // that make those magic, and escaping ! as [!] isn't valid,\n  // because [!]] is a valid glob class meaning not ']'.\n  if (magicalBraces) {\n    return windowsPathsNoEscape\n      ? s.replace(/[?*()[\\]{}]/g, '[$&]')\n      : s.replace(/[?*()[\\]\\\\{}]/g, '\\\\$&')\n  }\n  return windowsPathsNoEscape\n    ? s.replace(/[?*()[\\]]/g, '[$&]')\n    : s.replace(/[?*()[\\]\\\\]/g, '\\\\$&')\n}\n","import { expand } from '@isaacs/brace-expansion'\nimport { assertValidPattern } from './assert-valid-pattern.js'\nimport { AST, ExtglobType } from './ast.js'\nimport { escape } from './escape.js'\nimport { unescape } from './unescape.js'\n\nexport type Platform =\n  | 'aix'\n  | 'android'\n  | 'darwin'\n  | 'freebsd'\n  | 'haiku'\n  | 'linux'\n  | 'openbsd'\n  | 'sunos'\n  | 'win32'\n  | 'cygwin'\n  | 'netbsd'\n\nexport interface MinimatchOptions {\n  nobrace?: boolean\n  nocomment?: boolean\n  nonegate?: boolean\n  debug?: boolean\n  noglobstar?: boolean\n  noext?: boolean\n  nonull?: boolean\n  windowsPathsNoEscape?: boolean\n  allowWindowsEscape?: boolean\n  partial?: boolean\n  dot?: boolean\n  nocase?: boolean\n  nocaseMagicOnly?: boolean\n  magicalBraces?: boolean\n  matchBase?: boolean\n  flipNegate?: boolean\n  preserveMultipleSlashes?: boolean\n  optimizationLevel?: number\n  platform?: Platform\n  windowsNoMagicRoot?: boolean\n}\n\nexport const minimatch = (\n  p: string,\n  pattern: string,\n  options: MinimatchOptions = {},\n) => {\n  assertValidPattern(pattern)\n\n  // shortcut: comments match nothing.\n  if (!options.nocomment && pattern.charAt(0) === '#') {\n    return false\n  }\n\n  return new Minimatch(pattern, options).match(p)\n}\n\n// Optimized checking for the most common glob patterns.\nconst starDotExtRE = /^\\*+([^+@!?\\*\\[\\(]*)$/\nconst starDotExtTest = (ext: string) => (f: string) =>\n  !f.startsWith('.') && f.endsWith(ext)\nconst starDotExtTestDot = (ext: string) => (f: string) => f.endsWith(ext)\nconst starDotExtTestNocase = (ext: string) => {\n  ext = ext.toLowerCase()\n  return (f: string) => !f.startsWith('.') && f.toLowerCase().endsWith(ext)\n}\nconst starDotExtTestNocaseDot = (ext: string) => {\n  ext = ext.toLowerCase()\n  return (f: string) => f.toLowerCase().endsWith(ext)\n}\nconst starDotStarRE = /^\\*+\\.\\*+$/\nconst starDotStarTest = (f: string) => !f.startsWith('.') && f.includes('.')\nconst starDotStarTestDot = (f: string) =>\n  f !== '.' && f !== '..' && f.includes('.')\nconst dotStarRE = /^\\.\\*+$/\nconst dotStarTest = (f: string) => f !== '.' && f !== '..' && f.startsWith('.')\nconst starRE = /^\\*+$/\nconst starTest = (f: string) => f.length !== 0 && !f.startsWith('.')\nconst starTestDot = (f: string) => f.length !== 0 && f !== '.' && f !== '..'\nconst qmarksRE = /^\\?+([^+@!?\\*\\[\\(]*)?$/\nconst qmarksTestNocase = ([$0, ext = '']: RegExpMatchArray) => {\n  const noext = qmarksTestNoExt([$0])\n  if (!ext) return noext\n  ext = ext.toLowerCase()\n  return (f: string) => noext(f) && f.toLowerCase().endsWith(ext)\n}\nconst qmarksTestNocaseDot = ([$0, ext = '']: RegExpMatchArray) => {\n  const noext = qmarksTestNoExtDot([$0])\n  if (!ext) return noext\n  ext = ext.toLowerCase()\n  return (f: string) => noext(f) && f.toLowerCase().endsWith(ext)\n}\nconst qmarksTestDot = ([$0, ext = '']: RegExpMatchArray) => {\n  const noext = qmarksTestNoExtDot([$0])\n  return !ext ? noext : (f: string) => noext(f) && f.endsWith(ext)\n}\nconst qmarksTest = ([$0, ext = '']: RegExpMatchArray) => {\n  const noext = qmarksTestNoExt([$0])\n  return !ext ? noext : (f: string) => noext(f) && f.endsWith(ext)\n}\nconst qmarksTestNoExt = ([$0]: RegExpMatchArray) => {\n  const len = $0.length\n  return (f: string) => f.length === len && !f.startsWith('.')\n}\nconst qmarksTestNoExtDot = ([$0]: RegExpMatchArray) => {\n  const len = $0.length\n  return (f: string) => f.length === len && f !== '.' && f !== '..'\n}\n\n/* c8 ignore start */\nconst defaultPlatform: Platform = (\n  typeof process === 'object' && process\n    ? (typeof process.env === 'object' &&\n        process.env &&\n        process.env.__MINIMATCH_TESTING_PLATFORM__) ||\n      process.platform\n    : 'posix'\n) as Platform\n\nexport type Sep = '\\\\' | '/'\n\nconst path: { [k: string]: { sep: Sep } } = {\n  win32: { sep: '\\\\' },\n  posix: { sep: '/' },\n}\n/* c8 ignore stop */\n\nexport const sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep\nminimatch.sep = sep\n\nexport const GLOBSTAR = Symbol('globstar **')\nminimatch.GLOBSTAR = GLOBSTAR\n\n// any single thing other than /\n// don't need to escape / when using new RegExp()\nconst qmark = '[^/]'\n\n// * => any number of characters\nconst star = qmark + '*?'\n\n// ** when dots are allowed.  Anything goes, except .. and .\n// not (^ or / followed by one or two dots followed by $ or /),\n// followed by anything, any number of times.\nconst twoStarDot = '(?:(?!(?:\\\\/|^)(?:\\\\.{1,2})($|\\\\/)).)*?'\n\n// not a ^ or / followed by a dot,\n// followed by anything, any number of times.\nconst twoStarNoDot = '(?:(?!(?:\\\\/|^)\\\\.).)*?'\n\nexport const filter =\n  (pattern: string, options: MinimatchOptions = {}) =>\n  (p: string) =>\n    minimatch(p, pattern, options)\nminimatch.filter = filter\n\nconst ext = (a: MinimatchOptions, b: MinimatchOptions = {}) =>\n  Object.assign({}, a, b)\n\nexport const defaults = (def: MinimatchOptions): typeof minimatch => {\n  if (!def || typeof def !== 'object' || !Object.keys(def).length) {\n    return minimatch\n  }\n\n  const orig = minimatch\n\n  const m = (p: string, pattern: string, options: MinimatchOptions = {}) =>\n    orig(p, pattern, ext(def, options))\n\n  return Object.assign(m, {\n    Minimatch: class Minimatch extends orig.Minimatch {\n      constructor(pattern: string, options: MinimatchOptions = {}) {\n        super(pattern, ext(def, options))\n      }\n      static defaults(options: MinimatchOptions) {\n        return orig.defaults(ext(def, options)).Minimatch\n      }\n    },\n\n    AST: class AST extends orig.AST {\n      /* c8 ignore start */\n      constructor(\n        type: ExtglobType | null,\n        parent?: AST,\n        options: MinimatchOptions = {},\n      ) {\n        super(type, parent, ext(def, options))\n      }\n      /* c8 ignore stop */\n\n      static fromGlob(pattern: string, options: MinimatchOptions = {}) {\n        return orig.AST.fromGlob(pattern, ext(def, options))\n      }\n    },\n\n    unescape: (\n      s: string,\n      options: Pick<\n        MinimatchOptions,\n        'windowsPathsNoEscape' | 'magicalBraces'\n      > = {},\n    ) => orig.unescape(s, ext(def, options)),\n\n    escape: (\n      s: string,\n      options: Pick<\n        MinimatchOptions,\n        'windowsPathsNoEscape' | 'magicalBraces'\n      > = {},\n    ) => orig.escape(s, ext(def, options)),\n\n    filter: (pattern: string, options: MinimatchOptions = {}) =>\n      orig.filter(pattern, ext(def, options)),\n\n    defaults: (options: MinimatchOptions) => orig.defaults(ext(def, options)),\n\n    makeRe: (pattern: string, options: MinimatchOptions = {}) =>\n      orig.makeRe(pattern, ext(def, options)),\n\n    braceExpand: (pattern: string, options: MinimatchOptions = {}) =>\n      orig.braceExpand(pattern, ext(def, options)),\n\n    match: (list: string[], pattern: string, options: MinimatchOptions = {}) =>\n      orig.match(list, pattern, ext(def, options)),\n\n    sep: orig.sep,\n    GLOBSTAR: GLOBSTAR as typeof GLOBSTAR,\n  })\n}\nminimatch.defaults = defaults\n\n// Brace expansion:\n// a{b,c}d -> abd acd\n// a{b,}c -> abc ac\n// a{0..3}d -> a0d a1d a2d a3d\n// a{b,c{d,e}f}g -> abg acdfg acefg\n// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg\n//\n// Invalid sets are not expanded.\n// a{2..}b -> a{2..}b\n// a{b}c -> a{b}c\nexport const braceExpand = (\n  pattern: string,\n  options: MinimatchOptions = {},\n) => {\n  assertValidPattern(pattern)\n\n  // Thanks to Yeting Li <https://github.com/yetingli> for\n  // improving this regexp to avoid a ReDOS vulnerability.\n  if (options.nobrace || !/\\{(?:(?!\\{).)*\\}/.test(pattern)) {\n    // shortcut. no need to expand.\n    return [pattern]\n  }\n\n  return expand(pattern)\n}\nminimatch.braceExpand = braceExpand\n\n// parse a component of the expanded set.\n// At this point, no pattern may contain \"/\" in it\n// so we're going to return a 2d array, where each entry is the full\n// pattern, split on '/', and then turned into a regular expression.\n// A regexp is made at the end which joins each array with an\n// escaped /, and another full one which joins each regexp with |.\n//\n// Following the lead of Bash 4.1, note that \"**\" only has special meaning\n// when it is the *only* thing in a path portion.  Otherwise, any series\n// of * is equivalent to a single *.  Globstar behavior is enabled by\n// default, and can be disabled by setting options.noglobstar.\n\nexport const makeRe = (pattern: string, options: MinimatchOptions = {}) =>\n  new Minimatch(pattern, options).makeRe()\nminimatch.makeRe = makeRe\n\nexport const match = (\n  list: string[],\n  pattern: string,\n  options: MinimatchOptions = {},\n) => {\n  const mm = new Minimatch(pattern, options)\n  list = list.filter(f => mm.match(f))\n  if (mm.options.nonull && !list.length) {\n    list.push(pattern)\n  }\n  return list\n}\nminimatch.match = match\n\n// replace stuff like \\* with *\nconst globMagic = /[?*]|[+@!]\\(.*?\\)|\\[|\\]/\nconst regExpEscape = (s: string) =>\n  s.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&')\n\nexport type MMRegExp = RegExp & {\n  _src?: string\n  _glob?: string\n}\n\nexport type ParseReturnFiltered = string | MMRegExp | typeof GLOBSTAR\nexport type ParseReturn = ParseReturnFiltered | false\n\nexport class Minimatch {\n  options: MinimatchOptions\n  set: ParseReturnFiltered[][]\n  pattern: string\n\n  windowsPathsNoEscape: boolean\n  nonegate: boolean\n  negate: boolean\n  comment: boolean\n  empty: boolean\n  preserveMultipleSlashes: boolean\n  partial: boolean\n  globSet: string[]\n  globParts: string[][]\n  nocase: boolean\n\n  isWindows: boolean\n  platform: Platform\n  windowsNoMagicRoot: boolean\n\n  regexp: false | null | MMRegExp\n  constructor(pattern: string, options: MinimatchOptions = {}) {\n    assertValidPattern(pattern)\n\n    options = options || {}\n    this.options = options\n    this.pattern = pattern\n    this.platform = options.platform || defaultPlatform\n    this.isWindows = this.platform === 'win32'\n    this.windowsPathsNoEscape =\n      !!options.windowsPathsNoEscape || options.allowWindowsEscape === false\n    if (this.windowsPathsNoEscape) {\n      this.pattern = this.pattern.replace(/\\\\/g, '/')\n    }\n    this.preserveMultipleSlashes = !!options.preserveMultipleSlashes\n    this.regexp = null\n    this.negate = false\n    this.nonegate = !!options.nonegate\n    this.comment = false\n    this.empty = false\n    this.partial = !!options.partial\n    this.nocase = !!this.options.nocase\n    this.windowsNoMagicRoot =\n      options.windowsNoMagicRoot !== undefined\n        ? options.windowsNoMagicRoot\n        : !!(this.isWindows && this.nocase)\n\n    this.globSet = []\n    this.globParts = []\n    this.set = []\n\n    // make the set of regexps etc.\n    this.make()\n  }\n\n  hasMagic(): boolean {\n    if (this.options.magicalBraces && this.set.length > 1) {\n      return true\n    }\n    for (const pattern of this.set) {\n      for (const part of pattern) {\n        if (typeof part !== 'string') return true\n      }\n    }\n    return false\n  }\n\n  debug(..._: any[]) {}\n\n  make() {\n    const pattern = this.pattern\n    const options = this.options\n\n    // empty patterns and comments match nothing.\n    if (!options.nocomment && pattern.charAt(0) === '#') {\n      this.comment = true\n      return\n    }\n\n    if (!pattern) {\n      this.empty = true\n      return\n    }\n\n    // step 1: figure out negation, etc.\n    this.parseNegate()\n\n    // step 2: expand braces\n    this.globSet = [...new Set(this.braceExpand())]\n\n    if (options.debug) {\n      this.debug = (...args: any[]) => console.error(...args)\n    }\n\n    this.debug(this.pattern, this.globSet)\n\n    // step 3: now we have a set, so turn each one into a series of\n    // path-portion matching patterns.\n    // These will be regexps, except in the case of \"**\", which is\n    // set to the GLOBSTAR object for globstar behavior,\n    // and will not contain any / characters\n    //\n    // First, we preprocess to make the glob pattern sets a bit simpler\n    // and deduped.  There are some perf-killing patterns that can cause\n    // problems with a glob walk, but we can simplify them down a bit.\n    const rawGlobParts = this.globSet.map(s => this.slashSplit(s))\n    this.globParts = this.preprocess(rawGlobParts)\n    this.debug(this.pattern, this.globParts)\n\n    // glob --> regexps\n    let set = this.globParts.map((s, _, __) => {\n      if (this.isWindows && this.windowsNoMagicRoot) {\n        // check if it's a drive or unc path.\n        const isUNC =\n          s[0] === '' &&\n          s[1] === '' &&\n          (s[2] === '?' || !globMagic.test(s[2])) &&\n          !globMagic.test(s[3])\n        const isDrive = /^[a-z]:/i.test(s[0])\n        if (isUNC) {\n          return [...s.slice(0, 4), ...s.slice(4).map(ss => this.parse(ss))]\n        } else if (isDrive) {\n          return [s[0], ...s.slice(1).map(ss => this.parse(ss))]\n        }\n      }\n      return s.map(ss => this.parse(ss))\n    })\n\n    this.debug(this.pattern, set)\n\n    // filter out everything that didn't compile properly.\n    this.set = set.filter(\n      s => s.indexOf(false) === -1,\n    ) as ParseReturnFiltered[][]\n\n    // do not treat the ? in UNC paths as magic\n    if (this.isWindows) {\n      for (let i = 0; i < this.set.length; i++) {\n        const p = this.set[i]\n        if (\n          p[0] === '' &&\n          p[1] === '' &&\n          this.globParts[i][2] === '?' &&\n          typeof p[3] === 'string' &&\n          /^[a-z]:$/i.test(p[3])\n        ) {\n          p[2] = '?'\n        }\n      }\n    }\n\n    this.debug(this.pattern, this.set)\n  }\n\n  // various transforms to equivalent pattern sets that are\n  // faster to process in a filesystem walk.  The goal is to\n  // eliminate what we can, and push all ** patterns as far\n  // to the right as possible, even if it increases the number\n  // of patterns that we have to process.\n  preprocess(globParts: string[][]) {\n    // if we're not in globstar mode, then turn all ** into *\n    if (this.options.noglobstar) {\n      for (let i = 0; i < globParts.length; i++) {\n        for (let j = 0; j < globParts[i].length; j++) {\n          if (globParts[i][j] === '**') {\n            globParts[i][j] = '*'\n          }\n        }\n      }\n    }\n\n    const { optimizationLevel = 1 } = this.options\n\n    if (optimizationLevel >= 2) {\n      // aggressive optimization for the purpose of fs walking\n      globParts = this.firstPhasePreProcess(globParts)\n      globParts = this.secondPhasePreProcess(globParts)\n    } else if (optimizationLevel >= 1) {\n      // just basic optimizations to remove some .. parts\n      globParts = this.levelOneOptimize(globParts)\n    } else {\n      // just collapse multiple ** portions into one\n      globParts = this.adjascentGlobstarOptimize(globParts)\n    }\n\n    return globParts\n  }\n\n  // just get rid of adjascent ** portions\n  adjascentGlobstarOptimize(globParts: string[][]) {\n    return globParts.map(parts => {\n      let gs: number = -1\n      while (-1 !== (gs = parts.indexOf('**', gs + 1))) {\n        let i = gs\n        while (parts[i + 1] === '**') {\n          i++\n        }\n        if (i !== gs) {\n          parts.splice(gs, i - gs)\n        }\n      }\n      return parts\n    })\n  }\n\n  // get rid of adjascent ** and resolve .. portions\n  levelOneOptimize(globParts: string[][]) {\n    return globParts.map(parts => {\n      parts = parts.reduce((set: string[], part) => {\n        const prev = set[set.length - 1]\n        if (part === '**' && prev === '**') {\n          return set\n        }\n        if (part === '..') {\n          if (prev && prev !== '..' && prev !== '.' && prev !== '**') {\n            set.pop()\n            return set\n          }\n        }\n        set.push(part)\n        return set\n      }, [])\n      return parts.length === 0 ? [''] : parts\n    })\n  }\n\n  levelTwoFileOptimize(parts: string | string[]) {\n    if (!Array.isArray(parts)) {\n      parts = this.slashSplit(parts)\n    }\n    let didSomething: boolean = false\n    do {\n      didSomething = false\n      // <pre>/<e>/<rest> -> <pre>/<rest>\n      if (!this.preserveMultipleSlashes) {\n        for (let i = 1; i < parts.length - 1; i++) {\n          const p = parts[i]\n          // don't squeeze out UNC patterns\n          if (i === 1 && p === '' && parts[0] === '') continue\n          if (p === '.' || p === '') {\n            didSomething = true\n            parts.splice(i, 1)\n            i--\n          }\n        }\n        if (\n          parts[0] === '.' &&\n          parts.length === 2 &&\n          (parts[1] === '.' || parts[1] === '')\n        ) {\n          didSomething = true\n          parts.pop()\n        }\n      }\n\n      // <pre>/<p>/../<rest> -> <pre>/<rest>\n      let dd: number = 0\n      while (-1 !== (dd = parts.indexOf('..', dd + 1))) {\n        const p = parts[dd - 1]\n        if (p && p !== '.' && p !== '..' && p !== '**') {\n          didSomething = true\n          parts.splice(dd - 1, 2)\n          dd -= 2\n        }\n      }\n    } while (didSomething)\n    return parts.length === 0 ? [''] : parts\n  }\n\n  // First phase: single-pattern processing\n  // <pre> is 1 or more portions\n  // <rest> is 1 or more portions\n  // <p> is any portion other than ., .., '', or **\n  // <e> is . or ''\n  //\n  // **/.. is *brutal* for filesystem walking performance, because\n  // it effectively resets the recursive walk each time it occurs,\n  // and ** cannot be reduced out by a .. pattern part like a regexp\n  // or most strings (other than .., ., and '') can be.\n  //\n  // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}\n  // <pre>/<e>/<rest> -> <pre>/<rest>\n  // <pre>/<p>/../<rest> -> <pre>/<rest>\n  // **/**/<rest> -> **/<rest>\n  //\n  // **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow\n  // this WOULD be allowed if ** did follow symlinks, or * didn't\n  firstPhasePreProcess(globParts: string[][]) {\n    let didSomething = false\n    do {\n      didSomething = false\n      // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}\n      for (let parts of globParts) {\n        let gs: number = -1\n        while (-1 !== (gs = parts.indexOf('**', gs + 1))) {\n          let gss: number = gs\n          while (parts[gss + 1] === '**') {\n            // <pre>/**/**/<rest> -> <pre>/**/<rest>\n            gss++\n          }\n          // eg, if gs is 2 and gss is 4, that means we have 3 **\n          // parts, and can remove 2 of them.\n          if (gss > gs) {\n            parts.splice(gs + 1, gss - gs)\n          }\n\n          let next = parts[gs + 1]\n          const p = parts[gs + 2]\n          const p2 = parts[gs + 3]\n          if (next !== '..') continue\n          if (\n            !p ||\n            p === '.' ||\n            p === '..' ||\n            !p2 ||\n            p2 === '.' ||\n            p2 === '..'\n          ) {\n            continue\n          }\n          didSomething = true\n          // edit parts in place, and push the new one\n          parts.splice(gs, 1)\n          const other = parts.slice(0)\n          other[gs] = '**'\n          globParts.push(other)\n          gs--\n        }\n\n        // <pre>/<e>/<rest> -> <pre>/<rest>\n        if (!this.preserveMultipleSlashes) {\n          for (let i = 1; i < parts.length - 1; i++) {\n            const p = parts[i]\n            // don't squeeze out UNC patterns\n            if (i === 1 && p === '' && parts[0] === '') continue\n            if (p === '.' || p === '') {\n              didSomething = true\n              parts.splice(i, 1)\n              i--\n            }\n          }\n          if (\n            parts[0] === '.' &&\n            parts.length === 2 &&\n            (parts[1] === '.' || parts[1] === '')\n          ) {\n            didSomething = true\n            parts.pop()\n          }\n        }\n\n        // <pre>/<p>/../<rest> -> <pre>/<rest>\n        let dd: number = 0\n        while (-1 !== (dd = parts.indexOf('..', dd + 1))) {\n          const p = parts[dd - 1]\n          if (p && p !== '.' && p !== '..' && p !== '**') {\n            didSomething = true\n            const needDot = dd === 1 && parts[dd + 1] === '**'\n            const splin = needDot ? ['.'] : []\n            parts.splice(dd - 1, 2, ...splin)\n            if (parts.length === 0) parts.push('')\n            dd -= 2\n          }\n        }\n      }\n    } while (didSomething)\n\n    return globParts\n  }\n\n  // second phase: multi-pattern dedupes\n  // {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>\n  // {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>\n  // {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>\n  //\n  // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>\n  // ^-- not valid because ** doens't follow symlinks\n  secondPhasePreProcess(globParts: string[][]): string[][] {\n    for (let i = 0; i < globParts.length - 1; i++) {\n      for (let j = i + 1; j < globParts.length; j++) {\n        const matched = this.partsMatch(\n          globParts[i],\n          globParts[j],\n          !this.preserveMultipleSlashes,\n        )\n        if (matched) {\n          globParts[i] = []\n          globParts[j] = matched\n          break\n        }\n      }\n    }\n    return globParts.filter(gs => gs.length)\n  }\n\n  partsMatch(\n    a: string[],\n    b: string[],\n    emptyGSMatch: boolean = false,\n  ): false | string[] {\n    let ai = 0\n    let bi = 0\n    let result: string[] = []\n    let which: string = ''\n    while (ai < a.length && bi < b.length) {\n      if (a[ai] === b[bi]) {\n        result.push(which === 'b' ? b[bi] : a[ai])\n        ai++\n        bi++\n      } else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {\n        result.push(a[ai])\n        ai++\n      } else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {\n        result.push(b[bi])\n        bi++\n      } else if (\n        a[ai] === '*' &&\n        b[bi] &&\n        (this.options.dot || !b[bi].startsWith('.')) &&\n        b[bi] !== '**'\n      ) {\n        if (which === 'b') return false\n        which = 'a'\n        result.push(a[ai])\n        ai++\n        bi++\n      } else if (\n        b[bi] === '*' &&\n        a[ai] &&\n        (this.options.dot || !a[ai].startsWith('.')) &&\n        a[ai] !== '**'\n      ) {\n        if (which === 'a') return false\n        which = 'b'\n        result.push(b[bi])\n        ai++\n        bi++\n      } else {\n        return false\n      }\n    }\n    // if we fall out of the loop, it means they two are identical\n    // as long as their lengths match\n    return a.length === b.length && result\n  }\n\n  parseNegate() {\n    if (this.nonegate) return\n\n    const pattern = this.pattern\n    let negate = false\n    let negateOffset = 0\n\n    for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {\n      negate = !negate\n      negateOffset++\n    }\n\n    if (negateOffset) this.pattern = pattern.slice(negateOffset)\n    this.negate = negate\n  }\n\n  // set partial to true to test if, for example,\n  // \"/a/b\" matches the start of \"/*/b/*/d\"\n  // Partial means, if you run out of file before you run\n  // out of pattern, then that's fine, as long as all\n  // the parts match.\n  matchOne(file: string[], pattern: ParseReturn[], partial: boolean = false) {\n    const options = this.options\n\n    // UNC paths like //?/X:/... can match X:/... and vice versa\n    // Drive letters in absolute drive or unc paths are always compared\n    // case-insensitively.\n    if (this.isWindows) {\n      const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0])\n      const fileUNC =\n        !fileDrive &&\n        file[0] === '' &&\n        file[1] === '' &&\n        file[2] === '?' &&\n        /^[a-z]:$/i.test(file[3])\n\n      const patternDrive =\n        typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0])\n      const patternUNC =\n        !patternDrive &&\n        pattern[0] === '' &&\n        pattern[1] === '' &&\n        pattern[2] === '?' &&\n        typeof pattern[3] === 'string' &&\n        /^[a-z]:$/i.test(pattern[3])\n\n      const fdi = fileUNC ? 3 : fileDrive ? 0 : undefined\n      const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined\n      if (typeof fdi === 'number' && typeof pdi === 'number') {\n        const [fd, pd]: [string, string] = [file[fdi], pattern[pdi] as string]\n        if (fd.toLowerCase() === pd.toLowerCase()) {\n          pattern[pdi] = fd\n          if (pdi > fdi) {\n            pattern = pattern.slice(pdi)\n          } else if (fdi > pdi) {\n            file = file.slice(fdi)\n          }\n        }\n      }\n    }\n\n    // resolve and reduce . and .. portions in the file as well.\n    // don't need to do the second phase, because it's only one string[]\n    const { optimizationLevel = 1 } = this.options\n    if (optimizationLevel >= 2) {\n      file = this.levelTwoFileOptimize(file)\n    }\n\n    this.debug('matchOne', this, { file, pattern })\n    this.debug('matchOne', file.length, pattern.length)\n\n    for (\n      var fi = 0, pi = 0, fl = file.length, pl = pattern.length;\n      fi < fl && pi < pl;\n      fi++, pi++\n    ) {\n      this.debug('matchOne loop')\n      var p = pattern[pi]\n      var f = file[fi]\n\n      this.debug(pattern, p, f)\n\n      // should be impossible.\n      // some invalid regexp stuff in the set.\n      /* c8 ignore start */\n      if (p === false) {\n        return false\n      }\n      /* c8 ignore stop */\n\n      if (p === GLOBSTAR) {\n        this.debug('GLOBSTAR', [pattern, p, f])\n\n        // \"**\"\n        // a/**/b/**/c would match the following:\n        // a/b/x/y/z/c\n        // a/x/y/z/b/c\n        // a/b/x/b/x/c\n        // a/b/c\n        // To do this, take the rest of the pattern after\n        // the **, and see if it would match the file remainder.\n        // If so, return success.\n        // If not, the ** \"swallows\" a segment, and try again.\n        // This is recursively awful.\n        //\n        // a/**/b/**/c matching a/b/x/y/z/c\n        // - a matches a\n        // - doublestar\n        //   - matchOne(b/x/y/z/c, b/**/c)\n        //     - b matches b\n        //     - doublestar\n        //       - matchOne(x/y/z/c, c) -> no\n        //       - matchOne(y/z/c, c) -> no\n        //       - matchOne(z/c, c) -> no\n        //       - matchOne(c, c) yes, hit\n        var fr = fi\n        var pr = pi + 1\n        if (pr === pl) {\n          this.debug('** at the end')\n          // a ** at the end will just swallow the rest.\n          // We have found a match.\n          // however, it will not swallow /.x, unless\n          // options.dot is set.\n          // . and .. are *never* matched by **, for explosively\n          // exponential reasons.\n          for (; fi < fl; fi++) {\n            if (\n              file[fi] === '.' ||\n              file[fi] === '..' ||\n              (!options.dot && file[fi].charAt(0) === '.')\n            )\n              return false\n          }\n          return true\n        }\n\n        // ok, let's see if we can swallow whatever we can.\n        while (fr < fl) {\n          var swallowee = file[fr]\n\n          this.debug('\\nglobstar while', file, fr, pattern, pr, swallowee)\n\n          // XXX remove this slice.  Just pass the start index.\n          if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {\n            this.debug('globstar found match!', fr, fl, swallowee)\n            // found a match.\n            return true\n          } else {\n            // can't swallow \".\" or \"..\" ever.\n            // can only swallow \".foo\" when explicitly asked.\n            if (\n              swallowee === '.' ||\n              swallowee === '..' ||\n              (!options.dot && swallowee.charAt(0) === '.')\n            ) {\n              this.debug('dot detected!', file, fr, pattern, pr)\n              break\n            }\n\n            // ** swallows a segment, and continue.\n            this.debug('globstar swallow a segment, and continue')\n            fr++\n          }\n        }\n\n        // no match was found.\n        // However, in partial mode, we can't say this is necessarily over.\n        /* c8 ignore start */\n        if (partial) {\n          // ran out of file\n          this.debug('\\n>>> no match, partial?', file, fr, pattern, pr)\n          if (fr === fl) {\n            return true\n          }\n        }\n        /* c8 ignore stop */\n        return false\n      }\n\n      // something other than **\n      // non-magic patterns just have to match exactly\n      // patterns with magic have been turned into regexps.\n      let hit: boolean\n      if (typeof p === 'string') {\n        hit = f === p\n        this.debug('string match', p, f, hit)\n      } else {\n        hit = p.test(f)\n        this.debug('pattern match', p, f, hit)\n      }\n\n      if (!hit) return false\n    }\n\n    // Note: ending in / means that we'll get a final \"\"\n    // at the end of the pattern.  This can only match a\n    // corresponding \"\" at the end of the file.\n    // If the file ends in /, then it can only match a\n    // a pattern that ends in /, unless the pattern just\n    // doesn't have any more for it. But, a/b/ should *not*\n    // match \"a/b/*\", even though \"\" matches against the\n    // [^/]*? pattern, except in partial mode, where it might\n    // simply not be reached yet.\n    // However, a/b/ should still satisfy a/*\n\n    // now either we fell off the end of the pattern, or we're done.\n    if (fi === fl && pi === pl) {\n      // ran out of pattern and filename at the same time.\n      // an exact hit!\n      return true\n    } else if (fi === fl) {\n      // ran out of file, but still had pattern left.\n      // this is ok if we're doing the match as part of\n      // a glob fs traversal.\n      return partial\n    } else if (pi === pl) {\n      // ran out of pattern, still have file left.\n      // this is only acceptable if we're on the very last\n      // empty segment of a file with a trailing slash.\n      // a/* should match a/b/\n      return fi === fl - 1 && file[fi] === ''\n\n      /* c8 ignore start */\n    } else {\n      // should be unreachable.\n      throw new Error('wtf?')\n    }\n    /* c8 ignore stop */\n  }\n\n  braceExpand() {\n    return braceExpand(this.pattern, this.options)\n  }\n\n  parse(pattern: string): ParseReturn {\n    assertValidPattern(pattern)\n\n    const options = this.options\n\n    // shortcuts\n    if (pattern === '**') return GLOBSTAR\n    if (pattern === '') return ''\n\n    // far and away, the most common glob pattern parts are\n    // *, *.*, and *.<ext>  Add a fast check method for those.\n    let m: RegExpMatchArray | null\n    let fastTest: null | ((f: string) => boolean) = null\n    if ((m = pattern.match(starRE))) {\n      fastTest = options.dot ? starTestDot : starTest\n    } else if ((m = pattern.match(starDotExtRE))) {\n      fastTest = (\n        options.nocase\n          ? options.dot\n            ? starDotExtTestNocaseDot\n            : starDotExtTestNocase\n          : options.dot\n            ? starDotExtTestDot\n            : starDotExtTest\n      )(m[1])\n    } else if ((m = pattern.match(qmarksRE))) {\n      fastTest = (\n        options.nocase\n          ? options.dot\n            ? qmarksTestNocaseDot\n            : qmarksTestNocase\n          : options.dot\n            ? qmarksTestDot\n            : qmarksTest\n      )(m)\n    } else if ((m = pattern.match(starDotStarRE))) {\n      fastTest = options.dot ? starDotStarTestDot : starDotStarTest\n    } else if ((m = pattern.match(dotStarRE))) {\n      fastTest = dotStarTest\n    }\n\n    const re = AST.fromGlob(pattern, this.options).toMMPattern()\n    if (fastTest && typeof re === 'object') {\n      // Avoids overriding in frozen environments\n      Reflect.defineProperty(re, 'test', { value: fastTest })\n    }\n    return re\n  }\n\n  makeRe() {\n    if (this.regexp || this.regexp === false) return this.regexp\n\n    // at this point, this.set is a 2d array of partial\n    // pattern strings, or \"**\".\n    //\n    // It's better to use .match().  This function shouldn't\n    // be used, really, but it's pretty convenient sometimes,\n    // when you just want to work with a regex.\n    const set = this.set\n\n    if (!set.length) {\n      this.regexp = false\n      return this.regexp\n    }\n    const options = this.options\n\n    const twoStar = options.noglobstar\n      ? star\n      : options.dot\n        ? twoStarDot\n        : twoStarNoDot\n    const flags = new Set(options.nocase ? ['i'] : [])\n\n    // regexpify non-globstar patterns\n    // if ** is only item, then we just do one twoStar\n    // if ** is first, and there are more, prepend (\\/|twoStar\\/)? to next\n    // if ** is last, append (\\/twoStar|) to previous\n    // if ** is in the middle, append (\\/|\\/twoStar\\/) to previous\n    // then filter out GLOBSTAR symbols\n    let re = set\n      .map(pattern => {\n        const pp: (string | typeof GLOBSTAR)[] = pattern.map(p => {\n          if (p instanceof RegExp) {\n            for (const f of p.flags.split('')) flags.add(f)\n          }\n          return typeof p === 'string'\n            ? regExpEscape(p)\n            : p === GLOBSTAR\n              ? GLOBSTAR\n              : p._src\n        }) as (string | typeof GLOBSTAR)[]\n        pp.forEach((p, i) => {\n          const next = pp[i + 1]\n          const prev = pp[i - 1]\n          if (p !== GLOBSTAR || prev === GLOBSTAR) {\n            return\n          }\n          if (prev === undefined) {\n            if (next !== undefined && next !== GLOBSTAR) {\n              pp[i + 1] = '(?:\\\\/|' + twoStar + '\\\\/)?' + next\n            } else {\n              pp[i] = twoStar\n            }\n          } else if (next === undefined) {\n            pp[i - 1] = prev + '(?:\\\\/|\\\\/' + twoStar + ')?'\n          } else if (next !== GLOBSTAR) {\n            pp[i - 1] = prev + '(?:\\\\/|\\\\/' + twoStar + '\\\\/)' + next\n            pp[i + 1] = GLOBSTAR\n          }\n        })\n        const filtered = pp.filter(p => p !== GLOBSTAR)\n\n        // For partial matches, we need to make the pattern match\n        // any prefix of the full path. We do this by generating\n        // alternative patterns that match progressively longer prefixes.\n        if (this.partial && filtered.length >= 1) {\n          const prefixes: string[] = []\n          for (let i = 1; i <= filtered.length; i++) {\n            prefixes.push(filtered.slice(0, i).join('/'))\n          }\n          return '(?:' + prefixes.join('|') + ')'\n        }\n\n        return filtered.join('/')\n      })\n      .join('|')\n\n    // need to wrap in parens if we had more than one thing with |,\n    // otherwise only the first will be anchored to ^ and the last to $\n    const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', '']\n    // must match entire pattern\n    // ending in a * or ** will make it less strict.\n    re = '^' + open + re + close + '$'\n\n    // In partial mode, '/' should always match as it's a valid prefix for any pattern\n    if (this.partial) {\n      re = '^(?:\\\\/|' + open + re.slice(1, -1) + close + ')$'\n    }\n\n    // can match anything, as long as it's not this.\n    if (this.negate) re = '^(?!' + re + ').+$'\n\n    try {\n      this.regexp = new RegExp(re, [...flags].join(''))\n      /* c8 ignore start */\n    } catch (ex) {\n      // should be impossible\n      this.regexp = false\n    }\n    /* c8 ignore stop */\n    return this.regexp\n  }\n\n  slashSplit(p: string) {\n    // if p starts with // on windows, we preserve that\n    // so that UNC paths aren't broken.  Otherwise, any number of\n    // / characters are coalesced into one, unless\n    // preserveMultipleSlashes is set to true.\n    if (this.preserveMultipleSlashes) {\n      return p.split('/')\n    } else if (this.isWindows && /^\\/\\/[^\\/]+/.test(p)) {\n      // add an extra '' for the one we lose\n      return ['', ...p.split(/\\/+/)]\n    } else {\n      return p.split(/\\/+/)\n    }\n  }\n\n  match(f: string, partial = this.partial) {\n    this.debug('match', f, this.pattern)\n    // short-circuit in the case of busted things.\n    // comments, etc.\n    if (this.comment) {\n      return false\n    }\n    if (this.empty) {\n      return f === ''\n    }\n\n    if (f === '/' && partial) {\n      return true\n    }\n\n    const options = this.options\n\n    // windows: need to use /, not \\\n    if (this.isWindows) {\n      f = f.split('\\\\').join('/')\n    }\n\n    // treat the test path as a set of pathparts.\n    const ff = this.slashSplit(f)\n    this.debug(this.pattern, 'split', ff)\n\n    // just ONE of the pattern sets in this.set needs to match\n    // in order for it to be valid.  If negating, then just one\n    // match means that we have failed.\n    // Either way, return on the first hit.\n\n    const set = this.set\n    this.debug(this.pattern, 'set', set)\n\n    // Find the basename of the path by looking for the last non-empty segment\n    let filename: string = ff[ff.length - 1]\n    if (!filename) {\n      for (let i = ff.length - 2; !filename && i >= 0; i--) {\n        filename = ff[i]\n      }\n    }\n\n    for (let i = 0; i < set.length; i++) {\n      const pattern = set[i]\n      let file = ff\n      if (options.matchBase && pattern.length === 1) {\n        file = [filename]\n      }\n      const hit = this.matchOne(file, pattern, partial)\n      if (hit) {\n        if (options.flipNegate) {\n          return true\n        }\n        return !this.negate\n      }\n    }\n\n    // didn't get any hits.  this is success if it's a negative\n    // pattern, failure otherwise.\n    if (options.flipNegate) {\n      return false\n    }\n    return this.negate\n  }\n\n  static defaults(def: MinimatchOptions) {\n    return minimatch.defaults(def).Minimatch\n  }\n}\n/* c8 ignore start */\nexport { AST } from './ast.js'\nexport { escape } from './escape.js'\nexport { unescape } from './unescape.js'\n/* c8 ignore stop */\nminimatch.AST = AST\nminimatch.Minimatch = Minimatch\nminimatch.escape = escape\nminimatch.unescape = unescape\n","import { minimatch } from \"minimatch\";\n\n/**\n * Safely decodes a URI component, returning the original string if decoding fails.\n */\nexport function safeDecode(value: string): string {\n  try {\n    return decodeURIComponent(value);\n  } catch {\n    return value;\n  }\n}\n\n/**\n * Checks if a key matches any of the provided patterns using exact, separator-bounded prefix, or glob matching.\n * Separator-bounded means the key must equal the pattern exactly, or continue with a \".\", \"/\", or \"-\" separator.\n * This prevents \"inbox\" from matching \"inbox_url\" while still matching \"inbox.title\", \"inbox/details\", or \"inbox-0\".\n */\nexport function matchesKeyPattern(key: string, patterns: string[]): boolean {\n  return patterns.some(\n    (pattern) =>\n      key === pattern ||\n      key.startsWith(pattern + \".\") ||\n      key.startsWith(pattern + \"/\") ||\n      key.startsWith(pattern + \"-\") ||\n      minimatch(key, pattern),\n  );\n}\n\n/**\n * Filters entries based on key matching patterns\n */\nexport function filterEntriesByPattern(\n  entries: [string, any][],\n  patterns: string[],\n): [string, any][] {\n  return entries.filter(([key]) => matchesKeyPattern(key, patterns));\n}\n\n/**\n * Formats a value for display, truncating long strings\n */\nexport function formatDisplayValue(value: any, maxLength = 50): string {\n  if (typeof value === \"string\") {\n    return value.length > maxLength\n      ? `${value.substring(0, maxLength)}...`\n      : value;\n  }\n  return JSON.stringify(value);\n}\n","import prettier, { Options } from \"prettier\";\nimport { ILoader } from \"../_types\";\nimport { createBaseFormatterLoader } from \"./_base\";\n\nexport type PrettierLoaderOptions = {\n  parser: Options[\"parser\"];\n  bucketPathPattern: string;\n  stage?: \"pull\" | \"push\" | \"both\";\n  alwaysFormat?: boolean;\n};\n\nexport default function createPrettierLoader(\n  options: PrettierLoaderOptions,\n): ILoader<string, string> {\n  return createBaseFormatterLoader(options, async (data, filePath) => {\n    return await formatDataWithPrettier(data, filePath, options);\n  });\n}\n\nasync function loadPrettierConfig(filePath: string) {\n  try {\n    const config = await prettier.resolveConfig(filePath);\n    return config;\n  } catch (error) {\n    return {};\n  }\n}\n\nasync function formatDataWithPrettier(\n  data: string,\n  filePath: string,\n  options: PrettierLoaderOptions,\n): Promise<string> {\n  const prettierConfig = await loadPrettierConfig(filePath);\n\n  // Skip formatting if no config found and alwaysFormat is not enabled\n  if (!prettierConfig && !options.alwaysFormat) {\n    return data;\n  }\n\n  const config: Options = {\n    ...(prettierConfig || { printWidth: 2500, bracketSameLine: false }),\n    parser: options.parser,\n    // For HTML parser, preserve comments and quotes\n    ...(options.parser === \"html\"\n      ? {\n          htmlWhitespaceSensitivity: \"ignore\",\n          singleQuote: false,\n          embeddedLanguageFormatting: \"off\",\n        }\n      : {}),\n  };\n\n  try {\n    // format with prettier\n    return await prettier.format(data, config);\n  } catch (error) {\n    if (\n      error instanceof Error &&\n      error.message.startsWith(\"Cannot find package\")\n    ) {\n      console.log();\n      console.log(\n        \"⚠️  Prettier plugins are not installed. Formatting without plugins.\",\n      );\n      console.log(\n        \"⚠️  To use prettier plugins install project dependencies before running Lingo.dev.\",\n      );\n\n      config.plugins = [];\n\n      // clear file system structure cache\n      await prettier.clearConfigCache();\n\n      // format again without plugins\n      return await prettier.format(data, config);\n    }\n\n    throw error;\n  }\n}\n","import path from \"path\";\nimport { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\n\nexport type BaseFormatterOptions = {\n  bucketPathPattern: string;\n  stage?: \"pull\" | \"push\" | \"both\";\n  alwaysFormat?: boolean;\n};\n\nexport function createBaseFormatterLoader(\n  options: BaseFormatterOptions,\n  formatFn: (data: string, filePath: string) => Promise<string>,\n): ILoader<string, string> {\n  const stage = options.stage || \"both\";\n\n  const formatData = async (locale: string, data: string) => {\n    const draftPath = options.bucketPathPattern.replaceAll(\"[locale]\", locale);\n    const finalPath = path.resolve(draftPath);\n    return await formatFn(data, finalPath);\n  };\n\n  return createLoader({\n    async pull(locale, data) {\n      if (![\"pull\", \"both\"].includes(stage)) {\n        return data;\n      }\n      return await formatData(locale, data);\n    },\n    async push(locale, data) {\n      if (![\"push\", \"both\"].includes(stage)) {\n        return data;\n      }\n      return await formatData(locale, data);\n    },\n  });\n}\n","import path from \"path\";\nimport fs from \"fs/promises\";\nimport { Biome, Distribution } from \"@biomejs/js-api\";\nimport { parse as parseJsonc } from \"jsonc-parser\";\nimport { ILoader } from \"../_types\";\nimport { createBaseFormatterLoader } from \"./_base\";\n\nexport type BiomeLoaderOptions = {\n  bucketPathPattern: string;\n  stage?: \"pull\" | \"push\" | \"both\";\n  alwaysFormat?: boolean;\n};\n\nexport default function createBiomeLoader(\n  options: BiomeLoaderOptions,\n): ILoader<string, string> {\n  return createBaseFormatterLoader(options, async (data, filePath) => {\n    return await formatDataWithBiome(data, filePath, options);\n  });\n}\n\nasync function findBiomeConfig(startPath: string): Promise<string | null> {\n  let currentDir = path.dirname(startPath);\n  const root = path.parse(currentDir).root;\n\n  while (currentDir !== root) {\n    for (const configName of [\"biome.json\", \"biome.jsonc\"]) {\n      const configPath = path.join(currentDir, configName);\n      try {\n        await fs.access(configPath);\n        return configPath;\n      } catch {\n        // Config file doesn't exist, continue searching\n      }\n    }\n\n    const parentDir = path.dirname(currentDir);\n    if (parentDir === currentDir) break;\n    currentDir = parentDir;\n  }\n\n  return null;\n}\n\nasync function formatDataWithBiome(\n  data: string,\n  filePath: string,\n  options: BiomeLoaderOptions,\n): Promise<string> {\n  let configPath: string | null = null;\n\n  try {\n    const biome = await Biome.create({\n      distribution: Distribution.NODE,\n    });\n\n    // Open a project (required in v3.0.0+)\n    const openResult = biome.openProject(\".\");\n    const projectKey = openResult.projectKey;\n\n    // Load config from biome.json/biome.jsonc if exists\n    configPath = await findBiomeConfig(filePath);\n    if (!configPath && !options.alwaysFormat) {\n      console.log();\n      console.log(\n        `⚠️  Biome config not found for ${path.basename(filePath)} - skipping formatting`,\n      );\n      return data;\n    }\n\n    if (configPath) {\n      const configContent = await fs.readFile(configPath, \"utf-8\");\n      try {\n        // Parse JSONC (JSON with comments) properly using jsonc-parser\n        const config = parseJsonc(configContent);\n\n        // WORKAROUND: Biome JS API v3 has a bug where applying the full config\n        // causes formatter settings to be ignored. Apply only relevant sections.\n        // Specifically, exclude $schema, vcs, and files from the config.\n        const { $schema, vcs, files, ...relevantConfig } = config;\n\n        biome.applyConfiguration(projectKey, relevantConfig);\n      } catch (parseError) {\n        throw new Error(\n          `Invalid Biome configuration in ${configPath}: ${parseError instanceof Error ? parseError.message : \"JSON parse error\"}`,\n        );\n      }\n    }\n\n    const formatted = biome.formatContent(projectKey, data, {\n      filePath,\n    });\n\n    return formatted.content;\n  } catch (error) {\n    // Extract error message from Biome\n    const errorMessage =\n      error instanceof Error\n        ? error.message || (error as any).stackTrace?.toString().split(\"\\n\")[0]\n        : \"\";\n\n    if (errorMessage?.includes(\"does not exist in the workspace\")) {\n      // Biome says \"file does not exist in workspace\" for unsupported formats - skip\n    } else {\n      console.log(`⚠️  Biome skipped ${path.basename(filePath)}`);\n      if (errorMessage) {\n        console.log(`   ${errorMessage}`);\n      }\n    }\n\n    return data; // Fallback to unformatted\n  }\n}\n","import createPrettierLoader, { PrettierLoaderOptions } from \"./prettier\";\nimport createBiomeLoader from \"./biome\";\nimport { ILoader } from \"../_types\";\nimport { Options } from \"prettier\";\n\nexport type FormatterType = \"prettier\" | \"biome\" | undefined;\nexport type ParserType = Options[\"parser\"];\n\nexport function createFormatterLoader(\n  formatterType: FormatterType,\n  parser: ParserType,\n  bucketPathPattern: string,\n): ILoader<string, string> {\n  // If explicitly set to undefined, auto-detect (prefer prettier for backward compatibility)\n  if (formatterType === undefined) {\n    return createPrettierLoader({ parser, bucketPathPattern });\n  }\n\n  if (formatterType === \"prettier\") {\n    return createPrettierLoader({ parser, bucketPathPattern });\n  }\n\n  if (formatterType === \"biome\") {\n    return createBiomeLoader({ bucketPathPattern });\n  }\n\n  throw new Error(`Unknown formatter: ${formatterType}`);\n}\n\n// Re-export for direct access if needed\nexport { createPrettierLoader, createBiomeLoader };\nexport type { PrettierLoaderOptions };\nexport type { BiomeLoaderOptions } from \"./biome\";\n","import _ from \"lodash\";\nimport gettextParser from \"gettext-parser\";\nimport { GetTextTranslations } from \"gettext-parser\";\nimport { ILoader } from \"../_types\";\nimport { composeLoaders, createLoader } from \"../_utils\";\n\nexport type PoTranslationEntry = GetTextTranslations[\"translations\"][\"\"];\nexport type PoTranslationValue = { singular: string; plural: string | null };\n\nexport type PoLoaderParams = {\n  multiline: boolean;\n};\n\nexport default function createPoLoader(\n  params: PoLoaderParams = { multiline: false },\n): ILoader<string, Record<string, PoTranslationValue>> {\n  return composeLoaders(createPoDataLoader(params), createPoContentLoader());\n}\n\nexport function createPoDataLoader(\n  params: PoLoaderParams,\n): ILoader<string, PoTranslationEntry> {\n  return createLoader({\n    async pull(locale, input) {\n      const parsedPo = gettextParser.po.parse(input);\n      const result: PoTranslationEntry = {};\n      const sections = input.split(\"\\n\\n\").filter(Boolean);\n      for (const section of sections) {\n        const sectionPo = gettextParser.po.parse(section);\n        // skip section with no translations (some sections might have only obsolete entries)\n        if (Object.keys(sectionPo.translations).length === 0) {\n          continue;\n        }\n\n        const contextKey = _.keys(sectionPo.translations)[0];\n        const entries = sectionPo.translations[contextKey];\n        Object.entries(entries).forEach(([msgid, entry]) => {\n          if (msgid && entry.msgid) {\n            const context = entry.msgctxt || \"\";\n            const fullEntry = parsedPo.translations[context]?.[msgid];\n            if (fullEntry) {\n              result[msgid] = fullEntry;\n            }\n          }\n        });\n      }\n      return result;\n    },\n\n    async push(locale, data, originalInput, originalLocale, pullInput) {\n      // Parse each section to maintain structure\n      const originalSections =\n        originalInput?.split(\"\\n\\n\").filter(Boolean) || [];\n      const result = originalSections\n        .map((section) => {\n          const sectionPo = gettextParser.po.parse(section);\n          // skip section with no translations (some sections might have only obsolete entries)\n          if (Object.keys(sectionPo.translations).length === 0) {\n            return null;\n          }\n\n          const contextKey = _.keys(sectionPo.translations)[0];\n          const entries = sectionPo.translations[contextKey];\n          const msgid = Object.keys(entries).find((key) => entries[key].msgid);\n\n          if (!msgid) {\n            // If the section is empty, try to find it in the current sections\n            const currentSections =\n              pullInput?.split(\"\\n\\n\").filter(Boolean) || [];\n            const currentSection = currentSections.find((cs) => {\n              const csPo = gettextParser.po.parse(cs);\n              if (Object.keys(csPo.translations).length === 0) {\n                return false;\n              }\n              const csContextKey = _.keys(csPo.translations)[0];\n              const csEntries = csPo.translations[csContextKey];\n              if (!csEntries) {\n                return false;\n              }\n              const csMsgid = Object.keys(csEntries).find(\n                (key) => csEntries[key].msgid,\n              );\n              return csMsgid === msgid;\n            });\n            return currentSection || section;\n          }\n\n          const entriesToMerge: Record<string, { msgstr: string[] }> = {};\n          for (const [id, entry] of Object.entries(entries)) {\n            if (entry.msgid && data[id]) {\n              entriesToMerge[id] = { msgstr: data[id].msgstr };\n            }\n          }\n\n          if (Object.keys(entriesToMerge).length > 0) {\n            const updatedPo = _.merge({}, sectionPo, {\n              headers: resolveTargetHeaders(pullInput, sectionPo),\n              translations: { [contextKey]: entriesToMerge },\n            });\n            const updatedSection = gettextParser.po\n              .compile(updatedPo, { foldLength: params.multiline ? 76 : false })\n              .toString()\n              .replace(\n                [`msgid \"\"`, `msgstr \"Content-Type: text/plain\\\\n\"`].join(\"\\n\"),\n                \"\",\n              )\n              .trim();\n            return preserveCommentOrder(updatedSection, section);\n          }\n          return section.trim();\n        })\n        .filter(Boolean)\n        .join(\"\\n\\n\");\n      return result;\n    },\n  });\n}\n\nexport function createPoContentLoader(): ILoader<\n  PoTranslationEntry,\n  Record<string, PoTranslationEntry>\n> {\n  return createLoader({\n    async pull(locale, input, initCtx, originalLocale) {\n      const result = _.chain(input)\n        .entries()\n        .filter(([, entry]) => !!entry.msgid)\n        .map(([, entry]) => {\n          const singularFallback =\n            locale === originalLocale ? entry.msgid : null;\n          const pluralFallback =\n            locale === originalLocale\n              ? entry.msgid_plural || entry.msgid\n              : null;\n          const hasPlural = entry.msgstr.length > 1;\n          return [\n            entry.msgid,\n            {\n              singular: entry.msgstr[0] || singularFallback,\n              plural: hasPlural\n                ? ((entry.msgstr[1] || pluralFallback) as string | null)\n                : null,\n            },\n          ];\n        })\n        .fromPairs()\n        .value();\n      return result;\n    },\n    async push(locale, data, originalInput) {\n      const result = _.chain(originalInput)\n        .entries()\n        .map(([, entry]) => [\n          entry.msgid,\n          {\n            ...entry,\n            msgstr: [\n              data[entry.msgid]?.singular,\n              data[entry.msgid]?.plural || null,\n            ].filter(Boolean),\n          },\n        ])\n        .fromPairs()\n        .value();\n\n      return result;\n    },\n  });\n}\n\nfunction resolveTargetHeaders(\n  pullInput: string | null | undefined,\n  sectionPo: GetTextTranslations,\n): Record<string, string> | undefined {\n  // Only needed for embedded headers (header entry + regular entries in the same section)\n  if (!sectionPo.translations[\"\"]?.[\"\"] || !pullInput) {\n    return undefined;\n  }\n  const headerSection = pullInput\n    .split(\"\\n\\n\")\n    .find((s) => s.includes('msgid \"\"'));\n  if (!headerSection) {\n    return undefined;\n  }\n  return gettextParser.po.parse(headerSection).headers;\n}\n\nfunction preserveCommentOrder(section: string, originalSection: string) {\n  // Split both sections into lines\n  const sectionLines = section.split(/\\r?\\n/);\n  const originalLines = originalSection.split(/\\r?\\n/);\n\n  // Helper: is a comment line\n  const isComment = (line: string) => line.trim().startsWith(\"#\");\n\n  // Extract comment lines and their indices\n  const sectionComments = sectionLines.filter(isComment);\n  const nonCommentLines = sectionLines.filter((line) => !isComment(line));\n\n  // If there are no comments in the section, return the section as is\n  if (sectionComments.length <= 1) {\n    return section;\n  }\n\n  // Extract the order of comment lines from the original section\n  const originalCommentOrder = originalLines.filter(isComment);\n\n  // Build a map from comment content (trimmed) to the actual comment line in the new section\n  const commentMap = new Map<string, string>();\n  for (const line of sectionComments) {\n    commentMap.set(line.trim(), line);\n  }\n\n  // Reorder comments to match the original order, using the new section's comment lines\n  const reorderedComments: string[] = [];\n  for (const orig of originalCommentOrder) {\n    const trimmed = orig.trim();\n    if (commentMap.has(trimmed)) {\n      reorderedComments.push(commentMap.get(trimmed)!);\n      commentMap.delete(trimmed);\n    }\n  }\n  // Add any new comments from the new section that weren't in the original, preserving their order\n  for (const line of sectionComments) {\n    if (!originalCommentOrder.some((orig) => orig.trim() === line.trim())) {\n      reorderedComments.push(line);\n    }\n  }\n\n  // Reconstruct the section: comments (in order) + non-comment lines (in order)\n  return [...reorderedComments, ...nonCommentLines]\n    .join(\"\\n\")\n    .replace(/\\n{3,}/g, \"\\n\\n\")\n    .trim();\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { JSDOM } from \"jsdom\";\n\n/**\n * Creates a comprehensive XLIFF loader supporting versions 1.2 and 2.0\n * with deterministic key generation and structure preservation\n */\nexport default function createXliffLoader(): ILoader<\n  string,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(locale, input, _ctx, originalLocale) {\n      const trimmedInput = (input ?? \"\").trim();\n\n      if (!trimmedInput) {\n        return createEmptyResult(originalLocale, locale);\n      }\n\n      try {\n        const dom = new JSDOM(trimmedInput, { contentType: \"text/xml\" });\n        const document = dom.window.document;\n\n        // Check for parsing errors\n        const parserError = document.querySelector(\"parsererror\");\n        if (parserError) {\n          throw new Error(`XML parsing failed: ${parserError.textContent}`);\n        }\n\n        const xliffElement = document.documentElement;\n        if (!xliffElement || xliffElement.tagName !== \"xliff\") {\n          throw new Error(\"Invalid XLIFF: missing root <xliff> element\");\n        }\n\n        const version = xliffElement.getAttribute(\"version\") || \"1.2\";\n        const isV2 = version === \"2.0\";\n\n        if (isV2) {\n          return pullV2(xliffElement, locale, originalLocale);\n        } else {\n          return pullV1(xliffElement, locale, originalLocale);\n        }\n      } catch (error: any) {\n        throw new Error(`Failed to parse XLIFF file: ${error.message}`);\n      }\n    },\n\n    async push(locale, translations, originalInput, originalLocale, pullInput) {\n      if (!originalInput) {\n        // Create new file from scratch\n        return pushNewFile(locale, translations, originalLocale);\n      }\n\n      try {\n        const dom = new JSDOM(originalInput, { contentType: \"text/xml\" });\n        const document = dom.window.document;\n        const xliffElement = document.documentElement;\n        const version = xliffElement.getAttribute(\"version\") || \"1.2\";\n        const isV2 = version === \"2.0\";\n\n        if (isV2) {\n          return pushV2(\n            dom,\n            xliffElement,\n            locale,\n            translations,\n            originalLocale,\n            originalInput,\n          );\n        } else {\n          return pushV1(\n            dom,\n            xliffElement,\n            locale,\n            translations,\n            originalLocale,\n            originalInput,\n          );\n        }\n      } catch (error: any) {\n        throw new Error(`Failed to update XLIFF file: ${error.message}`);\n      }\n    },\n  });\n}\n\n/* -------------------------------------------------------------------------- */\n/*                            Version 1.2 Support                            */\n/* -------------------------------------------------------------------------- */\n\nfunction pullV1(\n  xliffElement: Element,\n  locale: string,\n  originalLocale: string,\n): Record<string, string> {\n  const result: Record<string, string> = {};\n  const fileElement = xliffElement.querySelector(\"file\");\n\n  if (!fileElement) {\n    return result;\n  }\n\n  const sourceLanguage =\n    fileElement.getAttribute(\"source-language\") || originalLocale;\n  const isSourceLocale = sourceLanguage === locale;\n  const bodyElement = fileElement.querySelector(\"body\");\n\n  if (!bodyElement) {\n    return result;\n  }\n\n  const transUnits = bodyElement.querySelectorAll(\"trans-unit\");\n  const seenKeys = new Set<string>();\n\n  transUnits.forEach((unit) => {\n    let key = getTransUnitKey(unit as Element);\n    if (!key) return;\n\n    // Handle duplicates deterministically\n    if (seenKeys.has(key)) {\n      const id = (unit as Element).getAttribute(\"id\")?.trim();\n      if (id) {\n        key = `${key}#${id}`;\n      } else {\n        let counter = 1;\n        let newKey = `${key}__${counter}`;\n        while (seenKeys.has(newKey)) {\n          counter++;\n          newKey = `${key}__${counter}`;\n        }\n        key = newKey;\n      }\n    }\n    seenKeys.add(key);\n\n    const elementName = isSourceLocale ? \"source\" : \"target\";\n    const textElement = (unit as Element).querySelector(elementName);\n\n    if (textElement) {\n      result[key] = extractTextContent(textElement);\n    } else if (isSourceLocale) {\n      result[key] = key; // fallback for source\n    } else {\n      result[key] = \"\"; // empty for missing target\n    }\n  });\n\n  return result;\n}\n\nfunction pushV1(\n  dom: JSDOM,\n  xliffElement: Element,\n  locale: string,\n  translations: Record<string, string>,\n  originalLocale: string,\n  originalInput?: string,\n): string {\n  const document = dom.window.document;\n  const fileElement = xliffElement.querySelector(\"file\");\n\n  if (!fileElement) {\n    throw new Error(\"Invalid XLIFF 1.2: missing <file> element\");\n  }\n\n  // Update language attributes\n  const sourceLanguage =\n    fileElement.getAttribute(\"source-language\") || originalLocale;\n  const isSourceLocale = sourceLanguage === locale;\n\n  if (!isSourceLocale) {\n    fileElement.setAttribute(\"target-language\", locale);\n  }\n\n  let bodyElement = fileElement.querySelector(\"body\");\n  if (!bodyElement) {\n    bodyElement = document.createElement(\"body\");\n    fileElement.appendChild(bodyElement);\n  }\n\n  // Build current index\n  const existingUnits = new Map<string, Element>();\n  const seenKeys = new Set<string>();\n\n  bodyElement.querySelectorAll(\"trans-unit\").forEach((unit) => {\n    let key = getTransUnitKey(unit as Element);\n    if (!key) return;\n\n    if (seenKeys.has(key)) {\n      const id = (unit as Element).getAttribute(\"id\")?.trim();\n      if (id) {\n        key = `${key}#${id}`;\n      } else {\n        let counter = 1;\n        let newKey = `${key}__${counter}`;\n        while (seenKeys.has(newKey)) {\n          counter++;\n          newKey = `${key}__${counter}`;\n        }\n        key = newKey;\n      }\n    }\n    seenKeys.add(key);\n    existingUnits.set(key, unit as Element);\n  });\n\n  // Update/create translation units\n  Object.entries(translations).forEach(([key, value]) => {\n    let unit = existingUnits.get(key);\n\n    if (!unit) {\n      unit = document.createElement(\"trans-unit\");\n      unit.setAttribute(\"id\", key);\n      unit.setAttribute(\"resname\", key);\n      unit.setAttribute(\"restype\", \"string\");\n      unit.setAttribute(\"datatype\", \"plaintext\");\n\n      const sourceElement = document.createElement(\"source\");\n      setTextContent(sourceElement, isSourceLocale ? value : key);\n      unit.appendChild(sourceElement);\n\n      if (!isSourceLocale) {\n        const targetElement = document.createElement(\"target\");\n        targetElement.setAttribute(\"state\", value ? \"translated\" : \"new\");\n        setTextContent(targetElement, value);\n        unit.appendChild(targetElement);\n      }\n\n      bodyElement.appendChild(unit);\n      existingUnits.set(key, unit);\n    } else {\n      updateTransUnitV1(unit, key, value, isSourceLocale);\n    }\n  });\n\n  // Remove orphaned units\n  const translationKeys = new Set(Object.keys(translations));\n  existingUnits.forEach((unit, key) => {\n    if (!translationKeys.has(key)) {\n      unit.parentNode?.removeChild(unit);\n    }\n  });\n\n  return serializeWithDeclaration(\n    dom,\n    extractXmlDeclaration(originalInput || \"\"),\n  );\n}\n\nfunction updateTransUnitV1(\n  unit: Element,\n  key: string,\n  value: string,\n  isSourceLocale: boolean,\n): void {\n  const document = unit.ownerDocument!;\n\n  if (isSourceLocale) {\n    let sourceElement = unit.querySelector(\"source\");\n    if (!sourceElement) {\n      sourceElement = document.createElement(\"source\");\n      unit.appendChild(sourceElement);\n    }\n    setTextContent(sourceElement, value);\n  } else {\n    let targetElement = unit.querySelector(\"target\");\n    if (!targetElement) {\n      targetElement = document.createElement(\"target\");\n      unit.appendChild(targetElement);\n    }\n\n    setTextContent(targetElement, value);\n    targetElement.setAttribute(\"state\", value.trim() ? \"translated\" : \"new\");\n  }\n}\n\n/* -------------------------------------------------------------------------- */\n/*                            Version 2.0 Support                            */\n/* -------------------------------------------------------------------------- */\n\nfunction pullV2(\n  xliffElement: Element,\n  locale: string,\n  originalLocale: string,\n): Record<string, string> {\n  const result: Record<string, string> = {};\n\n  // Add source language metadata\n  const srcLang = xliffElement.getAttribute(\"srcLang\") || originalLocale;\n  result.sourceLanguage = srcLang;\n\n  const fileElements = xliffElement.querySelectorAll(\"file\");\n\n  fileElements.forEach((fileElement) => {\n    const fileId = fileElement.getAttribute(\"id\");\n    if (!fileId) return;\n\n    traverseUnitsV2(fileElement, fileId, \"\", result);\n  });\n\n  return result;\n}\n\nfunction traverseUnitsV2(\n  container: Element,\n  fileId: string,\n  currentPath: string,\n  result: Record<string, string>,\n): void {\n  Array.from(container.children).forEach((child) => {\n    const tagName = child.tagName;\n\n    if (tagName === \"unit\") {\n      const unitId = child.getAttribute(\"id\")?.trim();\n      if (!unitId) return;\n\n      const key = `resources/${fileId}/${currentPath}${unitId}/source`;\n      const segment = child.querySelector(\"segment\");\n      const source = segment?.querySelector(\"source\");\n\n      if (source) {\n        result[key] = extractTextContent(source);\n      } else {\n        result[key] = unitId; // fallback\n      }\n    } else if (tagName === \"group\") {\n      const groupId = child.getAttribute(\"id\")?.trim();\n      const newPath = groupId\n        ? `${currentPath}${groupId}/groupUnits/`\n        : currentPath;\n      traverseUnitsV2(child, fileId, newPath, result);\n    }\n  });\n}\n\nfunction pushV2(\n  dom: JSDOM,\n  xliffElement: Element,\n  locale: string,\n  translations: Record<string, string>,\n  originalLocale: string,\n  originalInput?: string,\n): string {\n  const document = dom.window.document;\n\n  // Handle sourceLanguage metadata\n  if (translations.sourceLanguage) {\n    xliffElement.setAttribute(\"srcLang\", translations.sourceLanguage);\n    delete translations.sourceLanguage; // Don't process as regular translation\n  }\n\n  // Build index of existing units\n  const existingUnits = new Map<string, Element>();\n  const fileElements = xliffElement.querySelectorAll(\"file\");\n\n  fileElements.forEach((fileElement) => {\n    const fileId = fileElement.getAttribute(\"id\");\n    if (!fileId) return;\n\n    indexUnitsV2(fileElement, fileId, \"\", existingUnits);\n  });\n\n  // Update existing units\n  Object.entries(translations).forEach(([key, value]) => {\n    const unit = existingUnits.get(key);\n    if (unit) {\n      updateUnitV2(unit, value);\n    } else {\n      // For new units, we'd need to create the structure\n      // This is complex in V2 due to the hierarchical nature\n      console.warn(`Cannot create new unit for key: ${key} in XLIFF 2.0`);\n    }\n  });\n\n  return serializeWithDeclaration(\n    dom,\n    extractXmlDeclaration(originalInput || \"\"),\n  );\n}\n\nfunction indexUnitsV2(\n  container: Element,\n  fileId: string,\n  currentPath: string,\n  index: Map<string, Element>,\n): void {\n  Array.from(container.children).forEach((child) => {\n    const tagName = child.tagName;\n\n    if (tagName === \"unit\") {\n      const unitId = child.getAttribute(\"id\")?.trim();\n      if (!unitId) return;\n\n      const key = `resources/${fileId}/${currentPath}${unitId}/source`;\n      index.set(key, child);\n    } else if (tagName === \"group\") {\n      const groupId = child.getAttribute(\"id\")?.trim();\n      const newPath = groupId\n        ? `${currentPath}${groupId}/groupUnits/`\n        : currentPath;\n      indexUnitsV2(child, fileId, newPath, index);\n    }\n  });\n}\n\nfunction updateUnitV2(unit: Element, value: string): void {\n  const document = unit.ownerDocument!;\n\n  let segment = unit.querySelector(\"segment\");\n  if (!segment) {\n    segment = document.createElement(\"segment\");\n    unit.appendChild(segment);\n  }\n\n  let source = segment.querySelector(\"source\");\n  if (!source) {\n    source = document.createElement(\"source\");\n    segment.appendChild(source);\n  }\n\n  setTextContent(source, value);\n}\n\n/* -------------------------------------------------------------------------- */\n/*                              Utilities                                     */\n/* -------------------------------------------------------------------------- */\n\nfunction getTransUnitKey(transUnit: Element): string {\n  const resname = transUnit.getAttribute(\"resname\")?.trim();\n  if (resname) return resname;\n\n  const id = transUnit.getAttribute(\"id\")?.trim();\n  if (id) return id;\n\n  const sourceElement = transUnit.querySelector(\"source\");\n  if (sourceElement) {\n    const sourceText = extractTextContent(sourceElement).trim();\n    if (sourceText) return sourceText;\n  }\n\n  return \"\";\n}\n\nfunction extractTextContent(element: Element): string {\n  // Handle CDATA sections\n  const cdataNode = Array.from(element.childNodes).find(\n    (node) => node.nodeType === element.CDATA_SECTION_NODE,\n  );\n\n  if (cdataNode) {\n    return cdataNode.nodeValue || \"\";\n  }\n\n  return element.textContent || \"\";\n}\n\nfunction setTextContent(element: Element, content: string): void {\n  const document = element.ownerDocument!;\n\n  // Clear existing content\n  while (element.firstChild) {\n    element.removeChild(element.firstChild);\n  }\n\n  // Use CDATA if content contains XML-sensitive characters\n  if (/[<>&\"']/.test(content)) {\n    const cdataSection = document.createCDATASection(content);\n    element.appendChild(cdataSection);\n  } else {\n    element.textContent = content;\n  }\n}\n\nfunction extractXmlDeclaration(xmlContent: string): string {\n  const match = xmlContent.match(/^<\\?xml[^>]*\\?>/);\n  return match ? match[0] : \"\";\n}\n\nfunction serializeWithDeclaration(dom: JSDOM, declaration: string): string {\n  let serialized = dom.serialize();\n\n  // Add proper indentation for readability\n  serialized = formatXml(serialized);\n\n  if (declaration) {\n    serialized = `${declaration}\\n${serialized}`;\n  }\n\n  return serialized;\n}\n\nfunction formatXml(xml: string): string {\n  // Parse and reformat XML with proper indentation using JSDOM\n  const dom = new JSDOM(xml, { contentType: \"text/xml\" });\n  const doc = dom.window.document;\n\n  function formatElement(element: Element, depth: number = 0): string {\n    const indent = \"  \".repeat(depth);\n    const tagName = element.tagName;\n    const attributes = Array.from(element.attributes)\n      .map((attr) => `${attr.name}=\"${attr.value}\"`)\n      .join(\" \");\n\n    const openTag = attributes ? `<${tagName} ${attributes}>` : `<${tagName}>`;\n\n    // Check for CDATA sections first\n    const cdataNode = Array.from(element.childNodes).find(\n      (node) => node.nodeType === element.CDATA_SECTION_NODE,\n    );\n\n    if (cdataNode) {\n      return `${indent}${openTag}<![CDATA[${cdataNode.nodeValue}]]></${tagName}>`;\n    }\n\n    // Check if element has only text content\n    const textContent = element.textContent?.trim() || \"\";\n    const hasOnlyText =\n      element.childNodes.length === 1 && element.childNodes[0].nodeType === 3;\n\n    if (hasOnlyText && textContent) {\n      return `${indent}${openTag}${textContent}</${tagName}>`;\n    }\n\n    // Element has child elements\n    const children = Array.from(element.children);\n    if (children.length === 0) {\n      return `${indent}${openTag}</${tagName}>`;\n    }\n\n    let result = `${indent}${openTag}\\n`;\n    for (const child of children) {\n      result += formatElement(child, depth + 1) + \"\\n\";\n    }\n    result += `${indent}</${tagName}>`;\n\n    return result;\n  }\n\n  return formatElement(doc.documentElement);\n}\n\nfunction createEmptyResult(\n  originalLocale: string,\n  locale: string,\n): Record<string, string> {\n  return {};\n}\n\nfunction pushNewFile(\n  locale: string,\n  translations: Record<string, string>,\n  originalLocale: string,\n): string {\n  const skeleton = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xliff xmlns=\"urn:oasis:names:tc:xliff:document:1.2\" version=\"1.2\">\n  <file original=\"\" source-language=\"${originalLocale}\" target-language=\"${locale}\" datatype=\"plaintext\">\n    <header></header>\n    <body></body>\n  </file>\n</xliff>`;\n\n  const dom = new JSDOM(skeleton, { contentType: \"text/xml\" });\n  const document = dom.window.document;\n  const bodyElement = document.querySelector(\"body\")!;\n\n  Object.entries(translations).forEach(([key, value]) => {\n    const unit = document.createElement(\"trans-unit\");\n    unit.setAttribute(\"id\", key);\n    unit.setAttribute(\"resname\", key);\n    unit.setAttribute(\"restype\", \"string\");\n    unit.setAttribute(\"datatype\", \"plaintext\");\n\n    const sourceElement = document.createElement(\"source\");\n    setTextContent(sourceElement, key);\n    unit.appendChild(sourceElement);\n\n    const targetElement = document.createElement(\"target\");\n    targetElement.setAttribute(\"state\", value ? \"translated\" : \"new\");\n    setTextContent(targetElement, value);\n    unit.appendChild(targetElement);\n\n    bodyElement.appendChild(unit);\n  });\n\n  return serializeWithDeclaration(\n    dom,\n    '<?xml version=\"1.0\" encoding=\"utf-8\"?>',\n  );\n}\n","import { parseStringPromise, Builder } from \"xml2js\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nfunction normalizeXMLString(xmlString: string): string {\n  return xmlString\n    .replace(/\\s+/g, \" \")\n    .replace(/>\\s+</g, \"><\")\n    .replace(\"\\n\", \"\")\n    .trim();\n}\n\nexport default function createXmlLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      let result: Record<string, any> = {};\n\n      try {\n        const parsed = await parseStringPromise(input, {\n          explicitArray: false,\n          mergeAttrs: false,\n          normalize: true,\n          preserveChildrenOrder: true,\n          normalizeTags: true,\n          includeWhiteChars: true,\n          trim: true,\n        });\n        result = parsed;\n      } catch (error) {\n        console.error(\"Failed to parse XML:\", error);\n        result = {};\n      }\n\n      return result;\n    },\n\n    async push(locale, data) {\n      try {\n        const builder = new Builder({ headless: true });\n        const xmlOutput = builder.buildObject(data);\n        const expectedOutput = normalizeXMLString(xmlOutput);\n        return expectedOutput;\n      } catch (error) {\n        console.error(\"Failed to build XML:\", error);\n        return \"\";\n      }\n    },\n  });\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport srtParser from \"srt-parser-2\";\n\nexport default function createSrtLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  const parser = new srtParser();\n  return createLoader({\n    async pull(locale, input) {\n      const parsed = parser.fromSrt(input) || [];\n      const result: Record<string, string> = {};\n\n      parsed.forEach((entry) => {\n        if (entry.text !== undefined && entry.text !== null) {\n          const key = `${entry.id}#${entry.startTime}-${entry.endTime}`;\n          result[key] = entry.text;\n        }\n      });\n\n      return result;\n    },\n\n    async push(locale, payload) {\n      const output = Object.entries(payload)\n        .filter(([, text]) => text !== undefined && text !== null)\n        .map(([key, text]) => {\n          const [id, timeRange] = key.split(\"#\");\n          const [startTime, endTime] = timeRange.split(\"-\");\n\n          return {\n            id: id,\n            startTime: startTime,\n            startSeconds: 0,\n            endTime: endTime,\n            endSeconds: 0,\n            text: text,\n          };\n        });\n\n      const srtContent = parser.toSrt(output).trim().replace(/\\r?\\n/g, \"\\n\");\n      return srtContent;\n    },\n  });\n}\n","import fs from \"fs\";\nimport JSON5 from \"json5\";\nimport { composeLoaders } from \"../_utils\";\nimport { datoConfigSchema } from \"./_base\";\nimport createDatoFilterLoader from \"./filter\";\nimport createDatoApiLoader from \"./api\";\nimport createDatoExtractLoader from \"./extract\";\n\nexport default function createDatoLoader(configFilePath: string) {\n  try {\n    const configContent = fs.readFileSync(configFilePath, \"utf-8\");\n    const datoConfig = datoConfigSchema.parse(JSON5.parse(configContent));\n\n    return composeLoaders(\n      createDatoApiLoader(datoConfig, (updatedConfig) =>\n        fs.writeFileSync(\n          configFilePath,\n          JSON5.stringify(updatedConfig, null, 2),\n        ),\n      ),\n      createDatoFilterLoader(),\n      createDatoExtractLoader(),\n    );\n  } catch (error: any) {\n    throw new Error(\n      [`Failed to parse DatoCMS config file.`, `Error: ${error.message}`].join(\n        \"\\n\\n\",\n      ),\n    );\n  }\n}\n","import Z from \"zod\";\n\n// DatoCMS config\nexport const datoConfigSchema = Z.object({\n  project: Z.string(),\n  models: Z.record(\n    Z.string(),\n    Z.object({\n      records: Z.array(Z.string()).optional(),\n      fields: Z.array(Z.string()).optional(),\n    }),\n  ),\n});\n\nexport type DatoConfig = Z.infer<typeof datoConfigSchema>;\n\n// DatoCMS settings\nexport const datoSettingsSchema = Z.object({\n  auth: Z.object({\n    apiKey: Z.string(),\n  }),\n});\n\nexport type DatoSettings = Z.infer<typeof datoSettingsSchema>;\n\nexport const DEFAULT_LOCALE = \"en\";\n\n//\n\nexport type DatoRecordPayload = {\n  [field: string]: {\n    [locale: string]: DatoValue;\n  };\n};\n\nexport type DatoValue = DatoSimpleValue | DatoComplexValue;\nexport type DatoSimpleValue = DatoPrimitive | DastDocument;\nexport type DatoComplexValue = DatoBlock | DatoBlock[];\n\nexport type DatoPrimitive = null | string | boolean | number;\n\nexport type DastDocument = {\n  schema: \"dast\";\n  document: DastDocumentNode;\n};\n\nexport type DastDocumentNode = {\n  type: \"root\" | \"span\" | \"paragraph\";\n  value?: DatoPrimitive;\n  children?: DastDocumentNode[];\n};\n\nexport type DatoBlock = {\n  id?: string;\n  type: \"item\";\n  attributes: Record<string, DatoSimpleValue>;\n  relationships: any;\n};\n","import _ from \"lodash\";\nimport fs from \"fs\";\nimport { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport { DatoApiLoaderOutput } from \"./api\";\n\nexport type DatoFilterLoaderOutput = {\n  [modelId: string]: {\n    [recordId: string]: {\n      [fieldName: string]: any;\n    };\n  };\n};\n\nexport default function createDatoFilterLoader(): ILoader<\n  DatoApiLoaderOutput,\n  DatoFilterLoaderOutput\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const result: DatoFilterLoaderOutput = {};\n\n      for (const [modelId, modelInfo] of _.entries(input)) {\n        result[modelId] = {};\n        for (const record of modelInfo.records) {\n          result[modelId][record.id] = _.chain(modelInfo.fields)\n            .mapKeys((field) => field.api_key)\n            .mapValues((field) => _.get(record, [field.api_key, locale]))\n            .value();\n        }\n      }\n\n      return result;\n    },\n    async push(locale, data, originalInput, originalLocale) {\n      const result = _.cloneDeep(originalInput || {});\n\n      for (const [modelId, modelInfo] of _.entries(result)) {\n        for (const record of modelInfo.records) {\n          for (const [fieldId, fieldValue] of _.entries(record)) {\n            const fieldInfo = modelInfo.fields.find(\n              (field) => field.api_key === fieldId,\n            );\n            if (fieldInfo) {\n              const sourceFieldValue = _.get(fieldValue, [originalLocale]);\n              const targetFieldValue = _.get(data, [\n                modelId,\n                record.id,\n                fieldId,\n              ]);\n              if (targetFieldValue) {\n                _.set(record, [fieldId, locale], targetFieldValue);\n              } else {\n                _.set(record, [fieldId, locale], sourceFieldValue);\n              }\n\n              _.chain(fieldValue)\n                .keys()\n                .reject((loc) => loc === locale || loc === originalLocale)\n                .filter((loc) => _.isEmpty(_.get(fieldValue, [loc])))\n                .forEach((loc) =>\n                  _.set(record, [fieldId, loc], sourceFieldValue),\n                )\n                .value();\n            }\n          }\n        }\n      }\n\n      return result;\n    },\n  });\n}\n","import _ from \"lodash\";\nimport { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport createDatoClient, { DatoClient } from \"./_utils\";\nimport { SimpleSchemaTypes } from \"@datocms/cma-client-node\";\nimport { DatoConfig } from \"./_base\";\nimport inquirer from \"inquirer\";\n\nexport type DatoApiLoaderOutput = {\n  [modelId: string]: {\n    fields: SimpleSchemaTypes.Field[];\n    records: SimpleSchemaTypes.Item[];\n  };\n};\n\nexport type DatoApiLoaderCtx = {\n  models: {\n    [modelId: string]: {\n      fields: SimpleSchemaTypes.Field[];\n      records: SimpleSchemaTypes.Item[];\n    };\n  };\n};\n\nexport default function createDatoApiLoader(\n  config: DatoConfig,\n  onConfigUpdate: (config: DatoConfig) => void,\n): ILoader<void, DatoApiLoaderOutput, DatoApiLoaderCtx> {\n  const dato = createDatoClient({\n    apiKey: process.env.DATO_API_TOKEN || \"\",\n    projectId: config.project,\n  });\n  return createLoader({\n    init: async () => {\n      const result: DatoApiLoaderCtx = {\n        models: {},\n      };\n      const updatedConfig = _.cloneDeep(config);\n      console.log(`Initializing DatoCMS loader...`);\n\n      const project = await dato.findProject();\n      const modelChoices = await getModelChoices(dato, config);\n      const selectedModels = await promptModelSelection(modelChoices);\n\n      for (const modelId of selectedModels) {\n        if (!updatedConfig.models[modelId]) {\n          updatedConfig.models[modelId] = {\n            fields: [],\n            records: [],\n          };\n        }\n      }\n\n      for (const modelId of Object.keys(updatedConfig.models)) {\n        if (!selectedModels.includes(modelId)) {\n          delete updatedConfig.models[modelId];\n        }\n      }\n\n      for (const modelId of _.keys(updatedConfig.models)) {\n        const { modelName, fields } = await getModelFields(dato, modelId);\n\n        if (fields.length > 0) {\n          result.models[modelId] = { fields: [], records: [] };\n\n          const fieldInfos = await getFieldDetails(dato, fields);\n          const fieldChoices = createFieldChoices(fieldInfos);\n          const selectedFields = await promptFieldSelection(\n            modelName,\n            fieldChoices,\n          );\n\n          for (const fieldInfo of fieldInfos) {\n            const isLocalized = await updateFieldLocalization(\n              dato,\n              fieldInfo,\n              selectedFields.includes(fieldInfo.id),\n            );\n            if (isLocalized) {\n              result.models[modelId].fields.push(fieldInfo);\n              updatedConfig.models[modelId].fields = _.uniq([\n                ...(updatedConfig.models[modelId].fields || []),\n                fieldInfo.api_key,\n              ]);\n            }\n          }\n\n          const records = await dato.findRecordsForModel(modelId);\n          const recordChoices = createRecordChoices(\n            records,\n            config.models[modelId]?.records || [],\n            project,\n          );\n          const selectedRecords = await promptRecordSelection(\n            modelName,\n            recordChoices,\n          );\n\n          result.models[modelId].records = records.filter((record) =>\n            selectedRecords.includes(record.id),\n          );\n          updatedConfig.models[modelId].records = selectedRecords;\n        }\n      }\n      console.log(`DatoCMS loader initialized.`);\n      onConfigUpdate(updatedConfig);\n      return result;\n    },\n    async pull(locale, input, initCtx) {\n      const result: DatoApiLoaderOutput = {};\n\n      for (const modelId of _.keys(initCtx?.models || {})) {\n        let records = initCtx?.models[modelId].records || [];\n        const recordIds = records.map((record) => record.id);\n        records = await dato.findRecords(recordIds);\n        console.log(`Fetched ${records.length} records for model ${modelId}`);\n\n        if (records.length > 0) {\n          result[modelId] = {\n            fields: initCtx?.models?.[modelId]?.fields || [],\n            records: records,\n          };\n        }\n      }\n      return result;\n    },\n    async push(locale, data, originalInput) {\n      for (const modelId of _.keys(data)) {\n        for (let i = 0; i < data[modelId].records.length; i++) {\n          const record = data[modelId].records[i];\n          console.log(\n            `Updating record ${i + 1}/${\n              data[modelId].records.length\n            } for model ${modelId}...`,\n          );\n          await dato.updateRecord(record.id, record);\n        }\n      }\n    },\n  });\n}\n\nexport async function getModelFields(dato: any, modelId: string) {\n  const modelInfo = await dato.findModel(modelId);\n  return {\n    modelName: modelInfo.name,\n    fields: _.filter(modelInfo.fields, (field) => field.type === \"field\"),\n  };\n}\n\nexport async function getFieldDetails(\n  dato: DatoClient,\n  fields: SimpleSchemaTypes.Field[],\n) {\n  return Promise.all(fields.map((field) => dato.findField(field.id)));\n}\n\nexport function createFieldChoices(fieldInfos: SimpleSchemaTypes.Field[]) {\n  return fieldInfos.map((field) => ({\n    name: field.label,\n    value: field.id,\n    checked: field.localized,\n  }));\n}\n\nexport async function promptFieldSelection(modelName: string, choices: any[]) {\n  const { selectedFields } = await inquirer.prompt([\n    {\n      type: \"checkbox\",\n      name: \"selectedFields\",\n      message: `Select fields to enable localization for model \"${modelName}\":`,\n      choices,\n      pageSize: process.stdout.rows - 4, // Subtract some rows for prompt text and margins\n    },\n  ]);\n  return selectedFields;\n}\n\nexport async function updateFieldLocalization(\n  dato: any,\n  fieldInfo: SimpleSchemaTypes.Field,\n  shouldBeLocalized: boolean,\n) {\n  if (shouldBeLocalized !== fieldInfo.localized) {\n    console.log(\n      `${shouldBeLocalized ? \"Enabling\" : \"Disabling\"} localization for ${\n        fieldInfo.label\n      }...`,\n    );\n    await dato.updateField(fieldInfo.id, { localized: shouldBeLocalized });\n  }\n  return shouldBeLocalized;\n}\n\nexport function createRecordChoices(\n  records: SimpleSchemaTypes.Item[],\n  selectedIds: string[] = [],\n  project: SimpleSchemaTypes.Site,\n) {\n  return records.map((record) => ({\n    name: `${record.id} - https://${project.internal_domain}/editor/item_types/${record.item_type.id}/items/${record.id}`,\n    value: record.id,\n    checked: selectedIds?.includes(record.id),\n  }));\n}\n\nexport async function promptRecordSelection(modelName: string, choices: any[]) {\n  const { selectedRecords } = await inquirer.prompt([\n    {\n      type: \"checkbox\",\n      name: \"selectedRecords\",\n      message: `Select records to include for model \"${modelName}\":`,\n      choices,\n      pageSize: process.stdout.rows - 4, // Subtract some rows for prompt text and margins\n    },\n  ]);\n  return selectedRecords;\n}\n\nexport async function getModelChoices(dato: DatoClient, config: DatoConfig) {\n  const models = await dato.findModels();\n  return models.map((model) => ({\n    name: `${model.name} (${model.api_key})`,\n    value: model.id,\n    checked: config.models[model.id] !== undefined,\n    pageSize: process.stdout.rows - 4, // Subtract some rows for prompt text and margins\n  }));\n}\n\nexport async function promptModelSelection(choices: any[]) {\n  const { selectedModels } = await inquirer.prompt([\n    {\n      type: \"checkbox\",\n      name: \"selectedModels\",\n      message: \"Select models to include:\",\n      choices,\n      pageSize: process.stdout.rows - 4, // Subtract some rows for prompt text and margins\n    },\n  ]);\n  return selectedModels;\n}\n","import _ from \"lodash\";\nimport { buildClient, SimpleSchemaTypes } from \"@datocms/cma-client-node\";\nimport { DastDocument, DatoBlock, DatoSimpleValue, DatoValue } from \"./_base\";\nimport { DastDocumentNode } from \"./_base\";\n\ntype DatoClientParams = {\n  apiKey: string;\n  projectId: string;\n};\n\nexport type DatoClient = ReturnType<typeof createDatoClient>;\n\nexport default function createDatoClient(params: DatoClientParams) {\n  if (!params.apiKey) {\n    throw new Error(\n      \"Missing required environment variable: DATO_API_TOKEN. Please set this variable and try again.\",\n    );\n  }\n  const dato = buildClient({\n    apiToken: params.apiKey,\n    extraHeaders: {\n      \"X-Exclude-Invalid\": \"true\",\n    },\n  });\n\n  return {\n    findProject: async (): Promise<SimpleSchemaTypes.Site> => {\n      const project = await dato.site.find();\n      return project;\n    },\n    updateField: async (\n      fieldId: string,\n      payload: SimpleSchemaTypes.FieldUpdateSchema,\n    ): Promise<void> => {\n      try {\n        await dato.fields.update(fieldId, payload);\n      } catch (_error: any) {\n        throw new Error(\n          [\n            `Failed to update field in DatoCMS.`,\n            `Field ID: ${fieldId}`,\n            `Payload: ${JSON.stringify(payload, null, 2)}`,\n            `Error: ${JSON.stringify(_error, null, 2)}`,\n          ].join(\"\\n\\n\"),\n        );\n      }\n    },\n    findField: async (fieldId: string): Promise<SimpleSchemaTypes.Field> => {\n      try {\n        const field = await dato.fields.find(fieldId);\n        if (!field) {\n          throw new Error(`Field ${fieldId} not found`);\n        }\n        return field;\n      } catch (_error: any) {\n        throw new Error(\n          [\n            `Failed to find field in DatoCMS.`,\n            `Field ID: ${fieldId}`,\n            `Error: ${JSON.stringify(_error, null, 2)}`,\n          ].join(\"\\n\\n\"),\n        );\n      }\n    },\n    findModels: async (): Promise<SimpleSchemaTypes.ItemType[]> => {\n      try {\n        const models = await dato.itemTypes.list();\n        const modelsWithoutBlocks = models.filter(\n          (model) => !model.modular_block,\n        );\n        return modelsWithoutBlocks;\n      } catch (_error: any) {\n        throw new Error(\n          [\n            `Failed to find models in DatoCMS.`,\n            `Error: ${JSON.stringify(_error, null, 2)}`,\n          ].join(\"\\n\\n\"),\n        );\n      }\n    },\n    findModel: async (modelId: string): Promise<SimpleSchemaTypes.ItemType> => {\n      try {\n        const model = await dato.itemTypes.find(modelId);\n        if (!model) {\n          throw new Error(`Model ${modelId} not found`);\n        }\n        return model;\n      } catch (_error: any) {\n        throw new Error(\n          [\n            `Failed to find model in DatoCMS.`,\n            `Model ID: ${modelId}`,\n            `Error: ${JSON.stringify(_error, null, 2)}`,\n          ].join(\"\\n\\n\"),\n        );\n      }\n    },\n    findRecords: async (\n      records: string[],\n      limit: number = 100,\n    ): Promise<SimpleSchemaTypes.Item[]> => {\n      return dato.items\n        .list({\n          nested: true,\n          version: \"current\",\n          limit,\n          filter: {\n            projectId: params.projectId,\n            only_valid: \"true\",\n            ids: !records.length ? undefined : records.join(\",\"),\n          },\n        })\n        .catch((error: any) =>\n          Promise.reject(error?.response?.body?.data?.[0] || error),\n        );\n    },\n    findRecordsForModel: async (\n      modelId: string,\n      records?: string[],\n    ): Promise<SimpleSchemaTypes.Item[]> => {\n      try {\n        const result = await dato.items\n          .list({\n            nested: true,\n            version: \"current\",\n            filter: {\n              type: modelId,\n              only_valid: \"true\",\n              ids: !records?.length ? undefined : records.join(\",\"),\n            },\n          })\n          .catch((error: any) =>\n            Promise.reject(error?.response?.body?.data?.[0] || error),\n          );\n        return result;\n      } catch (_error: any) {\n        throw new Error(\n          [\n            `Failed to find records for model in DatoCMS.`,\n            `Model ID: ${modelId}`,\n            `Error: ${JSON.stringify(_error, null, 2)}`,\n          ].join(\"\\n\\n\"),\n        );\n      }\n    },\n    updateRecord: async (id: string, payload: any): Promise<void> => {\n      try {\n        await dato.items\n          .update(id, payload)\n          .catch((error: any) =>\n            Promise.reject(error?.response?.body?.data?.[0] || error),\n          );\n      } catch (_error: any) {\n        if (_error?.attributes?.details?.message) {\n          throw new Error(\n            [\n              `${_error.attributes.details.message}`,\n              `Payload: ${JSON.stringify(payload, null, 2)}`,\n              `Error: ${JSON.stringify(_error, null, 2)}`,\n            ].join(\"\\n\\n\"),\n          );\n        }\n\n        throw new Error(\n          [\n            `Failed to update record in DatoCMS.`,\n            `Record ID: ${id}`,\n            `Payload: ${JSON.stringify(payload, null, 2)}`,\n            `Error: ${JSON.stringify(_error, null, 2)}`,\n          ].join(\"\\n\\n\"),\n        );\n      }\n    },\n    enableFieldLocalization: async (args: {\n      modelId: string;\n      fieldId: string;\n    }): Promise<void> => {\n      try {\n        await dato.fields\n          .update(`${args.modelId}::${args.fieldId}`, { localized: true })\n          .catch((error: any) =>\n            Promise.reject(error?.response?.body?.data?.[0] || error),\n          );\n      } catch (_error: any) {\n        if (_error?.attributes?.code === \"NOT_FOUND\") {\n          throw new Error(\n            [\n              `Field \"${args.fieldId}\" not found in model \"${args.modelId}\".`,\n              `Error: ${JSON.stringify(_error, null, 2)}`,\n            ].join(\"\\n\\n\"),\n          );\n        }\n\n        if (_error?.attributes?.details?.message) {\n          throw new Error(\n            [\n              `${_error.attributes.details.message}`,\n              `Error: ${JSON.stringify(_error, null, 2)}`,\n            ].join(\"\\n\\n\"),\n          );\n        }\n\n        throw new Error(\n          [\n            `Failed to enable field localization in DatoCMS.`,\n            `Field ID: ${args.fieldId}`,\n            `Model ID: ${args.modelId}`,\n            `Error: ${JSON.stringify(_error, null, 2)}`,\n          ].join(\"\\n\\n\"),\n        );\n      }\n    },\n  };\n}\n\ntype TraverseDatoCallbackMap = {\n  onValue?: (\n    path: string[],\n    value: DatoSimpleValue,\n    setValue: (value: DatoSimpleValue) => void,\n  ) => void;\n  onBlock?: (path: string[], value: DatoBlock) => void;\n};\n\nexport function traverseDatoPayload(\n  payload: Record<string, DatoValue>,\n  callbackMap: TraverseDatoCallbackMap,\n  path: string[] = [],\n) {\n  for (const fieldName of Object.keys(payload)) {\n    const fieldValue = payload[fieldName];\n    traverseDatoValue(payload, fieldValue, callbackMap, [...path, fieldName]);\n  }\n}\n\nexport function traverseDatoValue(\n  parent: Record<string, DatoValue>,\n  value: DatoValue,\n  callbackMap: TraverseDatoCallbackMap,\n  path: string[] = [],\n) {\n  if (_.isArray(value)) {\n    for (let i = 0; i < value.length; i++) {\n      traverseDatoValue(parent, value[i], callbackMap, [...path, i.toString()]);\n    }\n  } else if (_.isObject(value)) {\n    if (\"schema\" in value && value.schema === \"dast\") {\n      traverseDastDocument(value, callbackMap, [...path]);\n    } else if (\"type\" in value && value.type === \"item\") {\n      traverseDatoBlock(value, callbackMap, [...path]);\n    } else {\n      throw new Error(\n        [\n          \"Unsupported dato object value type:\",\n          JSON.stringify(value, null, 2),\n        ].join(\"\\n\\n\"),\n      );\n    }\n  } else {\n    callbackMap.onValue?.(path, value, (value) => {\n      _.set(parent, path[path.length - 1], value);\n    });\n  }\n}\n\nexport function traverseDastDocument(\n  dast: DastDocument,\n  callbackMap: TraverseDatoCallbackMap,\n  path: string[] = [],\n) {\n  traverseDastNode(dast.document, callbackMap, [...path, \"document\"]);\n}\n\nexport function traverseDatoBlock(\n  block: DatoBlock,\n  callbackMap: TraverseDatoCallbackMap,\n  path: string[] = [],\n) {\n  callbackMap.onBlock?.(path, block);\n  traverseDatoPayload(block.attributes, callbackMap, [...path, \"attributes\"]);\n}\n\nexport function traverseDastNode(\n  node: DastDocumentNode,\n  callbackMap: TraverseDatoCallbackMap,\n  path: string[] = [],\n) {\n  if (node.value) {\n    callbackMap.onValue?.(path, node.value, (value) => {\n      _.set(node, \"value\", value);\n    });\n  }\n  if (node.children?.length) {\n    for (let i = 0; i < node.children.length; i++) {\n      traverseDastNode(node.children[i], callbackMap, [...path, i.toString()]);\n    }\n  }\n}\n","import _ from \"lodash\";\nimport { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport { DatoFilterLoaderOutput } from \"./filter\";\nimport fs from \"fs\";\nimport Z from \"zod\";\n\nexport type DatoExtractLoaderOutput = {\n  [modelId: string]: {\n    [recordId: string]: {\n      [fieldName: string]: string | Record<string, object>;\n    };\n  };\n};\n\nexport default function createDatoExtractLoader(): ILoader<\n  DatoFilterLoaderOutput,\n  DatoExtractLoaderOutput\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const result: DatoExtractLoaderOutput = {};\n\n      for (const [modelId, modelInfo] of _.entries(input)) {\n        for (const [recordId, record] of _.entries(modelInfo)) {\n          for (const [fieldName, fieldValue] of _.entries(record)) {\n            const parsedValue = createParsedDatoValue(fieldValue);\n            if (parsedValue) {\n              _.set(result, [modelId, `_${recordId}`, fieldName], parsedValue);\n            }\n          }\n        }\n      }\n\n      return result;\n    },\n    async push(locale, data, originalInput) {\n      const result = _.cloneDeep(originalInput || {});\n\n      for (const [modelId, modelInfo] of _.entries(data)) {\n        for (const [virtualRecordId, record] of _.entries(modelInfo)) {\n          for (const [fieldName, fieldValue] of _.entries(record)) {\n            const [, recordId] = virtualRecordId.split(\"_\");\n            const originalFieldValue = _.get(originalInput, [\n              modelId,\n              recordId,\n              fieldName,\n            ]);\n            const rawValue = createRawDatoValue(\n              fieldValue,\n              originalFieldValue,\n              true,\n            );\n            _.set(\n              result,\n              [modelId, recordId, fieldName],\n              rawValue || originalFieldValue,\n            );\n          }\n        }\n      }\n\n      return result;\n    },\n  });\n}\n\nexport type DatoValueRaw = any;\nexport type DatoValueParsed = any;\n\nexport function detectDatoFieldType(rawDatoValue: DatoValueRaw): string | null {\n  if (\n    _.has(rawDatoValue, \"document\") &&\n    _.get(rawDatoValue, \"schema\") === \"dast\"\n  ) {\n    return \"structured_text\";\n  } else if (\n    _.has(rawDatoValue, \"no_index\") ||\n    _.has(rawDatoValue, \"twitter_card\")\n  ) {\n    return \"seo\";\n  } else if (_.get(rawDatoValue, \"type\") === \"item\") {\n    return \"single_block\";\n  } else if (\n    _.isArray(rawDatoValue) &&\n    _.every(rawDatoValue, (item) => _.get(item, \"type\") === \"item\")\n  ) {\n    return \"rich_text\";\n  } else if (_isFile(rawDatoValue)) {\n    return \"file\";\n  } else if (\n    _.isArray(rawDatoValue) &&\n    _.every(rawDatoValue, (item) => _isFile(item))\n  ) {\n    return \"gallery\";\n  } else if (_isJson(rawDatoValue)) {\n    return \"json\";\n  } else if (_.isString(rawDatoValue)) {\n    return \"string\";\n  } else if (_isVideo(rawDatoValue)) {\n    return \"video\";\n  } else if (\n    _.isArray(rawDatoValue) &&\n    _.every(rawDatoValue, (item) => _.isString(item))\n  ) {\n    return \"ref_list\";\n  } else {\n    return null;\n  }\n}\n\nexport function createParsedDatoValue(\n  rawDatoValue: DatoValueRaw,\n): DatoValueParsed {\n  const fieldType = detectDatoFieldType(rawDatoValue);\n  switch (fieldType) {\n    default:\n      return rawDatoValue;\n    case \"structured_text\":\n      return serializeStructuredText(rawDatoValue);\n    case \"seo\":\n      return serializeSeo(rawDatoValue);\n    case \"single_block\":\n      return serializeBlock(rawDatoValue);\n    case \"rich_text\":\n      return serializeBlockList(rawDatoValue);\n    case \"json\":\n      return JSON.parse(rawDatoValue);\n    case \"video\":\n      return serializeVideo(rawDatoValue);\n    case \"file\":\n      return serializeFile(rawDatoValue);\n    case \"gallery\":\n      return serializeGallery(rawDatoValue);\n    case \"ref_list\":\n      return null;\n  }\n}\n\nexport function createRawDatoValue(\n  parsedDatoValue: DatoValueParsed,\n  originalRawDatoValue: any,\n  isClean = false,\n): DatoValueRaw {\n  const fieldType = detectDatoFieldType(originalRawDatoValue);\n  switch (fieldType) {\n    default:\n      return parsedDatoValue;\n    case \"structured_text\":\n      return deserializeStructuredText(parsedDatoValue, originalRawDatoValue);\n    case \"seo\":\n      return deserializeSeo(parsedDatoValue, originalRawDatoValue);\n    case \"single_block\":\n      return deserializeBlock(parsedDatoValue, originalRawDatoValue, isClean);\n    case \"rich_text\":\n      return deserializeBlockList(\n        parsedDatoValue,\n        originalRawDatoValue,\n        isClean,\n      );\n    case \"json\":\n      return JSON.stringify(parsedDatoValue, null, 2);\n    case \"video\":\n      return deserializeVideo(parsedDatoValue, originalRawDatoValue);\n    case \"file\":\n      return deserializeFile(parsedDatoValue, originalRawDatoValue);\n    case \"gallery\":\n      return deserializeGallery(parsedDatoValue, originalRawDatoValue);\n    case \"ref_list\":\n      return originalRawDatoValue;\n  }\n}\n\nfunction serializeStructuredText(rawStructuredText: any) {\n  return serializeStructuredTextNode(rawStructuredText);\n  // Encapsulates helper function args\n  function serializeStructuredTextNode(\n    node: any,\n    path: string[] = [],\n    acc: Record<string, any> = {},\n  ) {\n    if (\"document\" in node) {\n      return serializeStructuredTextNode(\n        node.document,\n        [...path, \"document\"],\n        acc,\n      );\n    }\n\n    if (!_.isNil(node.value)) {\n      acc[[...path, \"value\"].join(\".\")] = node.value;\n    } else if (_.get(node, \"type\") === \"block\") {\n      acc[[...path, \"item\"].join(\".\")] = serializeBlock(node.item);\n    }\n\n    if (node.children) {\n      for (let i = 0; i < node.children.length; i++) {\n        serializeStructuredTextNode(\n          node.children[i],\n          [...path, i.toString()],\n          acc,\n        );\n      }\n    }\n\n    return acc;\n  }\n}\n\nfunction serializeSeo(rawSeo: any) {\n  return _.chain(rawSeo).pick([\"title\", \"description\"]).value();\n}\n\nfunction serializeBlock(rawBlock: any) {\n  if (_.get(rawBlock, \"type\") === \"item\" && _.has(rawBlock, \"id\")) {\n    return serializeBlock(rawBlock.attributes);\n  }\n\n  const result: Record<string, any> = {};\n  for (const [attributeName, attributeValue] of _.entries(rawBlock)) {\n    result[attributeName] = createParsedDatoValue(attributeValue);\n  }\n\n  return result;\n}\n\nfunction serializeBlockList(rawBlockList: any) {\n  return _.chain(rawBlockList)\n    .map((block) => serializeBlock(block))\n    .value();\n}\n\nfunction serializeVideo(rawVideo: any) {\n  return _.chain(rawVideo).pick([\"title\"]).value();\n}\n\nfunction serializeFile(rawFile: any) {\n  return _.chain(rawFile).pick([\"alt\", \"title\"]).value();\n}\n\nfunction serializeGallery(rawGallery: any) {\n  return _.chain(rawGallery)\n    .map((item) => serializeFile(item))\n    .value();\n}\n\nfunction deserializeFile(parsedFile: any, originalRawFile: any) {\n  return _.chain(parsedFile).defaults(originalRawFile).value();\n}\n\nfunction deserializeGallery(parsedGallery: any, originalRawGallery: any) {\n  return _.chain(parsedGallery)\n    .map((item, i) => deserializeFile(item, originalRawGallery[i]))\n    .value();\n}\n\nfunction deserializeVideo(parsedVideo: any, originalRawVideo: any) {\n  return _.chain(parsedVideo).defaults(originalRawVideo).value();\n}\n\nfunction deserializeBlock(payload: any, rawNode: any, isClean = false) {\n  const result = _.cloneDeep(rawNode);\n\n  for (const [attributeName, attributeValue] of _.entries(rawNode.attributes)) {\n    const rawValue = createRawDatoValue(\n      payload[attributeName],\n      attributeValue,\n      isClean,\n    );\n    _.set(result, [\"attributes\", attributeName], rawValue);\n  }\n\n  if (isClean) {\n    delete result[\"id\"];\n  }\n\n  return result;\n}\n\nfunction deserializeSeo(parsedSeo: any, originalRawSeo: any) {\n  return _.chain(parsedSeo)\n    .pick([\"title\", \"description\"])\n    .defaults(originalRawSeo)\n    .value();\n}\n\nfunction deserializeBlockList(\n  parsedBlockList: any,\n  originalRawBlockList: any,\n  isClean = false,\n) {\n  return _.chain(parsedBlockList)\n    .map((block, i) =>\n      deserializeBlock(block, originalRawBlockList[i], isClean),\n    )\n    .value();\n}\n\nfunction deserializeStructuredText(\n  parsedStructuredText: Record<string, string>,\n  originalRawStructuredText: any,\n) {\n  const result = _.cloneDeep(originalRawStructuredText);\n\n  for (const [path, value] of _.entries(parsedStructuredText)) {\n    const realPath = _.chain(path.split(\".\"))\n      .flatMap((s) => (!_.isNaN(_.toNumber(s)) ? [\"children\", s] : s))\n      .value();\n    const deserializedValue = createRawDatoValue(\n      value,\n      _.get(originalRawStructuredText, realPath),\n      true,\n    );\n    _.set(result, realPath, deserializedValue);\n  }\n\n  return result;\n}\n\nfunction _isJson(rawDatoValue: DatoValueRaw): boolean {\n  try {\n    return (\n      _.isString(rawDatoValue) &&\n      rawDatoValue.startsWith(\"{\") &&\n      rawDatoValue.endsWith(\"}\") &&\n      !!JSON.parse(rawDatoValue)\n    );\n  } catch (e) {\n    return false;\n  }\n}\n\nfunction _isFile(rawDatoValue: DatoValueRaw): boolean {\n  return (\n    _.isObject(rawDatoValue) &&\n    [\"alt\", \"title\", \"custom_data\", \"focal_point\", \"upload_id\"].every((key) =>\n      _.has(rawDatoValue, key),\n    )\n  );\n}\n\nfunction _isVideo(rawDatoValue: DatoValueRaw): boolean {\n  return (\n    _.isObject(rawDatoValue) &&\n    [\n      \"url\",\n      \"title\",\n      \"width\",\n      \"height\",\n      \"provider\",\n      \"provider_uid\",\n      \"thumbnail_url\",\n    ].every((key) => _.has(rawDatoValue, key))\n  );\n}\n","import webvtt from \"node-webvtt\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nconst UNSUPPORTED_BLOCK_REGEX = /^(?:STYLE|REGION)\\s*$/;\n\nfunction isUnsupportedBlock(block: string): boolean {\n  const firstLine = block.trimStart().split(\"\\n\", 1)[0];\n  return UNSUPPORTED_BLOCK_REGEX.test(firstLine);\n}\n\n// node-webvtt doesn't handle STYLE/REGION blocks — strip them before parsing\nfunction stripUnsupportedBlocks(input: string): string {\n  return input\n    .replace(/\\r\\n/g, \"\\n\")\n    .split(\"\\n\\n\")\n    .filter((part) => !isUnsupportedBlock(part))\n    .join(\"\\n\\n\");\n}\n\nfunction getUnsupportedBlocks(input: string): string[] {\n  return input.replace(/\\r\\n/g, \"\\n\").split(\"\\n\\n\").filter(isUnsupportedBlock);\n}\n\nexport default function createVttLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      if (!input) {\n        return \"\"; // if VTT file does not exist yet we can not parse it - return empty string\n      }\n      const vtt = webvtt.parse(stripUnsupportedBlocks(input))?.cues;\n      if (Object.keys(vtt).length === 0) {\n        return {};\n      } else {\n        return vtt.reduce((result: any, cue: any, index: number) => {\n          const key = `${index}#${cue.start}-${cue.end}#${cue.identifier}`;\n          result[key] = cue.text;\n          return result;\n        }, {});\n      }\n    },\n    async push(locale, payload, originalInput) {\n      const output = Object.entries(payload).reduce(\n        (cues: any[], [key, text]) => {\n          if (!text) return cues;\n\n          const [, timeRange, identifier] = key.split(\"#\");\n          const [startTime, endTime] = timeRange.split(\"-\");\n\n          cues.push({\n            end: Number(endTime),\n            identifier,\n            start: Number(startTime),\n            styles: \"\",\n            text,\n          });\n          return cues;\n        },\n        [],\n      );\n\n      const input = {\n        valid: true,\n        strict: true,\n        cues: output,\n      };\n\n      const compiled = webvtt.compile(input);\n\n      // Re-insert STYLE/REGION blocks after the WEBVTT header\n      const blocks = getUnsupportedBlocks(originalInput ?? \"\");\n      if (blocks.length === 0) return compiled;\n\n      const [header, ...rest] = compiled.split(\"\\n\\n\");\n      return [header, ...blocks, ...rest].join(\"\\n\\n\");\n    },\n  });\n}\n","import _ from \"lodash\";\nimport { ILoader } from \"../_types\";\nimport { composeLoaders, createLoader } from \"../_utils\";\n\nexport type VariableLoaderParams = {\n  type: \"ieee\" | \"python\";\n};\n\nexport default function createVariableLoader(\n  params: VariableLoaderParams,\n): ILoader<Record<string, any>, Record<string, string>> {\n  return composeLoaders(variableExtractLoader(params), variableContentLoader());\n}\n\ntype VariableExtractionPayload = {\n  variables: string[];\n  value: string;\n};\n\nfunction variableExtractLoader(\n  params: VariableLoaderParams,\n): ILoader<Record<string, any>, Record<string, VariableExtractionPayload>> {\n  const specifierPattern = getFormatSpecifierPattern(params.type);\n  return createLoader({\n    pull: async (locale, input, initXtx, originalLocale, originalInput) => {\n      const result: Record<string, VariableExtractionPayload> = {};\n      const inputValues = _.omitBy(input, _.isEmpty);\n      for (const [key, value] of Object.entries(inputValues)) {\n        const originalValue = originalInput[key];\n\n        // Extract format specifiers from the original value\n        const matches = originalValue.match(specifierPattern) || [];\n        result[key] = result[key] || {\n          value,\n          variables: [],\n        };\n        for (let i = 0; i < matches.length; i++) {\n          const match = matches[i];\n          const currentValue = result[key].value;\n          const newValue = currentValue?.replace(match, `{variable:${i}}`);\n\n          result[key].value = newValue;\n          result[key].variables[i] = match;\n        }\n      }\n      return result;\n    },\n    push: async (\n      locale,\n      data,\n      originalInput,\n      originalDefaultLocale,\n      pullInput,\n      pullOutput,\n    ) => {\n      const result: Record<string, any> = {};\n      for (const [key, valueObj] of Object.entries(data)) {\n        result[key] = valueObj.value;\n\n        for (let i = 0; i < valueObj.variables.length; i++) {\n          const variable = valueObj.variables[i];\n          const currentValue = result[key];\n          if (typeof currentValue === \"string\") {\n            const newValue = currentValue?.replaceAll(\n              `{variable:${i}}`,\n              variable,\n            );\n            result[key] = newValue;\n          }\n        }\n      }\n      return result;\n    },\n  });\n}\n\nfunction variableContentLoader(): ILoader<\n  Record<string, VariableExtractionPayload>,\n  Record<string, string>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      const result = _.mapValues(input, (payload) => payload.value);\n      return result;\n    },\n    push: async (locale, data, originalInput, defaultLocale, pullInput) => {\n      const result: Record<string, VariableExtractionPayload> = _.cloneDeep(\n        originalInput || {},\n      );\n      for (const [key, originalValueObj] of Object.entries(result)) {\n        result[key] = {\n          ...originalValueObj,\n          value: data[key],\n        };\n      }\n      return result;\n    },\n  });\n}\n\nfunction getFormatSpecifierPattern(type: VariableLoaderParams[\"type\"]): RegExp {\n  switch (type) {\n    case \"ieee\":\n      return /%(?:\\d+\\$)?[+-]?(?:[ 0]|'.)?-?\\d*(?:\\.\\d+)?(?:[hljztL]|ll|hh)?[@diuoxXfFeEgGaAcspn%]/g;\n    case \"python\":\n      return /%\\([^)]+\\)[diouxXeEfFgGcrs%]/g;\n    default:\n      throw new Error(`Unsupported variable format type: ${type}`);\n  }\n}\n","import _ from \"lodash\";\n\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createSyncLoader(): ILoader<\n  Record<string, string>,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(locale, input, initCtx, originalLocale, originalInput) {\n      if (!originalInput) {\n        return input;\n      }\n\n      return _.chain(originalInput)\n        .mapValues((value, key) => input[key])\n        .value() as Record<string, string>;\n    },\n    async push(locale, data, originalInput) {\n      if (!originalInput) {\n        return data;\n      }\n\n      return _.chain(originalInput || {})\n        .mapValues((value, key) => data[key])\n        .value();\n    },\n  });\n}\n","export function formatPlutilStyle(\n  jsonData: any,\n  existingJson?: string,\n): string {\n  // Detect indentation from existing JSON if provided\n  const indent = existingJson ? detectIndentation(existingJson) : \"  \";\n\n  function format(data: any, level = 0): string {\n    const currentIndent = indent.repeat(level);\n    const nextIndent = indent.repeat(level + 1);\n\n    if (typeof data !== \"object\" || data === null) {\n      return JSON.stringify(data);\n    }\n\n    if (Array.isArray(data)) {\n      if (data.length === 0) return \"[]\";\n      const items = data.map(\n        (item) => `${nextIndent}${format(item, level + 1)}`,\n      );\n      return `[\\n${items.join(\",\\n\")}\\n${currentIndent}]`;\n    }\n\n    const keys = Object.keys(data);\n    if (keys.length === 0) {\n      return `{\\n\\n${currentIndent}}`; // Empty object with proper indentation\n    }\n\n    // Sort keys to ensure whitespace keys come first\n    const sortedKeys = keys.sort((a, b) => {\n      // If both keys are whitespace or both are non-whitespace, maintain stable order\n      const aIsWhitespace = /^\\s*$/.test(a);\n      const bIsWhitespace = /^\\s*$/.test(b);\n\n      if (aIsWhitespace && !bIsWhitespace) return -1;\n      if (!aIsWhitespace && bIsWhitespace) return 1;\n      return a.localeCompare(b, undefined, { numeric: true });\n    });\n\n    const items = sortedKeys.map((key) => {\n      const value = data[key];\n      return `${nextIndent}${JSON.stringify(key)} : ${format(\n        value,\n        level + 1,\n      )}`;\n    });\n\n    return `{\\n${items.join(\",\\n\")}\\n${currentIndent}}`;\n  }\n\n  const result = format(jsonData);\n  return result;\n}\n\nfunction detectIndentation(jsonStr: string): string {\n  // Find the first indented line\n  const match = jsonStr.match(/\\n(\\s+)/);\n  return match ? match[1] : \"    \"; // fallback to 4 spaces if no indentation found\n}\n","import { formatPlutilStyle } from \"../utils/plutil-formatter\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createPlutilJsonTextLoader(): ILoader<string, string> {\n  return createLoader({\n    async pull(locale, data) {\n      return data;\n    },\n    async push(locale, data, originalInput) {\n      const jsonData = JSON.parse(data);\n      const result = formatPlutilStyle(jsonData, originalInput || \"\");\n\n      return result;\n    },\n  });\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { fromString } from \"php-array-reader\";\n\nexport default function createPhpLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      try {\n        const output = fromString(input);\n        return output;\n      } catch (error) {\n        throw new Error(`Error parsing PHP file for locale ${locale}`);\n      }\n    },\n    push: async (locale, data, originalInput) => {\n      const output = toPhpString(data, originalInput);\n      return output;\n    },\n  });\n}\n\nfunction toPhpString(\n  data: Record<string, any>,\n  originalPhpString: string | null,\n) {\n  const defaultFilePrefix = \"<?php\\n\\n\";\n  if (originalPhpString) {\n    const [filePrefix = defaultFilePrefix] = originalPhpString.split(\"return \");\n    const shortArraySyntax = !originalPhpString.includes(\"array(\");\n    const output = `${filePrefix}return ${toPhpArray(data, shortArraySyntax)};`;\n    return output;\n  }\n  return `${defaultFilePrefix}return ${toPhpArray(data)};`;\n}\n\nfunction toPhpArray(data: any, shortSyntax = true, indentLevel = 1): string {\n  if (data === null || data === undefined) {\n    return \"null\";\n  }\n  if (typeof data === \"string\") {\n    return `'${escapePhpString(data)}'`;\n  }\n  if (typeof data === \"number\") {\n    return data.toString();\n  }\n  if (typeof data === \"boolean\") {\n    return data ? \"true\" : \"false\";\n  }\n\n  const arrayStart = shortSyntax ? \"[\" : \"array(\";\n  const arrayEnd = shortSyntax ? \"]\" : \")\";\n\n  if (Array.isArray(data)) {\n    return `${arrayStart}\\n${data\n      .map(\n        (value) =>\n          `${indent(indentLevel)}${toPhpArray(\n            value,\n            shortSyntax,\n            indentLevel + 1,\n          )}`,\n      )\n      .join(\",\\n\")}\\n${indent(indentLevel - 1)}${arrayEnd}`;\n  }\n\n  const output = `${arrayStart}\\n${Object.entries(data)\n    .map(\n      ([key, value]) =>\n        `${indent(indentLevel)}'${key}' => ${toPhpArray(\n          value,\n          shortSyntax,\n          indentLevel + 1,\n        )}`,\n    )\n    .join(\",\\n\")}\\n${indent(indentLevel - 1)}${arrayEnd}`;\n  return output;\n}\n\nfunction indent(level: number) {\n  return \"  \".repeat(level);\n}\n\nfunction escapePhpString(str: string) {\n  return str\n    .replaceAll(\"\\\\\", \"\\\\\\\\\")\n    .replaceAll(\"'\", \"\\\\'\")\n    .replaceAll(\"\\r\", \"\\\\r\")\n    .replaceAll(\"\\n\", \"\\\\n\")\n    .replaceAll(\"\\t\", \"\\\\t\");\n}\n","import { jsonrepair } from \"jsonrepair\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createVueJsonLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (locale, input, ctx) => {\n      const parsed = parseVueFile(input);\n      return parsed?.i18n?.[locale] ?? {};\n    },\n    push: async (locale, data, originalInput) => {\n      const parsed = parseVueFile(originalInput ?? \"\");\n      if (!parsed) {\n        return originalInput ?? \"\";\n      }\n\n      parsed.i18n[locale] = data;\n      return `${parsed.before}<i18n>\\n${JSON.stringify(\n        parsed.i18n,\n        null,\n        2,\n      )}\\n</i18n>${parsed.after}`;\n    },\n  });\n}\n\nfunction parseVueFile(input: string) {\n  const match = input.match(/^([\\s\\S]*)<i18n>([\\s\\S]*)<\\/i18n>([\\s\\S]*)$/);\n\n  if (!match) {\n    return null;\n  }\n\n  const [, before, jsonString = \"{}\", after] = match;\n  let i18n: Record<string, any>;\n  try {\n    i18n = JSON.parse(jsonString);\n  } catch (error) {\n    i18n = JSON.parse(jsonrepair(jsonString));\n  }\n\n  return { before, after, i18n };\n}\n","import { parse } from \"@babel/parser\";\nimport _ from \"lodash\";\nimport babelTraverseModule from \"@babel/traverse\";\nimport type { NodePath } from \"@babel/traverse\";\nimport * as t from \"@babel/types\";\nimport babelGenerateModule from \"@babel/generator\";\nimport { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport { resolveCjsExport } from \"./cjs-interop\";\n\nconst traverse = resolveCjsExport(babelTraverseModule, \"@babel/traverse\");\nconst generate = resolveCjsExport(babelGenerateModule, \"@babel/generator\");\n\nexport default function createTypescriptLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      if (!input) {\n        return {};\n      }\n\n      const ast = parseTypeScript(input);\n      const extractedStrings = extractStringsFromDefaultExport(ast);\n      return extractedStrings;\n    },\n    push: async (\n      locale,\n      data,\n      originalInput,\n      defaultLocale,\n      pullInput,\n      pullOutput,\n    ) => {\n      const ast = parseTypeScript(originalInput || \"\");\n      const finalData = _.merge({}, pullOutput, data);\n      updateStringsInDefaultExport(ast, finalData);\n\n      const { code } = generate(ast, {\n        jsescOption: {\n          minimal: true,\n        },\n      });\n      return code;\n    },\n  });\n}\n\n/**\n * Parse TypeScript code into an AST\n */\nfunction parseTypeScript(input: string) {\n  return parse(input, {\n    sourceType: \"module\",\n    plugins: [\"typescript\"],\n  });\n}\n\n/**\n * Extract the localizable (string literal) content from the default export\n * and return it as a nested object that mirrors the original structure.\n */\nfunction extractStringsFromDefaultExport(ast: t.File): Record<string, any> {\n  let extracted: Record<string, any> = {};\n\n  traverse(ast, {\n    ExportDefaultDeclaration(path: NodePath<t.ExportDefaultDeclaration>) {\n      const { declaration } = path.node;\n\n      const decl = unwrapTSAsExpression(declaration);\n\n      if (t.isObjectExpression(decl)) {\n        extracted = objectExpressionToObject(decl);\n      } else if (t.isArrayExpression(decl)) {\n        extracted = arrayExpressionToArray(decl) as unknown as Record<\n          string,\n          any\n        >;\n      } else if (t.isIdentifier(decl)) {\n        // Handle: const foo = {...}; export default foo;\n        const binding = path.scope.bindings[decl.name];\n        if (\n          binding &&\n          t.isVariableDeclarator(binding.path.node) &&\n          binding.path.node.init\n        ) {\n          const initRaw = binding.path.node.init;\n          const init = initRaw ? unwrapTSAsExpression(initRaw) : initRaw;\n          if (t.isObjectExpression(init)) {\n            extracted = objectExpressionToObject(init);\n          } else if (t.isArrayExpression(init)) {\n            extracted = arrayExpressionToArray(init) as unknown as Record<\n              string,\n              any\n            >;\n          }\n        }\n      }\n    },\n  });\n\n  return extracted;\n}\n\n/**\n * Helper: unwraps nested TSAsExpression nodes (e.g. `obj as const`)\n * to get to the underlying expression/node we care about.\n */\nfunction unwrapTSAsExpression<T extends t.Node>(node: T): t.Node {\n  let current: t.Node = node;\n  // TSAsExpression is produced for `expr as const` assertions.\n  // We want to get to the underlying expression so that the rest of the\n  // loader logic can work unchanged.\n  // There could theoretically be multiple nested `as const` assertions, so we\n  // unwrap in a loop.\n  // eslint-disable-next-line no-constant-condition\n  while (t.isTSAsExpression(current)) {\n    current = current.expression;\n  }\n  return current;\n}\n\n/**\n * Recursively converts an `ObjectExpression` into a plain JavaScript object that\n * only contains the string-literal values we care about. Non-string primitives\n * (numbers, booleans, etc.) are ignored.\n */\nfunction objectExpressionToObject(\n  objectExpression: t.ObjectExpression,\n): Record<string, any> {\n  const obj: Record<string, any> = {};\n\n  objectExpression.properties.forEach((prop) => {\n    if (!t.isObjectProperty(prop)) return;\n\n    const key = getPropertyKey(prop);\n\n    if (t.isStringLiteral(prop.value)) {\n      obj[key] = prop.value.value;\n    } else if (\n      t.isTemplateLiteral(prop.value) &&\n      prop.value.expressions.length === 0\n    ) {\n      // Handle template literals without expressions as plain strings\n      obj[key] = prop.value.quasis[0].value.cooked ?? \"\";\n    } else if (t.isObjectExpression(prop.value)) {\n      const nested = objectExpressionToObject(prop.value);\n      if (Object.keys(nested).length > 0) {\n        obj[key] = nested;\n      }\n    } else if (t.isArrayExpression(prop.value)) {\n      const arr = arrayExpressionToArray(prop.value);\n      if (arr.length > 0) {\n        obj[key] = arr;\n      }\n    }\n  });\n\n  return obj;\n}\n\n/**\n * Recursively converts an `ArrayExpression` into a JavaScript array that\n * contains string literals and nested objects/arrays when relevant.\n */\nfunction arrayExpressionToArray(arrayExpression: t.ArrayExpression): any[] {\n  const arr: any[] = [];\n\n  arrayExpression.elements.forEach((element) => {\n    if (!element) return; // holes in the array\n\n    if (t.isStringLiteral(element)) {\n      arr.push(element.value);\n    } else if (\n      t.isTemplateLiteral(element) &&\n      element.expressions.length === 0\n    ) {\n      arr.push(element.quasis[0].value.cooked ?? \"\");\n    } else if (t.isObjectExpression(element)) {\n      const nestedObj = objectExpressionToObject(element);\n      arr.push(nestedObj);\n    } else if (t.isArrayExpression(element)) {\n      arr.push(arrayExpressionToArray(element));\n    }\n  });\n\n  return arr;\n}\n\n// ------------------ updating helpers (nested data) ------------------------\n\nfunction updateStringsInDefaultExport(\n  ast: t.File,\n  data: Record<string, any>,\n): boolean {\n  let modified = false;\n\n  traverse(ast, {\n    ExportDefaultDeclaration(path: NodePath<t.ExportDefaultDeclaration>) {\n      const { declaration } = path.node;\n\n      const decl = unwrapTSAsExpression(declaration);\n\n      if (t.isObjectExpression(decl)) {\n        modified = updateStringsInObjectExpression(decl, data) || modified;\n      } else if (t.isArrayExpression(decl)) {\n        if (Array.isArray(data)) {\n          modified = updateStringsInArrayExpression(decl, data) || modified;\n        }\n      } else if (t.isIdentifier(decl)) {\n        modified = updateStringsInExportedIdentifier(path, data) || modified;\n      }\n    },\n  });\n\n  return modified;\n}\n\nfunction updateStringsInObjectExpression(\n  objectExpression: t.ObjectExpression,\n  data: Record<string, any>,\n): boolean {\n  let modified = false;\n\n  objectExpression.properties.forEach((prop) => {\n    if (!t.isObjectProperty(prop)) return;\n\n    const key = getPropertyKey(prop);\n    const incomingVal = data?.[key];\n\n    if (incomingVal === undefined) {\n      // nothing to update for this key\n      return;\n    }\n\n    if (t.isStringLiteral(prop.value) && typeof incomingVal === \"string\") {\n      if (prop.value.value !== incomingVal) {\n        prop.value.value = incomingVal;\n        modified = true;\n      }\n    } else if (\n      t.isTemplateLiteral(prop.value) &&\n      prop.value.expressions.length === 0 &&\n      typeof incomingVal === \"string\"\n    ) {\n      const currentVal = prop.value.quasis[0].value.cooked ?? \"\";\n      if (currentVal !== incomingVal) {\n        // Replace the existing template literal with an updated one\n        prop.value.quasis[0].value.raw = incomingVal;\n        prop.value.quasis[0].value.cooked = incomingVal;\n        modified = true;\n      }\n    } else if (\n      t.isObjectExpression(prop.value) &&\n      typeof incomingVal === \"object\" &&\n      !Array.isArray(incomingVal)\n    ) {\n      const subModified = updateStringsInObjectExpression(\n        prop.value,\n        incomingVal,\n      );\n      modified = subModified || modified;\n    } else if (t.isArrayExpression(prop.value) && Array.isArray(incomingVal)) {\n      const subModified = updateStringsInArrayExpression(\n        prop.value,\n        incomingVal,\n      );\n      modified = subModified || modified;\n    }\n  });\n\n  return modified;\n}\n\nfunction updateStringsInArrayExpression(\n  arrayExpression: t.ArrayExpression,\n  incoming: any[],\n): boolean {\n  let modified = false;\n\n  arrayExpression.elements.forEach((element, index) => {\n    if (!element) return;\n\n    const incomingVal = incoming?.[index];\n    if (incomingVal === undefined) return;\n\n    if (t.isStringLiteral(element) && typeof incomingVal === \"string\") {\n      if (element.value !== incomingVal) {\n        element.value = incomingVal;\n        modified = true;\n      }\n    } else if (\n      t.isTemplateLiteral(element) &&\n      element.expressions.length === 0 &&\n      typeof incomingVal === \"string\"\n    ) {\n      const currentVal = element.quasis[0].value.cooked ?? \"\";\n      if (currentVal !== incomingVal) {\n        element.quasis[0].value.raw = incomingVal;\n        element.quasis[0].value.cooked = incomingVal;\n        modified = true;\n      }\n    } else if (\n      t.isObjectExpression(element) &&\n      typeof incomingVal === \"object\" &&\n      !Array.isArray(incomingVal)\n    ) {\n      const subModified = updateStringsInObjectExpression(element, incomingVal);\n      modified = subModified || modified;\n    } else if (t.isArrayExpression(element) && Array.isArray(incomingVal)) {\n      const subModified = updateStringsInArrayExpression(element, incomingVal);\n      modified = subModified || modified;\n    }\n  });\n\n  return modified;\n}\n\nfunction updateStringsInExportedIdentifier(\n  path: NodePath<t.ExportDefaultDeclaration>,\n  data: Record<string, any>,\n): boolean {\n  const exportName = (path.node.declaration as t.Identifier).name;\n  const binding = path.scope.bindings[exportName];\n\n  if (!binding || !binding.path.node) return false;\n\n  if (t.isVariableDeclarator(binding.path.node) && binding.path.node.init) {\n    const initRaw = binding.path.node.init;\n    const init = initRaw ? unwrapTSAsExpression(initRaw) : initRaw;\n    if (t.isObjectExpression(init)) {\n      return updateStringsInObjectExpression(init, data);\n    } else if (t.isArrayExpression(init)) {\n      return updateStringsInArrayExpression(init, data as any[]);\n    }\n  }\n\n  return false;\n}\n\n/**\n * Get the string key from an object property\n */\nfunction getPropertyKey(prop: t.ObjectProperty): string {\n  if (t.isIdentifier(prop.key)) {\n    return prop.key.name;\n  } else if (t.isStringLiteral(prop.key)) {\n    return prop.key.value;\n  } else if (t.isNumericLiteral(prop.key)) {\n    return String(prop.key.value);\n  }\n  return String(prop.key);\n}\n","/**\n * @fileoverview Helpers for CommonJS ⇆ ES Module inter-op quirks.\n */\n\n/**\n * Resolve the actual default export value of a CommonJS module that has been\n * imported via an ES-module `import` statement.\n *\n * Why is this needed?\n * -------------------\n * When a package that is published as **CommonJS** (for example, `@babel/traverse`)\n * is imported inside native **ESM** code (or via a bundler in ESM mode) the\n * runtime value you receive is not consistent across environments:\n *\n *   • **Node.js** (native ESM) wraps the CJS module in an object like\n *     `{ default: moduleExports, …namedReExports }`.\n *   • **esbuild / Vite / Vitest** may decide to mimic TypeScript's\n *     `esModuleInterop` behaviour and give you `moduleExports` directly.\n *   • Other tools can produce yet different shapes.\n *\n * If you blindly assume one shape, you will hit runtime errors such as\n * `TypeError: traverse is not a function` when the actual function lives on the\n * `.default` property — or the opposite, depending on the environment.\n *\n * This helper inspects the imported value at runtime and returns what looks like\n * the real default export regardless of how it was wrapped.  It hides the ugly\n * `typeof mod === \"function\" ? … : mod.default` branching behind a single call\n * site.\n *\n * Example\n * -------\n * ```ts\n * import traverseModule from \"@babel/traverse\";\n * import { resolveCjsExport } from \"../utils/cjs-interop\";\n *\n * const traverse = resolveCjsExport<typeof traverseModule>(\n *   traverseModule,\n *   \"@babel/traverse\",\n * );\n * ```\n *\n * @template T Expected type of the resolved export.\n * @param mod  The runtime value returned by the `import` statement.\n * @param name Friendly name of the module (for error messages).\n * @returns    The resolved default export value.\n */\nexport function resolveCjsExport<T = any>(mod: T, name: string = \"module\"): T {\n  // If the module value itself is callable or clearly not an object, assume it's\n  // already the export we want (covers most bundler scenarios).\n  if (typeof mod === \"function\" || typeof mod !== \"object\" || mod === null) {\n    return mod as T;\n  }\n\n  // Otherwise, look for a `.default` property which is common in Node's CJS->ESM\n  // wrapper as well as in Babel's `interopRequireDefault` helpers.\n  if (\"default\" in mod && typeof mod.default !== \"undefined\") {\n    return mod.default as T;\n  }\n\n  // Give up: log the mysterious shape and throw to fail fast.\n  /* eslint-disable no-console */\n  console.error(\n    `[resolveCjsExport] Unable to determine default export for ${name}.`,\n    \"Received value:\",\n    mod,\n  );\n  throw new Error(`Failed to resolve default export for ${name}.`);\n}\n","import _ from \"lodash\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { minimatch } from \"minimatch\";\n\nexport default function createInjectLocaleLoader(\n  injectLocaleKeys?: string[],\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    async pull(locale, data) {\n      if (!injectLocaleKeys) {\n        return data;\n      }\n      const omitKeys = _getKeysWithLocales(data, injectLocaleKeys, locale);\n      const result = _.omit(data, omitKeys);\n      return result;\n    },\n    async push(locale, data, originalInput, originalLocale) {\n      if (!injectLocaleKeys || !originalInput) {\n        return data;\n      }\n\n      const localeKeys = _getKeysWithLocales(\n        originalInput,\n        injectLocaleKeys,\n        originalLocale,\n      );\n\n      localeKeys.forEach((key) => {\n        _.set(data, key, locale);\n      });\n\n      return data;\n    },\n  });\n}\n\nfunction _getKeysWithLocales(\n  data: Record<string, any>,\n  injectLocaleKeys: string[],\n  locale: string,\n) {\n  const allKeys = _getAllKeys(data);\n  return allKeys.filter((key) => {\n    return (\n      injectLocaleKeys.some((pattern) => minimatch(key, pattern)) &&\n      _.get(data, key) === locale\n    );\n  });\n}\n\n// Helper to get all deep keys in lodash path style (e.g., 'a.b.c')\nfunction _getAllKeys(obj: Record<string, any>, prefix = \"\"): string[] {\n  let keys: string[] = [];\n  for (const key in obj) {\n    if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;\n    const path = prefix ? `${prefix}.${key}` : key;\n    if (\n      typeof obj[key] === \"object\" &&\n      obj[key] !== null &&\n      !Array.isArray(obj[key])\n    ) {\n      keys = keys.concat(_getAllKeys(obj[key], path));\n    } else {\n      keys.push(path);\n    }\n  }\n  return keys;\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport _ from \"lodash\";\nimport { matchesKeyPattern } from \"../utils/key-matching\";\n\nexport default function createLockedKeysLoader(\n  lockedKeys: string[],\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    pull: async (locale, data) => {\n      return _.pickBy(\n        data,\n        (value, key) => !matchesKeyPattern(key, lockedKeys),\n      );\n    },\n    push: async (locale, data, originalInput) => {\n      const lockedSubObject = _.chain(originalInput)\n        .pickBy((value, key) => matchesKeyPattern(key, lockedKeys))\n        .value();\n\n      return _.merge({}, data, lockedSubObject);\n    },\n  });\n}\n","import matter from \"gray-matter\";\nimport YAML from \"yaml\";\nimport { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport { RawMdx } from \"./_types\";\n\nexport default function createMdxFrontmatterSplitLoader(): ILoader<\n  string,\n  RawMdx\n> {\n  const fmEngine = createFmEngine();\n\n  return createLoader({\n    async pull(locale, input) {\n      const source = input || \"\";\n      const { data: frontmatter, content } = fmEngine.parse(source);\n\n      return {\n        frontmatter: frontmatter as Record<string, any>,\n        content,\n      };\n    },\n\n    async push(locale, data) {\n      const { frontmatter = {}, content = \"\" } = data || ({} as RawMdx);\n\n      const result = fmEngine.stringify(content, frontmatter).trim();\n\n      return result;\n    },\n  });\n}\n\nfunction createFmEngine() {\n  const yamlEngine = {\n    parse: (str: string) => YAML.parse(str),\n    stringify: (obj: any) =>\n      YAML.stringify(obj, { defaultStringType: \"PLAIN\" }),\n  };\n\n  return {\n    parse: (input: string) =>\n      matter(input, {\n        engines: {\n          yaml: yamlEngine,\n        },\n      }),\n    stringify: (content: string, frontmatter: Record<string, any>) =>\n      matter.stringify(content, frontmatter, {\n        engines: {\n          yaml: yamlEngine,\n        },\n      }),\n  };\n}\n","import { MD5 } from \"object-hash\";\n\nexport function md5(input: any) {\n  return MD5(input);\n}\n","import { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport { md5 } from \"../../utils/md5\";\nimport _ from \"lodash\";\n\nconst fenceRegex = /([ \\t]*)(^>\\s*)?```([\\s\\S]*?)```/gm;\nconst inlineCodeRegex = /(?<!`)`([^`\\r\\n]+?)`(?!`)/g;\n\n// Matches markdown image tags, with optional alt text & parenthesis URL, possibly inside blockquotes\n// Captures patterns like ![](url) or ![alt](url), with optional leading '> ' for blockquotes\nconst imageRegex =\n  /([ \\t]*)(^>\\s*)?!\\[[^\\]]*?\\]\\(([^()]*(\\([^()]*\\)[^()]*)*)\\)/gm;\n\n/**\n * Ensures that markdown image tags are surrounded by blank lines (\\n\\n) so that they are properly\n * treated as separate blocks during subsequent processing and serialization.\n *\n * Behaviour mirrors `ensureTrailingFenceNewline` logic for code fences:\n *   • If an image tag is already inside a blockquote (starts with `>` after trimming) we leave it untouched.\n *   • Otherwise we add two newlines before and after the image tag, then later collapse multiple\n *     consecutive blank lines back to exactly one separation using lodash chain logic.\n */\nfunction ensureSurroundingImageNewlines(_content: string) {\n  let found = false;\n  let content = _content;\n  let workingContent = content;\n\n  do {\n    found = false;\n    const matches = workingContent.match(imageRegex);\n    if (matches) {\n      const match = matches[0];\n\n      const replacement = match.trim().startsWith(\">\")\n        ? match\n        : `\\n\\n${match}\\n\\n`;\n\n      content = content.replaceAll(match, () => replacement);\n      workingContent = workingContent.replaceAll(match, \"\");\n      found = true;\n    }\n  } while (found);\n\n  content = _.chain(content)\n    .split(\"\\n\\n\")\n    .map((section) => _.trim(section, \"\\n\"))\n    .filter(Boolean)\n    .join(\"\\n\\n\")\n    .value();\n\n  return content;\n}\n\nfunction ensureTrailingFenceNewline(_content: string) {\n  let found = false;\n  let content = _content;\n  let workingContent = content;\n\n  do {\n    found = false;\n    const matches = workingContent.match(fenceRegex);\n    if (matches) {\n      const match = matches[0];\n\n      const replacement = match.trim().startsWith(\">\")\n        ? match\n        : `\\n\\n${match}\\n\\n`;\n      content = content.replaceAll(match, () => replacement);\n      workingContent = workingContent.replaceAll(match, \"\");\n      found = true;\n    }\n  } while (found);\n\n  content = _.chain(content)\n    .split(\"\\n\\n\")\n    .map((section) => _.trim(section, \"\\n\"))\n    .filter(Boolean)\n    .join(\"\\n\\n\")\n    .value();\n\n  return content;\n}\n\n// Helper that replaces code (block & inline) with stable placeholders and returns\n// both the transformed content and the placeholder → original mapping so it can\n// later be restored. Extracted so that we can reuse the exact same logic in both\n// `pull` and `push` phases (e.g. to recreate the mapping from `originalInput`).\nfunction extractCodePlaceholders(content: string): {\n  content: string;\n  codePlaceholders: Record<string, string>;\n} {\n  let finalContent = content;\n  finalContent = ensureTrailingFenceNewline(finalContent);\n  finalContent = ensureSurroundingImageNewlines(finalContent);\n\n  const codePlaceholders: Record<string, string> = {};\n\n  const codeBlockMatches = finalContent.matchAll(fenceRegex);\n  for (const match of codeBlockMatches) {\n    const codeBlock = match[0];\n    const codeBlockHash = md5(codeBlock).slice(0, 16);\n    const placeholder = `CODE_PLACEHOLDER_${codeBlockHash}_END`;\n\n    codePlaceholders[placeholder] = codeBlock;\n\n    const replacement = codeBlock.trim().startsWith(\">\")\n      ? `> ${placeholder}`\n      : `${placeholder}`;\n    finalContent = finalContent.replace(codeBlock, () => replacement);\n  }\n\n  const inlineCodeMatches = finalContent.matchAll(inlineCodeRegex);\n  for (const match of inlineCodeMatches) {\n    const inlineCode = match[0];\n    const inlineCodeHash = md5(inlineCode).slice(0, 16);\n    const placeholder = `INLINE_CODE_PLACEHOLDER_${inlineCodeHash}_END`;\n    codePlaceholders[placeholder] = inlineCode;\n    const replacement = placeholder;\n    finalContent = finalContent.replace(inlineCode, () => replacement);\n  }\n\n  return {\n    content: finalContent,\n    codePlaceholders,\n  };\n}\n\nexport default function createMdxCodePlaceholderLoader(): ILoader<\n  string,\n  string\n> {\n  // Keep a global registry of all placeholders we've ever created\n  // This solves the state synchronization issue\n  const globalPlaceholderRegistry: Record<string, string> = {};\n\n  return createLoader({\n    async pull(locale, input) {\n      const response = extractCodePlaceholders(input);\n\n      // Register all placeholders we create so we can use them later\n      Object.assign(globalPlaceholderRegistry, response.codePlaceholders);\n\n      return response.content;\n    },\n\n    async push(locale, data, originalInput, originalLocale, pullInput) {\n      const sourceInfo = extractCodePlaceholders(originalInput ?? \"\");\n      const currentInfo = extractCodePlaceholders(pullInput ?? \"\");\n\n      // Use the global registry to ensure all placeholders can be replaced,\n      // including those from previous pulls that are no longer in current state\n      const codePlaceholders = _.merge(\n        sourceInfo.codePlaceholders,\n        currentInfo.codePlaceholders,\n        globalPlaceholderRegistry, // Include ALL placeholders ever created\n      );\n\n      let result = data;\n      for (const [placeholder, original] of Object.entries(codePlaceholders)) {\n        const replacement = original.startsWith(\">\")\n          ? _.trimStart(original, \"> \")\n          : original;\n\n        // Use function replacer to avoid special $ character handling\n        // When using a string, $ has special meaning (e.g., $` inserts text before match)\n        result = result.replaceAll(placeholder, () => replacement);\n      }\n\n      return result;\n    },\n  });\n}\n","import { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport { LocalizableMdxDocument, SectionedMdx } from \"./_types\";\n\nexport default function createLocalizableMdxDocumentLoader(): ILoader<\n  SectionedMdx,\n  LocalizableMdxDocument\n> {\n  return createLoader({\n    async pull(_locale, input) {\n      return {\n        meta: input.frontmatter,\n        content: input.sections,\n      };\n    },\n\n    async push(_locale, data, originalInput, _originalLocale, pullInput) {\n      const result: SectionedMdx = {\n        frontmatter: data.meta || {},\n        sections: data.content || {},\n      };\n\n      return result;\n    },\n  });\n}\n","import { ILoader } from \"../_types\";\nimport { createLoader } from \"../_utils\";\nimport { PlaceholderedMdx, SectionedMdx } from \"./_types\";\nimport _ from \"lodash\";\n\nexport default function createMdxSectionsSplit2Loader(): ILoader<\n  PlaceholderedMdx,\n  SectionedMdx\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const sections = _.chain(input.content)\n        .split(\"\\n\\n\")\n        .filter(Boolean)\n        .map((section, index) => [index, section])\n        .fromPairs()\n        .value();\n\n      const result: SectionedMdx = {\n        frontmatter: input.frontmatter,\n        sections,\n      };\n\n      return result;\n    },\n\n    async push(locale, data, originalInput, _originalLocale, pullInput) {\n      const content = _.chain(data.sections).values().join(\"\\n\\n\").value();\n\n      const result: PlaceholderedMdx = {\n        frontmatter: data.frontmatter,\n        codePlaceholders: pullInput?.codePlaceholders || {},\n        content,\n      };\n\n      return result;\n    },\n  });\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { md5 } from \"../utils/md5\";\n\n/**\n * Extracts content matching regex patterns and replaces it with placeholders.\n * Returns the transformed content and a mapping of placeholders to original content.\n */\nfunction extractLockedPatterns(\n  content: string,\n  patterns: string[] = [],\n): {\n  content: string;\n  lockedPlaceholders: Record<string, string>;\n} {\n  let finalContent = content;\n  const lockedPlaceholders: Record<string, string> = {};\n\n  if (!patterns || patterns.length === 0) {\n    return { content: finalContent, lockedPlaceholders };\n  }\n\n  for (const patternStr of patterns) {\n    try {\n      const pattern = new RegExp(patternStr, \"gm\");\n      const matches = Array.from(finalContent.matchAll(pattern));\n\n      for (const match of matches) {\n        const matchedText = match[0];\n        const matchHash = md5(matchedText);\n        const placeholder = `{/* LOCKED_PATTERN_${matchHash} */}`;\n\n        lockedPlaceholders[placeholder] = matchedText;\n        finalContent = finalContent.replace(matchedText, placeholder);\n      }\n    } catch (error) {\n      console.warn(`Invalid regex pattern: ${patternStr}`);\n    }\n  }\n\n  return {\n    content: finalContent,\n    lockedPlaceholders,\n  };\n}\n\n/**\n * Creates a loader that preserves content matching regex patterns during translation.\n *\n * This loader extracts content matching the provided regex patterns and replaces it\n * with placeholders before translation. After translation, the placeholders are\n * restored with the original content.\n *\n * This is useful for preserving technical terms, code snippets, URLs, template\n * variables, and other non-translatable content within translatable files.\n *\n * Works with any string-based format (JSON, YAML, XML, Markdown, HTML, etc.).\n * Note: For structured formats (JSON, XML, YAML), ensure patterns only match\n * content within values, not structural syntax, to avoid breaking parsing.\n *\n * @param defaultPatterns - Array of regex pattern strings to match and preserve\n * @returns A loader that handles pattern locking/unlocking\n */\nexport default function createLockedPatternsLoader(\n  defaultPatterns?: string[],\n): ILoader<string, string> {\n  return createLoader({\n    async pull(locale, input, initCtx, originalLocale) {\n      const patterns = defaultPatterns || [];\n\n      const { content } = extractLockedPatterns(input || \"\", patterns);\n\n      return content;\n    },\n\n    async push(\n      locale,\n      data,\n      originalInput,\n      originalLocale,\n      pullInput,\n      pullOutput,\n    ) {\n      const patterns = defaultPatterns || [];\n\n      if (!pullInput) {\n        return data;\n      }\n\n      const { lockedPlaceholders } = extractLockedPatterns(\n        pullInput as string,\n        patterns,\n      );\n\n      let result = data;\n      for (const [placeholder, original] of Object.entries(\n        lockedPlaceholders,\n      )) {\n        result = result.replaceAll(placeholder, original);\n      }\n\n      return result;\n    },\n  });\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport _ from \"lodash\";\nimport { matchesKeyPattern } from \"../utils/key-matching\";\n\nexport default function createIgnoredKeysLoader(\n  ignoredKeys: string[],\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    pull: async (locale, data) => {\n      const result = _.omitBy(data, (value, key) =>\n        matchesKeyPattern(key, ignoredKeys),\n      );\n      return result;\n    },\n    push: async (locale, data, originalInput, originalLocale, pullInput) => {\n      // Remove ignored keys from the data being pushed\n      const result = _.omitBy(data, (value, key) =>\n        matchesKeyPattern(key, ignoredKeys),\n      );\n      return result;\n    },\n  });\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport _ from \"lodash\";\nimport { matchesKeyPattern } from \"../utils/key-matching\";\n\nexport default function createPreservedKeysLoader(\n  preservedKeys: string[],\n): ILoader<Record<string, any>, Record<string, any>> {\n  return createLoader({\n    pull: async (_locale, data) => {\n      // Remove preserved keys from data (don't send them for translation)\n      return _.pickBy(\n        data,\n        (_value, key) => !matchesKeyPattern(key, preservedKeys),\n      );\n    },\n    push: async (_locale, data, originalInput, _originalLocale, pullInput) => {\n      // For preserved keys:\n      // - If exists in pullInput (target file), keep target value\n      // - If missing in pullInput, use source value from originalInput\n      const preservedSubObject = _.chain(originalInput)\n        .pickBy((_value, key) => matchesKeyPattern(key, preservedKeys))\n        .mapValues((sourceValue, key) => {\n          // Use existing target value if present, otherwise use source\n          const targetValue = pullInput?.[key];\n          return targetValue !== undefined ? targetValue : sourceValue;\n        })\n        .value();\n\n      return _.merge({}, data, preservedSubObject);\n    },\n  });\n}\n","import * as ejs from \"ejs\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\ninterface EjsParseResult {\n  content: string;\n  translatable: Record<string, string>;\n}\n\nfunction parseEjsForTranslation(input: string): EjsParseResult {\n  const translatable: Record<string, string> = {};\n  let counter = 0;\n\n  // Regular expression for all EJS tags\n  const ejsTagRegex = /<%[\\s\\S]*?%>/g;\n\n  // Split content by EJS tags, preserving both text and EJS parts\n  const parts: Array<{ type: \"text\" | \"ejs\"; content: string }> = [];\n  let lastIndex = 0;\n  let match;\n\n  while ((match = ejsTagRegex.exec(input)) !== null) {\n    // Add text before the tag\n    if (match.index > lastIndex) {\n      parts.push({\n        type: \"text\",\n        content: input.slice(lastIndex, match.index),\n      });\n    }\n    // Add the EJS tag\n    parts.push({\n      type: \"ejs\",\n      content: match[0],\n    });\n    lastIndex = match.index + match[0].length;\n  }\n\n  // Add remaining text after the last tag\n  if (lastIndex < input.length) {\n    parts.push({\n      type: \"text\",\n      content: input.slice(lastIndex),\n    });\n  }\n\n  // Build the template and extract translatable content\n  let template = \"\";\n\n  for (const part of parts) {\n    if (part.type === \"ejs\") {\n      // Keep EJS tags as-is\n      template += part.content;\n    } else {\n      // For text content, extract translatable parts while preserving HTML structure\n      const textContent = part.content;\n\n      // Extract text content from HTML tags while preserving structure\n      const htmlTagRegex = /<[^>]+>/g;\n      const textParts: Array<{ type: \"html\" | \"text\"; content: string }> = [];\n      let lastTextIndex = 0;\n      let htmlMatch;\n\n      while ((htmlMatch = htmlTagRegex.exec(textContent)) !== null) {\n        // Add text before the HTML tag\n        if (htmlMatch.index > lastTextIndex) {\n          const textBefore = textContent.slice(lastTextIndex, htmlMatch.index);\n          if (textBefore.trim()) {\n            textParts.push({ type: \"text\", content: textBefore });\n          } else {\n            textParts.push({ type: \"html\", content: textBefore });\n          }\n        }\n        // Add the HTML tag\n        textParts.push({ type: \"html\", content: htmlMatch[0] });\n        lastTextIndex = htmlMatch.index + htmlMatch[0].length;\n      }\n\n      // Add remaining text after the last HTML tag\n      if (lastTextIndex < textContent.length) {\n        const remainingText = textContent.slice(lastTextIndex);\n        if (remainingText.trim()) {\n          textParts.push({ type: \"text\", content: remainingText });\n        } else {\n          textParts.push({ type: \"html\", content: remainingText });\n        }\n      }\n\n      // If no HTML tags found, treat entire content as text\n      if (textParts.length === 0) {\n        const trimmedContent = textContent.trim();\n        if (trimmedContent) {\n          textParts.push({ type: \"text\", content: textContent });\n        } else {\n          textParts.push({ type: \"html\", content: textContent });\n        }\n      }\n\n      // Process text parts\n      for (const textPart of textParts) {\n        if (textPart.type === \"text\") {\n          const trimmedContent = textPart.content.trim();\n          if (trimmedContent) {\n            const key = `text_${counter++}`;\n            translatable[key] = trimmedContent;\n            template += textPart.content.replace(\n              trimmedContent,\n              `__LINGO_PLACEHOLDER_${key}__`,\n            );\n          } else {\n            template += textPart.content;\n          }\n        } else {\n          template += textPart.content;\n        }\n      }\n    }\n  }\n\n  return { content: template, translatable };\n}\n\nfunction reconstructEjsWithTranslation(\n  template: string,\n  translatable: Record<string, string>,\n): string {\n  let result = template;\n\n  // Replace placeholders with translated content\n  for (const [key, value] of Object.entries(translatable)) {\n    const placeholder = `__LINGO_PLACEHOLDER_${key}__`;\n    result = result.replace(new RegExp(placeholder, \"g\"), value);\n  }\n\n  return result;\n}\n\nexport default function createEjsLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      if (!input || input.trim() === \"\") {\n        return {};\n      }\n\n      try {\n        const parseResult = parseEjsForTranslation(input);\n        return parseResult.translatable;\n      } catch (error) {\n        console.warn(\n          \"Warning: Could not parse EJS template, treating as plain text\",\n        );\n        // Fallback: treat entire input as translatable content\n        return { content: input.trim() };\n      }\n    },\n\n    async push(locale, data, originalInput) {\n      if (!originalInput) {\n        // If no original input, reconstruct from data\n        return Object.values(data).join(\"\\n\");\n      }\n\n      try {\n        const parseResult = parseEjsForTranslation(originalInput);\n\n        // Merge original translatable content with new translations\n        const mergedTranslatable = { ...parseResult.translatable, ...data };\n\n        return reconstructEjsWithTranslation(\n          parseResult.content,\n          mergedTranslatable,\n        );\n      } catch (error) {\n        console.warn(\n          \"Warning: Could not reconstruct EJS template, returning translated data\",\n        );\n        return Object.values(data).join(\"\\n\");\n      }\n    },\n  });\n}\n","import * as htmlparser2 from \"htmlparser2\";\nimport { DomHandler, Element } from \"domhandler\";\nimport * as domutils from \"domutils\";\nimport * as DomSerializer from \"dom-serializer\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport {\n  createElementExtractor,\n  BASE_LOCALIZABLE_ATTRIBUTES,\n} from \"../utils/element-extraction\";\n\nexport default function createTwigLoader(): ILoader<\n  string,\n  Record<string, string>\n> {\n  // Extend base attributes with aria-label for Twig templates\n  const LOCALIZABLE_ATTRIBUTES: Record<string, string[]> = {\n    ...BASE_LOCALIZABLE_ATTRIBUTES,\n    input: [\"placeholder\", \"title\", \"aria-label\"],\n    textarea: [\"placeholder\", \"title\", \"aria-label\"],\n    button: [\"title\", \"aria-label\"],\n    a: [\"title\", \"aria-label\"],\n  };\n\n  // Preprocess Twig: Replace Twig control blocks with placeholders\n  function preprocessTwig(input: string): {\n    processed: string;\n    twigBlocks: string[];\n  } {\n    const twigBlocks: string[] = [];\n    let counter = 0;\n\n    // Replace {% ... %} blocks (but NOT {{ ... }})\n    // {{ }} expressions are kept as-is - they're part of the translatable content\n    const processed = input.replace(/\\{%[\\s\\S]*?%\\}/g, (match) => {\n      twigBlocks.push(match);\n      return `__TWIG_BLOCK_${counter++}__`;\n    });\n\n    // Also replace {# ... #} comments\n    return {\n      processed: processed.replace(/\\{#[\\s\\S]*?#\\}/g, (match) => {\n        twigBlocks.push(match);\n        return `__TWIG_BLOCK_${counter++}__`;\n      }),\n      twigBlocks,\n    };\n  }\n\n  // Postprocess: Restore Twig blocks from placeholders\n  function postprocessTwig(text: string, twigBlocks: string[]): string {\n    return text.replace(/__TWIG_BLOCK_(\\d+)__/g, (_, index) => {\n      return twigBlocks[parseInt(index, 10)] || \"\";\n    });\n  }\n\n  return createLoader({\n    async pull(locale, input) {\n      const result: Record<string, string> = {};\n\n      // Preprocess Twig syntax\n      const { processed, twigBlocks } = preprocessTwig(input);\n\n      // Parse HTML with htmlparser2 (preserves structure, no foster parenting)\n      const handler = new DomHandler();\n      const parser = new htmlparser2.Parser(handler, {\n        lowerCaseTags: false,\n        lowerCaseAttributeNames: false,\n      });\n      parser.write(processed);\n      parser.end();\n\n      const dom = handler.dom;\n\n      // Get innerHTML equivalent (serialize children) - with Twig restoration\n      function getInnerHTML(element: Element): string {\n        const html = element.children\n          .map((child) =>\n            DomSerializer.default(child, { encodeEntities: false }),\n          )\n          .join(\"\");\n\n        // Restore Twig blocks in the innerHTML\n        return postprocessTwig(html, twigBlocks);\n      }\n\n      // Extract localizable attributes from element - with Twig restoration\n      function extractAttributes(element: Element, path: string): void {\n        const tagName = element.name.toLowerCase();\n        const attrs = LOCALIZABLE_ATTRIBUTES[tagName];\n        if (!attrs) return;\n\n        for (const attr of attrs) {\n          const value = element.attribs?.[attr];\n          if (value && value.trim()) {\n            // Restore Twig blocks in attribute values\n            const restoredValue = postprocessTwig(value.trim(), twigBlocks);\n            result[`${path}#${attr}`] = restoredValue;\n          }\n        }\n      }\n\n      // Recursively extract translation units from element tree\n      const extractFromElement = createElementExtractor(\n        { getInnerHTML, extractAttributes },\n        result,\n      );\n\n      // Find head and body elements\n      const html = domutils.findOne(\n        (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"html\",\n        dom,\n        true,\n      ) as Element | null;\n\n      if (html) {\n        const head = domutils.findOne(\n          (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"head\",\n          html.children,\n          true,\n        ) as Element | null;\n\n        const body = domutils.findOne(\n          (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"body\",\n          html.children,\n          true,\n        ) as Element | null;\n\n        // Process head children\n        if (head) {\n          let headIndex = 0;\n          const headChildren = head.children.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n          for (const child of headChildren) {\n            extractFromElement(child, [\"head\", headIndex++]);\n          }\n        }\n\n        // Process body children\n        if (body) {\n          let bodyIndex = 0;\n          const bodyChildren = body.children.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n          for (const child of bodyChildren) {\n            extractFromElement(child, [\"body\", bodyIndex++]);\n          }\n        }\n      } else {\n        // Handle HTML fragments (no <html> element) - process root elements directly\n        let rootIndex = 0;\n        const rootElements = dom.filter(\n          (child): child is Element => child.type === \"tag\",\n        );\n        for (const child of rootElements) {\n          extractFromElement(child, [rootIndex++]);\n        }\n      }\n\n      return result;\n    },\n\n    async push(locale, data, originalInput) {\n      // Preprocess Twig syntax in original input\n      const { processed, twigBlocks } = preprocessTwig(originalInput || \"\");\n\n      // Parse original HTML\n      const handler = new DomHandler();\n      const parser = new htmlparser2.Parser(handler, {\n        lowerCaseTags: false,\n        lowerCaseAttributeNames: false,\n      });\n      parser.write(\n        processed || \"<!DOCTYPE html><html><head></head><body></body></html>\",\n      );\n      parser.end();\n\n      const dom = handler.dom;\n\n      // Find HTML element and set lang attribute\n      const html = domutils.findOne(\n        (elem) => elem.type === \"tag\" && elem.name.toLowerCase() === \"html\",\n        dom,\n        true,\n      ) as Element | null;\n\n      if (html) {\n        html.attribs = html.attribs || {};\n        html.attribs.lang = locale;\n      }\n\n      // Helper to traverse child elements by numeric indices\n      function traverseByIndices(\n        element: Element | null,\n        indices: string[],\n      ): Element | null {\n        let current = element;\n\n        for (const indexStr of indices) {\n          if (!current) return null;\n\n          const index = parseInt(indexStr, 10);\n          const children: Element[] = current.children.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n\n          if (index >= children.length) {\n            return null; // Path doesn't exist\n          }\n\n          current = children[index];\n        }\n\n        return current;\n      }\n\n      // Resolve path to element in the DOM\n      function resolvePathToElement(path: string): Element | null {\n        const parts = path.split(\"/\");\n        const [rootTag, ...indices] = parts;\n\n        let current: Element | null = null;\n\n        if (html) {\n          // Full HTML document with <html>, <head>, <body>\n          // Find head or body\n          if (rootTag === \"head\") {\n            current = domutils.findOne(\n              (elem) =>\n                elem.type === \"tag\" && elem.name.toLowerCase() === \"head\",\n              html.children,\n              true,\n            ) as Element | null;\n          } else if (rootTag === \"body\") {\n            current = domutils.findOne(\n              (elem) =>\n                elem.type === \"tag\" && elem.name.toLowerCase() === \"body\",\n              html.children,\n              true,\n            ) as Element | null;\n          }\n\n          if (!current) return null;\n\n          // Traverse by indices\n          return traverseByIndices(current, indices);\n        } else {\n          // HTML fragment - no <html> element\n          // Path is just numeric indices from root\n          const rootElements = dom.filter(\n            (child): child is Element => child.type === \"tag\",\n          );\n\n          // First part is the root index\n          const rootIndex = parseInt(rootTag, 10);\n          if (rootIndex >= rootElements.length) {\n            return null;\n          }\n\n          current = rootElements[rootIndex];\n\n          // Traverse remaining indices\n          return traverseByIndices(current, indices);\n        }\n      }\n\n      // Apply translations\n      for (const [path, value] of Object.entries(data)) {\n        const [nodePath, attribute] = path.split(\"#\");\n\n        const element = resolvePathToElement(nodePath);\n        if (!element) {\n          console.warn(`Path not found in original template: ${nodePath}`);\n          continue;\n        }\n\n        if (attribute) {\n          // Set attribute (value already contains Twig syntax if any)\n          element.attribs = element.attribs || {};\n          element.attribs[attribute] = value;\n        } else {\n          // Set innerHTML (parse value as HTML and replace children)\n          // Value may contain Twig syntax ({{ }}) which we need to preserve\n          if (value) {\n            // Preprocess the translated value to handle any Twig blocks\n            const { processed: processedValue, twigBlocks: valueTwigBlocks } =\n              preprocessTwig(value);\n\n            const valueHandler = new DomHandler();\n            const valueParser = new htmlparser2.Parser(valueHandler);\n            valueParser.write(processedValue);\n            valueParser.end();\n\n            element.children = valueHandler.dom;\n\n            // Postprocess the children to restore Twig blocks\n            element.children.forEach((child: any) => {\n              if (child.type === \"text\" && child.data) {\n                child.data = postprocessTwig(child.data, valueTwigBlocks);\n              }\n            });\n          } else {\n            // If value is empty/null, clear children\n            element.children = [];\n          }\n        }\n      }\n\n      // Serialize back to HTML and restore Twig blocks\n      const serialized = DomSerializer.default(dom, {\n        encodeEntities: false,\n      });\n      return postprocessTwig(serialized, twigBlocks);\n    },\n  });\n}\n","import _ from \"lodash\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createEnsureKeyOrderLoader(): ILoader<\n  Record<string, any>,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (_locale, input) => {\n      return input;\n    },\n    push: async (_locale, data, originalInput) => {\n      if (!originalInput || !data) {\n        return data;\n      }\n      return reorderKeys(data, originalInput);\n    },\n  });\n}\n\nfunction reorderKeys(\n  data: Record<string, any>,\n  originalInput: Record<string, any>,\n): Record<string, any> {\n  if (_.isArray(originalInput) && _.isArray(data)) {\n    // If both are arrays, recursively reorder keys in each element\n    return data.map((item, idx) => reorderKeys(item, originalInput[idx] ?? {}));\n  }\n  if (!_.isObject(data) || _.isArray(data) || _.isDate(data)) {\n    return data;\n  }\n\n  const orderedData: Record<string, any> = {};\n  const originalKeys = Object.keys(originalInput);\n  const dataKeys = new Set(Object.keys(data));\n\n  for (const key of originalKeys) {\n    if (dataKeys.has(key)) {\n      orderedData[key] = reorderKeys(data[key], originalInput[key]);\n      dataKeys.delete(key);\n    }\n  }\n\n  return orderedData;\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nexport default function createTxtLoader(): ILoader<\n  string,\n  Record<string, string>\n> {\n  return createLoader({\n    async pull(locale, input) {\n      const result: Record<string, string> = {};\n\n      if (input !== undefined && input !== null && input !== \"\") {\n        const lines = input.split(\"\\n\");\n        lines.forEach((line, index) => {\n          result[String(index + 1)] = line;\n        });\n      }\n\n      return result;\n    },\n\n    async push(locale, payload) {\n      const sortedEntries = Object.entries(payload).sort(\n        ([a], [b]) => parseInt(a) - parseInt(b),\n      );\n      return sortedEntries.map(([_, value]) => value).join(\"\\n\");\n    },\n  });\n}\n","import _ from \"lodash\";\nimport { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\n\nconst TOP_LEVEL_KEY = \"--content--\";\n\nexport default function createJsonDictionaryLoader(): ILoader<\n  Record<string, any>,\n  Record<string, any>\n> {\n  return createLoader({\n    pull: async (locale, input) => {\n      const result = extractTranslatables(input, locale);\n\n      // if locale keys are on top level, only single string is extracted and returned under special key\n      if (typeof result === \"string\") {\n        return { [TOP_LEVEL_KEY]: result };\n      }\n\n      return result;\n    },\n    push: async (locale, data, originalInput, originalLocale) => {\n      if (!originalInput) {\n        throw new Error(\"Error while parsing json-dictionary bucket\");\n      }\n      const input = _.cloneDeep(originalInput);\n\n      // if content is under special key, locale keys are on top level\n      if (\n        Object.keys(data).length === 1 &&\n        Object.keys(data)[0] === TOP_LEVEL_KEY\n      ) {\n        setNestedLocale(\n          { [TOP_LEVEL_KEY]: input },\n          [TOP_LEVEL_KEY],\n          locale,\n          data[TOP_LEVEL_KEY],\n          originalLocale,\n        );\n        return input;\n      }\n\n      // set the translation under the target locale key, use value from data (which is now a string)\n      function walk(obj: any, dataNode: any, path: string[] = []) {\n        if (Array.isArray(obj) && Array.isArray(dataNode)) {\n          obj.forEach((item, idx) =>\n            walk(item, dataNode[idx], [...path, String(idx)]),\n          );\n        } else if (\n          obj &&\n          typeof obj === \"object\" &&\n          dataNode &&\n          typeof dataNode === \"object\" &&\n          !Array.isArray(dataNode)\n        ) {\n          for (const key of Object.keys(obj)) {\n            if (dataNode.hasOwnProperty(key)) {\n              walk(obj[key], dataNode[key], [...path, key]);\n            }\n          }\n        } else if (\n          obj &&\n          typeof obj === \"object\" &&\n          !Array.isArray(obj) &&\n          typeof dataNode === \"string\"\n        ) {\n          // dataNode is the new string for the target locale\n          setNestedLocale(input, path, locale, dataNode, originalLocale);\n        }\n      }\n      walk(input, data);\n\n      return input;\n    },\n  });\n}\n\n// extract all keys that match locale from object\nfunction extractTranslatables(obj: any, locale: string): any {\n  if (Array.isArray(obj)) {\n    return obj.map((item) => extractTranslatables(item, locale));\n  } else if (isTranslatableObject(obj, locale)) {\n    return obj[locale];\n  } else if (obj && typeof obj === \"object\") {\n    const result: any = {};\n    for (const key of Object.keys(obj)) {\n      const value = extractTranslatables(obj[key], locale);\n      if (\n        (typeof value === \"object\" &&\n          value !== null &&\n          Object.keys(value).length > 0) ||\n        (Array.isArray(value) && value.length > 0) ||\n        (typeof value === \"string\" && value.length > 0)\n      ) {\n        result[key] = value;\n      }\n    }\n    return result;\n  }\n  return undefined;\n}\n\nfunction isTranslatableObject(obj: any, locale: string): boolean {\n  return (\n    obj &&\n    typeof obj === \"object\" &&\n    !Array.isArray(obj) &&\n    Object.prototype.hasOwnProperty.call(obj, locale)\n  );\n}\n\nfunction setNestedLocale(\n  obj: any,\n  path: string[],\n  locale: string,\n  value: string,\n  originalLocale: string,\n) {\n  let curr = obj;\n  for (let i = 0; i < path.length - 1; i++) {\n    const key = path[i];\n    if (!(key in curr)) curr[key] = {};\n    curr = curr[key];\n  }\n  const last = path[path.length - 1];\n  if (curr[last] && typeof curr[last] === \"object\") {\n    curr[last][locale] = value;\n    // Reorder keys: source locale first, then others alphabetically\n    if (originalLocale && curr[last][originalLocale]) {\n      const entries = Object.entries(curr[last]);\n      const first = entries.filter(([k]) => k === originalLocale);\n      const rest = entries\n        .filter(([k]) => k !== originalLocale)\n        .sort(([a], [b]) => a.localeCompare(b));\n      const ordered = [...first, ...rest];\n      const reordered: Record<string, string> = {};\n      for (const [k, v] of ordered) {\n        reordered[k] = v as string;\n      }\n      curr[last] = reordered;\n    }\n  }\n}\n","import { ILoader } from \"./_types\";\nimport { createLoader } from \"./_utils\";\nimport { parse } from \"csv-parse/sync\";\nimport { stringify } from \"csv-stringify/sync\";\n\nfunction dedupeHeaders(headers: string[]) {\n  const seen = new Map<string, number>();\n\n  return headers.map((h) => {\n    const count = seen.get(h) ?? 0;\n    seen.set(h, count + 1);\n    return count === 0 ? h : `${h}__${count + 1}`;\n  });\n}\n\nexport default function createCsvPerLocaleLoader(): ILoader<\n  string,\n  Record<string, any>\n> {\n  return createLoader({\n    async pull(_locale, input) {\n      if (!input?.trim()) return {};\n\n\n      const parsed = parse(input, {\n        skip_empty_lines: true,\n        columns: (headers: string[]) => {\n          const dedupedHeaders = dedupeHeaders(headers);\n          return dedupedHeaders;\n        },\n      }) as Array<Record<string, any>>;\n\n      if (parsed.length === 0) return {};\n\n      return parsed;\n    },\n    async push(_locale, data, originalInput) {\n\n      const rawRows = parse(originalInput || \"\", {\n        skip_empty_lines: true,\n      }) as string[][];\n\n      const originalHeaders = rawRows[0];\n\n      const dedupedHeaders = dedupeHeaders(originalHeaders);\n\n      const columns = originalHeaders.map((header, i) => ({\n        key: dedupedHeaders[i],\n        header,\n      }));\n\n      const rows = Object.values(data).filter(\n        (row) =>\n          row &&\n          Object.values(row).some(\n            (v) => v !== undefined && v !== null,\n          ),\n      );\n      // const output = stringify(rows, { header: true }).trimEnd();\n      const output = stringify(rows, {\n        header: true,\n        columns,\n      }).trimEnd();\n\n      return output;\n    },\n  });\n}\n","import Z from \"zod\";\nimport { bucketTypeSchema } from \"@lingo.dev/_spec\";\nimport { composeLoaders } from \"./_utils\";\nimport createJsonLoader from \"./json\";\nimport createJson5Loader from \"./json5\";\nimport createJsoncLoader from \"./jsonc\";\nimport createFlatLoader from \"./flat\";\nimport createTextFileLoader from \"./text-file\";\nimport createYamlLoader from \"./yaml\";\nimport createRootKeyLoader from \"./root-key\";\nimport createFlutterLoader from \"./flutter\";\nimport { ILoader } from \"./_types\";\nimport createAilLoader from \"./ail\";\nimport createAndroidLoader from \"./android\";\nimport createCsvLoader from \"./csv\";\nimport createHtmlLoader from \"./html\";\nimport createMarkdownLoader from \"./markdown\";\nimport createMarkdocLoader from \"./markdoc\";\nimport createMjmlLoader from \"./mjml\";\nimport createPropertiesLoader from \"./properties\";\nimport createXcodeStringsLoader from \"./xcode-strings\";\nimport createXcodeStringsdictLoader from \"./xcode-stringsdict\";\nimport createXcodeXcstringsLoader from \"./xcode-xcstrings\";\nimport createXcodeXcstringsV2Loader from \"./xcode-xcstrings-v2\";\nimport createUnlocalizableLoader from \"./unlocalizable\";\nimport { createFormatterLoader, FormatterType } from \"./formatters\";\nimport createPoLoader from \"./po\";\nimport createXliffLoader from \"./xliff\";\nimport createXmlLoader from \"./xml\";\nimport createSrtLoader from \"./srt\";\nimport createDatoLoader from \"./dato\";\nimport createVttLoader from \"./vtt\";\nimport createVariableLoader from \"./variable\";\nimport createSyncLoader from \"./sync\";\nimport createPlutilJsonTextLoader from \"./plutil-json-loader\";\nimport createPhpLoader from \"./php\";\nimport createVueJsonLoader from \"./vue-json\";\nimport createTypescriptLoader from \"./typescript\";\nimport createInjectLocaleLoader from \"./inject-locale\";\nimport createLockedKeysLoader from \"./locked-keys\";\nimport createMdxFrontmatterSplitLoader from \"./mdx2/frontmatter-split\";\nimport createMdxCodePlaceholderLoader from \"./mdx2/code-placeholder\";\nimport createLocalizableMdxDocumentLoader from \"./mdx2/localizable-document\";\nimport createMdxSectionsSplit2Loader from \"./mdx2/sections-split-2\";\nimport createLockedPatternsLoader from \"./locked-patterns\";\nimport createIgnoredKeysLoader from \"./ignored-keys\";\nimport createPreservedKeysLoader from \"./preserved-keys\";\nimport createEjsLoader from \"./ejs\";\nimport createTwigLoader from \"./twig\";\nimport createEnsureKeyOrderLoader from \"./ensure-key-order\";\nimport createTxtLoader from \"./txt\";\nimport createJsonKeysLoader from \"./json-dictionary\";\nimport createCsvPerLocaleLoader from \"./csv-per-locale\";\n\ntype BucketLoaderOptions = {\n  returnUnlocalizedKeys?: boolean;\n  defaultLocale: string;\n  injectLocale?: string[];\n  targetLocale?: string;\n  formatter?: FormatterType;\n  keyColumn?: string;\n};\n\n/**\n * Helper function to encode keys for buckets that use flat loader\n * The flat loader encodes keys using encodeURIComponent, so we need to\n * encode locked/ignored keys patterns to match against the encoded keys\n */\nfunction encodeKeys(keys: string[]): string[] {\n  return keys.map((key) => encodeURIComponent(key));\n}\n\n/**\n * Normalizes patterns for CSV buckets (csv, csv-per-locale)\n * Automatically adds \"*\\/\" prefix if pattern doesn't contain \"/\" and doesn't start with \"*\\/\"\n * This allows users to write \"id\" instead of \"*\\/id\"\n */\n\nfunction normalizeCsvPatterns(patterns: string[]): string[] {\n  return patterns.map((pattern) => {\n    if (pattern.includes(\"/\") || pattern.startsWith(\"*/\")) {\n      return pattern;\n    }\n    return `*/${pattern}`;\n  });\n}\n\nexport default function createBucketLoader(\n  bucketType: Z.infer<typeof bucketTypeSchema>,\n  bucketPathPattern: string,\n  options: BucketLoaderOptions,\n  lockedKeys?: string[],\n  lockedPatterns?: string[],\n  ignoredKeys?: string[],\n  preservedKeys?: string[],\n  localizableKeys?: string[],\n): ILoader<void, Record<string, any>> {\n  switch (bucketType) {\n    default:\n      throw new Error(`Unsupported bucket type: ${bucketType}`);\n    case \"ail\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createAilLoader(),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"android\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createAndroidLoader(),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"csv\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createCsvLoader({ keyColumn: options.keyColumn }),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"csv-per-locale\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createCsvPerLocaleLoader(),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createLockedKeysLoader(normalizeCsvPatterns(lockedKeys || [])),\n        createIgnoredKeysLoader(normalizeCsvPatterns(ignoredKeys || [])),\n        createPreservedKeysLoader(normalizeCsvPatterns(preservedKeys || [])),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          normalizeCsvPatterns(localizableKeys || []),\n        ),\n      );\n    case \"html\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"html\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createHtmlLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"ejs\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createEjsLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"json\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"json\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createJsonLoader(),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createInjectLocaleLoader(options.injectLocale),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"json5\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createJson5Loader(),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createInjectLocaleLoader(options.injectLocale),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"jsonc\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createJsoncLoader(),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createInjectLocaleLoader(options.injectLocale),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"markdown\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"markdown\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createMarkdownLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"markdoc\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createMarkdocLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"mdx\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"mdx\", bucketPathPattern),\n        createMdxCodePlaceholderLoader(),\n        createLockedPatternsLoader(lockedPatterns),\n        createMdxFrontmatterSplitLoader(),\n        createMdxSectionsSplit2Loader(),\n        createLocalizableMdxDocumentLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"mjml\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"html\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createMjmlLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"po\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createPoLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createVariableLoader({ type: \"python\" }),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"properties\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createPropertiesLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"xcode-strings\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createXcodeStringsLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"xcode-stringsdict\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createXcodeStringsdictLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"xcode-xcstrings\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createPlutilJsonTextLoader(),\n        createLockedPatternsLoader(lockedPatterns),\n        createJsonLoader(),\n        createXcodeXcstringsLoader(options.defaultLocale),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(encodeKeys(lockedKeys || [])),\n        createIgnoredKeysLoader(encodeKeys(ignoredKeys || [])),\n        createPreservedKeysLoader(encodeKeys(preservedKeys || [])),\n        createSyncLoader(),\n        createVariableLoader({ type: \"ieee\" }),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          encodeKeys(localizableKeys || []),\n        ),\n      );\n    case \"xcode-xcstrings-v2\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createPlutilJsonTextLoader(),\n        createLockedPatternsLoader(lockedPatterns),\n        createJsonLoader(),\n        createXcodeXcstringsV2Loader(options.defaultLocale),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(encodeKeys(lockedKeys || [])),\n        createIgnoredKeysLoader(encodeKeys(ignoredKeys || [])),\n        createPreservedKeysLoader(encodeKeys(preservedKeys || [])),\n        createSyncLoader(),\n        createVariableLoader({ type: \"ieee\" }),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          encodeKeys(localizableKeys || []),\n        ),\n      );\n    case \"yaml\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"yaml\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createYamlLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"yaml-root-key\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"yaml\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createYamlLoader(),\n        createRootKeyLoader(true),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"flutter\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"json\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createJsonLoader(),\n        createEnsureKeyOrderLoader(),\n        createFlutterLoader(),\n        createFlatLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"xliff\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createXliffLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"xml\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createXmlLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"srt\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createSrtLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"dato\":\n      return composeLoaders(\n        createDatoLoader(bucketPathPattern),\n        createSyncLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"vtt\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createVttLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"php\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createPhpLoader(),\n        createSyncLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"vue-json\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createVueJsonLoader(),\n        createSyncLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"typescript\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(\n          options.formatter,\n          \"typescript\",\n          bucketPathPattern,\n        ),\n        createLockedPatternsLoader(lockedPatterns),\n        createTypescriptLoader(),\n        createFlatLoader(),\n        createEnsureKeyOrderLoader(),\n        createSyncLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"twig\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createTwigLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"txt\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createTxtLoader(),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n    case \"json-dictionary\":\n      return composeLoaders(\n        createTextFileLoader(bucketPathPattern),\n        createFormatterLoader(options.formatter, \"json\", bucketPathPattern),\n        createLockedPatternsLoader(lockedPatterns),\n        createJsonLoader(),\n        createJsonKeysLoader(),\n        createEnsureKeyOrderLoader(),\n        createFlatLoader(),\n        createInjectLocaleLoader(options.injectLocale),\n        createLockedKeysLoader(lockedKeys || []),\n        createIgnoredKeysLoader(ignoredKeys || []),\n        createPreservedKeysLoader(preservedKeys || []),\n        createSyncLoader(),\n        createUnlocalizableLoader(\n          options.returnUnlocalizedKeys,\n          localizableKeys,\n        ),\n      );\n  }\n}\n","import { Command } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport { getConfig } from \"../../utils/config\";\nimport { CLIError } from \"../../utils/errors\";\nimport { getBuckets } from \"../../utils/buckets\";\nimport { executeKeyCommand } from \"./_shared-key-command\";\n\nexport default new Command()\n  .command(\"ignored-keys\")\n  .description(\n    \"Show which key-value pairs in source files match ignoredKeys patterns\",\n  )\n  .option(\"--bucket <name>\", \"Only show ignored keys for a specific bucket\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (options) => {\n    const ora = Ora();\n    try {\n      const i18nConfig = await getConfig();\n\n      if (!i18nConfig) {\n        throw new CLIError({\n          message:\n            \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n          docUrl: \"i18nNotFound\",\n        });\n      }\n\n      const buckets = getBuckets(i18nConfig);\n\n      await executeKeyCommand(i18nConfig, buckets, options, {\n        filterType: \"ignoredKeys\",\n        displayName: \"ignored\",\n      });\n    } catch (error: any) {\n      ora.fail(error.message);\n      process.exit(1);\n    }\n  });\n","import { Command } from \"interactive-commander\";\nimport Ora from \"ora\";\nimport { getConfig } from \"../../utils/config\";\nimport { CLIError } from \"../../utils/errors\";\nimport { getBuckets } from \"../../utils/buckets\";\nimport { executeKeyCommand } from \"./_shared-key-command\";\n\nexport default new Command()\n  .command(\"preserved-keys\")\n  .description(\n    \"Show which key-value pairs in source files match preservedKeys patterns\",\n  )\n  .option(\"--bucket <name>\", \"Only show preserved keys for a specific bucket\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (options) => {\n    const ora = Ora();\n    try {\n      const i18nConfig = await getConfig();\n\n      if (!i18nConfig) {\n        throw new CLIError({\n          message:\n            \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n          docUrl: \"i18nNotFound\",\n        });\n      }\n\n      const buckets = getBuckets(i18nConfig);\n\n      await executeKeyCommand(i18nConfig, buckets, options, {\n        filterType: \"preservedKeys\",\n        displayName: \"preserved\",\n      });\n    } catch (error: any) {\n      ora.fail(error.message);\n      process.exit(1);\n    }\n  });\n","import { Command } from \"interactive-commander\";\n\nimport setCmd from \"./set\";\nimport unsetCmd from \"./unset\";\nimport getCmd from \"./get\";\n\nexport default new Command()\n  .command(\"config\")\n  .description(\n    \"Manage CLI settings (authentication, API keys) stored in ~/.lingodotdevrc\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .addCommand(setCmd)\n  .addCommand(unsetCmd)\n  .addCommand(getCmd);\n","import { Command } from \"interactive-commander\";\nimport chalk from \"chalk\";\nimport dedent from \"dedent\";\nimport _ from \"lodash\";\nimport {\n  SETTINGS_KEYS,\n  loadSystemSettings,\n  saveSettings,\n} from \"../../utils/settings\";\n\nexport default new Command()\n  .name(\"set\")\n  .description(\"Set or update a CLI setting in ~/.lingodotdevrc\")\n  .addHelpText(\"afterAll\", `\\nAvailable keys:\\n  ${SETTINGS_KEYS.join(\"\\n  \")}`)\n  .argument(\n    \"<key>\",\n    \"Configuration key to set (dot notation, e.g., auth.apiKey)\",\n  )\n  .argument(\"<value>\", \"The configuration value to set\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (key: string, value: string) => {\n    if (!SETTINGS_KEYS.includes(key)) {\n      console.error(\n        dedent`\n          ${chalk.red(\"✖\")} Unknown configuration key: ${chalk.bold(key)}\n          Run ${chalk.dim(\"lingo.dev config set --help\")} to see available keys.\n        `,\n      );\n      process.exitCode = 1;\n      return;\n    }\n\n    const current = loadSystemSettings();\n    const updated: any = _.cloneDeep(current);\n    _.set(updated, key, value);\n\n    try {\n      saveSettings(updated as any);\n      console.log(`${chalk.green(\"✔\")} Set ${chalk.bold(key)}`);\n    } catch (err) {\n      console.error(\n        chalk.red(\n          `✖ Failed to save configuration: ${chalk.dim(\n            err instanceof Error ? err.message : String(err),\n          )}`,\n        ),\n      );\n      process.exitCode = 1;\n    }\n  });\n","import { Command } from \"interactive-commander\";\nimport chalk from \"chalk\";\nimport dedent from \"dedent\";\nimport _ from \"lodash\";\nimport {\n  SETTINGS_KEYS,\n  loadSystemSettings,\n  saveSettings,\n} from \"../../utils/settings\";\n\nexport default new Command()\n  .name(\"unset\")\n  .description(\"Remove a CLI setting from ~/.lingodotdevrc\")\n  .addHelpText(\"afterAll\", `\\nAvailable keys:\\n  ${SETTINGS_KEYS.join(\"\\n  \")}`)\n  .argument(\n    \"<key>\",\n    \"Configuration key to remove (must match one of the available keys listed below)\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (key: string) => {\n    // Validate key first (defensive; choices() should already restrict but keep for safety).\n    if (!SETTINGS_KEYS.includes(key)) {\n      console.error(\n        dedent`\n          ${chalk.red(\"✖\")} Unknown configuration key: ${chalk.bold(key)}\n          Run ${chalk.dim(\n            \"lingo.dev config unset --help\",\n          )} to see available keys.\n        `,\n      );\n      process.exitCode = 1;\n      return;\n    }\n\n    // Load existing settings.\n    const settings = loadSystemSettings();\n    const currentValue = _.get(settings, key);\n\n    if (!_.trim(String(currentValue || \"\"))) {\n      console.log(`${chalk.cyan(\"ℹ\")} ${chalk.bold(key)} is not set.`);\n      return;\n    } else {\n      const updated: any = _.cloneDeep(settings);\n      _.unset(updated, key);\n      try {\n        saveSettings(updated as any);\n        console.log(\n          `${chalk.green(\"✔\")} Removed configuration key ${chalk.bold(key)}`,\n        );\n      } catch (err) {\n        console.error(\n          chalk.red(\n            `✖ Failed to save configuration: ${chalk.dim(\n              err instanceof Error ? err.message : String(err),\n            )}`,\n          ),\n        );\n        process.exitCode = 1;\n      }\n    }\n  });\n","import { Command } from \"interactive-commander\";\nimport chalk from \"chalk\";\nimport _ from \"lodash\";\nimport { SETTINGS_KEYS, loadSystemSettings } from \"../../utils/settings\";\nimport dedent from \"dedent\";\n\nexport default new Command()\n  .name(\"get\")\n  .description(\"Display the value of a CLI setting from ~/.lingodotdevrc\")\n  .addHelpText(\"afterAll\", `\\nAvailable keys:\\n  ${SETTINGS_KEYS.join(\"\\n  \")}`)\n  .argument(\n    \"<key>\",\n    \"Configuration key to read (choose from the available keys listed below)\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async (key: string) => {\n    // Validate that the provided key is one of the recognised configuration keys.\n    if (!SETTINGS_KEYS.includes(key)) {\n      console.error(\n        dedent`\n          ${chalk.red(\"✖\")} Unknown configuration key: ${chalk.bold(key)}\n          Run ${chalk.dim(\"lingo.dev config get --help\")} to see available keys.\n        `,\n      );\n      process.exitCode = 1;\n      return;\n    }\n\n    const settings = loadSystemSettings();\n    const value = _.get(settings, key);\n\n    if (!value) {\n      // Key is valid but not set in the configuration file.\n      console.log(`${chalk.cyan(\"ℹ\")} ${chalk.bold(key)} is not set.`);\n      return;\n    }\n\n    if (typeof value === \"object\") {\n      console.log(JSON.stringify(value, null, 2));\n    } else {\n      console.log(value);\n    }\n  });\n","import {\n  bucketTypeSchema,\n  I18nConfig,\n  localeCodeSchema,\n  resolveOverriddenLocale,\n} from \"@lingo.dev/_spec\";\nimport { Command } from \"interactive-commander\";\nimport Z from \"zod\";\nimport _ from \"lodash\";\nimport * as path from \"path\";\nimport { getConfig } from \"../utils/config\";\nimport { getSettings } from \"../utils/settings\";\nimport {\n  ConfigError,\n  AuthenticationError,\n  ValidationError,\n  LocalizationError,\n  BucketProcessingError,\n  getCLIErrorType,\n  isLocalizationError,\n  isBucketProcessingError,\n  ErrorDetail,\n  aggregateErrorAnalytics,\n  createPreviousErrorContext,\n} from \"../utils/errors\";\nimport Ora from \"ora\";\nimport createBucketLoader from \"../loaders\";\nimport { createAuthenticator } from \"../utils/auth\";\nimport { getBuckets } from \"../utils/buckets\";\nimport chalk from \"chalk\";\nimport { createTwoFilesPatch } from \"diff\";\nimport inquirer from \"inquirer\";\nimport externalEditor from \"external-editor\";\nimport updateGitignore from \"../utils/update-gitignore\";\nimport createProcessor from \"../processor\";\nimport { withExponentialBackoff } from \"../utils/exp-backoff\";\nimport trackEvent, { UserIdentity } from \"../utils/observability\";\nimport { createDeltaProcessor } from \"../utils/delta\";\n\nexport default new Command()\n  .command(\"i18n\")\n  .description(\n    \"DEPRECATED: Run localization pipeline (prefer `run` command instead)\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .option(\n    \"--locale <locale>\",\n    \"Limit processing to the listed target locale codes from i18n.json. Repeat the flag to include multiple locales. Defaults to all configured target locales\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--bucket <bucket>\",\n    \"Limit processing to specific bucket types defined in i18n.json (e.g., json, yaml, android). Repeat the flag to include multiple bucket types. Defaults to all buckets\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--key <key>\",\n    \"Limit processing to a single translation key by exact match. Filters all buckets and locales to process only this key, useful for testing or debugging specific translations. Example: auth.login.title\",\n    (val: string) => encodeURIComponent(val),\n  )\n  .option(\n    \"--file [files...]\",\n    \"Filter processing to only buckets whose file paths contain these substrings. Example: 'components' to process only files in components directories\",\n  )\n  .option(\n    \"--frozen\",\n    \"Validate translations are up-to-date without making changes - fails if source files, target files, or lockfile are out of sync. Ideal for CI/CD to ensure translation consistency before deployment\",\n  )\n  .option(\n    \"--force\",\n    \"Force re-translation of all keys, bypassing change detection. Useful when you want to regenerate translations with updated AI models or translation settings\",\n  )\n  .option(\n    \"--verbose\",\n    \"Print the translation data being processed as formatted JSON for each bucket and locale\",\n  )\n  .option(\n    \"--interactive\",\n    \"Review and edit AI-generated translations interactively before applying changes to files\",\n  )\n  .option(\n    \"--api-key <api-key>\",\n    \"Override API key from settings or environment variables\",\n  )\n  .option(\n    \"--debug\",\n    \"Pause before processing localization so you can attach a debugger\",\n  )\n  .option(\n    \"--strict\",\n    \"Stop immediately on first error instead of continuing to process remaining buckets and locales (fail-fast mode)\",\n  )\n  .action(async function (options) {\n    updateGitignore();\n\n    const ora = Ora();\n\n    // Show deprecation warning\n    console.log();\n    ora.warn(\n      chalk.yellow(\n        \" DEPRECATED: 'i18n' is deprecated. Please use 'run' instead. Docs: https://lingo.dev/cli/commands/run\",\n      ),\n    );\n    console.log();\n\n    let flags: ReturnType<typeof parseFlags>;\n\n    try {\n      flags = parseFlags(options);\n    } catch (parseError: any) {\n      // Handle flag validation errors (like invalid locale codes)\n      await trackEvent(null, \"cmd.i18n.error\", {\n        errorType: \"validation_error\",\n        errorName: parseError.name || \"ValidationError\",\n        errorMessage: parseError.message || \"Invalid command line options\",\n        errorStack: parseError.stack,\n        fatal: true,\n        errorCount: 1,\n        stage: \"flag_validation\",\n      });\n      await new Promise((resolve) => setTimeout(resolve, 50));\n      throw parseError;\n    }\n\n    if (flags.debug) {\n      // wait for user input, use inquirer\n      const { debug } = await inquirer.prompt([\n        {\n          type: \"confirm\",\n          name: \"debug\",\n          message: \"Debug mode. Wait for user input before continuing.\",\n        },\n      ]);\n    }\n\n    let hasErrors = false;\n    let userIdentity: UserIdentity = null;\n    const errorDetails: ErrorDetail[] = [];\n    try {\n      ora.start(\"Loading configuration...\");\n      const i18nConfig = getConfig();\n      const settings = getSettings(flags.apiKey);\n      ora.succeed(\"Configuration loaded\");\n\n      ora.start(\"Validating localization configuration...\");\n      validateParams(i18nConfig, flags);\n      ora.succeed(\"Localization configuration is valid\");\n\n      ora.start(\"Connecting to Lingo.dev Localization Engine...\");\n      const isByokMode = !!i18nConfig?.provider;\n\n      if (isByokMode) {\n        userIdentity = null;\n        ora.succeed(\"Using external provider (BYOK mode)\");\n      } else {\n        const auth = await validateAuth(settings);\n        userIdentity = { email: auth.email, id: auth.id };\n        ora.succeed(`Authenticated as ${auth.email}`);\n      }\n\n      await trackEvent(userIdentity, \"cmd.i18n.start\", {\n        i18nConfig,\n        flags,\n      });\n\n      let buckets = getBuckets(i18nConfig!);\n      if (flags.bucket?.length) {\n        buckets = buckets.filter((bucket: any) =>\n          flags.bucket!.includes(bucket.type),\n        );\n      }\n      ora.succeed(\"Buckets retrieved\");\n\n      if (flags.file?.length) {\n        buckets = buckets\n          .map((bucket: any) => {\n            const paths = bucket.paths.filter((path: any) =>\n              flags.file!.find((file) => path.pathPattern?.includes(file)),\n            );\n            return { ...bucket, paths };\n          })\n          .filter((bucket: any) => bucket.paths.length > 0);\n        if (buckets.length === 0) {\n          ora.fail(\n            \"No buckets found. All buckets were filtered out by --file option.\",\n          );\n          throw new Error(\n            \"No buckets found. All buckets were filtered out by --file option.\",\n          );\n        } else {\n          ora.info(`\\x1b[36mProcessing only filtered buckets:\\x1b[0m`);\n          buckets.map((bucket: any) => {\n            ora.info(`  ${bucket.type}:`);\n            bucket.paths.forEach((path: any) => {\n              ora.info(`    - ${path.pathPattern}`);\n            });\n          });\n        }\n      }\n\n      const targetLocales = flags.locale?.length\n        ? flags.locale\n        : i18nConfig!.locale.targets;\n\n      // Ensure the lockfile exists\n      ora.start(\"Setting up localization cache...\");\n      const checkLockfileProcessor = createDeltaProcessor(\"\");\n      const lockfileExists = await checkLockfileProcessor.checkIfLockExists();\n      if (!lockfileExists) {\n        ora.start(\"Creating i18n.lock...\");\n        for (const bucket of buckets) {\n          for (const bucketPath of bucket.paths) {\n            const sourceLocale = resolveOverriddenLocale(\n              i18nConfig!.locale.source,\n              bucketPath.delimiter,\n            );\n            const bucketLoader = createBucketLoader(\n              bucket.type,\n              bucketPath.pathPattern,\n              {\n                defaultLocale: sourceLocale,\n                injectLocale: bucket.injectLocale,\n                formatter: i18nConfig!.formatter,\n                keyColumn: bucket.keyColumn,\n              },\n              bucket.lockedKeys,\n              bucket.lockedPatterns,\n              bucket.ignoredKeys,\n              bucket.preservedKeys,\n              bucket.localizableKeys,\n            );\n            bucketLoader.setDefaultLocale(sourceLocale);\n            await bucketLoader.init();\n\n            const sourceData = await bucketLoader.pull(\n              i18nConfig!.locale.source,\n            );\n\n            const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);\n            const checksums = await deltaProcessor.createChecksums(sourceData);\n            await deltaProcessor.saveChecksums(checksums);\n          }\n        }\n        ora.succeed(\"Localization cache initialized\");\n      } else {\n        ora.succeed(\"Localization cache loaded\");\n      }\n\n      if (flags.frozen) {\n        ora.start(\"Checking for lockfile updates...\");\n        let requiresUpdate: string | null = null;\n        bucketLoop: for (const bucket of buckets) {\n          for (const bucketPath of bucket.paths) {\n            const sourceLocale = resolveOverriddenLocale(\n              i18nConfig!.locale.source,\n              bucketPath.delimiter,\n            );\n\n            const bucketLoader = createBucketLoader(\n              bucket.type,\n              bucketPath.pathPattern,\n              {\n                defaultLocale: sourceLocale,\n                returnUnlocalizedKeys: true,\n                injectLocale: bucket.injectLocale,\n                keyColumn: bucket.keyColumn,\n              },\n              bucket.lockedKeys,\n              bucket.lockedPatterns,\n              bucket.ignoredKeys,\n              bucket.preservedKeys,\n              bucket.localizableKeys,\n            );\n            bucketLoader.setDefaultLocale(sourceLocale);\n            await bucketLoader.init();\n\n            const { unlocalizable: sourceUnlocalizable, ...sourceData } =\n              await bucketLoader.pull(i18nConfig!.locale.source);\n            const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);\n            const sourceChecksums =\n              await deltaProcessor.createChecksums(sourceData);\n            const savedChecksums = await deltaProcessor.loadChecksums();\n\n            // Get updated data by comparing current checksums with saved checksums\n            const updatedSourceData = _.pickBy(\n              sourceData,\n              (value, key) => sourceChecksums[key] !== savedChecksums[key],\n            );\n\n            // translation was updated in the source file\n            if (Object.keys(updatedSourceData).length > 0) {\n              requiresUpdate = \"updated\";\n              break bucketLoop;\n            }\n\n            for (const _targetLocale of targetLocales) {\n              const targetLocale = resolveOverriddenLocale(\n                _targetLocale,\n                bucketPath.delimiter,\n              );\n              const { unlocalizable: targetUnlocalizable, ...targetData } =\n                await bucketLoader.pull(targetLocale);\n\n              const missingKeys = _.difference(\n                Object.keys(sourceData),\n                Object.keys(targetData),\n              );\n              const extraKeys = _.difference(\n                Object.keys(targetData),\n                Object.keys(sourceData),\n              );\n              const unlocalizableDataDiff = !_.isEqual(\n                sourceUnlocalizable,\n                targetUnlocalizable,\n              );\n\n              // translation is missing in the target file\n              if (missingKeys.length > 0) {\n                requiresUpdate = \"missing\";\n                break bucketLoop;\n              }\n\n              // target file has extra translations\n              if (extraKeys.length > 0) {\n                requiresUpdate = \"extra\";\n                break bucketLoop;\n              }\n\n              // unlocalizable keys do not match\n              if (unlocalizableDataDiff) {\n                requiresUpdate = \"unlocalizable\";\n                break bucketLoop;\n              }\n            }\n          }\n        }\n\n        if (requiresUpdate) {\n          const message = {\n            updated: \"Source file has been updated.\",\n            missing: \"Target file is missing translations.\",\n            extra:\n              \"Target file has extra translations not present in the source file.\",\n            unlocalizable:\n              \"Unlocalizable data (such as booleans, dates, URLs, etc.) do not match.\",\n          }[requiresUpdate];\n          ora.fail(\n            `Localization data has changed; please update i18n.lock or run without --frozen.`,\n          );\n          ora.fail(`  Details: ${message}`);\n          throw new Error(\n            `Localization data has changed; please update i18n.lock or run without --frozen. Details: ${message}`,\n          );\n        } else {\n          ora.succeed(\"No lockfile updates required.\");\n        }\n      }\n\n      // Process each bucket\n      for (const bucket of buckets) {\n        try {\n          console.log();\n          ora.info(`Processing bucket: ${bucket.type}`);\n          for (const bucketPath of bucket.paths) {\n            const bucketOra = Ora({ indent: 2 }).info(\n              `Processing path: ${bucketPath.pathPattern}`,\n            );\n\n            const sourceLocale = resolveOverriddenLocale(\n              i18nConfig!.locale.source,\n              bucketPath.delimiter,\n            );\n\n            const bucketLoader = createBucketLoader(\n              bucket.type,\n              bucketPath.pathPattern,\n              {\n                defaultLocale: sourceLocale,\n                injectLocale: bucket.injectLocale,\n                formatter: i18nConfig!.formatter,\n                keyColumn: bucket.keyColumn,\n              },\n              bucket.lockedKeys,\n              bucket.lockedPatterns,\n              bucket.ignoredKeys,\n              bucket.preservedKeys,\n              bucket.localizableKeys,\n            );\n            bucketLoader.setDefaultLocale(sourceLocale);\n            await bucketLoader.init();\n            let sourceData = await bucketLoader.pull(sourceLocale);\n\n            for (const _targetLocale of targetLocales) {\n              const targetLocale = resolveOverriddenLocale(\n                _targetLocale,\n                bucketPath.delimiter,\n              );\n              try {\n                bucketOra.start(\n                  `[${sourceLocale} -> ${targetLocale}] (0%) Localization in progress...`,\n                );\n\n                sourceData = await bucketLoader.pull(sourceLocale);\n\n                const targetData = await bucketLoader.pull(targetLocale);\n                const deltaProcessor = createDeltaProcessor(\n                  bucketPath.pathPattern,\n                );\n                const checksums = await deltaProcessor.loadChecksums();\n                const delta = await deltaProcessor.calculateDelta({\n                  sourceData,\n                  targetData,\n                  checksums,\n                });\n                let processableData = _.chain(sourceData)\n                  .entries()\n                  .filter(\n                    ([key, value]) =>\n                      delta.added.includes(key) ||\n                      delta.updated.includes(key) ||\n                      !!flags.force,\n                  )\n                  .fromPairs()\n                  .value();\n\n                if (flags.key) {\n                  processableData = _.pickBy(\n                    processableData,\n                    (_, key) => key === flags.key,\n                  );\n                }\n                if (flags.verbose) {\n                  bucketOra.info(JSON.stringify(processableData, null, 2));\n                }\n\n                bucketOra.start(\n                  `[${sourceLocale} -> ${targetLocale}] [${\n                    Object.keys(processableData).length\n                  } entries] (0%) AI localization in progress...`,\n                );\n                let processPayload = createProcessor(i18nConfig!.provider, {\n                  apiKey: settings.auth.apiKey,\n                  apiUrl: settings.auth.apiUrl,\n                  engineId: i18nConfig!.engineId,\n                });\n                processPayload = withExponentialBackoff(\n                  processPayload,\n                  3,\n                  1000,\n                );\n\n                const processedTargetData = await processPayload(\n                  {\n                    sourceLocale,\n                    sourceData,\n                    processableData,\n                    targetLocale,\n                    // When --force is used, exclude previous translations from reference to ensure fresh translations\n                    targetData: flags.force ? {} : targetData,\n                  },\n                  (progress, sourceChunk, processedChunk) => {\n                    bucketOra.text = `[${sourceLocale} -> ${targetLocale}] [${\n                      Object.keys(processableData).length\n                    } entries] (${progress}%) AI localization in progress...`;\n                  },\n                );\n\n                if (flags.verbose) {\n                  bucketOra.info(JSON.stringify(processedTargetData, null, 2));\n                }\n\n                let finalTargetData = _.merge(\n                  {},\n                  sourceData,\n                  targetData,\n                  processedTargetData,\n                );\n\n                // rename keys\n                finalTargetData = _.chain(finalTargetData)\n                  .entries()\n                  .map(([key, value]) => {\n                    const renaming = delta.renamed.find(\n                      ([oldKey, newKey]) => oldKey === key,\n                    );\n                    if (!renaming) {\n                      return [key, value];\n                    }\n                    return [renaming[1], value];\n                  })\n                  .fromPairs()\n                  .value();\n\n                if (flags.interactive) {\n                  bucketOra.stop();\n                  const reviewedData = await reviewChanges({\n                    pathPattern: bucketPath.pathPattern,\n                    targetLocale,\n                    currentData: targetData,\n                    proposedData: finalTargetData,\n                    sourceData,\n                    force: flags.force!,\n                  });\n\n                  finalTargetData = reviewedData;\n                  bucketOra.start(\n                    `Applying changes to ${bucketPath} (${targetLocale})`,\n                  );\n                }\n\n                const finalDiffSize = _.chain(finalTargetData)\n                  .omitBy((value, key) => {\n                    const targetValue = targetData[key];\n\n                    // For objects (like plural variations), use deep equality\n                    // For primitives (strings, numbers), use strict equality\n                    if (typeof value === \"object\" && value !== null) {\n                      return _.isEqual(value, targetValue);\n                    }\n                    return value === targetValue;\n                  })\n                  .size()\n                  .value();\n\n                // Push to bucket all the time as there might be changes to unlocalizable keys\n                await bucketLoader.push(targetLocale, finalTargetData);\n\n                if (finalDiffSize > 0 || flags.force) {\n                  bucketOra.succeed(\n                    `[${sourceLocale} -> ${targetLocale}] Localization completed`,\n                  );\n                } else {\n                  bucketOra.succeed(\n                    `[${sourceLocale} -> ${targetLocale}] Localization completed (no changes).`,\n                  );\n                }\n              } catch (_error: any) {\n                const error = new LocalizationError(\n                  `[${sourceLocale} -> ${targetLocale}] Localization failed: ${_error.message}`,\n                  {\n                    bucket: bucket.type,\n                    sourceLocale,\n                    targetLocale,\n                    pathPattern: bucketPath.pathPattern,\n                  },\n                );\n                errorDetails.push({\n                  type: \"locale_error\",\n                  bucket: bucket.type,\n                  locale: `${sourceLocale} -> ${targetLocale}`,\n                  pathPattern: bucketPath.pathPattern,\n                  message: _error.message,\n                  stack: _error.stack,\n                });\n                if (flags.strict) {\n                  throw error;\n                } else {\n                  bucketOra.fail(error.message);\n                  hasErrors = true;\n                }\n              }\n            }\n\n            const deltaProcessor = createDeltaProcessor(bucketPath.pathPattern);\n            const checksums = await deltaProcessor.createChecksums(sourceData);\n            if (!flags.locale?.length) {\n              await deltaProcessor.saveChecksums(checksums);\n            }\n          }\n        } catch (_error: any) {\n          const error = new BucketProcessingError(\n            `Failed to process bucket ${bucket.type}: ${_error.message}`,\n            bucket.type,\n          );\n          errorDetails.push({\n            type: \"bucket_error\",\n            bucket: bucket.type,\n            message: _error.message,\n            stack: _error.stack,\n          });\n          if (flags.strict) {\n            throw error;\n          } else {\n            ora.fail(error.message);\n            hasErrors = true;\n          }\n        }\n      }\n      console.log();\n      if (!hasErrors) {\n        ora.succeed(\"Localization completed.\");\n        await trackEvent(userIdentity, \"cmd.i18n.success\", {\n          i18nConfig: {\n            sourceLocale: i18nConfig!.locale.source,\n            targetLocales: i18nConfig!.locale.targets,\n            bucketTypes: Object.keys(i18nConfig!.buckets),\n          },\n          flags,\n          bucketCount: buckets.length,\n          localeCount: targetLocales.length,\n          processedSuccessfully: true,\n        });\n        await new Promise((resolve) => setTimeout(resolve, 50));\n      } else {\n        ora.warn(\"Localization completed with errors.\");\n        process.exitCode = 1;\n        await trackEvent(userIdentity, \"cmd.i18n.error\", {\n          flags,\n          ...aggregateErrorAnalytics(\n            errorDetails,\n            buckets,\n            targetLocales,\n            i18nConfig!,\n          ),\n        });\n        await new Promise((resolve) => setTimeout(resolve, 50));\n      }\n    } catch (error: any) {\n      ora.fail(error.message);\n\n      // Use robust error type detection\n      const errorType = getCLIErrorType(error);\n\n      // Extract additional context from typed errors\n      let errorContext: any = {};\n      if (isLocalizationError(error)) {\n        errorContext = {\n          bucket: error.bucket,\n          sourceLocale: error.sourceLocale,\n          targetLocale: error.targetLocale,\n          pathPattern: error.pathPattern,\n        };\n      } else if (isBucketProcessingError(error)) {\n        errorContext = {\n          bucket: error.bucket,\n        };\n      }\n\n      await trackEvent(userIdentity, \"cmd.i18n.error\", {\n        flags,\n        errorType,\n        errorName: error.name || \"Error\",\n        errorMessage: error.message,\n        errorStack: error.stack,\n        errorContext,\n        fatal: true,\n        errorCount: errorDetails.length + 1,\n        previousErrors: createPreviousErrorContext(errorDetails),\n      });\n      await new Promise((resolve) => setTimeout(resolve, 50));\n    }\n  });\n\nfunction parseFlags(options: any) {\n  return Z.object({\n    apiKey: Z.string().optional(),\n    locale: Z.array(localeCodeSchema).optional(),\n    bucket: Z.array(bucketTypeSchema).optional(),\n    force: Z.boolean().optional(),\n    frozen: Z.boolean().optional(),\n    verbose: Z.boolean().optional(),\n    strict: Z.boolean().optional(),\n    key: Z.string().optional(),\n    file: Z.array(Z.string()).optional(),\n    interactive: Z.boolean().prefault(false),\n    debug: Z.boolean().prefault(false),\n  }).parse(options);\n}\n\n// Export validateAuth for use in other commands\nexport async function validateAuth(settings: ReturnType<typeof getSettings>) {\n  if (!settings.auth.apiKey) {\n    throw new AuthenticationError({\n      message:\n        \"Not authenticated. Please run `lingo.dev login` to authenticate.\",\n      docUrl: \"authError\",\n    });\n  }\n\n  const authenticator = createAuthenticator({\n    apiKey: settings.auth.apiKey,\n    apiUrl: settings.auth.apiUrl,\n  });\n  const user = await authenticator.whoami();\n  if (!user) {\n    throw new AuthenticationError({\n      message: \"Invalid API key. Please run `lingo.dev login` to authenticate.\",\n      docUrl: \"authError\",\n    });\n  }\n\n  return user;\n}\n\nfunction validateParams(\n  i18nConfig: I18nConfig | null,\n  flags: ReturnType<typeof parseFlags>,\n) {\n  if (!i18nConfig) {\n    throw new ConfigError({\n      message:\n        \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n      docUrl: \"i18nNotFound\",\n    });\n  } else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {\n    throw new ConfigError({\n      message:\n        \"No buckets found in i18n.json. Please add at least one bucket containing i18n content.\",\n      docUrl: \"bucketNotFound\",\n    });\n  } else if (\n    flags.locale?.some((locale) => !i18nConfig.locale.targets.includes(locale))\n  ) {\n    throw new ValidationError({\n      message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,\n      docUrl: \"localeTargetNotFound\",\n    });\n  } else if (\n    flags.bucket?.some(\n      (bucket) =>\n        !i18nConfig.buckets[bucket as keyof typeof i18nConfig.buckets],\n    )\n  ) {\n    throw new ValidationError({\n      message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,\n      docUrl: \"bucketNotFound\",\n    });\n  }\n}\n\nasync function reviewChanges(args: {\n  pathPattern: string;\n  targetLocale: string;\n  currentData: Record<string, any>;\n  proposedData: Record<string, any>;\n  sourceData: Record<string, any>;\n  force: boolean;\n}): Promise<Record<string, any>> {\n  const currentStr = JSON.stringify(args.currentData, null, 2);\n  const proposedStr = JSON.stringify(args.proposedData, null, 2);\n\n  // Early return if no changes\n  if (currentStr === proposedStr && !args.force) {\n    console.log(\n      `\\n${chalk.blue(args.pathPattern)} (${chalk.yellow(\n        args.targetLocale,\n      )}): ${chalk.gray(\"No changes to review\")}`,\n    );\n    return args.proposedData;\n  }\n\n  const patch = createTwoFilesPatch(\n    `${args.pathPattern} (current)`,\n    `${args.pathPattern} (proposed)`,\n    currentStr,\n    proposedStr,\n    undefined,\n    undefined,\n    { context: 3 },\n  );\n\n  // Color the diff output\n  const coloredDiff = patch\n    .split(\"\\n\")\n    .map((line) => {\n      if (line.startsWith(\"+\")) return chalk.green(line);\n      if (line.startsWith(\"-\")) return chalk.red(line);\n      if (line.startsWith(\"@\")) return chalk.cyan(line);\n      return line;\n    })\n    .join(\"\\n\");\n\n  console.log(\n    `\\nReviewing changes for ${chalk.blue(args.pathPattern)} (${chalk.yellow(\n      args.targetLocale,\n    )}):`,\n  );\n  console.log(coloredDiff);\n\n  const { action } = await inquirer.prompt([\n    {\n      type: \"list\",\n      name: \"action\",\n      message: \"Choose action:\",\n      choices: [\n        { name: \"Approve changes\", value: \"approve\" },\n        { name: \"Skip changes\", value: \"skip\" },\n        { name: \"Edit individually\", value: \"edit\" },\n      ],\n      default: \"approve\",\n    },\n  ]);\n\n  if (action === \"approve\") {\n    return args.proposedData;\n  }\n\n  if (action === \"skip\") {\n    return args.currentData;\n  }\n\n  // If edit was chosen, prompt for each changed value\n  const customData = { ...args.currentData };\n  const changes = _.reduce(\n    args.proposedData,\n    (result: string[], value: string, key: string) => {\n      if (args.currentData[key] !== value) {\n        result.push(key);\n      }\n      return result;\n    },\n    [],\n  );\n\n  for (const key of changes) {\n    console.log(`\\nEditing value for: ${chalk.cyan(key)}`);\n    console.log(chalk.gray(\"Source text:\"), chalk.blue(args.sourceData[key]));\n    console.log(\n      chalk.gray(\"Current value:\"),\n      chalk.red(args.currentData[key] || \"(empty)\"),\n    );\n    console.log(\n      chalk.gray(\"Suggested value:\"),\n      chalk.green(args.proposedData[key]),\n    );\n    console.log(\n      chalk.gray(\n        \"\\nYour editor will open. Edit the text and save to continue.\",\n      ),\n    );\n    console.log(chalk.gray(\"------------\"));\n\n    try {\n      // Prepare the editor content with a header comment and the suggested value\n      const editorContent = [\n        \"# Edit the translation below.\",\n        \"# Lines starting with # will be ignored.\",\n        \"# Save and exit the editor to continue.\",\n        \"#\",\n        `# Source text (${chalk.blue(\"English\")}):`,\n        `# ${args.sourceData[key]}`,\n        \"#\",\n        `# Current value (${chalk.red(args.targetLocale)}):`,\n        `# ${args.currentData[key] || \"(empty)\"}`,\n        \"#\",\n        args.proposedData[key],\n      ].join(\"\\n\");\n\n      const result = externalEditor.edit(editorContent);\n\n      // Clean up the result by removing comments and trimming\n      const customValue = result\n        .split(\"\\n\")\n        .filter((line) => !line.startsWith(\"#\"))\n        .join(\"\\n\")\n        .trim();\n\n      if (customValue) {\n        customData[key] = customValue;\n      } else {\n        console.log(\n          chalk.yellow(\"Empty value provided, keeping the current value.\"),\n        );\n        customData[key] = args.currentData[key] || args.proposedData[key];\n      }\n    } catch (error) {\n      console.log(\n        chalk.red(\"Error while editing, keeping the suggested value.\"),\n      );\n      customData[key] = args.proposedData[key];\n    }\n  }\n\n  return customData;\n}\n","import { I18nConfig } from \"@lingo.dev/_spec\";\nimport chalk from \"chalk\";\nimport dedent from \"dedent\";\nimport { LocalizerFn } from \"./_base\";\nimport { createLingoLocalizer } from \"./lingo\";\nimport { createBasicTranslator } from \"./basic\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport { colors } from \"../constants\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenRouter } from \"@openrouter/ai-sdk-provider\";\nimport { createMistral } from \"@ai-sdk/mistral\";\nimport { createOllama } from \"ollama-ai-provider-v2\";\n\nexport default function createProcessor(\n  provider: I18nConfig[\"provider\"],\n  params: { apiKey?: string; apiUrl: string; engineId?: string },\n): LocalizerFn {\n  if (!provider) {\n    const result = createLingoLocalizer(params);\n    return result;\n  } else {\n    const model = getPureModelProvider(provider);\n    const settings = provider.settings || {};\n    const result = createBasicTranslator(model, provider.prompt, settings);\n    return result;\n  }\n}\n\nfunction getPureModelProvider(provider: I18nConfig[\"provider\"]) {\n  const createMissingKeyErrorMessage = (\n    providerId: string,\n    envVar?: string,\n  ) => dedent`\n  You're trying to use raw ${chalk.dim(providerId)} API for translation. ${\n    envVar\n      ? `However, ${chalk.dim(envVar)} environment variable is not set.`\n      : \"However, that provider is unavailable.\"\n  }\n\n  To fix this issue:\n  1. ${\n    envVar\n      ? `Set ${chalk.dim(envVar)} in your environment variables`\n      : \"Set the environment variable for your provider (if required)\"\n  }, or\n  2. Remove the ${chalk.italic(\n    \"provider\",\n  )} node from your i18n.json configuration to switch to ${chalk.hex(\n    colors.green,\n  )(\"Lingo.dev\")}\n\n  ${chalk.hex(colors.blue)(\"Docs: https://lingo.dev/go/docs\")}\n`;\n\n  const createUnsupportedProviderErrorMessage = (providerId?: string) =>\n    dedent`\n  You're trying to use unsupported provider: ${chalk.dim(providerId)}.\n\n  To fix this issue:\n  1. Switch to one of the supported providers, or\n  2. Remove the ${chalk.italic(\n    \"provider\",\n  )} node from your i18n.json configuration to switch to ${chalk.hex(\n    colors.green,\n  )(\"Lingo.dev\")}\n\n  ${chalk.hex(colors.blue)(\"Docs: https://lingo.dev/go/docs\")}\n  `;\n\n  switch (provider?.id) {\n    case \"openai\": {\n      if (!process.env.OPENAI_API_KEY) {\n        throw new Error(\n          createMissingKeyErrorMessage(\"OpenAI\", \"OPENAI_API_KEY\"),\n        );\n      }\n      return createOpenAI({\n        apiKey: process.env.OPENAI_API_KEY,\n        baseURL: provider.baseUrl,\n      })(provider.model);\n    }\n    case \"anthropic\": {\n      if (!process.env.ANTHROPIC_API_KEY) {\n        throw new Error(\n          createMissingKeyErrorMessage(\"Anthropic\", \"ANTHROPIC_API_KEY\"),\n        );\n      }\n      return createAnthropic({\n        apiKey: process.env.ANTHROPIC_API_KEY,\n      })(provider.model);\n    }\n    case \"google\": {\n      if (!process.env.GOOGLE_API_KEY) {\n        throw new Error(\n          createMissingKeyErrorMessage(\"Google\", \"GOOGLE_API_KEY\"),\n        );\n      }\n      return createGoogleGenerativeAI({\n        apiKey: process.env.GOOGLE_API_KEY,\n      })(provider.model);\n    }\n    case \"openrouter\": {\n      if (!process.env.OPENROUTER_API_KEY) {\n        throw new Error(\n          createMissingKeyErrorMessage(\"OpenRouter\", \"OPENROUTER_API_KEY\"),\n        );\n      }\n      return createOpenRouter({\n        apiKey: process.env.OPENROUTER_API_KEY,\n        baseURL: provider.baseUrl,\n      })(provider.model);\n    }\n    case \"ollama\": {\n      // No API key check needed for Ollama\n      return createOllama()(provider.model);\n    }\n    case \"mistral\": {\n      if (!process.env.MISTRAL_API_KEY) {\n        throw new Error(\n          createMissingKeyErrorMessage(\"Mistral\", \"MISTRAL_API_KEY\"),\n        );\n      }\n      return createMistral({\n        apiKey: process.env.MISTRAL_API_KEY,\n        baseURL: provider.baseUrl,\n      })(provider.model);\n    }\n    default: {\n      throw new Error(createUnsupportedProviderErrorMessage(provider?.id));\n    }\n  }\n}\n","import { LingoDotDevEngine } from \"@lingo.dev/_sdk\";\nimport { LocalizerInput, LocalizerProgressFn } from \"./_base\";\n\nexport function createLingoLocalizer(params: {\n  apiKey?: string;\n  apiUrl: string;\n  engineId?: string;\n}) {\n  return async (input: LocalizerInput, onProgress: LocalizerProgressFn) => {\n    if (!Object.keys(input.processableData).length) {\n      return input.processableData;\n    }\n\n    const lingo = new LingoDotDevEngine({\n      apiKey: params.apiKey,\n      apiUrl: params.apiUrl,\n      ...(params.engineId && { engineId: params.engineId }),\n    });\n\n    const result = await lingo.localizeObject(\n      input.processableData,\n      {\n        sourceLocale: input.sourceLocale,\n        targetLocale: input.targetLocale,\n        reference: {\n          [input.sourceLocale]: input.sourceData,\n          [input.targetLocale]: input.targetData,\n        },\n      },\n      onProgress,\n    );\n\n    return result;\n  };\n}\n","import { generateText, LanguageModel } from \"ai\";\nimport { LocalizerInput, LocalizerProgressFn } from \"./_base\";\nimport _ from \"lodash\";\n\ntype ModelSettings = {\n  temperature?: number;\n};\n\nexport function createBasicTranslator(\n  model: LanguageModel,\n  systemPrompt: string,\n  settings: ModelSettings = {},\n) {\n  return async (input: LocalizerInput, onProgress: LocalizerProgressFn) => {\n    const chunks = extractPayloadChunks(input.processableData);\n\n    const subResults: Record<string, any>[] = [];\n    for (let i = 0; i < chunks.length; i++) {\n      const chunk = chunks[i];\n      const result = await doJob({\n        ...input,\n        processableData: chunk,\n      });\n      subResults.push(result);\n      onProgress((i / chunks.length) * 100, chunk, result);\n    }\n\n    const result = _.merge({}, ...subResults);\n\n    return result;\n  };\n\n  async function doJob(input: LocalizerInput) {\n    if (!Object.keys(input.processableData).length) {\n      return input.processableData;\n    }\n\n    const response = await generateText({\n      model,\n      ...settings,\n      messages: [\n        {\n          role: \"system\",\n          content: JSON.stringify({\n            role: \"system\",\n            content: systemPrompt\n              .replaceAll(\"{source}\", input.sourceLocale)\n              .replaceAll(\"{target}\", input.targetLocale),\n          }),\n        },\n        {\n          role: \"user\",\n          content: JSON.stringify({\n            sourceLocale: \"en\",\n            targetLocale: \"es\",\n            data: {\n              message: \"Hello, world!\",\n            },\n          }),\n        },\n        {\n          role: \"assistant\",\n          content: JSON.stringify({\n            sourceLocale: \"en\",\n            targetLocale: \"es\",\n            data: {\n              message: \"Hola, mundo!\",\n            },\n          }),\n        },\n        {\n          role: \"user\",\n          content: JSON.stringify({\n            sourceLocale: input.sourceLocale,\n            targetLocale: input.targetLocale,\n            data: input.processableData,\n          }),\n        },\n      ],\n    });\n\n    const result = JSON.parse(response.text);\n\n    return result?.data || {};\n  }\n}\n\n/**\n * Extract payload chunks based on the ideal chunk size\n * @param payload - The payload to be chunked\n * @returns An array of payload chunks\n */\nfunction extractPayloadChunks(\n  payload: Record<string, string>,\n): Record<string, string>[] {\n  const idealBatchItemSize = 250;\n  const batchSize = 25;\n  const result: Record<string, string>[] = [];\n  let currentChunk: Record<string, string> = {};\n  let currentChunkItemCount = 0;\n\n  const payloadEntries = Object.entries(payload);\n  for (let i = 0; i < payloadEntries.length; i++) {\n    const [key, value] = payloadEntries[i];\n    currentChunk[key] = value;\n    currentChunkItemCount++;\n\n    const currentChunkSize = countWordsInRecord(currentChunk);\n    if (\n      currentChunkSize > idealBatchItemSize ||\n      currentChunkItemCount >= batchSize ||\n      i === payloadEntries.length - 1\n    ) {\n      result.push(currentChunk);\n      currentChunk = {};\n      currentChunkItemCount = 0;\n    }\n  }\n\n  return result;\n}\n\n/**\n * Count words in a record or array\n * @param payload - The payload to count words in\n * @returns The total number of words\n */\nfunction countWordsInRecord(\n  payload: any | Record<string, any> | Array<any>,\n): number {\n  if (Array.isArray(payload)) {\n    return payload.reduce((acc, item) => acc + countWordsInRecord(item), 0);\n  } else if (typeof payload === \"object\" && payload !== null) {\n    return Object.values(payload).reduce(\n      (acc: number, item) => acc + countWordsInRecord(item),\n      0,\n    );\n  } else if (typeof payload === \"string\") {\n    return payload.trim().split(/\\s+/).filter(Boolean).length;\n  } else {\n    return 0;\n  }\n}\n","export function withExponentialBackoff<T, Args extends any[]>(\n  fn: (...args: Args) => Promise<T>,\n  maxAttempts: number = 3,\n  baseDelay: number = 1000,\n): (...args: Args) => Promise<T> {\n  return async (...args: Args): Promise<T> => {\n    for (let attempt = 0; attempt < maxAttempts; attempt++) {\n      try {\n        return await fn(...args);\n      } catch (error) {\n        if (attempt === maxAttempts - 1) throw error;\n\n        const delay = baseDelay * Math.pow(2, attempt);\n        await new Promise((resolve) => setTimeout(resolve, delay));\n      }\n    }\n    throw new Error(\"Unreachable code\");\n  };\n}\n","import pkg from \"node-machine-id\";\nconst { machineIdSync } = pkg;\nimport https from \"https\";\nimport { getOrgId } from \"./org-id\";\n\nconst POSTHOG_API_KEY = \"phc_eR0iSoQufBxNY36k0f0T15UvHJdTfHlh8rJcxsfhfXk\";\nconst POSTHOG_HOST = \"eu.i.posthog.com\";\nconst POSTHOG_PATH = \"/i/v0/e/\";\nconst REQUEST_TIMEOUT_MS = 3000;\nconst TRACKING_VERSION = \"2.0\";\n\nexport type UserIdentity = {\n  email: string;\n  id: string;\n} | null;\n\nfunction determineDistinctId(user: UserIdentity): {\n  distinct_id: string;\n  distinct_id_source: string;\n  org_id: string | null;\n} {\n  const orgId = getOrgId();\n\n  if (user) {\n    return {\n      distinct_id: user.id,\n      distinct_id_source: \"database_id\",\n      org_id: orgId,\n    };\n  }\n\n  if (orgId) {\n    return {\n      distinct_id: orgId,\n      distinct_id_source: \"git_org\",\n      org_id: orgId,\n    };\n  }\n\n  const deviceId = `device-${machineIdSync()}`;\n  if (process.env.DEBUG === \"true\") {\n    console.warn(\n      \"[Tracking] Using device ID fallback. Consider using git repository for consistent tracking.\",\n    );\n  }\n  return {\n    distinct_id: deviceId,\n    distinct_id_source: \"device\",\n    org_id: null,\n  };\n}\n\nexport default function trackEvent(\n  user: UserIdentity,\n  event: string,\n  properties?: Record<string, any>,\n): void {\n  if (process.env.DO_NOT_TRACK === \"1\") {\n    return;\n  }\n\n  setImmediate(() => {\n    try {\n      const identityInfo = determineDistinctId(user);\n\n      if (process.env.DEBUG === \"true\") {\n        console.log(\n          `[Tracking] Event: ${event}, ID: ${identityInfo.distinct_id}, Source: ${identityInfo.distinct_id_source}`,\n        );\n      }\n\n      const eventData = {\n        api_key: POSTHOG_API_KEY,\n        event,\n        distinct_id: identityInfo.distinct_id,\n        properties: {\n          ...properties,\n          $set: {\n            ...(properties?.$set || {}),\n            ...(user ? { email: user.email } : {}),\n          },\n          $lib: \"lingo.dev-cli\",\n          $lib_version: process.env.npm_package_version || \"unknown\",\n          tracking_version: TRACKING_VERSION,\n          distinct_id_source: identityInfo.distinct_id_source,\n          org_id: identityInfo.org_id,\n          node_version: process.version,\n          is_ci: !!process.env.CI,\n          debug_enabled: process.env.DEBUG === \"true\",\n        },\n        timestamp: new Date().toISOString(),\n      };\n\n      const payload = JSON.stringify(eventData);\n\n      const options: https.RequestOptions = {\n        hostname: POSTHOG_HOST,\n        path: POSTHOG_PATH,\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          \"Content-Length\": Buffer.byteLength(payload).toString(),\n        },\n        timeout: REQUEST_TIMEOUT_MS,\n      };\n\n      const req = https.request(options);\n\n      req.on(\"timeout\", () => {\n        req.destroy();\n      });\n\n      req.on(\"error\", (error) => {\n        if (process.env.DEBUG === \"true\") {\n          console.error(\"[Tracking] Error ignored:\", error.message);\n        }\n      });\n\n      req.write(payload);\n      req.end();\n\n      // TODO: remove after 2026-04-30 — temporary alias to merge old email-based distinct_ids with database user ID\n      if (user) {\n        const aliasData = JSON.stringify({\n          api_key: POSTHOG_API_KEY,\n          event: \"$create_alias\",\n          distinct_id: user.id,\n          properties: {\n            alias: user.email,\n          },\n          timestamp: new Date().toISOString(),\n        });\n\n        const aliasReq = https.request({\n          ...options,\n          headers: {\n            \"Content-Type\": \"application/json\",\n            \"Content-Length\": Buffer.byteLength(aliasData).toString(),\n          },\n        });\n        aliasReq.on(\"timeout\", () => aliasReq.destroy());\n        aliasReq.on(\"error\", () => {});\n        aliasReq.write(aliasData);\n        aliasReq.end();\n        setTimeout(() => {\n          if (!aliasReq.destroyed) aliasReq.destroy();\n        }, REQUEST_TIMEOUT_MS);\n      }\n\n      setTimeout(() => {\n        if (!req.destroyed) {\n          req.destroy();\n        }\n      }, REQUEST_TIMEOUT_MS);\n    } catch (error) {\n      if (process.env.DEBUG === \"true\") {\n        console.error(\"[Tracking] Failed to send event:\", error);\n      }\n    }\n  });\n}\n","import { execSync } from \"child_process\";\n\nlet cachedGitOrgId: string | null | undefined = undefined;\n\nfunction extractOrg(fullPath: string): string | null {\n  const parts = fullPath.split(\"/\");\n  if (parts.length < 1) {\n    return null;\n  }\n  return parts[0];\n}\n\nexport function clearOrgIdCache(): void {\n  cachedGitOrgId = undefined;\n}\n\nexport function getOrgId(): string | null {\n  const ciOrgId = getCIOrgId();\n  if (ciOrgId) return ciOrgId;\n\n  const gitOrgId = getGitOrgId();\n  if (gitOrgId) return gitOrgId;\n\n  return null;\n}\n\nfunction getCIOrgId(): string | null {\n  if (process.env.GITHUB_REPOSITORY) {\n    const org = extractOrg(process.env.GITHUB_REPOSITORY);\n    if (org) return `github:${org}`;\n  }\n\n  if (process.env.CI_PROJECT_PATH) {\n    const org = extractOrg(process.env.CI_PROJECT_PATH);\n    if (org) return `gitlab:${org}`;\n  }\n\n  if (process.env.BITBUCKET_REPO_FULL_NAME) {\n    const org = extractOrg(process.env.BITBUCKET_REPO_FULL_NAME);\n    if (org) return `bitbucket:${org}`;\n  }\n\n  return null;\n}\n\nfunction getGitOrgId(): string | null {\n  if (cachedGitOrgId !== undefined) {\n    return cachedGitOrgId;\n  }\n\n  try {\n    const remoteUrl = execSync(\"git config --get remote.origin.url\", {\n      encoding: \"utf8\",\n      stdio: [\"pipe\", \"pipe\", \"ignore\"],\n    }).trim();\n\n    if (!remoteUrl) {\n      cachedGitOrgId = null;\n      return null;\n    }\n\n    cachedGitOrgId = parseGitUrl(remoteUrl);\n    return cachedGitOrgId;\n  } catch {\n    cachedGitOrgId = null;\n    return null;\n  }\n}\n\nfunction parseGitUrl(url: string): string | null {\n  const cleanUrl = url.replace(/\\.git$/, \"\");\n\n  let platform: string | null = null;\n  if (cleanUrl.includes(\"github.com\")) {\n    platform = \"github\";\n  } else if (cleanUrl.includes(\"gitlab.com\")) {\n    platform = \"gitlab\";\n  } else if (cleanUrl.includes(\"bitbucket.org\")) {\n    platform = \"bitbucket\";\n  }\n\n  const sshMatch = cleanUrl.match(/[@:]([^:/@]+\\/[^:/@]+)$/);\n  const httpsMatch = cleanUrl.match(/\\/([^/]+\\/[^/]+)$/);\n\n  const repoPath = sshMatch?.[1] || httpsMatch?.[1];\n\n  if (!repoPath) return null;\n\n  const org = extractOrg(repoPath);\n  if (!org) return null;\n\n  if (platform) {\n    return `${platform}:${org}`;\n  }\n\n  return `git:${org}`;\n}\n","import _ from \"lodash\";\nimport z from \"zod\";\nimport { md5 } from \"./md5\";\nimport { tryReadFile, writeFile, checkIfFileExists } from \"../utils/fs\";\nimport * as path from \"path\";\nimport YAML from \"yaml\";\nimport { deduplicateLockfileYaml } from \"./lockfile\";\n\nconst LockSchema = z.object({\n  version: z.literal(1).prefault(1),\n  checksums: z\n    .record(\n      z.string(), // localizable files' keys\n      // checksums hashmap\n      z\n        .record(\n          // key\n          z.string(),\n          // checksum of the key's value in the source locale\n          z.string(),\n        )\n        .prefault({}),\n    )\n    .prefault({}),\n});\nexport type LockData = z.infer<typeof LockSchema>;\n\nexport type Delta = {\n  added: string[];\n  removed: string[];\n  updated: string[];\n  renamed: [string, string][];\n  hasChanges: boolean;\n};\n\nexport function createDeltaProcessor(fileKey: string) {\n  const lockfilePath = path.join(process.cwd(), \"i18n.lock\");\n  return {\n    async checkIfLockExists() {\n      return checkIfFileExists(lockfilePath);\n    },\n    async calculateDelta(params: {\n      sourceData: Record<string, any>;\n      targetData: Record<string, any>;\n      checksums: Record<string, string>;\n    }): Promise<Delta> {\n      let added = _.difference(\n        Object.keys(params.sourceData),\n        Object.keys(params.targetData),\n      );\n      let removed = _.difference(\n        Object.keys(params.targetData),\n        Object.keys(params.sourceData),\n      );\n      const updated = Object.keys(params.sourceData).filter(\n        (key) =>\n          md5(params.sourceData[key]) !== params.checksums[key] &&\n          params.checksums[key],\n      );\n\n      const renamed: [string, string][] = [];\n      for (const addedKey of added) {\n        const addedHash = md5(params.sourceData[addedKey]);\n        for (const removedKey of removed) {\n          if (params.checksums[removedKey] === addedHash) {\n            renamed.push([removedKey, addedKey]);\n            break;\n          }\n        }\n      }\n      added = added.filter(\n        (key) => !renamed.some(([oldKey, newKey]) => newKey === key),\n      );\n      removed = removed.filter(\n        (key) => !renamed.some(([oldKey, newKey]) => oldKey === key),\n      );\n\n      const hasChanges = [\n        added.length > 0,\n        removed.length > 0,\n        updated.length > 0,\n        renamed.length > 0,\n      ].some((v) => v);\n\n      return {\n        added,\n        removed,\n        updated,\n        renamed,\n        hasChanges,\n      };\n    },\n    async loadLock() {\n      const lockfileContent = tryReadFile(lockfilePath, null);\n\n      if (!lockfileContent) {\n        return {\n          version: 1,\n          checksums: {},\n        } as const;\n      }\n\n      // Deduplicate using the universal function\n      const { deduplicatedContent, duplicatesRemoved } = deduplicateLockfileYaml(lockfileContent);\n\n      // Write back to disk if duplicates were found\n      if (duplicatesRemoved > 0) {\n        writeFile(lockfilePath, deduplicatedContent);\n        console.log(\n          `Removed ${duplicatesRemoved} duplicate ${duplicatesRemoved === 1 ? \"entry\" : \"entries\"} from i18n.lock`,\n        );\n      }\n\n      const parsed = LockSchema.parse(YAML.parse(deduplicatedContent));\n      return parsed;\n    },\n    async saveLock(lockData: LockData) {\n      const lockfileYaml = YAML.stringify(lockData);\n      writeFile(lockfilePath, lockfileYaml);\n    },\n    async loadChecksums() {\n      const id = md5(fileKey);\n      const lockfileData = await this.loadLock();\n      const checksums = lockfileData.checksums as Record<string, Record<string, string>>;\n      return checksums[id] || {};\n    },\n    async saveChecksums(checksums: Record<string, string>) {\n      const id = md5(fileKey);\n      const lockfileData = await this.loadLock();\n      const lockChecksums = lockfileData.checksums as Record<string, Record<string, string>>;\n      lockChecksums[id] = checksums;\n      await this.saveLock(lockfileData);\n    },\n    async createChecksums(sourceData: Record<string, any>) {\n      const checksums = _.mapValues(sourceData, (value) => md5(value));\n      return checksums;\n    },\n  };\n}\n","import * as fs from \"fs\";\nimport * as path from \"path\";\n\nexport function tryReadFile(\n  filePath: string,\n  defaultValue: string | null = null,\n): string | null {\n  try {\n    const content = fs.readFileSync(filePath, \"utf-8\");\n    return content;\n  } catch (error) {\n    return defaultValue;\n  }\n}\n\nexport function writeFile(filePath: string, content: string) {\n  // create dirs\n  const dir = path.dirname(filePath);\n  if (!fs.existsSync(dir)) {\n    fs.mkdirSync(dir, { recursive: true });\n  }\n  fs.writeFileSync(filePath, content);\n}\n\nexport function checkIfFileExists(filePath: string) {\n  return fs.existsSync(filePath);\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport Z from \"zod\";\nimport YAML from \"yaml\";\nimport { MD5 } from \"object-hash\";\nimport _ from \"lodash\";\n\nexport function createLockfileHelper() {\n  return {\n    isLockfileExists: () => {\n      const lockfilePath = _getLockfilePath();\n      return fs.existsSync(lockfilePath);\n    },\n    registerSourceData: (\n      pathPattern: string,\n      sourceData: Record<string, any>,\n    ) => {\n      const lockfile = _loadLockfile();\n\n      const sectionKey = MD5(pathPattern);\n      const sectionChecksums = _.mapValues(sourceData, (value) => MD5(value));\n\n      lockfile.checksums[sectionKey] = sectionChecksums;\n\n      _saveLockfile(lockfile);\n    },\n    registerPartialSourceData: (\n      pathPattern: string,\n      partialSourceData: Record<string, any>,\n    ) => {\n      const lockfile = _loadLockfile();\n\n      const sectionKey = MD5(pathPattern);\n      const sectionChecksums = _.mapValues(partialSourceData, (value) =>\n        MD5(value),\n      );\n\n      lockfile.checksums[sectionKey] = _.merge(\n        {},\n        lockfile.checksums[sectionKey] ?? {},\n        sectionChecksums,\n      );\n\n      _saveLockfile(lockfile);\n    },\n    extractUpdatedData: (\n      pathPattern: string,\n      sourceData: Record<string, any>,\n    ) => {\n      const lockfile = _loadLockfile();\n\n      const sectionKey = MD5(pathPattern);\n      const currentChecksums = _.mapValues(sourceData, (value) => MD5(value));\n\n      const savedChecksums = lockfile.checksums[sectionKey] || {};\n      const updatedData = _.pickBy(\n        sourceData,\n        (value, key) => savedChecksums[key] !== currentChecksums[key],\n      );\n\n      return updatedData;\n    },\n  };\n\n  function _loadLockfile() {\n    const lockfilePath = _getLockfilePath();\n    if (!fs.existsSync(lockfilePath)) {\n      return LockfileSchema.parse({});\n    }\n    const content = fs.readFileSync(lockfilePath, \"utf-8\");\n\n    const { deduplicatedContent, duplicatesRemoved } = deduplicateLockfileYaml(content);\n\n    if (duplicatesRemoved > 0) {\n      fs.writeFileSync(lockfilePath, deduplicatedContent);\n      console.log(\n        `Removed ${duplicatesRemoved} duplicate ${duplicatesRemoved === 1 ? \"entry\" : \"entries\"} from i18n.lock`,\n      );\n    }\n\n    const parsed = LockfileSchema.parse(YAML.parse(deduplicatedContent));\n    return parsed;\n  }\n\n  function _saveLockfile(lockfile: Z.infer<typeof LockfileSchema>) {\n    const lockfilePath = _getLockfilePath();\n    const content = YAML.stringify(lockfile);\n    fs.writeFileSync(lockfilePath, content);\n  }\n\n  function _getLockfilePath() {\n    return path.join(process.cwd(), \"i18n.lock\");\n  }\n}\n\nconst LockfileSchema = Z.object({\n  version: Z.literal(1).prefault(1),\n  checksums: Z.record(\n    Z.string(), // localizable files' keys\n    Z.record(\n      // checksums hashmap\n      Z.string(), // key\n      Z.string(), // checksum of the key's value in the source locale\n    ).prefault({}),\n  ).prefault({}),\n});\n\nexport function deduplicateLockfileYaml(yamlContent: string): {\n  deduplicatedContent: string;\n  duplicatesRemoved: number;\n} {\n  // Parse using parseDocument to access the raw YAML structure\n  const doc = YAML.parseDocument(yamlContent);\n  let duplicatesRemoved = 0;\n\n  // Remove duplicate keys from the YAML document structure\n  if (doc.contents && YAML.isMap(doc.contents)) {\n    const checksums = doc.contents.get('checksums');\n    if (checksums && YAML.isMap(checksums)) {\n      // Iterate through each path pattern hash\n      for (const pathItem of checksums.items) {\n        if (YAML.isMap(pathItem.value)) {\n          // Track key positions - last occurrence wins\n          const keyPositions = new Map<string, number[]>();\n\n          // First pass: collect all positions for each key\n          for (let i = 0; i < pathItem.value.items.length; i++) {\n            const translationItem = pathItem.value.items[i];\n            const key = String(YAML.isScalar(translationItem.key) ? translationItem.key.value : translationItem.key);\n\n            if (!keyPositions.has(key)) {\n              keyPositions.set(key, []);\n            }\n            keyPositions.get(key)!.push(i);\n          }\n\n          // Second pass: identify duplicates to remove (all but the last occurrence)\n          const indicesToRemove: number[] = [];\n          for (const positions of keyPositions.values()) {\n            if (positions.length > 1) {\n              // Keep the last occurrence, remove all earlier ones\n              indicesToRemove.push(...positions.slice(0, -1));\n              duplicatesRemoved += positions.length - 1;\n            }\n          }\n\n          // Remove items in reverse order to maintain correct indices\n          indicesToRemove.sort((a, b) => b - a);\n          for (const index of indicesToRemove) {\n            pathItem.value.items.splice(index, 1);\n          }\n        }\n      }\n    }\n  }\n\n  // Convert to JavaScript object (which is now clean)\n  const cleanedData = doc.toJSON();\n  // Create a new document from the cleaned data\n  const cleanDoc = new YAML.Document(cleanedData);\n  const deduplicatedContent = cleanDoc.toString();\n\n  return {\n    deduplicatedContent,\n    duplicatesRemoved,\n  };\n}\n","import { Command } from \"interactive-commander\";\nimport Z from \"zod\";\nimport Ora from \"ora\";\nimport { createLockfileHelper } from \"../utils/lockfile\";\nimport { bucketTypeSchema, resolveOverriddenLocale } from \"@lingo.dev/_spec\";\nimport { getConfig } from \"../utils/config\";\nimport createBucketLoader from \"../loaders\";\nimport { getBuckets } from \"../utils/buckets\";\n\nexport default new Command()\n  .command(\"lockfile\")\n  .description(\n    \"Generate or refresh i18n.lock based on the current source locale content\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .option(\n    \"-f, --force\",\n    \"Overwrite existing lockfile to reset translation tracking\",\n  )\n  .action(async (options) => {\n    const flags = flagsSchema.parse(options);\n    const ora = Ora();\n\n    const lockfileHelper = createLockfileHelper();\n    if (lockfileHelper.isLockfileExists() && !flags.force) {\n      ora.warn(\n        `Lockfile won't be created because it already exists. Use --force to overwrite.`,\n      );\n    } else {\n      const i18nConfig = getConfig();\n      const buckets = getBuckets(i18nConfig!);\n\n      for (const bucket of buckets) {\n        for (const bucketConfig of bucket.paths) {\n          const sourceLocale = resolveOverriddenLocale(\n            i18nConfig!.locale.source,\n            bucketConfig.delimiter,\n          );\n          const bucketLoader = createBucketLoader(\n            bucket.type,\n            bucketConfig.pathPattern,\n            {\n              defaultLocale: sourceLocale,\n              formatter: i18nConfig!.formatter,\n              keyColumn: bucket.keyColumn,\n            },\n            bucket.lockedKeys,\n            bucket.lockedPatterns,\n            bucket.ignoredKeys,\n            bucket.preservedKeys,\n            bucket.localizableKeys,\n          );\n          bucketLoader.setDefaultLocale(sourceLocale);\n\n          const sourceData = await bucketLoader.pull(sourceLocale);\n          lockfileHelper.registerSourceData(\n            bucketConfig.pathPattern,\n            sourceData,\n          );\n        }\n      }\n      ora.succeed(\"Lockfile created\");\n    }\n  });\n\nconst flagsSchema = Z.object({\n  force: Z.boolean().prefault(false),\n});\n","import { I18nConfig, resolveOverriddenLocale } from \"@lingo.dev/_spec\";\nimport { Command } from \"interactive-commander\";\nimport _ from \"lodash\";\nimport { getConfig } from \"../utils/config\";\nimport { CLIError } from \"../utils/errors\";\nimport Ora from \"ora\";\nimport createBucketLoader from \"../loaders\";\nimport { getBuckets } from \"../utils/buckets\";\n\nexport default new Command()\n  .command(\"cleanup\")\n  .description(\n    \"Remove translation keys from target locales that no longer exist in the source locale\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .option(\n    \"--locale <locale>\",\n    \"Limit cleanup to a specific target locale from i18n.json. Defaults to all configured target locales\",\n  )\n  .option(\n    \"--bucket <bucket>\",\n    \"Limit cleanup to a specific bucket type defined under `buckets` in i18n.json\",\n  )\n  .option(\n    \"--dry-run\",\n    \"Preview which keys would be deleted without making any changes\",\n  )\n  .option(\n    \"--verbose\",\n    \"Print detailed output showing the specific keys to be removed for each locale\",\n  )\n  .action(async function (options) {\n    const ora = Ora();\n    const results: any = [];\n\n    try {\n      ora.start(\"Loading configuration...\");\n      const i18nConfig = getConfig();\n      validateConfig(i18nConfig);\n      ora.succeed(\"Configuration loaded\");\n\n      let buckets = getBuckets(i18nConfig!);\n      if (options.bucket) {\n        buckets = buckets.filter(\n          (bucket: any) => bucket.type === options.bucket,\n        );\n      }\n\n      const targetLocales = options.locale\n        ? [options.locale]\n        : i18nConfig!.locale.targets;\n\n      // Process each bucket\n      for (const bucket of buckets) {\n        console.log();\n        ora.info(`Processing bucket: ${bucket.type}`);\n\n        for (const bucketConfig of bucket.paths) {\n          const sourceLocale = resolveOverriddenLocale(\n            i18nConfig!.locale.source,\n            bucketConfig.delimiter,\n          );\n          const bucketOra = Ora({ indent: 2 }).info(\n            `Processing path: ${bucketConfig.pathPattern}`,\n          );\n          const bucketLoader = createBucketLoader(\n            bucket.type,\n            bucketConfig.pathPattern,\n            {\n              defaultLocale: sourceLocale,\n              formatter: i18nConfig!.formatter,\n              keyColumn: bucket.keyColumn,\n            },\n            bucket.lockedKeys,\n            bucket.lockedPatterns,\n            bucket.ignoredKeys,\n            bucket.preservedKeys,\n            bucket.localizableKeys,\n          );\n          bucketLoader.setDefaultLocale(sourceLocale);\n\n          // Load source data\n          const sourceData = await bucketLoader.pull(sourceLocale);\n          const sourceKeys = Object.keys(sourceData);\n\n          for (const _targetLocale of targetLocales) {\n            const targetLocale = resolveOverriddenLocale(\n              _targetLocale,\n              bucketConfig.delimiter,\n            );\n            try {\n              const targetData = await bucketLoader.pull(targetLocale);\n              const targetKeys = Object.keys(targetData);\n              const keysToRemove = _.difference(targetKeys, sourceKeys);\n\n              if (keysToRemove.length === 0) {\n                bucketOra.succeed(`[${targetLocale}] No keys to remove`);\n                continue;\n              }\n\n              if (options.verbose) {\n                bucketOra.info(\n                  `[${targetLocale}] Keys to remove: ${JSON.stringify(\n                    keysToRemove,\n                    null,\n                    2,\n                  )}`,\n                );\n              }\n\n              if (!options.dryRun) {\n                const cleanedData = _.pick(targetData, sourceKeys);\n                await bucketLoader.push(targetLocale, cleanedData);\n                bucketOra.succeed(\n                  `[${targetLocale}] Removed ${keysToRemove.length} keys`,\n                );\n              } else {\n                bucketOra.succeed(\n                  `[${targetLocale}] Would remove ${keysToRemove.length} keys (dry run)`,\n                );\n              }\n            } catch (error: any) {\n              bucketOra.fail(\n                `[${targetLocale}] Failed to cleanup: ${error.message}`,\n              );\n              results.push({\n                step: `Cleanup ${bucket.type}/${bucketConfig} for ${targetLocale}`,\n                status: \"Failed\",\n                error: error.message,\n              });\n            }\n          }\n        }\n      }\n\n      console.log();\n      ora.succeed(\"Cleanup completed!\");\n    } catch (error: any) {\n      ora.fail(error.message);\n      process.exit(1);\n    } finally {\n      displaySummary(results);\n    }\n  });\n\nfunction validateConfig(i18nConfig: I18nConfig | null) {\n  if (!i18nConfig) {\n    throw new CLIError({\n      message:\n        \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n      docUrl: \"i18nNotFound\",\n    });\n  }\n  if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {\n    throw new CLIError({\n      message:\n        \"No buckets found in i18n.json. Please add at least one bucket containing i18n content.\",\n      docUrl: \"bucketNotFound\",\n    });\n  }\n}\n\nfunction displaySummary(results: any[]) {\n  if (results.length === 0) return;\n\n  console.log(\"\\nProcess Summary:\");\n  results.forEach((result) => {\n    console.log(`${result.step}: ${result.status}`);\n    if (result.error) console.log(`  - Error: ${result.error}`);\n  });\n}\n","import { Command } from \"interactive-commander\";\nimport createOra from \"ora\";\nimport { getSettings } from \"../../utils/settings\";\nimport { createAuthenticator } from \"../../utils/auth\";\nimport { IIntegrationFlow } from \"./flows/_base\";\nimport { PullRequestFlow } from \"./flows/pull-request\";\nimport { InBranchFlow } from \"./flows/in-branch\";\nimport { getPlatformKit } from \"./platforms\";\n\ninterface CIOptions {\n  parallel?: boolean;\n  apiKey?: string;\n  debug?: boolean;\n  pullRequest?: boolean;\n  commitMessage?: string;\n  pullRequestTitle?: string;\n  commitAuthorName?: string;\n  commitAuthorEmail?: string;\n  workingDirectory?: string;\n  processOwnCommits?: boolean;\n  gpgSign?: boolean;\n}\n\nexport default new Command()\n  .command(\"ci\")\n  .description(\"Run localization pipeline in CI/CD environment\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .option(\n    \"--parallel [boolean]\",\n    \"Process translations concurrently for faster execution. Defaults to false\",\n    parseBooleanArg,\n  )\n  .option(\n    \"--api-key <key>\",\n    \"Override API key from settings or environment variables\",\n  )\n  .option(\n    \"--pull-request [boolean]\",\n    \"Create or update translations on a dedicated branch and manage pull requests automatically. When false, commits directly to current branch. Defaults to false\",\n    parseBooleanArg,\n  )\n  .option(\n    \"--commit-message <message>\",\n    \"Commit message for localization changes. Defaults to 'feat: update translations via @lingodotdev'\",\n  )\n  .option(\n    \"--pull-request-title <title>\",\n    \"Title for the pull request when using --pull-request mode. Defaults to 'feat: update translations via @lingodotdev'\",\n  )\n  .option(\n    \"--commit-author-name <name>\",\n    \"Git commit author name. Defaults to 'Lingo.dev'\",\n  )\n  .option(\n    \"--commit-author-email <email>\",\n    \"Git commit author email. Defaults to 'support@lingo.dev'\",\n  )\n  .option(\n    \"--working-directory <dir>\",\n    \"Directory to run localization from (useful for monorepos where localization files are in a subdirectory)\",\n  )\n  .option(\n    \"--process-own-commits [boolean]\",\n    \"Allow processing commits made by this CI user (bypasses infinite loop prevention)\",\n    parseBooleanArg,\n  )\n  .option(\n    \"--gpg-sign [boolean]\",\n    \"Sign commits with GPG. Requires GPG to be configured in the environment\",\n    parseBooleanArg,\n  )\n  .action(async (options: CIOptions) => {\n    const settings = getSettings(options.apiKey);\n\n    if (!settings.auth.apiKey) {\n      console.error(\n        \"No API key provided. Set LINGO_API_KEY environment variable or use --api-key flag.\",\n      );\n      return;\n    }\n\n    const authenticator = createAuthenticator({\n      apiUrl: settings.auth.apiUrl,\n      apiKey: settings.auth.apiKey,\n    });\n\n    const auth = await authenticator.whoami();\n    if (!auth) {\n      console.error(\"Not authenticated\");\n      return;\n    }\n\n    const env = {\n      ...(settings.auth.apiKey && {\n        LINGO_API_KEY: settings.auth.apiKey,\n      }),\n      LINGODOTDEV_PULL_REQUEST: options.pullRequest?.toString() || \"false\",\n      ...(options.commitMessage && {\n        LINGODOTDEV_COMMIT_MESSAGE: options.commitMessage,\n      }),\n      ...(options.pullRequestTitle && {\n        LINGODOTDEV_PULL_REQUEST_TITLE: options.pullRequestTitle,\n      }),\n      ...(options.commitAuthorName && {\n        LINGODOTDEV_COMMIT_AUTHOR_NAME: options.commitAuthorName,\n      }),\n      ...(options.commitAuthorEmail && {\n        LINGODOTDEV_COMMIT_AUTHOR_EMAIL: options.commitAuthorEmail,\n      }),\n      ...(options.workingDirectory && {\n        LINGODOTDEV_WORKING_DIRECTORY: options.workingDirectory,\n      }),\n      ...(options.processOwnCommits && {\n        LINGODOTDEV_PROCESS_OWN_COMMITS: options.processOwnCommits.toString(),\n      }),\n      ...(options.gpgSign && {\n        LINGODOTDEV_GPG_SIGN: options.gpgSign.toString(),\n      }),\n    };\n\n    process.env = { ...process.env, ...env };\n\n    const ora = createOra();\n    const platformKit = getPlatformKit();\n    const { isPullRequestMode } = platformKit.config;\n\n    ora.info(`Pull request mode: ${isPullRequestMode ? \"on\" : \"off\"}`);\n\n    const flow: IIntegrationFlow = isPullRequestMode\n      ? new PullRequestFlow(ora, platformKit)\n      : new InBranchFlow(ora, platformKit);\n\n    const canRun = await flow.preRun?.();\n    if (canRun === false) {\n      return;\n    }\n\n    const hasChanges = await flow.run({\n      parallel: options.parallel,\n    });\n    if (!hasChanges) {\n      return;\n    }\n\n    await flow.postRun?.();\n  });\n\nfunction parseBooleanArg(val: string | boolean | undefined): boolean {\n  if (val === true) return true;\n  if (typeof val === \"string\") {\n    return val.toLowerCase() === \"true\";\n  }\n  return false;\n}\n","import { execSync } from \"child_process\";\nimport { InBranchFlow } from \"./in-branch\";\nimport { IIntegrationFlowOptions } from \"./_base\";\n\nexport class PullRequestFlow extends InBranchFlow {\n  async preRun() {\n    const canContinue = await super.preRun?.();\n    if (!canContinue) {\n      return false;\n    }\n\n    this.ora.start(\"Calculating automated branch name\");\n    this.i18nBranchName = this.calculatePrBranchName();\n    this.ora.succeed(\n      `Automated branch name calculated: ${this.i18nBranchName}`,\n    );\n\n    this.ora.start(\"Checking if branch exists\");\n    const branchExists = await this.checkBranchExistance(this.i18nBranchName);\n    this.ora.succeed(branchExists ? \"Branch exists\" : \"Branch does not exist\");\n\n    if (branchExists) {\n      this.ora.start(`Checking out branch ${this.i18nBranchName}`);\n      this.checkoutI18nBranch(this.i18nBranchName);\n      this.ora.succeed(`Checked out branch ${this.i18nBranchName}`);\n\n      this.ora.start(\n        `Syncing with ${this.platformKit.platformConfig.baseBranchName}`,\n      );\n      this.syncI18nBranch();\n      this.ora.succeed(`Checked out and synced branch ${this.i18nBranchName}`);\n    } else {\n      this.ora.start(`Creating branch ${this.i18nBranchName}`);\n      this.createI18nBranch(this.i18nBranchName);\n      this.ora.succeed(`Created branch ${this.i18nBranchName}`);\n    }\n\n    return true;\n  }\n\n  override async run(options: IIntegrationFlowOptions) {\n    return super.run({\n      force: true,\n      ...options,\n    });\n  }\n\n  async postRun() {\n    if (!this.i18nBranchName) {\n      throw new Error(\n        \"i18nBranchName is not set. Did you forget to call preRun?\",\n      );\n    }\n\n    this.ora.start(\"Checking if PR already exists\");\n    const pullRequestNumber = await this.ensureFreshPr(this.i18nBranchName);\n    // await this.createLabelIfNotExists(pullRequestNumber, 'lingo.dev/i18n', false);\n    this.ora.succeed(\n      `Pull request ready: ${this.platformKit.buildPullRequestUrl(\n        pullRequestNumber,\n      )}`,\n    );\n  }\n\n  private calculatePrBranchName(): string {\n    return `lingo.dev/${this.platformKit.platformConfig.baseBranchName}`;\n  }\n\n  private async checkBranchExistance(prBranchName: string) {\n    return this.platformKit.branchExists({\n      branch: prBranchName,\n    });\n  }\n\n  private async ensureFreshPr(i18nBranchName: string) {\n    // Check if PR exists\n    this.ora.start(\n      `Checking for existing PR with head ${i18nBranchName} and base ${this.platformKit.platformConfig.baseBranchName}`,\n    );\n    let prNumber = await this.platformKit.getOpenPullRequestNumber({\n      branch: i18nBranchName,\n    });\n\n    if (prNumber) {\n      this.ora.succeed(`Existing PR found: #${prNumber}`);\n    } else {\n      // Create new PR\n      this.ora.start(`Creating new PR`);\n      prNumber = await this.platformKit.createPullRequest({\n        head: i18nBranchName,\n        title: this.platformKit.config.pullRequestTitle,\n        body: this.getPrBodyContent(),\n      });\n      this.ora.succeed(`Created new PR: #${prNumber}`);\n    }\n\n    return prNumber;\n  }\n\n  private checkoutI18nBranch(i18nBranchName: string) {\n    execSync(`git fetch origin ${i18nBranchName}`, { stdio: \"inherit\" });\n    execSync(`git checkout -b ${i18nBranchName} origin/${i18nBranchName}`, {\n      stdio: \"inherit\",\n    });\n  }\n\n  private createI18nBranch(i18nBranchName: string) {\n    try {\n      execSync(\n        `git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,\n        { stdio: \"inherit\" },\n      );\n      execSync(\n        `git checkout -b ${i18nBranchName} origin/${this.platformKit.platformConfig.baseBranchName}`,\n        {\n          stdio: \"inherit\",\n        },\n      );\n    } catch (error) {\n      const errorMessage =\n        error instanceof Error ? error.message : \"Unknown error occurred\";\n      this.ora.fail(`Failed to create branch: ${errorMessage}`);\n      this.ora.info(`\n      Troubleshooting tips:\n      1. Make sure you have permission to create branches\n      2. Check if the branch already exists locally (try 'git branch -a')\n      3. Verify connectivity to remote repository\n    `);\n      throw new Error(`Branch creation failed: ${errorMessage}`);\n    }\n  }\n\n  private syncI18nBranch() {\n    if (!this.i18nBranchName) {\n      throw new Error(\"i18nBranchName is not set\");\n    }\n\n    this.ora.start(\n      `Fetching latest changes from ${this.platformKit.platformConfig.baseBranchName}`,\n    );\n    execSync(\n      `git fetch origin ${this.platformKit.platformConfig.baseBranchName}`,\n      { stdio: \"inherit\" },\n    );\n    this.ora.succeed(\n      `Fetched latest changes from ${this.platformKit.platformConfig.baseBranchName}`,\n    );\n\n    try {\n      this.ora.start(\"Attempting to rebase branch\");\n      execSync(\n        `git rebase origin/${this.platformKit.platformConfig.baseBranchName}`,\n        { stdio: \"inherit\" },\n      );\n      this.ora.succeed(\"Successfully rebased branch\");\n    } catch (error) {\n      this.ora.warn(\"Rebase failed, falling back to alternative sync method\");\n\n      try {\n        this.ora.start(\"Aborting failed rebase\");\n        execSync(\"git rebase --abort\", { stdio: \"inherit\" });\n        this.ora.succeed(\"Aborted failed rebase\");\n      } catch {\n        this.ora.warn(\"No rebase in progress to abort\");\n      }\n\n      this.ora.start(\n        `Resetting to ${this.platformKit.platformConfig.baseBranchName}`,\n      );\n      execSync(\n        `git reset --hard origin/${this.platformKit.platformConfig.baseBranchName}`,\n        { stdio: \"inherit\" },\n      );\n      this.ora.succeed(\n        `Reset to ${this.platformKit.platformConfig.baseBranchName}`,\n      );\n\n      this.ora.start(\"Restoring target files\");\n      const targetFiles = [\"i18n.lock\"];\n      const targetFileNames = execSync(\n        `npx lingo.dev@latest show files --target ${this.platformKit.platformConfig.baseBranchName}`,\n        { encoding: \"utf8\" },\n      )\n        .split(\"\\n\")\n        .filter(Boolean);\n      targetFiles.push(...targetFileNames);\n      execSync(`git fetch origin ${this.i18nBranchName}`, { stdio: \"inherit\" });\n      for (const file of targetFiles) {\n        try {\n          // bring all files to the i18n branch's state\n          execSync(`git checkout FETCH_HEAD -- ${file}`, { stdio: \"inherit\" });\n        } catch (error) {\n          // If file doesn't exist in FETCH_HEAD, that's okay - just skip it\n          this.ora.warn(`Skipping non-existent file: ${file}`);\n          continue;\n        }\n      }\n      this.ora.succeed(\"Restored target files\");\n    }\n\n    this.ora.start(\"Checking for changes to commit\");\n    const hasChanges = this.checkCommitableChanges();\n    if (hasChanges) {\n      execSync(\"git add .\", { stdio: \"inherit\" });\n      const signFlag = this.platformKit.config.gpgSign ? \"-S \" : \"\";\n      execSync(\n        `git commit ${signFlag}-m \"chore: sync with ${this.platformKit.platformConfig.baseBranchName}\" --no-verify`,\n        {\n          stdio: \"inherit\",\n        },\n      );\n      this.ora.succeed(\"Committed additional changes\");\n    } else {\n      this.ora.succeed(\"No changes to commit\");\n    }\n  }\n\n  private getPrBodyContent(): string {\n    return `\nHey team,\n\n[**Lingo.dev**](https://lingo.dev) here with fresh translations!\n\n### In this update\n\n- Added missing translations\n- Performed brand voice, context and glossary checks\n- Enhanced translations using Lingo.dev Localization Engine\n\n### Next Steps\n\n- [ ] Review the changes\n- [ ] Merge when ready\n    `.trim();\n  }\n}\n","import { execSync } from \"child_process\";\nimport path from \"path\";\nimport {\n  getGitConfig,\n  IntegrationFlow,\n  escapeShellArg,\n  IIntegrationFlowOptions,\n} from \"./_base\";\nimport i18nCmd from \"../../i18n\";\nimport runCmd from \"../../run\";\n\nexport class InBranchFlow extends IntegrationFlow {\n  async preRun() {\n    this.ora.start(\"Configuring git\");\n    const canContinue = this.configureGit();\n    this.ora.succeed(\"Git configured\");\n\n    return canContinue;\n  }\n\n  async run(options: IIntegrationFlowOptions) {\n    this.ora.start(\"Running Lingo.dev\");\n    await this.runLingoDotDev(options.parallel);\n    this.ora.succeed(\"Done running Lingo.dev\");\n\n    execSync(`rm -f i18n.cache`, { stdio: \"inherit\" }); // do not commit cache file if it exists\n\n    this.ora.start(\"Checking for changes\");\n    const hasChanges = this.checkCommitableChanges();\n    this.ora.succeed(hasChanges ? \"Changes detected\" : \"No changes detected\");\n\n    if (hasChanges) {\n      this.ora.start(\"Committing changes\");\n      execSync(`git add .`, { stdio: \"inherit\" });\n      execSync(`git status --porcelain`, { stdio: \"inherit\" });\n      const signFlag = this.platformKit.config.gpgSign ? \"-S \" : \"\";\n      execSync(\n        `git commit ${signFlag}-m ${escapeShellArg(\n          this.platformKit.config.commitMessage,\n        )} --no-verify`,\n        {\n          stdio: \"inherit\",\n        },\n      );\n      this.ora.succeed(\"Changes committed\");\n\n      this.ora.start(\"Pushing changes to remote\");\n      const currentBranch =\n        this.i18nBranchName ?? this.platformKit.platformConfig.baseBranchName;\n      execSync(\n        `git push origin ${currentBranch} ${options.force ? \"--force\" : \"\"}`,\n        {\n          stdio: \"inherit\",\n        },\n      );\n      this.ora.succeed(\"Changes pushed to remote\");\n    }\n\n    return hasChanges;\n  }\n\n  protected checkCommitableChanges() {\n    return (\n      execSync('git status --porcelain || echo \"has_changes\"', {\n        encoding: \"utf8\",\n      }).length > 0\n    );\n  }\n\n  private async runLingoDotDev(isParallel?: boolean) {\n    try {\n      if (!isParallel) {\n        await i18nCmd\n          .exitOverride()\n          .parseAsync([\"--api-key\", this.platformKit.config.replexicaApiKey], {\n            from: \"user\",\n          });\n      } else {\n        await runCmd\n          .exitOverride()\n          .parseAsync([\"--api-key\", this.platformKit.config.replexicaApiKey], {\n            from: \"user\",\n          });\n      }\n    } catch (err: any) {\n      if (err.code === \"commander.helpDisplayed\") return;\n      throw err;\n    }\n  }\n\n  private configureGit() {\n    const { processOwnCommits } = this.platformKit.config;\n    const { baseBranchName } = this.platformKit.platformConfig;\n    const gitConfig = getGitConfig(this.platformKit);\n\n    this.ora.info(`Current working directory:`);\n    execSync(`pwd`, { stdio: \"inherit\" });\n    execSync(`ls -la`, { stdio: \"inherit\" });\n\n    execSync(`git config --global safe.directory ${process.cwd()}`);\n\n    execSync(`git config user.name \"${gitConfig.userName}\"`);\n    execSync(`git config user.email \"${gitConfig.userEmail}\"`);\n\n    // Configure GPG signing if enabled\n    if (this.platformKit.config.gpgSign) {\n      // Preflight check: verify signing key is configured\n      try {\n        const signingKey = execSync(`git config user.signingkey`, {\n          encoding: \"utf8\",\n        }).trim();\n        if (!signingKey) {\n          throw new Error(\"No signing key configured\");\n        }\n      } catch {\n        throw new Error(\n          \"GPG signing is enabled but no signing key is configured. \" +\n            \"Import a GPG key (e.g., using crazy-max/ghaction-import-gpg) before running this action, \" +\n            \"or set gpg-sign to false.\",\n        );\n      }\n      execSync(`git config commit.gpgsign true`);\n    }\n\n    // perform platform-specific configuration before fetching or pushing to the remote\n    this.platformKit?.gitConfig();\n\n    execSync(`git fetch origin ${baseBranchName}`, { stdio: \"inherit\" });\n    execSync(`git checkout ${baseBranchName} --`, { stdio: \"inherit\" });\n\n    if (!processOwnCommits) {\n      const currentAuthor = `${gitConfig.userName} <${gitConfig.userEmail}>`;\n      const authorOfLastCommit = execSync(\n        `git log -1 --pretty=format:'%an <%ae>'`,\n      ).toString();\n      if (authorOfLastCommit === currentAuthor) {\n        this.ora.warn(\n          `The last commit was already made by ${currentAuthor}, so this run will be skipped, as running again would have no effect. See docs: https://lingo.dev/ci`,\n        );\n        return false;\n      }\n    }\n\n    const workingDir = path.resolve(\n      process.cwd(),\n      this.platformKit.config.workingDir,\n    );\n    if (workingDir !== process.cwd()) {\n      this.ora.info(\n        `Changing to working directory: ${this.platformKit.config.workingDir}`,\n      );\n      process.chdir(workingDir);\n    }\n\n    return true;\n  }\n}\n","import { Ora } from \"ora\";\nimport { PlatformKit } from \"../platforms/_base\";\n\nexport type IIntegrationFlowOptions = {\n  parallel?: boolean;\n  force?: boolean;\n};\n\nexport interface IIntegrationFlow {\n  preRun?(): Promise<boolean>;\n  run(options: IIntegrationFlowOptions): Promise<boolean>;\n  postRun?(): Promise<void>;\n}\n\nexport abstract class IntegrationFlow implements IIntegrationFlow {\n  protected i18nBranchName?: string;\n\n  constructor(\n    protected ora: Ora,\n    protected platformKit: PlatformKit,\n  ) {}\n\n  abstract run(options: IIntegrationFlowOptions): Promise<boolean>;\n}\n\nexport function getGitConfig(platformKit: PlatformKit) {\n  return {\n    userName: platformKit.config.commitAuthorName,\n    userEmail: platformKit.config.commitAuthorEmail,\n  };\n}\n\nexport function escapeShellArg(arg: string): string {\n  return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n}\n","import { Command } from \"interactive-commander\";\nimport { exec } from \"child_process\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport os from \"os\";\nimport setup from \"./setup\";\nimport plan from \"./plan\";\nimport execute from \"./execute\";\nimport watch from \"./watch\";\nimport { CmdRunContext, flagsSchema } from \"./_types\";\nimport frozen from \"./frozen\";\nimport { applyRunExitCode } from \"./exit-code\";\nimport {\n  renderClear,\n  renderSpacer,\n  renderBanner,\n  renderHero,\n  pauseIfDebug,\n  renderSummary,\n} from \"../../utils/ui\";\nimport trackEvent, { UserIdentity } from \"../../utils/observability\";\nimport { determineUserIdentity } from \"./_utils\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nfunction playSound(type: \"success\" | \"failure\") {\n  const platform = os.platform();\n\n  return new Promise<void>((resolve) => {\n    const assetDir = path.join(__dirname, \"../assets\");\n    const soundFiles = [path.join(assetDir, `${type}.mp3`)];\n\n    let command = \"\";\n\n    if (platform === \"linux\") {\n      command = soundFiles\n        .map(\n          (file) =>\n            `mpg123 -q \"${file}\" 2>/dev/null || aplay \"${file}\" 2>/dev/null`,\n        )\n        .join(\" || \");\n    } else if (platform === \"darwin\") {\n      command = soundFiles.map((file) => `afplay \"${file}\"`).join(\" || \");\n    } else if (platform === \"win32\") {\n      command = `powershell -c \"try { (New-Object Media.SoundPlayer '${soundFiles[1]}').PlaySync() } catch { Start-Process -FilePath '${soundFiles[0]}' -WindowStyle Hidden -Wait }\"`;\n    } else {\n      command = soundFiles\n        .map(\n          (file) =>\n            `aplay \"${file}\" 2>/dev/null || afplay \"${file}\" 2>/dev/null`,\n        )\n        .join(\" || \");\n    }\n\n    exec(command, () => {\n      resolve();\n    });\n    setTimeout(resolve, 3000);\n  });\n}\n\nexport default new Command()\n  .command(\"run\")\n  .description(\"Run localization pipeline\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .option(\n    \"--source-locale <source-locale>\",\n    \"Override the source locale from i18n.json for this run\",\n  )\n  .option(\n    \"--target-locale <target-locale>\",\n    \"Limit processing to the listed target locale codes from i18n.json. Repeat the flag to include multiple locales. Defaults to all configured target locales\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--bucket <bucket>\",\n    \"Limit processing to specific bucket types defined in i18n.json (e.g., json, yaml, android). Repeat the flag to include multiple bucket types. Defaults to all configured buckets\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--file <file>\",\n    \"Filter bucket path pattern values by substring match. Examples: messages.json or locale/. Repeat to add multiple filters\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--key <key>\",\n    \"Filter keys by prefix matching on dot-separated paths. Example: auth.login to match all keys starting with auth.login. Repeat for multiple patterns\",\n    (val: string, prev: string[]) =>\n      prev ? [...prev, encodeURIComponent(val)] : [encodeURIComponent(val)],\n  )\n  .option(\n    \"--force\",\n    \"Force re-translation of all keys, bypassing change detection. Useful when you want to regenerate translations with updated AI models or translation settings\",\n  )\n  .option(\n    \"--frozen\",\n    \"Validate translations are up-to-date without making changes - fails if source files, target files, or lockfile are out of sync. Ideal for CI/CD to ensure translation consistency before deployment\",\n  )\n  .option(\n    \"--api-key <api-key>\",\n    \"Override API key from settings or environment variables\",\n  )\n  .option(\"--debug\", \"Pause before processing to allow attaching a debugger.\")\n  .option(\n    \"--concurrency <concurrency>\",\n    \"Number of translation jobs to run concurrently. Higher values can speed up large translation batches but may increase memory usage. Defaults to 10 (maximum 10)\",\n    (val: string) => parseInt(val),\n  )\n  .option(\n    \"--watch\",\n    \"Watch source locale files continuously and retranslate automatically when files change\",\n  )\n  .option(\n    \"--debounce <milliseconds>\",\n    \"Delay in milliseconds after file changes before retranslating in watch mode. Defaults to 5000\",\n    (val: string) => parseInt(val),\n  )\n  .option(\n    \"--sound\",\n    \"Play audio feedback when translations complete (success or failure sounds)\",\n  )\n  .option(\n    \"--pseudo\",\n    \"Enable pseudo-localization mode: automatically pseudo-translates all extracted strings with accented characters and visual markers without calling any external API. Useful for testing UI internationalization readiness\",\n  )\n  .action(async (args) => {\n    let userIdentity: UserIdentity = null;\n    try {\n      const ctx: CmdRunContext = {\n        flags: flagsSchema.parse(args),\n        config: null,\n        results: new Map(),\n        tasks: [],\n        localizer: null,\n      };\n\n      await pauseIfDebug(ctx.flags.debug);\n      await renderClear();\n      await renderSpacer();\n      await renderBanner();\n      await renderHero();\n      await renderSpacer();\n\n      await setup(ctx);\n\n      userIdentity = await determineUserIdentity(ctx);\n\n      await trackEvent(userIdentity, \"cmd.run.start\", {\n        config: ctx.config,\n        flags: ctx.flags,\n      });\n\n      await renderSpacer();\n\n      await plan(ctx);\n      await renderSpacer();\n\n      await frozen(ctx);\n      await renderSpacer();\n\n      await execute(ctx);\n      await renderSpacer();\n\n      await renderSummary(ctx.results);\n      await renderSpacer();\n\n      const hasErrors = applyRunExitCode(ctx.results);\n\n      // Play sound after main tasks complete if sound flag is enabled\n      if (ctx.flags.sound) {\n        await playSound(hasErrors ? \"failure\" : \"success\");\n      }\n\n      // If watch mode is enabled, start watching for changes\n      if (ctx.flags.watch) {\n        await watch(ctx);\n      }\n\n      await trackEvent(userIdentity, \"cmd.run.success\", {\n        config: ctx.config,\n        flags: ctx.flags,\n      });\n      await new Promise((resolve) => setTimeout(resolve, 50));\n    } catch (error: any) {\n      await trackEvent(userIdentity, \"cmd.run.error\", {\n        flags: args,\n        error: error.message,\n        authenticated: !!userIdentity,\n      });\n      await new Promise((resolve) => setTimeout(resolve, 50));\n      // Play sad sound if sound flag is enabled\n      if (args.sound) {\n        await playSound(\"failure\");\n      }\n      throw error;\n    }\n  });\n","import chalk from \"chalk\";\nimport { Listr } from \"listr2\";\nimport { colors } from \"../../constants\";\nimport { CmdRunContext, flagsSchema } from \"./_types\";\nimport { commonTaskRendererOptions } from \"./_const\";\nimport { getConfig } from \"../../utils/config\";\nimport createLocalizer from \"../../localizer\";\n\nexport default async function setup(input: CmdRunContext) {\n  console.log(chalk.hex(colors.orange)(\"[Setup]\"));\n\n  return new Listr<CmdRunContext>(\n    [\n      {\n        title: \"Setting up the environment\",\n        task: async (ctx, task) => {\n          // setup gitignore, etc here\n          task.title = `Environment setup completed`;\n        },\n      },\n      {\n        title: \"Loading i18n configuration\",\n        task: async (ctx, task) => {\n          ctx.config = getConfig(true);\n\n          if (!ctx.config) {\n            throw new Error(\n              \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n            );\n          } else if (\n            !ctx.config.buckets ||\n            !Object.keys(ctx.config.buckets).length\n          ) {\n            throw new Error(\n              \"No buckets found in i18n.json. Please add at least one bucket containing i18n content.\",\n            );\n          } else if (\n            ctx.flags.bucket?.some(\n              (bucket) =>\n                !ctx.config?.buckets[bucket as keyof typeof ctx.config.buckets],\n            )\n          ) {\n            throw new Error(\n              `One or more specified buckets do not exist in i18n.json. Please add them to the list first and try again.`,\n            );\n          }\n          task.title = `Loaded i18n configuration`;\n        },\n      },\n      {\n        title: \"Selecting localization provider\",\n        task: async (ctx, task) => {\n          const isPseudo =\n            ctx.flags.pseudo || ctx.config?.dev?.usePseudotranslator;\n          const provider = isPseudo ? \"pseudo\" : ctx.config?.provider;\n          const engineId = ctx.config?.engineId;\n          ctx.localizer = createLocalizer(provider, engineId, ctx.flags.apiKey);\n          if (!ctx.localizer) {\n            throw new Error(\n              \"Could not create localization provider. Please check your i18n.json configuration.\",\n            );\n          }\n          task.title =\n            ctx.localizer.id === \"Lingo.dev\"\n              ? `Using ${chalk.hex(colors.green)(ctx.localizer.id)} provider`\n              : ctx.localizer.id === \"pseudo\"\n                ? `Using ${chalk.hex(colors.blue)(\"pseudo\")} mode for testing`\n                : `Using raw ${chalk.hex(colors.yellow)(ctx.localizer.id)} API`;\n        },\n      },\n      {\n        title: \"Checking authentication\",\n        enabled: (ctx) =>\n          ctx.localizer?.id === \"Lingo.dev\" &&\n          !ctx.flags.pseudo &&\n          !ctx.config?.dev?.usePseudotranslator,\n        task: async (ctx, task) => {\n          const authStatus = await ctx.localizer!.checkAuth();\n          if (!authStatus.authenticated) {\n            throw new Error(authStatus.error || \"Authentication failed\");\n          }\n          task.title = `Authenticated as ${chalk.hex(colors.yellow)(\n            authStatus.username,\n          )}`;\n        },\n      },\n      {\n        title: \"Validating configuration\",\n        enabled: (ctx) => ctx.localizer?.id !== \"Lingo.dev\",\n        task: async (ctx, task) => {\n          const validationStatus = await ctx.localizer!.validateSettings!();\n          if (!validationStatus.valid) {\n            throw new Error(\n              validationStatus.error || \"Configuration validation failed\",\n            );\n          }\n          task.title = `Configuration validated`;\n        },\n      },\n      {\n        title: \"Initializing localization provider\",\n        async task(ctx, task) {\n          const isLingoDotDev = ctx.localizer!.id === \"Lingo.dev\";\n          const isPseudo = ctx.localizer!.id === \"pseudo\";\n\n          const subTasks = isLingoDotDev\n            ? [\n                \"Brand voice enabled\",\n                \"Translation memory connected\",\n                \"Glossary enabled\",\n                \"Quality assurance enabled\",\n              ].map((title) => ({ title, task: () => {} }))\n            : isPseudo\n              ? [\n                  \"Pseudo-localization mode active\",\n                  \"Character replacement configured\",\n                  \"No external API calls\",\n                ].map((title) => ({ title, task: () => {} }))\n              : [\n                  \"Skipping brand voice\",\n                  \"Skipping glossary\",\n                  \"Skipping translation memory\",\n                  \"Skipping quality assurance\",\n                ].map((title) => ({ title, task: () => {}, skip: true }));\n\n          return task.newListr(subTasks, {\n            concurrent: true,\n            rendererOptions: { collapseSubtasks: false },\n          });\n        },\n      },\n    ],\n    {\n      rendererOptions: commonTaskRendererOptions,\n    },\n  ).run(input);\n}\n","import chalk from \"chalk\";\nimport { ListrDefaultRendererLogLevels } from \"listr2\";\nimport { colors } from \"../../constants\";\n\nexport const commonTaskRendererOptions = {\n  color: {\n    [ListrDefaultRendererLogLevels.COMPLETED]: (msg?: string) =>\n      msg ? chalk.hex(colors.green)(msg) : chalk.hex(colors.green)(\"\"),\n  },\n  icon: {\n    [ListrDefaultRendererLogLevels.COMPLETED]: chalk.hex(colors.green)(\"✓\"),\n  },\n};\n","import dedent from \"dedent\";\nimport { ILocalizer, LocalizerData } from \"./_types\";\nimport chalk from \"chalk\";\nimport { colors } from \"../constants\";\nimport { LingoDotDevEngine } from \"@lingo.dev/_sdk\";\nimport { getSettings } from \"../utils/settings\";\n\nexport default function createLingoDotDevLocalizer(\n  explicitApiKey?: string,\n  engineId?: string,\n): ILocalizer {\n  const settings = getSettings(explicitApiKey);\n\n  if (!settings.auth.apiKey) {\n    throw new Error(\n      dedent`\n        You're trying to use ${chalk.hex(colors.green)(\n          \"Lingo.dev\",\n        )} provider, however, you are not authenticated.\n\n        To fix this issue:\n        1. Run ${chalk.dim(\"lingo.dev login\")} to authenticate, or\n        2. Use the ${chalk.dim(\"--api-key\")} flag to provide an API key.\n        3. Set ${chalk.dim(\"LINGO_API_KEY\")} environment variable.\n      `,\n    );\n  }\n\n  const triggerType = process.env.CI ? \"ci\" : \"cli\";\n\n  const engine = new LingoDotDevEngine({\n    apiKey: settings.auth.apiKey,\n    apiUrl: settings.auth.apiUrl,\n    ...(engineId && { engineId }),\n  });\n\n  return {\n    id: \"Lingo.dev\",\n    checkAuth: async () => {\n      try {\n        const response = await engine.whoami();\n        if (!response) {\n          return {\n            authenticated: false,\n            error:\n              \"Invalid API key. Run `lingo.dev login` or check your LINGO_API_KEY.\",\n          };\n        }\n        return {\n          authenticated: true,\n          username: response.email,\n          userId: response.id,\n        };\n      } catch (error) {\n        const errorMessage =\n          error instanceof Error ? error.message : String(error);\n        return { authenticated: false, error: errorMessage };\n      }\n    },\n    localize: async (input: LocalizerData, onProgress) => {\n      // Nothing to translate – return the input as-is.\n      if (!Object.keys(input.processableData).length) {\n        return input.processableData;\n      }\n\n      const processedData = await engine.localizeObject(\n        input.processableData,\n        {\n          sourceLocale: input.sourceLocale,\n          targetLocale: input.targetLocale,\n          reference: {\n            [input.sourceLocale]: input.sourceData,\n            [input.targetLocale]: input.targetData,\n          },\n          hints: input.hints,\n          filePath: input.filePath,\n          triggerType,\n        },\n        onProgress,\n      );\n\n      return processedData;\n    },\n  };\n}\n","import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport { createOpenRouter } from \"@openrouter/ai-sdk-provider\";\nimport { createMistral } from \"@ai-sdk/mistral\";\nimport { I18nConfig } from \"@lingo.dev/_spec\";\nimport chalk from \"chalk\";\nimport dedent from \"dedent\";\nimport { ILocalizer, LocalizerData } from \"./_types\";\nimport { LanguageModel, ModelMessage, generateText } from \"ai\";\nimport { colors } from \"../constants\";\nimport { jsonrepair } from \"jsonrepair\";\nimport { createOllama } from \"ollama-ai-provider-v2\";\n\nexport default function createExplicitLocalizer(\n  provider: NonNullable<I18nConfig[\"provider\"]>,\n): ILocalizer {\n  const settings = provider.settings || {};\n\n  switch (provider.id) {\n    default:\n      throw new Error(\n        dedent`\n          You're trying to use unsupported provider: ${chalk.dim(provider.id)}.\n\n          To fix this issue:\n          1. Switch to one of the supported providers, or\n          2. Remove the ${chalk.italic(\n          \"provider\",\n        )} node from your i18n.json configuration to switch to ${chalk.hex(\n          colors.green,\n        )(\"Lingo.dev\")}\n\n          ${chalk.hex(colors.blue)(\"Docs: https://lingo.dev/go/docs\")}\n        `,\n      );\n    case \"openai\":\n      return createAiSdkLocalizer({\n        factory: (params) => createOpenAI(params).languageModel(provider.model),\n        id: provider.id,\n        prompt: provider.prompt,\n        apiKeyName: \"OPENAI_API_KEY\",\n        baseUrl: provider.baseUrl,\n        settings,\n      });\n    case \"anthropic\":\n      return createAiSdkLocalizer({\n        factory: (params) =>\n          createAnthropic(params).languageModel(provider.model),\n        id: provider.id,\n        prompt: provider.prompt,\n        apiKeyName: \"ANTHROPIC_API_KEY\",\n        baseUrl: provider.baseUrl,\n        settings,\n      });\n    case \"google\":\n      return createAiSdkLocalizer({\n        factory: (params) =>\n          createGoogleGenerativeAI(params).languageModel(provider.model),\n        id: provider.id,\n        prompt: provider.prompt,\n        apiKeyName: \"GOOGLE_API_KEY\",\n        baseUrl: provider.baseUrl,\n        settings,\n      });\n    case \"openrouter\":\n      return createAiSdkLocalizer({\n        factory: (params) =>\n          createOpenRouter(params).languageModel(provider.model),\n        id: provider.id,\n        prompt: provider.prompt,\n        apiKeyName: \"OPENROUTER_API_KEY\",\n        baseUrl: provider.baseUrl,\n        settings,\n      });\n    case \"ollama\":\n      return createAiSdkLocalizer({\n        factory: (_params) => createOllama().languageModel(provider.model),\n        id: provider.id,\n        prompt: provider.prompt,\n        skipAuth: true,\n        settings,\n      });\n    case \"mistral\":\n      return createAiSdkLocalizer({\n        factory: (params) =>\n          createMistral(params).languageModel(provider.model),\n        id: provider.id,\n        prompt: provider.prompt,\n        apiKeyName: \"MISTRAL_API_KEY\",\n        baseUrl: provider.baseUrl,\n        settings,\n      });\n  }\n}\n\n// Exported for testing\nexport function parseModelResponse(\n  text: string,\n): ReturnType<typeof JSON.parse> {\n  const firstBrace = text.indexOf(\"{\");\n  const lastBrace = text.lastIndexOf(\"}\");\n  const extracted =\n    firstBrace !== -1 && lastBrace >= firstBrace\n      ? text.slice(firstBrace, lastBrace + 1)\n      : text;\n\n  try {\n    return JSON.parse(extracted);\n  } catch {\n    return JSON.parse(jsonrepair(extracted));\n  }\n}\n\nfunction createAiSdkLocalizer(params: {\n  factory: (params: { apiKey?: string; baseUrl?: string }) => LanguageModel;\n  id: NonNullable<I18nConfig[\"provider\"]>[\"id\"];\n  prompt: string;\n  apiKeyName?: string;\n  baseUrl?: string;\n  skipAuth?: boolean;\n  settings?: { temperature?: number };\n}): ILocalizer {\n  const skipAuth = params.skipAuth === true;\n\n  const apiKey = process.env[params?.apiKeyName ?? \"\"];\n  if (!skipAuth && (!apiKey || !params.apiKeyName)) {\n    throw new Error(\n      dedent`\n        You're trying to use raw ${chalk.dim(params.id)} API for translation. ${params.apiKeyName\n          ? `However, ${chalk.dim(\n            params.apiKeyName,\n          )} environment variable is not set.`\n          : \"However, that provider is unavailable.\"\n        }\n\n        To fix this issue:\n        1. ${params.apiKeyName\n          ? `Set ${chalk.dim(\n            params.apiKeyName,\n          )} in your environment variables`\n          : \"Set the environment variable for your provider (if required)\"\n        }, or\n        2. Remove the ${chalk.italic(\n          \"provider\",\n        )} node from your i18n.json configuration to switch to ${chalk.hex(\n          colors.green,\n        )(\"Lingo.dev\")}\n\n        ${chalk.hex(colors.blue)(\"Docs: https://lingo.dev/go/docs\")}\n      `,\n    );\n  }\n\n  const model = params.factory(\n    skipAuth ? {} : { apiKey, baseUrl: params.baseUrl },\n  );\n\n  return {\n    id: params.id,\n    checkAuth: async () => {\n      // For BYOK providers, auth check is not meaningful\n      // Configuration validation happens in validateSettings\n      return { authenticated: true, username: \"anonymous\" };\n    },\n    validateSettings: async () => {\n      try {\n        await generateText({\n          model,\n          ...params.settings,\n          messages: [\n            { role: \"system\", content: \"You are an echo server\" },\n            { role: \"user\", content: \"OK\" },\n            { role: \"assistant\", content: \"OK\" },\n            { role: \"user\", content: \"OK\" },\n          ],\n        });\n\n        return { valid: true };\n      } catch (error) {\n        const errorMessage =\n          error instanceof Error ? error.message : String(error);\n        return { valid: false, error: errorMessage };\n      }\n    },\n    localize: async (input: LocalizerData) => {\n      const systemPrompt = params.prompt\n        .replaceAll(\"{source}\", input.sourceLocale)\n        .replaceAll(\"{target}\", input.targetLocale);\n      const shots = [\n        [\n          {\n            sourceLocale: \"en\",\n            targetLocale: \"es\",\n            data: {\n              message: \"Hello, world!\",\n            },\n          },\n          {\n            sourceLocale: \"en\",\n            targetLocale: \"es\",\n            data: {\n              message: \"Hola, mundo!\",\n            },\n          },\n        ],\n        [\n          {\n            sourceLocale: \"en\",\n            targetLocale: \"es\",\n            data: {\n              spring: \"Spring\",\n            },\n            hints: {\n              spring: [\"A source of water\"],\n            },\n          },\n          {\n            sourceLocale: \"en\",\n            targetLocale: \"es\",\n            data: {\n              spring: \"Manantial\",\n            },\n          },\n        ],\n      ];\n\n      const hasHints = input.hints && Object.keys(input.hints).length > 0;\n\n      const payload = {\n        sourceLocale: input.sourceLocale,\n        targetLocale: input.targetLocale,\n        data: input.processableData,\n        ...(hasHints && { hints: input.hints }),\n      };\n\n      const response = await generateText({\n        model,\n        ...params.settings,\n        messages: [\n          { role: \"system\", content: systemPrompt },\n          ...shots.flatMap(\n            ([userShot, assistantShot]) =>\n              [\n                { role: \"user\", content: JSON.stringify(userShot) },\n                { role: \"assistant\", content: JSON.stringify(assistantShot) },\n              ] as ModelMessage[],\n          ),\n          { role: \"user\", content: JSON.stringify(payload) },\n        ],\n      });\n\n      const result = parseModelResponse(response.text);\n\n      // Handle both object and string responses\n      if (typeof result.data === \"object\" && result.data !== null) {\n        return result.data;\n      }\n\n      // Handle string responses - extract and repair JSON\n      const index = result.data.indexOf(\"{\");\n      const lastIndex = result.data.lastIndexOf(\"}\");\n      const trimmed = result.data.slice(index, lastIndex + 1);\n      return JSON.parse(jsonrepair(trimmed)).data;\n    },\n  };\n}\n","/**\n * Pseudo-localization utility for testing UI internationalization readiness\n * without waiting for actual translations.\n *\n * Implements character replacement with accented versions and optional lengthening,\n * following standard i18n practices used by Google, Microsoft, and Mozilla.\n */\n\n/**\n * Character mapping for pseudo-localization (en-XA style)\n * Each ASCII character is replaced with a visually similar accented version\n */\nconst PSEUDO_CHAR_MAP: Record<string, string> = {\n  a: \"ã\",\n  b: \"ƀ\",\n  c: \"ç\",\n  d: \"ð\",\n  e: \"è\",\n  f: \"ƒ\",\n  g: \"ĝ\",\n  h: \"ĥ\",\n  i: \"í\",\n  j: \"ĵ\",\n  k: \"ķ\",\n  l: \"ļ\",\n  m: \"m\",\n  n: \"ñ\",\n  o: \"ø\",\n  p: \"þ\",\n  q: \"q\",\n  r: \"ŕ\",\n  s: \"š\",\n  t: \"ţ\",\n  u: \"û\",\n  v: \"ṽ\",\n  w: \"ŵ\",\n  x: \"x\",\n  y: \"ý\",\n  z: \"ž\",\n\n  A: \"Ã\",\n  B: \"Ḃ\",\n  C: \"Ĉ\",\n  D: \"Ð\",\n  E: \"È\",\n  F: \"Ḟ\",\n  G: \"Ĝ\",\n  H: \"Ĥ\",\n  I: \"Í\",\n  J: \"Ĵ\",\n  K: \"Ķ\",\n  L: \"Ļ\",\n  M: \"M\",\n  N: \"Ñ\",\n  O: \"Ø\",\n  P: \"Þ\",\n  Q: \"Q\",\n  R: \"Ŕ\",\n  S: \"Š\",\n  T: \"Ţ\",\n  U: \"Û\",\n  V: \"Ṽ\",\n  W: \"Ŵ\",\n  X: \"X\",\n  Y: \"Ý\",\n  Z: \"Ž\",\n};\n\n/**\n * Pseudo-localizes a string by replacing characters with accented versions\n * and optionally extending the length to simulate expansion.\n *\n * @param text - The text to pseudo-localize\n * @param options - Configuration options\n * @returns The pseudo-localized text\n *\n * @example\n * ```ts\n * pseudoLocalize(\"Submit\") // \"Šûbmíţ⚡\"\n * pseudoLocalize(\"Welcome back!\", { addLengthMarker: true }) // \"Ŵêļçømèƀäçķ!⚡\"\n * ```\n */\nexport function pseudoLocalize(\n  text: string,\n  options: {\n    /**\n     * Add a visual marker (⚡) at the end to indicate pseudo-localization\n     * @default true\n     */\n    addMarker?: boolean;\n    /**\n     * Extend text length by adding padding characters to simulate text expansion.\n     * Useful for testing UI layout with longer translations.\n     * @default false\n     */\n    addLengthMarker?: boolean;\n    /**\n     * The percentage to extend the text (0-100).\n     * @default 30\n     */\n    lengthExpansion?: number;\n  } = {},\n): string {\n  const {\n    addMarker = true,\n    addLengthMarker = false,\n    lengthExpansion = 30,\n  } = options;\n\n  if (!text) {\n    return text;\n  }\n\n  // Replace characters with accented versions\n  let result = \"\";\n  for (const char of text) {\n    result += PSEUDO_CHAR_MAP[char] ?? char;\n  }\n\n  // Add length expansion if requested\n  if (addLengthMarker) {\n    const extraChars = Math.ceil((text.length * lengthExpansion) / 100);\n    // Add combining diacritical marks to simulate expansion\n    result += \"̌\".repeat(extraChars);\n  }\n\n  // Add visual marker if requested\n  if (addMarker) {\n    result += \"⚡\";\n  }\n\n  return result;\n}\n\n/**\n * Pseudo-localizes all strings in an object (recursively).\n * Handles nested objects and arrays.\n *\n * @param obj - The object to pseudo-localize\n * @param options - Configuration options for pseudoLocalize\n * @returns A new object with all string values pseudo-localized\n */\nexport function pseudoLocalizeObject(\n  obj: any,\n  options?: Parameters<typeof pseudoLocalize>[1],\n): any {\n  if (typeof obj === \"string\") {\n    return pseudoLocalize(obj, options);\n  }\n\n  if (Array.isArray(obj)) {\n    return obj.map((item) => pseudoLocalizeObject(item, options));\n  }\n\n  if (obj !== null && typeof obj === \"object\") {\n    const result: Record<string, any> = {};\n    for (const [key, value] of Object.entries(obj)) {\n      result[key] = pseudoLocalizeObject(value, options);\n    }\n    return result;\n  }\n\n  return obj;\n}\n","import { ILocalizer, LocalizerData } from \"./_types\";\nimport { pseudoLocalizeObject } from \"../../utils/pseudo-localize\";\n\n/**\n * Creates a pseudo-localizer that doesn't call any external API.\n * Instead, it performs character replacement with accented versions,\n * useful for testing UI internationalization readiness.\n */\nexport default function createPseudoLocalizer(): ILocalizer {\n  return {\n    id: \"pseudo\",\n    checkAuth: async () => {\n      return {\n        authenticated: true,\n      };\n    },\n    validateSettings: async () => {\n      return { valid: true };\n    },\n    localize: async (input: LocalizerData, onProgress) => {\n      // Nothing to translate – return the input as-is.\n      if (!Object.keys(input.processableData).length) {\n        return input;\n      }\n\n      // Pseudo-localize all strings in the processable data\n      const processedData = pseudoLocalizeObject(input.processableData, {\n        addMarker: true,\n        addLengthMarker: false,\n      });\n\n      // Call progress callback if provided, simulating completion\n      if (onProgress) {\n        onProgress(100, input.processableData, processedData);\n      }\n\n      return processedData;\n    },\n  };\n}\n","import { I18nConfig } from \"@lingo.dev/_spec\";\n\nimport createLingoDotDevLocalizer from \"./lingodotdev\";\nimport createExplicitLocalizer from \"./explicit\";\nimport createPseudoLocalizer from \"./pseudo\";\nimport { ILocalizer } from \"./_types\";\n\nexport default function createLocalizer(\n  provider: I18nConfig[\"provider\"] | \"pseudo\" | null | undefined,\n  engineId?: string,\n  apiKey?: string,\n): ILocalizer {\n  if (provider === \"pseudo\") {\n    return createPseudoLocalizer();\n  }\n\n  if (!provider) {\n    return createLingoDotDevLocalizer(apiKey, engineId);\n  } else {\n    return createExplicitLocalizer(provider);\n  }\n}\n","import chalk from \"chalk\";\nimport { Listr } from \"listr2\";\nimport { minimatch } from \"minimatch\";\n\nimport { colors } from \"../../constants\";\nimport { resolveOverriddenLocale } from \"@lingo.dev/_spec\";\nimport { getBuckets } from \"../../utils/buckets\";\nimport { commonTaskRendererOptions } from \"./_const\";\nimport { CmdRunContext } from \"./_types\";\n\nexport default async function plan(\n  input: CmdRunContext,\n): Promise<CmdRunContext> {\n  console.log(chalk.hex(colors.orange)(\"[Planning]\"));\n\n  let buckets = getBuckets(input.config!);\n  if (input.flags.bucket) {\n    buckets = buckets.filter((b) => input.flags.bucket!.includes(b.type));\n  }\n\n  const _sourceLocale = input.flags.sourceLocale || input.config!.locale.source;\n  if (!_sourceLocale) {\n    throw new Error(\n      `No source locale provided. Use --source-locale to specify the source locale or add it to i18n.json (locale.source)`,\n    );\n  }\n  const _targetLocales =\n    input.flags.targetLocale || input.config!.locale.targets;\n  if (!_targetLocales.length) {\n    throw new Error(\n      `No target locales provided. Use --target-locale to specify the target locales or add them to i18n.json (locale.targets)`,\n    );\n  }\n\n  return new Listr<CmdRunContext>(\n    [\n      {\n        title: \"Locating content buckets\",\n        task: async (ctx, task) => {\n          const bucketCount = buckets.length;\n          const bucketFilter = input.flags.bucket\n            ? ` ${chalk.dim(`(filtered by: ${chalk.hex(colors.yellow)(input.flags.bucket!.join(\", \"))})`)}`\n            : \"\";\n          task.title = `Found ${chalk.hex(colors.yellow)(bucketCount.toString())} bucket(s)${bucketFilter}`;\n        },\n      },\n      {\n        title: \"Detecting locales\",\n        task: async (ctx, task) => {\n          task.title = `Found ${chalk.hex(colors.yellow)(_targetLocales.length.toString())} target locale(s)`;\n        },\n      },\n      {\n        title: \"Locating localizable files\",\n        task: async (ctx, task) => {\n          const patterns: string[] = [];\n\n          for (const bucket of buckets) {\n            for (const bucketPath of bucket.paths) {\n              if (input.flags.file) {\n                if (\n                  !input.flags.file.some(\n                    (f) =>\n                      bucketPath.pathPattern.includes(f) ||\n                      minimatch(bucketPath.pathPattern, f),\n                  )\n                ) {\n                  continue;\n                }\n              }\n\n              patterns.push(bucketPath.pathPattern);\n            }\n          }\n\n          const fileFilter = input.flags.file\n            ? ` ${chalk.dim(`(filtered by: ${chalk.hex(colors.yellow)(input.flags.file.join(\", \"))})`)}`\n            : \"\";\n          task.title = `Found ${chalk.hex(colors.yellow)(patterns.length.toString())} path pattern(s)${fileFilter}`;\n        },\n      },\n      {\n        title: \"Computing translation tasks\",\n        task: async (ctx, task) => {\n          for (const bucket of buckets) {\n            for (const bucketPath of bucket.paths) {\n              if (input.flags.file) {\n                if (\n                  !input.flags.file.some(\n                    (f) =>\n                      bucketPath.pathPattern.includes(f) ||\n                      minimatch(bucketPath.pathPattern, f),\n                  )\n                ) {\n                  continue;\n                }\n              }\n\n              const sourceLocale = resolveOverriddenLocale(\n                _sourceLocale,\n                bucketPath.delimiter,\n              );\n\n              for (const _targetLocale of _targetLocales) {\n                const targetLocale = resolveOverriddenLocale(\n                  _targetLocale,\n                  bucketPath.delimiter,\n                );\n\n                // Skip if source and target are identical (shouldn't happen but guard)\n                if (sourceLocale === targetLocale) continue;\n\n                ctx.tasks.push({\n                  sourceLocale,\n                  targetLocale,\n                  bucketType: bucket.type,\n                  bucketPathPattern: bucketPath.pathPattern,\n                  injectLocale: bucket.injectLocale || [],\n                  lockedKeys: bucket.lockedKeys || [],\n                  lockedPatterns: bucket.lockedPatterns || [],\n                  ignoredKeys: bucket.ignoredKeys || [],\n                  preservedKeys: bucket.preservedKeys || [],\n                  localizableKeys: bucket.localizableKeys || [],\n                  onlyKeys: input.flags.key || [],\n                  formatter: input.config!.formatter,\n                  keyColumn: bucket.keyColumn,\n                });\n              }\n            }\n          }\n\n          task.title = `Prepared ${chalk.hex(colors.green)(ctx.tasks.length.toString())} translation task(s)`;\n        },\n      },\n    ],\n    {\n      rendererOptions: commonTaskRendererOptions,\n    },\n  ).run(input);\n}\n","import chalk from \"chalk\";\nimport { Listr, ListrTask } from \"listr2\";\nimport pLimit, { LimitFunction } from \"p-limit\";\nimport _ from \"lodash\";\nimport { minimatch } from \"minimatch\";\n\nimport { safeDecode } from \"../../utils/key-matching\";\nimport { colors } from \"../../constants\";\nimport { CmdRunContext, CmdRunTask, CmdRunTaskResult } from \"./_types\";\nimport { commonTaskRendererOptions } from \"./_const\";\nimport createBucketLoader from \"../../loaders\";\nimport { createDeltaProcessor, Delta } from \"../../utils/delta\";\n\nconst WARN_CONCURRENCY_COUNT = 30;\n\nexport default async function execute(input: CmdRunContext) {\n  const effectiveConcurrency = Math.min(\n    input.flags.concurrency,\n    input.tasks.length,\n  );\n\n  if (effectiveConcurrency >= WARN_CONCURRENCY_COUNT) {\n    console.warn(\n      chalk.yellow(\n        `⚠️ High concurrency (${effectiveConcurrency}) may cause failures in some environments.`,\n      ),\n    );\n  }\n\n  console.log(chalk.hex(colors.orange)(`[Localization]`));\n\n  return new Listr<CmdRunContext>(\n    [\n      {\n        title: \"Initializing localization engine\",\n        task: async (ctx, task) => {\n          task.title = `Localization engine ${chalk.hex(colors.green)(\"ready\")} (${ctx.localizer!.id})`;\n        },\n      },\n      {\n        title: `Processing localization tasks ${chalk.dim(\n          `(tasks: ${input.tasks.length}, concurrency: ${effectiveConcurrency})`,\n        )}`,\n        task: async (ctx, task) => {\n          if (input.tasks.length < 1) {\n            task.title = `Skipping, nothing to localize.`;\n            task.skip();\n            return;\n          }\n\n          // Preload checksums for all unique bucket path patterns before starting any workers\n          const initialChecksumsMap = new Map<string, Record<string, string>>();\n          const uniqueBucketPatterns = _.uniq(\n            ctx.tasks.map((t) => t.bucketPathPattern),\n          );\n          for (const bucketPathPattern of uniqueBucketPatterns) {\n            const deltaProcessor = createDeltaProcessor(bucketPathPattern);\n            const checksums = await deltaProcessor.loadChecksums();\n            initialChecksumsMap.set(bucketPathPattern, checksums);\n          }\n\n          const i18nLimiter = pLimit(effectiveConcurrency);\n          const ioLimiter = pLimit(1);\n\n          const perFileIoLimiters = new Map<string, LimitFunction>();\n          const getFileIoLimiter = (\n            bucketPathPattern: string,\n          ): LimitFunction => {\n            const lockKey = bucketPathPattern;\n\n            if (!perFileIoLimiters.has(lockKey)) {\n              perFileIoLimiters.set(lockKey, pLimit(1));\n            }\n            return perFileIoLimiters.get(lockKey)!;\n          };\n\n          const workersCount = effectiveConcurrency;\n\n          const workerTasks: ListrTask[] = [];\n          for (let i = 0; i < workersCount; i++) {\n            const assignedTasks = ctx.tasks.filter(\n              (_, idx) => idx % workersCount === i,\n            );\n            workerTasks.push(\n              createWorkerTask({\n                ctx,\n                assignedTasks,\n                ioLimiter,\n                i18nLimiter,\n                initialChecksumsMap,\n                getFileIoLimiter,\n                onDone() {\n                  task.title = createExecutionProgressMessage(ctx);\n                },\n              }),\n            );\n          }\n\n          return task.newListr(workerTasks, {\n            concurrent: true,\n            exitOnError: false,\n            rendererOptions: {\n              ...commonTaskRendererOptions,\n              collapseSubtasks: true,\n            },\n          });\n        },\n      },\n    ],\n    {\n      exitOnError: false,\n      rendererOptions: commonTaskRendererOptions,\n    },\n  ).run(input);\n}\n\nfunction createWorkerStatusMessage(args: {\n  assignedTask: CmdRunTask;\n  percentage: number;\n}) {\n  const displayPath = args.assignedTask.bucketPathPattern.replace(\n    \"[locale]\",\n    args.assignedTask.targetLocale,\n  );\n  return `[${chalk.hex(colors.yellow)(`${args.percentage}%`)}] Processing: ${chalk.dim(displayPath)} (${chalk.hex(\n    colors.yellow,\n  )(\n    args.assignedTask.sourceLocale,\n  )} -> ${chalk.hex(colors.yellow)(args.assignedTask.targetLocale)})`;\n}\n\nfunction createExecutionProgressMessage(ctx: CmdRunContext) {\n  const succeededTasksCount = countTasks(\n    ctx,\n    (_t, result) => result.status === \"success\",\n  );\n  const failedTasksCount = countTasks(\n    ctx,\n    (_t, result) => result.status === \"error\",\n  );\n  const skippedTasksCount = countTasks(\n    ctx,\n    (_t, result) => result.status === \"skipped\",\n  );\n\n  return `Processed ${chalk.green(succeededTasksCount)}/${\n    ctx.tasks.length\n  }, Failed ${chalk.red(failedTasksCount)}, Skipped ${chalk.dim(skippedTasksCount)}`;\n}\n\nfunction createLoaderForTask(assignedTask: CmdRunTask) {\n  const bucketLoader = createBucketLoader(\n    assignedTask.bucketType,\n    assignedTask.bucketPathPattern,\n    {\n      defaultLocale: assignedTask.sourceLocale,\n      injectLocale: assignedTask.injectLocale,\n      formatter: assignedTask.formatter,\n      keyColumn: assignedTask.keyColumn,\n    },\n    assignedTask.lockedKeys,\n    assignedTask.lockedPatterns,\n    assignedTask.ignoredKeys,\n    assignedTask.preservedKeys,\n    assignedTask.localizableKeys,\n  );\n  bucketLoader.setDefaultLocale(assignedTask.sourceLocale);\n\n  return bucketLoader;\n}\n\nfunction createWorkerTask(args: {\n  ctx: CmdRunContext;\n  assignedTasks: CmdRunTask[];\n  ioLimiter: LimitFunction;\n  i18nLimiter: LimitFunction;\n  onDone: () => void;\n  initialChecksumsMap: Map<string, Record<string, string>>;\n  getFileIoLimiter: (bucketPathPattern: string) => LimitFunction;\n}): ListrTask {\n  return {\n    title: \"Initializing...\",\n    task: async (_subCtx: any, subTask: any) => {\n      for (const assignedTask of args.assignedTasks) {\n        subTask.title = createWorkerStatusMessage({\n          assignedTask,\n          percentage: 0,\n        });\n        const bucketLoader = createLoaderForTask(assignedTask);\n        const deltaProcessor = createDeltaProcessor(\n          assignedTask.bucketPathPattern,\n        );\n\n        // Get initial checksums from the preloaded map\n        const initialChecksums =\n          args.initialChecksumsMap.get(assignedTask.bucketPathPattern) || {};\n\n        const taskResult = await args.i18nLimiter(async () => {\n          try {\n            // Pull operations must be serialized per-file for single-file formats\n            // where multiple locales share the same file (e.g., xcode-xcstrings)\n            const fileIoLimiter = args.getFileIoLimiter(\n              assignedTask.bucketPathPattern,\n            );\n            const sourceData = await fileIoLimiter(async () =>\n              bucketLoader.pull(assignedTask.sourceLocale),\n            );\n            const hints = await fileIoLimiter(async () =>\n              bucketLoader.pullHints(),\n            );\n            const targetData = await fileIoLimiter(async () =>\n              bucketLoader.pull(assignedTask.targetLocale),\n            );\n            const delta = await deltaProcessor.calculateDelta({\n              sourceData,\n              targetData,\n              checksums: initialChecksums,\n            });\n\n            const processableData = _.chain(sourceData)\n              .entries()\n              .filter(\n                ([key, value]) =>\n                  delta.added.includes(key) ||\n                  delta.updated.includes(key) ||\n                  !!args.ctx.flags.force,\n              )\n              .filter(\n                ([key]) =>\n                  !assignedTask.onlyKeys.length ||\n                  assignedTask.onlyKeys?.some((pattern) =>\n                    minimatch(safeDecode(key), safeDecode(pattern)),\n                  ),\n              )\n              .fromPairs()\n              .value();\n\n            if (!Object.keys(processableData).length) {\n              await fileIoLimiter(async () => {\n                // re-push in case some of the unlocalizable / meta data changed\n                await bucketLoader.push(assignedTask.targetLocale, targetData);\n              });\n              return {\n                status: \"skipped\",\n                pathPattern: assignedTask.bucketPathPattern,\n                sourceLocale: assignedTask.sourceLocale,\n                targetLocale: assignedTask.targetLocale,\n              } satisfies CmdRunTaskResult;\n            }\n\n            const relevantHints = _.pick(hints, Object.keys(processableData));\n            const processedTargetData = await args.ctx.localizer!.localize(\n              {\n                sourceLocale: assignedTask.sourceLocale,\n                targetLocale: assignedTask.targetLocale,\n                sourceData,\n                // When --force is used, exclude previous translations from reference to ensure fresh translations\n                targetData: args.ctx.flags.force ? {} : targetData,\n                processableData,\n                hints: relevantHints,\n                filePath: assignedTask.bucketPathPattern,\n              },\n              async (progress, _sourceChunk, processedChunk) => {\n                // write translated chunks as they are received from LLM\n                await fileIoLimiter(async () => {\n                  // pull the latest source data before pushing for buckets that store all locales in a single file\n                  await bucketLoader.pull(assignedTask.sourceLocale);\n                  // pull the latest target data to include all already processed chunks\n                  const latestTargetData = await bucketLoader.pull(\n                    assignedTask.targetLocale,\n                  );\n\n                  // add the new chunk to target data\n                  const _partialData = _.merge(\n                    {},\n                    latestTargetData,\n                    processedChunk,\n                  );\n                  // process renamed keys\n                  const finalChunkTargetData = processRenamedKeys(\n                    delta,\n                    _partialData,\n                  );\n                  // push final chunk to the target locale\n                  await bucketLoader.push(\n                    assignedTask.targetLocale,\n                    finalChunkTargetData,\n                  );\n                });\n\n                subTask.title = createWorkerStatusMessage({\n                  assignedTask,\n                  percentage: progress,\n                });\n              },\n            );\n\n            const finalTargetData = _.merge(\n              {},\n              sourceData,\n              targetData,\n              processedTargetData,\n            );\n            const finalRenamedTargetData = processRenamedKeys(\n              delta,\n              finalTargetData,\n            );\n\n            await fileIoLimiter(async () => {\n              // not all localizers have progress callback (eg. explicit localizer),\n              // the final target data might not be pushed yet - push now to ensure it's up to date\n              await bucketLoader.pull(assignedTask.sourceLocale);\n              await bucketLoader.push(\n                assignedTask.targetLocale,\n                finalRenamedTargetData,\n              );\n\n              const checksums =\n                await deltaProcessor.createChecksums(sourceData);\n              if (!args.ctx.flags.targetLocale?.length) {\n                await deltaProcessor.saveChecksums(checksums);\n              }\n            });\n\n            return {\n              status: \"success\",\n              pathPattern: assignedTask.bucketPathPattern,\n              sourceLocale: assignedTask.sourceLocale,\n              targetLocale: assignedTask.targetLocale,\n            } satisfies CmdRunTaskResult;\n          } catch (error) {\n            return {\n              status: \"error\",\n              error: error as Error,\n              pathPattern: assignedTask.bucketPathPattern,\n              sourceLocale: assignedTask.sourceLocale,\n              targetLocale: assignedTask.targetLocale,\n            } satisfies CmdRunTaskResult;\n          }\n        });\n\n        args.ctx.results.set(assignedTask, taskResult);\n      }\n\n      subTask.title = \"Done\";\n    },\n  };\n}\n\nfunction countTasks(\n  ctx: CmdRunContext,\n  predicate: (task: CmdRunTask, result: CmdRunTaskResult) => boolean,\n) {\n  return Array.from(ctx.results.entries()).filter(([task, result]) =>\n    predicate(task, result),\n  ).length;\n}\n\nfunction processRenamedKeys(delta: Delta, targetData: Record<string, string>) {\n  return _.chain(targetData)\n    .entries()\n    .map(([key, value]) => {\n      const renaming = delta.renamed.find(([oldKey]) => oldKey === key);\n      if (!renaming) {\n        return [key, value];\n      }\n      return [renaming[1], value];\n    })\n    .fromPairs()\n    .value();\n}\n","import * as chokidar from \"chokidar\";\nimport chalk from \"chalk\";\nimport { minimatch } from \"minimatch\";\nimport { colors } from \"../../constants\";\nimport { CmdRunContext } from \"./_types\";\nimport plan from \"./plan\";\nimport execute from \"./execute\";\nimport { renderSummary } from \"../../utils/ui\";\nimport { getBuckets } from \"../../utils/buckets\";\n\ninterface WatchState {\n  isRunning: boolean;\n  pendingChanges: Set<string>;\n  debounceTimer?: NodeJS.Timeout;\n}\n\nexport default async function watch(ctx: CmdRunContext) {\n  const debounceDelay = ctx.flags.debounce || 5000; // Use configured debounce or 5s default\n\n  console.log(chalk.hex(colors.orange)(\"[Watch Mode]\"));\n  console.log(\n    `👀 Watching for changes... (Press ${chalk.yellow(\"Ctrl+C\")} to stop)`,\n  );\n  console.log(chalk.dim(`   Debounce delay: ${debounceDelay}ms`));\n  console.log(\"\");\n\n  const state: WatchState = {\n    isRunning: false,\n    pendingChanges: new Set(),\n  };\n\n  // Get all source file patterns to watch\n  const watchPatterns = await getWatchPatterns(ctx);\n\n  if (watchPatterns.length === 0) {\n    console.log(chalk.yellow(\"⚠️  No source files found to watch\"));\n    return;\n  }\n\n  console.log(chalk.dim(`Watching ${watchPatterns.length} file pattern(s):`));\n  watchPatterns.forEach((pattern) => {\n    console.log(chalk.dim(`  • ${pattern}`));\n  });\n  console.log(\"\");\n\n  // Initialize file watcher\n  const watcher = chokidar.watch(watchPatterns, {\n    ignoreInitial: true,\n    persistent: true,\n    awaitWriteFinish: {\n      stabilityThreshold: 500,\n      pollInterval: 100,\n    },\n  });\n\n  // Handle file changes\n  watcher.on(\"change\", (path) => {\n    handleFileChange(path, state, ctx);\n  });\n\n  watcher.on(\"add\", (path) => {\n    handleFileChange(path, state, ctx);\n  });\n\n  watcher.on(\"unlink\", (path) => {\n    handleFileChange(path, state, ctx);\n  });\n\n  watcher.on(\"error\", (error) => {\n    console.error(\n      chalk.red(\n        `Watch error: ${error instanceof Error ? error.message : String(error)}`,\n      ),\n    );\n  });\n\n  // Handle graceful shutdown\n  process.on(\"SIGINT\", () => {\n    console.log(chalk.yellow(\"\\n\\n🛑 Stopping watch mode...\"));\n    watcher.close();\n    process.exit(0);\n  });\n\n  // Keep the process running\n  await new Promise(() => {}); // Never resolves, keeps process alive\n}\n\nasync function getWatchPatterns(ctx: CmdRunContext): Promise<string[]> {\n  if (!ctx.config) return [];\n\n  const buckets = getBuckets(ctx.config);\n  const patterns: string[] = [];\n\n  for (const bucket of buckets) {\n    // Skip if specific buckets are filtered\n    if (ctx.flags.bucket && !ctx.flags.bucket.includes(bucket.type)) {\n      continue;\n    }\n\n    for (const bucketPath of bucket.paths) {\n      // Skip if specific files are filtered\n      if (ctx.flags.file) {\n        if (\n          !ctx.flags.file.some(\n            (f) =>\n              bucketPath.pathPattern.includes(f) ||\n              minimatch(bucketPath.pathPattern, f),\n          )\n        ) {\n          continue;\n        }\n      }\n\n      // Get the source locale pattern (replace [locale] with source locale)\n      const sourceLocale = ctx.flags.sourceLocale || ctx.config.locale.source;\n      const sourcePattern = bucketPath.pathPattern.replace(\n        \"[locale]\",\n        sourceLocale,\n      );\n\n      patterns.push(sourcePattern);\n    }\n  }\n\n  return patterns;\n}\n\nfunction handleFileChange(\n  filePath: string,\n  state: WatchState,\n  ctx: CmdRunContext,\n) {\n  const debounceDelay = ctx.flags.debounce || 5000; // Use configured debounce or 5s default\n\n  state.pendingChanges.add(filePath);\n\n  console.log(chalk.dim(`📝 File changed: ${filePath}`));\n\n  // Clear existing debounce timer\n  if (state.debounceTimer) {\n    clearTimeout(state.debounceTimer);\n  }\n\n  // Set new debounce timer\n  state.debounceTimer = setTimeout(async () => {\n    if (state.isRunning) {\n      console.log(\n        chalk.yellow(\"⏳ Translation already in progress, skipping...\"),\n      );\n      return;\n    }\n\n    await triggerRetranslation(state, ctx);\n  }, debounceDelay);\n}\n\nasync function triggerRetranslation(state: WatchState, ctx: CmdRunContext) {\n  if (state.isRunning) return;\n\n  state.isRunning = true;\n\n  try {\n    const changedFiles = Array.from(state.pendingChanges);\n    state.pendingChanges.clear();\n\n    console.log(chalk.hex(colors.green)(\"\\n🔄 Triggering retranslation...\"));\n    console.log(chalk.dim(`Changed files: ${changedFiles.join(\", \")}`));\n    console.log(\"\");\n\n    // Create a new context for this run (preserve original flags but reset tasks/results)\n    const runCtx: CmdRunContext = {\n      ...ctx,\n      tasks: [],\n      results: new Map(),\n    };\n\n    // Re-run the translation pipeline\n    await plan(runCtx);\n\n    if (runCtx.tasks.length === 0) {\n      console.log(chalk.dim(\"✨ No translation tasks needed\"));\n    } else {\n      await execute(runCtx);\n      await renderSummary(runCtx.results);\n    }\n\n    console.log(chalk.hex(colors.green)(\"✅ Retranslation completed\"));\n    console.log(chalk.dim(\"👀 Continuing to watch for changes...\\n\"));\n  } catch (error: any) {\n    console.error(chalk.red(`❌ Retranslation failed: ${error.message}`));\n    console.log(chalk.dim(\"👀 Continuing to watch for changes...\\n\"));\n  } finally {\n    state.isRunning = false;\n  }\n}\n","import { bucketTypeSchema, I18nConfig, bucketTypes } from \"@lingo.dev/_spec\";\nimport { z } from \"zod\";\nimport { ILocalizer } from \"../../localizer/_types\";\n\nexport type CmdRunContext = {\n  flags: CmdRunFlags;\n  config: I18nConfig | null;\n  localizer: ILocalizer | null;\n  tasks: CmdRunTask[];\n  results: Map<CmdRunTask, CmdRunTaskResult>;\n};\n\nexport type CmdRunTaskResult = {\n  status: \"success\" | \"error\" | \"skipped\";\n  error?: Error;\n  pathPattern?: string;\n  sourceLocale?: string;\n  targetLocale?: string;\n};\n\nexport type CmdRunTask = {\n  sourceLocale: string;\n  targetLocale: string;\n  bucketType: (typeof bucketTypes)[number];\n  bucketPathPattern: string;\n  injectLocale: string[];\n  lockedKeys: string[];\n  lockedPatterns: string[];\n  ignoredKeys: string[];\n  preservedKeys: string[];\n  localizableKeys: string[];\n  onlyKeys: string[];\n  formatter?: \"prettier\" | \"biome\";\n  keyColumn?: string;\n};\n\nexport const flagsSchema = z.object({\n  bucket: z.array(bucketTypeSchema).optional(),\n  key: z.array(z.string()).optional(),\n  file: z.array(z.string()).optional(),\n  apiKey: z.string().optional(),\n  force: z.boolean().optional(),\n  frozen: z.boolean().optional(),\n  verbose: z.boolean().optional(),\n  strict: z.boolean().optional(),\n  interactive: z.boolean().prefault(false),\n  concurrency: z.number().positive().prefault(10),\n  debug: z.boolean().prefault(false),\n  sourceLocale: z.string().optional(),\n  targetLocale: z.array(z.string()).optional(),\n  watch: z.boolean().prefault(false),\n  debounce: z.number().positive().prefault(5000), // 5 seconds default\n  sound: z.boolean().optional(),\n  pseudo: z.boolean().optional(),\n});\nexport type CmdRunFlags = z.infer<typeof flagsSchema>;\n","import chalk from \"chalk\";\nimport { Listr } from \"listr2\";\nimport _ from \"lodash\";\nimport { minimatch } from \"minimatch\";\n\nimport { colors } from \"../../constants\";\nimport { CmdRunContext } from \"./_types\";\nimport { commonTaskRendererOptions } from \"./_const\";\nimport { getBuckets } from \"../../utils/buckets\";\nimport createBucketLoader from \"../../loaders\";\nimport { createDeltaProcessor } from \"../../utils/delta\";\nimport { resolveOverriddenLocale } from \"@lingo.dev/_spec\";\n\nexport default async function frozen(input: CmdRunContext) {\n  console.log(chalk.hex(colors.orange)(\"[Frozen]\"));\n\n  // Prepare filtered buckets consistently with the planning step\n  let buckets = getBuckets(input.config!);\n  if (input.flags.bucket?.length) {\n    buckets = buckets.filter((b) => input.flags.bucket!.includes(b.type));\n  }\n\n  if (input.flags.file?.length) {\n    buckets = buckets\n      .map((bucket: any) => {\n        const paths = bucket.paths.filter((p: any) =>\n          input.flags.file!.some(\n            (f) => p.pathPattern.includes(f) || minimatch(p.pathPattern, f),\n          ),\n        );\n        return { ...bucket, paths };\n      })\n      .filter((bucket: any) => bucket.paths.length > 0);\n  }\n\n  const _sourceLocale = input.flags.sourceLocale || input.config!.locale.source;\n  const _targetLocales =\n    input.flags.targetLocale || input.config!.locale.targets;\n\n  return new Listr<CmdRunContext>(\n    [\n      {\n        title: \"Setting up localization cache\",\n        task: async (_ctx, task) => {\n          const checkLockfileProcessor = createDeltaProcessor(\"\");\n          const lockfileExists =\n            await checkLockfileProcessor.checkIfLockExists();\n          if (!lockfileExists) {\n            for (const bucket of buckets) {\n              for (const bucketPath of bucket.paths) {\n                const resolvedSourceLocale = resolveOverriddenLocale(\n                  _sourceLocale,\n                  bucketPath.delimiter,\n                );\n\n                const loader = createBucketLoader(\n                  bucket.type,\n                  bucketPath.pathPattern,\n                  {\n                    defaultLocale: resolvedSourceLocale,\n                    injectLocale: bucket.injectLocale,\n                    formatter: input.config!.formatter,\n                    keyColumn: bucket.keyColumn,\n                  },\n                  bucket.lockedKeys,\n                  bucket.lockedPatterns,\n                  bucket.ignoredKeys,\n                  bucket.preservedKeys,\n                  bucket.localizableKeys,\n                );\n                loader.setDefaultLocale(resolvedSourceLocale);\n                await loader.init();\n\n                const sourceData = await loader.pull(_sourceLocale);\n\n                const delta = createDeltaProcessor(bucketPath.pathPattern);\n                const checksums = await delta.createChecksums(sourceData);\n                await delta.saveChecksums(checksums);\n              }\n            }\n            task.title = \"Localization cache initialized\";\n          } else {\n            task.title = \"Localization cache loaded\";\n          }\n        },\n      },\n      {\n        title: \"Validating frozen state\",\n        enabled: () => !!input.flags.frozen,\n        task: async (_ctx, task) => {\n          for (const bucket of buckets) {\n            for (const bucketPath of bucket.paths) {\n              const resolvedSourceLocale = resolveOverriddenLocale(\n                _sourceLocale,\n                bucketPath.delimiter,\n              );\n\n              const loader = createBucketLoader(\n                bucket.type,\n                bucketPath.pathPattern,\n                {\n                  defaultLocale: resolvedSourceLocale,\n                  returnUnlocalizedKeys: true,\n                  injectLocale: bucket.injectLocale,\n                  keyColumn: bucket.keyColumn,\n                },\n                bucket.lockedKeys,\n                bucket.lockedPatterns,\n                bucket.ignoredKeys,\n                bucket.preservedKeys,\n                bucket.localizableKeys,\n              );\n              loader.setDefaultLocale(resolvedSourceLocale);\n              await loader.init();\n\n              const { unlocalizable: srcUnlocalizable, ...src } =\n                await loader.pull(_sourceLocale);\n\n              const delta = createDeltaProcessor(bucketPath.pathPattern);\n              const sourceChecksums = await delta.createChecksums(src);\n              const savedChecksums = await delta.loadChecksums();\n\n              const updatedSourceData = _.pickBy(\n                src,\n                (value, key) => sourceChecksums[key] !== savedChecksums[key],\n              );\n              if (Object.keys(updatedSourceData).length > 0) {\n                throw new Error(\n                  `Localization data has changed; please update i18n.lock or run without --frozen. Details: Source file has been updated.`,\n                );\n              }\n\n              for (const _tgt of _targetLocales) {\n                const resolvedTargetLocale = resolveOverriddenLocale(\n                  _tgt,\n                  bucketPath.delimiter,\n                );\n                const { unlocalizable: tgtUnlocalizable, ...tgt } =\n                  await loader.pull(resolvedTargetLocale);\n\n                const missingKeys = _.difference(\n                  Object.keys(src),\n                  Object.keys(tgt),\n                );\n                if (missingKeys.length > 0) {\n                  throw new Error(\n                    `Localization data has changed; please update i18n.lock or run without --frozen. Details: Target file is missing translations.`,\n                  );\n                }\n\n                const extraKeys = _.difference(\n                  Object.keys(tgt),\n                  Object.keys(src),\n                );\n                if (extraKeys.length > 0) {\n                  throw new Error(\n                    `Localization data has changed; please update i18n.lock or run without --frozen. Details: Target file has extra translations not present in the source file.`,\n                  );\n                }\n\n                const unlocalizableDataDiff = !_.isEqual(\n                  srcUnlocalizable,\n                  tgtUnlocalizable,\n                );\n                if (unlocalizableDataDiff) {\n                  throw new Error(\n                    `Localization data has changed; please update i18n.lock or run without --frozen. Details: Unlocalizable data (such as booleans, dates, URLs, etc.) do not match.`,\n                  );\n                }\n              }\n            }\n          }\n\n          task.title = \"No lockfile updates required\";\n        },\n      },\n    ],\n    {\n      rendererOptions: commonTaskRendererOptions,\n    },\n  ).run(input);\n}\n","import { CmdRunTask, CmdRunTaskResult } from \"./_types\";\n\nexport function applyRunExitCode(results: Map<CmdRunTask, CmdRunTaskResult>) {\n  const hasErrors = Array.from(results.values()).some(\n    (r) => r.status === \"error\",\n  );\n  if (hasErrors) {\n    process.exitCode = 1;\n  }\n  return hasErrors;\n}\n","import { CmdRunContext } from \"./_types\";\nimport { UserIdentity } from \"../../utils/observability\";\n\n/**\n * Determines the user's identity for tracking purposes.\n * Returns null if using BYOK mode or if authentication fails.\n */\nexport async function determineUserIdentity(\n  ctx: CmdRunContext,\n): Promise<UserIdentity> {\n  const isByokMode = !!ctx.config?.provider;\n\n  if (isByokMode) {\n    return null;\n  } else {\n    try {\n      const authStatus = await ctx.localizer?.checkAuth();\n      if (!authStatus?.username || !authStatus?.userId) return null;\n      return {\n        email: authStatus.username,\n        id: authStatus.userId,\n      };\n    } catch {\n      return null;\n    }\n  }\n}\n","import { execSync } from \"child_process\";\nimport bbLib from \"bitbucket\";\nimport Z from \"zod\";\nimport { PlatformKit } from \"./_base\";\n\nconst { Bitbucket } = bbLib;\n\ninterface BitbucketConfig {\n  baseBranchName: string;\n  repositoryOwner: string;\n  repositoryName: string;\n  bbToken?: string;\n}\n\nexport class BitbucketPlatformKit extends PlatformKit<BitbucketConfig> {\n  private _bb?: ReturnType<typeof Bitbucket>;\n\n  private get bb() {\n    if (!this._bb) {\n      this._bb = new Bitbucket({\n        auth: { token: this.platformConfig.bbToken || \"\" },\n      });\n    }\n    return this._bb;\n  }\n\n  async branchExists({ branch }: { branch: string }) {\n    return await this.bb.repositories\n      .getBranch({\n        workspace: this.platformConfig.repositoryOwner,\n        repo_slug: this.platformConfig.repositoryName,\n        name: branch,\n      })\n      .then((r) => r.data)\n      .then((v) => !!v)\n      .catch((r) => (r.status === 404 ? false : Promise.reject(r)));\n  }\n\n  async getOpenPullRequestNumber({ branch }: { branch: string }) {\n    return await this.bb.repositories\n      .listPullRequests({\n        workspace: this.platformConfig.repositoryOwner,\n        repo_slug: this.platformConfig.repositoryName,\n        state: \"OPEN\",\n      })\n      .then(({ data: { values } }) => {\n        // TODO: we might need to handle pagination in future\n        // bitbucket API does not support filtering pull requests\n        // https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/#api-repositories-workspace-repo-slug-pullrequests-get\n        return values?.find(\n          ({ source, destination }) =>\n            source?.branch?.name === branch &&\n            destination?.branch?.name === this.platformConfig.baseBranchName,\n        );\n      })\n      .then((pr) => pr?.id);\n  }\n\n  async closePullRequest({ pullRequestNumber }: { pullRequestNumber: number }) {\n    await this.bb.repositories.declinePullRequest({\n      workspace: this.platformConfig.repositoryOwner,\n      repo_slug: this.platformConfig.repositoryName,\n      pull_request_id: pullRequestNumber,\n    });\n  }\n\n  async createPullRequest({\n    title,\n    body,\n    head,\n  }: {\n    title: string;\n    body?: string;\n    head: string;\n  }) {\n    return await this.bb.repositories\n      .createPullRequest({\n        workspace: this.platformConfig.repositoryOwner,\n        repo_slug: this.platformConfig.repositoryName,\n        _body: {\n          title,\n          description: body,\n          source: { branch: { name: head } },\n          destination: { branch: { name: this.platformConfig.baseBranchName } },\n        } as any,\n      })\n      .then(({ data }) => data.id ?? 0);\n  }\n\n  async commentOnPullRequest({\n    pullRequestNumber,\n    body,\n  }: {\n    pullRequestNumber: number;\n    body: string;\n  }) {\n    await this.bb.repositories.createPullRequestComment({\n      workspace: this.platformConfig.repositoryOwner,\n      repo_slug: this.platformConfig.repositoryName,\n      pull_request_id: pullRequestNumber,\n      _body: {\n        content: {\n          raw: body,\n        },\n      } as any,\n    });\n  }\n\n  async gitConfig() {\n    execSync(\"git config --unset http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy\", {\n      stdio: \"inherit\",\n    });\n    execSync(\n      \"git config http.${BITBUCKET_GIT_HTTP_ORIGIN}.proxy http://host.docker.internal:29418/\",\n      {\n        stdio: \"inherit\",\n      },\n    );\n  }\n\n  get platformConfig() {\n    const env = Z.object({\n      BITBUCKET_BRANCH: Z.string(),\n      BITBUCKET_REPO_FULL_NAME: Z.string(),\n      BB_TOKEN: Z.string().optional(),\n    }).parse(process.env);\n\n    const [repositoryOwner, repositoryName] =\n      env.BITBUCKET_REPO_FULL_NAME.split(\"/\");\n\n    return {\n      baseBranchName: env.BITBUCKET_BRANCH,\n      repositoryOwner,\n      repositoryName,\n      bbToken: env.BB_TOKEN,\n    };\n  }\n\n  buildPullRequestUrl(pullRequestNumber: number) {\n    const { repositoryOwner, repositoryName } = this.platformConfig;\n    return `https://bitbucket.org/${repositoryOwner}/${repositoryName}/pull-requests/${pullRequestNumber}`;\n  }\n}\n","import { execSync } from \"child_process\";\nimport Z from \"zod\";\n\nconst defaultMessage = \"feat: update translations via @lingodotdev\";\n\ninterface BasePlatformConfig {\n  baseBranchName: string;\n  repositoryOwner: string;\n  repositoryName: string;\n}\n\nexport abstract class PlatformKit<\n  PlatformConfig extends BasePlatformConfig = BasePlatformConfig,\n> {\n  abstract branchExists(props: { branch: string }): Promise<boolean>;\n\n  abstract getOpenPullRequestNumber(props: {\n    branch: string;\n  }): Promise<number | undefined>;\n\n  abstract closePullRequest(props: {\n    pullRequestNumber: number;\n  }): Promise<void>;\n\n  abstract createPullRequest(props: {\n    head: string;\n    title: string;\n    body?: string;\n  }): Promise<number>;\n\n  abstract commentOnPullRequest(props: {\n    pullRequestNumber: number;\n    body: string;\n  }): Promise<void>;\n\n  abstract get platformConfig(): PlatformConfig;\n\n  abstract buildPullRequestUrl(pullRequestNumber: number): string;\n\n  gitConfig(token?: string, repoUrl?: string) {\n    if (token && repoUrl) {\n      execSync(`git remote set-url origin ${repoUrl}`, {\n        stdio: \"inherit\",\n      });\n    }\n  }\n\n  get config() {\n    const env = Z.object({\n      LINGO_API_KEY: Z.string().optional(),\n      LINGODOTDEV_API_KEY: Z.string().optional(),\n      LINGODOTDEV_PULL_REQUEST: Z.preprocess(\n        (val) => val === \"true\" || val === true,\n        Z.boolean(),\n      ),\n      LINGODOTDEV_COMMIT_MESSAGE: Z.string().optional(),\n      LINGODOTDEV_PULL_REQUEST_TITLE: Z.string().optional(),\n      LINGODOTDEV_COMMIT_AUTHOR_NAME: Z.string().optional(),\n      LINGODOTDEV_COMMIT_AUTHOR_EMAIL: Z.string().optional(),\n      LINGODOTDEV_WORKING_DIRECTORY: Z.string().optional(),\n      LINGODOTDEV_PROCESS_OWN_COMMITS: Z.preprocess(\n        (val) => val === \"true\" || val === true,\n        Z.boolean(),\n      ).optional(),\n      LINGODOTDEV_GPG_SIGN: Z.preprocess(\n        (val) => val === \"true\" || val === true,\n        Z.boolean(),\n      ).optional(),\n    }).parse(process.env);\n\n    return {\n      replexicaApiKey: env.LINGO_API_KEY || env.LINGODOTDEV_API_KEY || \"\",\n      isPullRequestMode: env.LINGODOTDEV_PULL_REQUEST,\n      commitMessage: env.LINGODOTDEV_COMMIT_MESSAGE || defaultMessage,\n      pullRequestTitle: env.LINGODOTDEV_PULL_REQUEST_TITLE || defaultMessage,\n      commitAuthorName: env.LINGODOTDEV_COMMIT_AUTHOR_NAME || \"Lingo.dev\",\n      commitAuthorEmail:\n        env.LINGODOTDEV_COMMIT_AUTHOR_EMAIL || \"support@lingo.dev\",\n      workingDir: env.LINGODOTDEV_WORKING_DIRECTORY || \".\",\n      processOwnCommits: env.LINGODOTDEV_PROCESS_OWN_COMMITS || false,\n      gpgSign: env.LINGODOTDEV_GPG_SIGN || false,\n    };\n  }\n}\n\nexport interface IConfig {\n  replexicaApiKey: string;\n  isPullRequestMode: boolean;\n  commitMessage: string;\n  pullRequestTitle: string;\n}\n","import { Octokit } from \"octokit\";\nimport { PlatformKit } from \"./_base\";\nimport Z from \"zod\";\n\nexport class GitHubPlatformKit extends PlatformKit {\n  private _octokit?: Octokit;\n\n  private get octokit(): Octokit {\n    if (!this._octokit) {\n      this._octokit = new Octokit({ auth: this.platformConfig.ghToken });\n    }\n    return this._octokit;\n  }\n\n  async branchExists({ branch }: { branch: string }) {\n    return await this.octokit.rest.repos\n      .getBranch({\n        branch,\n        owner: this.platformConfig.repositoryOwner,\n        repo: this.platformConfig.repositoryName,\n      })\n      .then((r) => r.data)\n      .then((v) => !!v)\n      .catch((r) => (r.status === 404 ? false : Promise.reject(r)));\n  }\n\n  async getOpenPullRequestNumber({ branch }: { branch: string }) {\n    return await this.octokit.rest.pulls\n      .list({\n        head: `${this.platformConfig.repositoryOwner}:${branch}`,\n        owner: this.platformConfig.repositoryOwner,\n        repo: this.platformConfig.repositoryName,\n        base: this.platformConfig.baseBranchName,\n        state: \"open\",\n      })\n      .then(({ data }) => data[0])\n      .then((pr) => pr?.number);\n  }\n\n  async closePullRequest({ pullRequestNumber }: { pullRequestNumber: number }) {\n    await this.octokit.rest.pulls.update({\n      pull_number: pullRequestNumber,\n      owner: this.platformConfig.repositoryOwner,\n      repo: this.platformConfig.repositoryName,\n      state: \"closed\",\n    });\n  }\n\n  async createPullRequest({\n    head,\n    title,\n    body,\n  }: {\n    head: string;\n    title: string;\n    body?: string;\n  }) {\n    return await this.octokit.rest.pulls\n      .create({\n        head,\n        title,\n        body,\n        owner: this.platformConfig.repositoryOwner,\n        repo: this.platformConfig.repositoryName,\n        base: this.platformConfig.baseBranchName,\n      })\n      .then(({ data }) => data.number);\n  }\n\n  async commentOnPullRequest({\n    pullRequestNumber,\n    body,\n  }: {\n    pullRequestNumber: number;\n    body: string;\n  }) {\n    await this.octokit.rest.issues.createComment({\n      issue_number: pullRequestNumber,\n      body,\n      owner: this.platformConfig.repositoryOwner,\n      repo: this.platformConfig.repositoryName,\n    });\n  }\n\n  async gitConfig() {\n    const { ghToken, repositoryOwner, repositoryName } = this.platformConfig;\n    const { processOwnCommits } = this.config;\n\n    if (ghToken && processOwnCommits) {\n      console.log(\n        \"Using provided GH_TOKEN. This will trigger your CI/CD pipeline to run again.\",\n      );\n\n      const url = `https://${ghToken}@github.com/${repositoryOwner}/${repositoryName}.git`;\n\n      super.gitConfig(ghToken, url);\n    }\n  }\n\n  get platformConfig() {\n    const env = Z.object({\n      GITHUB_REPOSITORY: Z.string(),\n      GITHUB_REPOSITORY_OWNER: Z.string(),\n      GITHUB_REF_NAME: Z.string(),\n      GITHUB_HEAD_REF: Z.string(),\n      GH_TOKEN: Z.string().optional(),\n    }).parse(process.env);\n\n    const baseBranchName = !env.GITHUB_REF_NAME.endsWith(\"/merge\")\n      ? env.GITHUB_REF_NAME\n      : env.GITHUB_HEAD_REF;\n\n    return {\n      ghToken: env.GH_TOKEN,\n      baseBranchName,\n      repositoryOwner: env.GITHUB_REPOSITORY_OWNER,\n      repositoryName: env.GITHUB_REPOSITORY.split(\"/\")[1],\n    };\n  }\n\n  buildPullRequestUrl(pullRequestNumber: number) {\n    const { repositoryOwner, repositoryName } = this.platformConfig;\n    return `https://github.com/${repositoryOwner}/${repositoryName}/pull/${pullRequestNumber}`;\n  }\n}\n","import { Gitlab } from \"@gitbeaker/rest\";\nimport Z from \"zod\";\nimport { PlatformKit } from \"./_base\";\n\nconst gl = new Gitlab({ token: \"\" });\n\nexport class GitlabPlatformKit extends PlatformKit {\n  private _gitlab?: InstanceType<typeof Gitlab>;\n\n  constructor() {\n    super();\n\n    // change directory to current repository before executing replexica\n    process.chdir(this.platformConfig.projectDir);\n  }\n\n  private get gitlab(): InstanceType<typeof Gitlab> {\n    if (!this._gitlab) {\n      this._gitlab = new Gitlab({\n        token: this.platformConfig.glToken || \"\",\n      });\n    }\n    return this._gitlab;\n  }\n\n  get platformConfig() {\n    const env = Z.object({\n      GL_TOKEN: Z.string().optional(),\n      CI_COMMIT_BRANCH: Z.string(),\n      CI_MERGE_REQUEST_SOURCE_BRANCH_NAME: Z.string().optional(),\n      CI_PROJECT_NAMESPACE: Z.string(),\n      CI_PROJECT_NAME: Z.string(),\n      CI_PROJECT_ID: Z.string(),\n      CI_PROJECT_DIR: Z.string(),\n      CI_REPOSITORY_URL: Z.string(),\n    }).parse(process.env);\n\n    const config = {\n      glToken: env.GL_TOKEN,\n      baseBranchName:\n        env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME ?? env.CI_COMMIT_BRANCH,\n      repositoryOwner: env.CI_PROJECT_NAMESPACE,\n      repositoryName: env.CI_PROJECT_NAME,\n      gitlabProjectId: env.CI_PROJECT_ID,\n      projectDir: env.CI_PROJECT_DIR,\n      reporitoryUrl: env.CI_REPOSITORY_URL,\n    };\n\n    return config;\n  }\n\n  async branchExists({ branch }: { branch: string }): Promise<boolean> {\n    try {\n      await this.gitlab.Branches.show(\n        this.platformConfig.gitlabProjectId,\n        branch,\n      );\n      return true;\n    } catch {\n      return false;\n    }\n  }\n\n  async getOpenPullRequestNumber({\n    branch,\n  }: {\n    branch: string;\n  }): Promise<number | undefined> {\n    const mergeRequests = await this.gitlab.MergeRequests.all({\n      projectId: this.platformConfig.gitlabProjectId,\n      sourceBranch: branch,\n      state: \"opened\",\n    });\n    return mergeRequests[0]?.iid;\n  }\n\n  async closePullRequest({\n    pullRequestNumber,\n  }: {\n    pullRequestNumber: number;\n  }): Promise<void> {\n    await this.gitlab.MergeRequests.edit(\n      this.platformConfig.gitlabProjectId,\n      pullRequestNumber,\n      {\n        stateEvent: \"close\",\n      },\n    );\n  }\n\n  async createPullRequest({\n    head,\n    title,\n    body,\n  }: {\n    head: string;\n    title: string;\n    body?: string;\n  }): Promise<number> {\n    const mr = await this.gitlab.MergeRequests.create(\n      this.platformConfig.gitlabProjectId,\n      head,\n      this.platformConfig.baseBranchName,\n      title,\n      {\n        description: body,\n      },\n    );\n    return mr.iid;\n  }\n\n  async commentOnPullRequest({\n    pullRequestNumber,\n    body,\n  }: {\n    pullRequestNumber: number;\n    body: string;\n  }): Promise<void> {\n    await this.gitlab.MergeRequestNotes.create(\n      this.platformConfig.gitlabProjectId,\n      pullRequestNumber,\n      body,\n    );\n  }\n\n  gitConfig(): Promise<void> | void {\n    const glToken = this.platformConfig.glToken;\n    const url = `https://oauth2:${glToken}@gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}.git`;\n\n    super.gitConfig(glToken, url);\n  }\n\n  buildPullRequestUrl(pullRequestNumber: number): string {\n    return `https://gitlab.com/${this.platformConfig.repositoryOwner}/${this.platformConfig.repositoryName}/-/merge_requests/${pullRequestNumber}`;\n  }\n}\n","import { BitbucketPlatformKit } from \"./bitbucket\";\nimport { GitHubPlatformKit } from \"./github\";\nimport { GitlabPlatformKit } from \"./gitlab\";\n\nexport const getPlatformKit = () => {\n  if (process.env.BITBUCKET_PIPELINE_UUID) {\n    return new BitbucketPlatformKit();\n  }\n\n  if (process.env.GITHUB_ACTION) {\n    return new GitHubPlatformKit();\n  }\n\n  if (process.env.GITLAB_CI) {\n    return new GitlabPlatformKit();\n  }\n\n  throw new Error(\"This platform is not supported\");\n};\n","import {\n  bucketTypeSchema,\n  I18nConfig,\n  localeCodeSchema,\n  resolveOverriddenLocale,\n} from \"@lingo.dev/_spec\";\nimport { Command } from \"interactive-commander\";\nimport Z from \"zod\";\nimport _ from \"lodash\";\nimport * as path from \"path\";\nimport { getConfig } from \"../utils/config\";\nimport { getSettings } from \"../utils/settings\";\nimport { CLIError } from \"../utils/errors\";\nimport Ora from \"ora\";\nimport createBucketLoader from \"../loaders\";\nimport { createAuthenticator } from \"../utils/auth\";\nimport { getBuckets } from \"../utils/buckets\";\nimport chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport { createDeltaProcessor } from \"../utils/delta\";\nimport trackEvent, { UserIdentity } from \"../utils/observability\";\nimport { minimatch } from \"minimatch\";\nimport { exitGracefully } from \"../utils/exit-gracefully\";\n\n// Define types for our language stats\ninterface LanguageStats {\n  complete: number;\n  missing: number;\n  updated: number;\n  words: number;\n}\n\nexport default new Command()\n  .command(\"status\")\n  .description(\"Show the status of the localization process\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .option(\n    \"--locale <locale>\",\n    \"Limit the report to specific target locales from i18n.json. Repeat the flag to include multiple locales. Defaults to all configured target locales\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--bucket <bucket>\",\n    \"Limit the report to specific bucket types defined in i18n.json (e.g., json, yaml, android). Repeat the flag to include multiple bucket types. Defaults to all buckets\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--file [files...]\",\n    \"Filter the status report to only include files whose paths contain these substrings. Example: 'components' to match any file path containing 'components'\",\n  )\n  .option(\n    \"--force\",\n    \"Force all keys to be counted as needing translation, bypassing change detection. Shows word estimates for a complete retranslation regardless of current translation status\",\n  )\n  .option(\n    \"--verbose\",\n    \"Print detailed output showing missing and updated key counts with example key names for each file and locale\",\n  )\n  .option(\n    \"--api-key <api-key>\",\n    \"Override the API key from settings or environment variables for this run\",\n  )\n  .action(async function (options) {\n    const ora = Ora();\n    const flags = parseFlags(options);\n    let userIdentity: UserIdentity = null;\n\n    try {\n      ora.start(\"Loading configuration...\");\n      const i18nConfig = getConfig();\n      const settings = getSettings(flags.apiKey);\n      ora.succeed(\"Configuration loaded\");\n\n      // Try to authenticate, but continue even if not authenticated\n      try {\n        ora.start(\"Checking authentication status...\");\n        const auth = await tryAuthenticate(settings);\n        if (auth) {\n          userIdentity = { email: auth.email, id: auth.id };\n          ora.succeed(`Authenticated as ${auth.email}`);\n        } else {\n          ora.info(\n            \"Not authenticated. Continuing without authentication. (Run `lingo.dev login` to authenticate)\",\n          );\n        }\n      } catch (error) {\n        ora.info(\"Authentication failed. Continuing without authentication.\");\n      }\n\n      ora.start(\"Validating localization configuration...\");\n      validateParams(i18nConfig, flags);\n      ora.succeed(\"Localization configuration is valid\");\n\n      // Track event with or without authentication\n      trackEvent(userIdentity, \"cmd.status.start\", {\n        i18nConfig,\n        flags,\n      });\n      await new Promise((resolve) => setTimeout(resolve, 50));\n\n      let buckets = getBuckets(i18nConfig!);\n      if (flags.bucket?.length) {\n        buckets = buckets.filter((bucket: any) =>\n          flags.bucket!.includes(bucket.type),\n        );\n      }\n      ora.succeed(\"Buckets retrieved\");\n\n      if (flags.file?.length) {\n        buckets = buckets\n          .map((bucket: any) => {\n            const paths = bucket.paths.filter((path: any) =>\n              flags.file!.find(\n                (file) =>\n                  path.pathPattern?.includes(file) ||\n                  path.pathPattern?.match(file) ||\n                  minimatch(path.pathPattern, file),\n              ),\n            );\n            return { ...bucket, paths };\n          })\n          .filter((bucket: any) => bucket.paths.length > 0);\n        if (buckets.length === 0) {\n          ora.fail(\n            \"No buckets found. All buckets were filtered out by --file option.\",\n          );\n          process.exit(1);\n        } else {\n          ora.info(`\\x1b[36mProcessing only filtered buckets:\\x1b[0m`);\n          buckets.map((bucket: any) => {\n            ora.info(`  ${bucket.type}:`);\n            bucket.paths.forEach((path: any) => {\n              ora.info(`    - ${path.pathPattern}`);\n            });\n          });\n        }\n      }\n\n      const targetLocales = flags.locale?.length\n        ? flags.locale\n        : i18nConfig!.locale.targets;\n\n      // Global stats\n      let totalSourceKeyCount = 0;\n      let uniqueKeysToTranslate = 0;\n      let totalExistingTranslations = 0;\n      const totalWordCount = new Map<string, number>(); // Words per language\n      const languageStats: Record<string, LanguageStats> = {};\n\n      // Initialize per-language stats\n      for (const locale of targetLocales) {\n        languageStats[locale] = {\n          complete: 0,\n          missing: 0,\n          updated: 0,\n          words: 0,\n        };\n        totalWordCount.set(locale, 0);\n      }\n\n      // Per-file stats\n      const fileStats: Record<\n        string,\n        {\n          path: string;\n          sourceKeys: number;\n          wordCount: number;\n          languageStats: Record<\n            string,\n            {\n              complete: number;\n              missing: number;\n              updated: number;\n              words: number;\n            }\n          >;\n        }\n      > = {};\n\n      // Process each bucket\n      for (const bucket of buckets) {\n        try {\n          console.log();\n          ora.info(`Analyzing bucket: ${bucket.type}`);\n\n          for (const bucketPath of bucket.paths) {\n            const bucketOra = Ora({ indent: 2 }).info(\n              `Analyzing path: ${bucketPath.pathPattern}`,\n            );\n\n            const sourceLocale = resolveOverriddenLocale(\n              i18nConfig!.locale.source,\n              bucketPath.delimiter,\n            );\n            const bucketLoader = createBucketLoader(\n              bucket.type,\n              bucketPath.pathPattern,\n              {\n                defaultLocale: sourceLocale,\n                injectLocale: bucket.injectLocale,\n                formatter: i18nConfig!.formatter,\n                keyColumn: bucket.keyColumn,\n              },\n              bucket.lockedKeys,\n              bucket.lockedPatterns,\n              bucket.ignoredKeys,\n              bucket.preservedKeys,\n              bucket.localizableKeys,\n            );\n\n            bucketLoader.setDefaultLocale(sourceLocale);\n            await bucketLoader.init();\n\n            // Initialize file stats\n            const filePath = bucketPath.pathPattern;\n            if (!fileStats[filePath]) {\n              fileStats[filePath] = {\n                path: filePath,\n                sourceKeys: 0,\n                wordCount: 0,\n                languageStats: {},\n              };\n\n              for (const locale of targetLocales) {\n                fileStats[filePath].languageStats[locale] = {\n                  complete: 0,\n                  missing: 0,\n                  updated: 0,\n                  words: 0,\n                };\n              }\n            }\n\n            // Get source data and count source keys\n            const sourceData = await bucketLoader.pull(sourceLocale);\n            const sourceKeys = Object.keys(sourceData);\n            fileStats[filePath].sourceKeys = sourceKeys.length;\n            totalSourceKeyCount += sourceKeys.length;\n\n            // Calculate source word count\n            let sourceWordCount = 0;\n            for (const key of sourceKeys) {\n              const value = sourceData[key];\n              if (typeof value === \"string\") {\n                const words = value.trim().split(/\\s+/).length;\n                sourceWordCount += words;\n              }\n            }\n            fileStats[filePath].wordCount = sourceWordCount;\n\n            // Process each target locale\n            for (const _targetLocale of targetLocales) {\n              const targetLocale = resolveOverriddenLocale(\n                _targetLocale,\n                bucketPath.delimiter,\n              );\n              bucketOra.start(\n                `[${sourceLocale} -> ${targetLocale}] Analyzing translation status...`,\n              );\n\n              let targetData = {};\n              let fileExists = true;\n\n              try {\n                targetData = await bucketLoader.pull(targetLocale);\n              } catch (error) {\n                fileExists = false;\n                bucketOra.info(\n                  `[${sourceLocale} -> ${targetLocale}] Target file not found, assuming all keys need translation.`,\n                );\n              }\n\n              if (!fileExists) {\n                // All keys are missing for this locale\n                fileStats[filePath].languageStats[_targetLocale].missing =\n                  sourceKeys.length;\n                fileStats[filePath].languageStats[_targetLocale].words =\n                  sourceWordCount;\n                languageStats[_targetLocale].missing += sourceKeys.length;\n                languageStats[_targetLocale].words += sourceWordCount;\n                totalWordCount.set(\n                  _targetLocale,\n                  (totalWordCount.get(_targetLocale) || 0) + sourceWordCount,\n                );\n\n                bucketOra.succeed(\n                  `[${sourceLocale} -> ${targetLocale}] ${chalk.red(\n                    `0% complete`,\n                  )} (0/${sourceKeys.length} keys) - file not found`,\n                );\n                continue;\n              }\n\n              // Calculate delta for existing file\n              const deltaProcessor = createDeltaProcessor(\n                bucketPath.pathPattern,\n              );\n              const checksums = await deltaProcessor.loadChecksums();\n              const delta = await deltaProcessor.calculateDelta({\n                sourceData,\n                targetData,\n                checksums,\n              });\n\n              const missingKeys = delta.added;\n              const updatedKeys = delta.updated;\n              const completeKeys = sourceKeys.filter(\n                (key) =>\n                  !missingKeys.includes(key) && !updatedKeys.includes(key),\n              );\n\n              // Count words that need translation\n              let wordsToTranslate = 0;\n              const keysToProcess = flags.force\n                ? sourceKeys\n                : [...missingKeys, ...updatedKeys];\n\n              for (const key of keysToProcess) {\n                const value = sourceData[String(key)];\n                if (typeof value === \"string\") {\n                  const words = value.trim().split(/\\s+/).length;\n                  wordsToTranslate += words;\n                }\n              }\n\n              // Update file stats\n              fileStats[filePath].languageStats[_targetLocale].missing =\n                missingKeys.length;\n              fileStats[filePath].languageStats[_targetLocale].updated =\n                updatedKeys.length;\n              fileStats[filePath].languageStats[_targetLocale].complete =\n                completeKeys.length;\n              fileStats[filePath].languageStats[_targetLocale].words =\n                wordsToTranslate;\n\n              // Update global stats\n              languageStats[_targetLocale].missing += missingKeys.length;\n              languageStats[_targetLocale].updated += updatedKeys.length;\n              languageStats[_targetLocale].complete += completeKeys.length;\n              languageStats[_targetLocale].words += wordsToTranslate;\n              totalWordCount.set(\n                _targetLocale,\n                (totalWordCount.get(_targetLocale) || 0) + wordsToTranslate,\n              );\n\n              // Display progress\n              const totalKeysInFile = sourceKeys.length;\n              const completionPercent = (\n                (completeKeys.length / totalKeysInFile) *\n                100\n              ).toFixed(1);\n\n              if (missingKeys.length === 0 && updatedKeys.length === 0) {\n                bucketOra.succeed(\n                  `[${sourceLocale} -> ${targetLocale}] ${chalk.green(\n                    `100% complete`,\n                  )} (${completeKeys.length}/${totalKeysInFile} keys)`,\n                );\n              } else {\n                const message = `[${sourceLocale} -> ${targetLocale}] ${\n                  parseFloat(completionPercent) > 50\n                    ? chalk.yellow(`${completionPercent}% complete`)\n                    : chalk.red(`${completionPercent}% complete`)\n                } (${completeKeys.length}/${totalKeysInFile} keys)`;\n\n                bucketOra.succeed(message);\n\n                if (flags.verbose) {\n                  if (missingKeys.length > 0) {\n                    console.log(\n                      `    ${chalk.red(`Missing:`)} ${missingKeys.length} keys, ~${wordsToTranslate} words`,\n                    );\n                    console.log(\n                      `    ${chalk.red(`Missing:`)} ${\n                        missingKeys.length\n                      } keys, ~${wordsToTranslate} words`,\n                    );\n                    console.log(\n                      `    ${chalk.dim(\n                        `Example missing: ${missingKeys\n                          .slice(0, 2)\n                          .join(\", \")}${missingKeys.length > 2 ? \"...\" : \"\"}`,\n                      )}`,\n                    );\n                  }\n                  if (updatedKeys.length > 0) {\n                    console.log(\n                      `    ${chalk.yellow(`Updated:`)} ${\n                        updatedKeys.length\n                      } keys that changed in source`,\n                    );\n                  }\n                }\n              }\n            }\n          }\n        } catch (error: any) {\n          ora.fail(`Failed to analyze bucket ${bucket.type}: ${error.message}`);\n        }\n      }\n\n      // Calculate unique keys needing translation and keys fully translated\n      // Count unique keys that need translation\n      const totalKeysNeedingTranslation = Object.values(languageStats).reduce(\n        (sum, stats) => {\n          return sum + stats.missing + stats.updated;\n        },\n        0,\n      );\n\n      // Calculate keys that are completely translated\n      const totalCompletedKeys =\n        totalSourceKeyCount -\n        totalKeysNeedingTranslation / targetLocales.length;\n\n      // Summary output\n      console.log();\n      ora.succeed(chalk.green(`Localization status completed.`));\n\n      // Create a visually impactful main header\n      console.log(chalk.bold.cyan(`\\n╔════════════════════════════════════╗`));\n      console.log(chalk.bold.cyan(`║   LOCALIZATION STATUS REPORT       ║`));\n      console.log(chalk.bold.cyan(`╚════════════════════════════════════╝`));\n\n      // Source content overview\n      console.log(chalk.bold(`\\n📝 SOURCE CONTENT:`));\n      console.log(\n        `• Source language: ${chalk.green(i18nConfig!.locale.source)}`,\n      );\n      console.log(\n        `• Source keys: ${chalk.yellow(\n          totalSourceKeyCount.toString(),\n        )} keys across all files`,\n      );\n\n      // Create a language-by-language breakdown table\n      console.log(chalk.bold(`\\n🌐 LANGUAGE BY LANGUAGE BREAKDOWN:`));\n\n      // Create a new table instance with cli-table3\n      const table = new Table({\n        head: [\n          \"Language\",\n          \"Status\",\n          \"Complete\",\n          \"Missing\",\n          \"Updated\",\n          \"Total Keys\",\n          \"Words to Translate\",\n        ],\n        style: {\n          head: [\"white\"], // White color for headers\n          border: [], // No color for borders\n        },\n        colWidths: [12, 20, 18, 12, 12, 12, 15], // Explicit column widths, making Status column wider\n      });\n\n      // Data rows\n      let totalWordsToTranslate = 0;\n      for (const locale of targetLocales) {\n        const stats = languageStats[locale];\n        const percentComplete = (\n          (stats.complete / totalSourceKeyCount) *\n          100\n        ).toFixed(1);\n        const totalNeeded = stats.missing + stats.updated;\n\n        // Determine status text and color\n        let statusText;\n        let statusColor;\n        if (stats.missing === totalSourceKeyCount) {\n          statusText = \"🔴 Not started\";\n          statusColor = chalk.red;\n        } else if (stats.missing === 0 && stats.updated === 0) {\n          statusText = \"✅ Complete\";\n          statusColor = chalk.green;\n        } else if (parseFloat(percentComplete) > 80) {\n          statusText = \"🟡 Almost done\";\n          statusColor = chalk.yellow;\n        } else if (parseFloat(percentComplete) > 0) {\n          statusText = \"🟠 In progress\";\n          statusColor = chalk.yellow;\n        } else {\n          statusText = \"🔴 Not started\";\n          statusColor = chalk.red;\n        }\n\n        // Create row data\n        const words = totalWordCount.get(locale) || 0;\n        totalWordsToTranslate += words;\n\n        // Add row to the table\n        table.push([\n          locale,\n          statusColor(statusText),\n          `${stats.complete}/${totalSourceKeyCount} (${percentComplete}%)`,\n          stats.missing > 0 ? chalk.red(stats.missing.toString()) : \"0\",\n          stats.updated > 0 ? chalk.yellow(stats.updated.toString()) : \"0\",\n          totalNeeded > 0 ? chalk.magenta(totalNeeded.toString()) : \"0\",\n          words > 0 ? `~${words.toLocaleString()}` : \"0\",\n        ]);\n      }\n\n      // Display the table\n      console.log(table.toString());\n\n      // Total usage summary\n      console.log(chalk.bold(`\\n📊 USAGE ESTIMATE:`));\n      console.log(\n        `• WORDS TO BE CONSUMED: ~${chalk.yellow.bold(\n          totalWordsToTranslate.toLocaleString(),\n        )} words across all languages`,\n      );\n      console.log(\n        `  (Words are counted from source language for keys that need translation in target languages)`,\n      );\n\n      // Breakdown by language if we have multiple languages\n      if (targetLocales.length > 1) {\n        console.log(`• Per-language breakdown:`);\n        for (const locale of targetLocales) {\n          const words = totalWordCount.get(locale) || 0;\n          const percent =\n            totalWordsToTranslate > 0\n              ? ((words / totalWordsToTranslate) * 100).toFixed(1)\n              : \"0.0\";\n          console.log(\n            `  - ${locale}: ~${words.toLocaleString()} words (${percent}% of total)`,\n          );\n        }\n      }\n\n      // Detailed stats if flags.confirm is specified\n      if (flags.confirm && Object.keys(fileStats).length > 0) {\n        console.log(chalk.bold(`\\n📑 BREAKDOWN BY FILE:`));\n\n        Object.entries(fileStats)\n          .sort((a, b) => b[1].wordCount - a[1].wordCount) // Sort by word count\n          .forEach(([path, stats]) => {\n            if (stats.sourceKeys === 0) return;\n\n            console.log(chalk.bold(`\\n• ${path}:`));\n            console.log(\n              `  ${\n                stats.sourceKeys\n              } source keys, ~${stats.wordCount.toLocaleString()} source words`,\n            );\n\n            // Create file detail table\n            const fileTable = new Table({\n              head: [\"Language\", \"Status\", \"Details\"],\n              style: {\n                head: [\"white\"],\n                border: [],\n              },\n              colWidths: [12, 20, 50], // Explicit column widths for file detail table\n            });\n\n            for (const locale of targetLocales) {\n              const langStats = stats.languageStats[locale];\n              const complete = langStats.complete;\n              const total = stats.sourceKeys;\n              const completion = ((complete / total) * 100).toFixed(1);\n\n              let status = \"✅ Complete\";\n              let statusColor = chalk.green;\n\n              if (langStats.missing === total) {\n                status = \"❌ Not started\";\n                statusColor = chalk.red;\n              } else if (langStats.missing > 0 || langStats.updated > 0) {\n                status = `⚠️ ${completion}% complete`;\n                statusColor = chalk.yellow;\n              }\n\n              // Show counts only if there's something missing or updated\n              let details = \"\";\n              if (langStats.missing > 0 || langStats.updated > 0) {\n                const parts = [];\n                if (langStats.missing > 0)\n                  parts.push(`${langStats.missing} missing`);\n                if (langStats.updated > 0)\n                  parts.push(`${langStats.updated} changed`);\n                details = `${parts.join(\", \")}, ~${langStats.words} words`;\n              } else {\n                details = \"All keys translated\";\n              }\n\n              fileTable.push([locale, statusColor(status), details]);\n            }\n\n            console.log(fileTable.toString());\n          });\n      }\n\n      // Find fully translated and missing languages\n      const completeLanguages = targetLocales.filter(\n        (locale) =>\n          languageStats[locale].missing === 0 &&\n          languageStats[locale].updated === 0,\n      );\n\n      const missingLanguages = targetLocales.filter(\n        (locale) => languageStats[locale].complete === 0,\n      );\n\n      // Add optimization tips\n      console.log(chalk.bold.green(`\\n💡 OPTIMIZATION TIPS:`));\n\n      if (missingLanguages.length > 0) {\n        console.log(\n          `• ${chalk.yellow(missingLanguages.join(\", \"))} ${\n            missingLanguages.length === 1 ? \"has\" : \"have\"\n          } no translations yet`,\n        );\n      }\n\n      if (completeLanguages.length > 0) {\n        console.log(\n          `• ${chalk.green(completeLanguages.join(\", \"))} ${\n            completeLanguages.length === 1 ? \"is\" : \"are\"\n          } completely translated`,\n        );\n      }\n\n      // Other tips\n      if (targetLocales.length > 1) {\n        console.log(`• Translating one language at a time reduces complexity`);\n        console.log(\n          `• Try 'lingo.dev@latest i18n --locale ${targetLocales[0]}' to process just one language`,\n        );\n      }\n\n      // Track successful completion\n      trackEvent(userIdentity, \"cmd.status.success\", {\n        i18nConfig,\n        flags,\n        totalSourceKeyCount,\n        languageStats,\n        totalWordsToTranslate,\n        authenticated: !!userIdentity,\n      });\n      await new Promise((resolve) => setTimeout(resolve, 50));\n      exitGracefully();\n    } catch (error: any) {\n      ora.fail(error.message);\n      trackEvent(userIdentity, \"cmd.status.error\", {\n        flags,\n        error: error.message,\n        authenticated: !!userIdentity,\n      });\n      await new Promise((resolve) => setTimeout(resolve, 50));\n      process.exit(1);\n    }\n  });\n\nfunction parseFlags(options: any) {\n  return Z.object({\n    locale: Z.array(localeCodeSchema).optional(),\n    bucket: Z.array(bucketTypeSchema).optional(),\n    force: Z.boolean().optional(),\n    confirm: Z.boolean().optional(),\n    verbose: Z.boolean().optional(),\n    file: Z.array(Z.string()).optional(),\n    apiKey: Z.string().optional(),\n  }).parse(options);\n}\n\nasync function tryAuthenticate(settings: ReturnType<typeof getSettings>) {\n  if (!settings.auth.apiKey) {\n    return null;\n  }\n\n  try {\n    const authenticator = createAuthenticator({\n      apiKey: settings.auth.apiKey,\n      apiUrl: settings.auth.apiUrl,\n    });\n    const user = await authenticator.whoami();\n    return user;\n  } catch (error) {\n    return null;\n  }\n}\n\nfunction validateParams(\n  i18nConfig: I18nConfig | null,\n  flags: ReturnType<typeof parseFlags>,\n) {\n  if (!i18nConfig) {\n    throw new CLIError({\n      message:\n        \"i18n.json not found. Please run `lingo.dev init` to initialize the project.\",\n      docUrl: \"i18nNotFound\",\n    });\n  } else if (!i18nConfig.buckets || !Object.keys(i18nConfig.buckets).length) {\n    throw new CLIError({\n      message:\n        \"No buckets found in i18n.json. Please add at least one bucket containing i18n content.\",\n      docUrl: \"bucketNotFound\",\n    });\n  } else if (\n    flags.locale?.some((locale) => !i18nConfig.locale.targets.includes(locale))\n  ) {\n    throw new CLIError({\n      message: `One or more specified locales do not exist in i18n.json locale.targets. Please add them to the list and try again.`,\n      docUrl: \"localeTargetNotFound\",\n    });\n  } else if (\n    flags.bucket?.some(\n      (bucket) =>\n        !i18nConfig.buckets[bucket as keyof typeof i18nConfig.buckets],\n    )\n  ) {\n    throw new CLIError({\n      message: `One or more specified buckets do not exist in i18n.json. Please add them to the list and try again.`,\n      docUrl: \"bucketNotFound\",\n    });\n  }\n}\n","const STEP_WAIT_INTERVAL = 250;\nconst MAX_WAIT_INTERVAL = 2000;\n\nexport function exitGracefully(elapsedMs = 0) {\n  // Check if there are any pending operations\n  const hasPendingOperations = checkForPendingOperations();\n\n  if (hasPendingOperations && elapsedMs < MAX_WAIT_INTERVAL) {\n    // Wait a bit longer if there are pending operations\n    setTimeout(\n      () => exitGracefully(elapsedMs + STEP_WAIT_INTERVAL),\n      STEP_WAIT_INTERVAL,\n    );\n  } else {\n    // Exit immediately if no pending operations\n    process.exit(0);\n  }\n}\n\nfunction checkForPendingOperations(): boolean {\n  // Check for active handles and requests using internal Node.js methods\n  const activeHandles = (process as any)._getActiveHandles?.() || [];\n  const activeRequests = (process as any)._getActiveRequests?.() || [];\n\n  // Filter out standard handles that are always present\n  const nonStandardHandles = activeHandles.filter((handle: any) => {\n    // Skip standard handles like process.stdin, process.stdout, etc.\n    if (\n      handle === process.stdin ||\n      handle === process.stdout ||\n      handle === process.stderr\n    ) {\n      return false;\n    }\n    // Skip timers that are part of the normal process\n    if (\n      handle &&\n      typeof handle === \"object\" &&\n      \"hasRef\" in handle &&\n      !handle.hasRef()\n    ) {\n      return false;\n    }\n    return true;\n  });\n\n  // Check if there are any file watchers or other async operations\n  const hasFileWatchers = nonStandardHandles.some(\n    (handle: any) => handle && typeof handle === \"object\" && \"close\" in handle,\n  );\n\n  // Check for pending promises or async operations\n  const hasPendingPromises = activeRequests.length > 0;\n\n  return nonStandardHandles.length > 0 || hasFileWatchers || hasPendingPromises;\n}\n","import { Command } from \"interactive-commander\";\nimport * as cp from \"node:child_process\";\nimport figlet from \"figlet\";\nimport chalk from \"chalk\";\nimport { vice } from \"gradient-string\";\n\nexport const colors = {\n  orange: \"#ff6600\",\n  green: \"#6ae300\",\n  blue: \"#0090ff\",\n  yellow: \"#ffcc00\",\n  grey: \"#808080\",\n  red: \"#ff0000\",\n};\n\nexport default new Command()\n  .command(\"may-the-fourth\")\n  .description(\"May the Fourth be with you\")\n  .helpOption(\"-h, --help\", \"Show help\")\n  .action(async () => {\n    await renderClear();\n    await renderBanner();\n    await renderSpacer();\n\n    console.log(chalk.hex(colors.yellow)(\"Loading the Star Wars movie...\"));\n    await renderSpacer();\n\n    await new Promise<void>((resolve, reject) => {\n      const ssh = cp.spawn(\"ssh\", [\"starwarstel.net\"], {\n        stdio: \"inherit\",\n      });\n\n      ssh.on(\"close\", (code) => {\n        if (code !== 0) {\n          console.error(`SSH process exited with code ${code}`);\n          // Optionally reject the promise if the exit code is non-zero\n          // reject(new Error(`SSH process exited with code ${code}`));\n        }\n        resolve(); // Resolve the promise when SSH closes\n      });\n\n      ssh.on(\"error\", (err) => {\n        console.error(\"Failed to start SSH process:\", err);\n        reject(err); // Reject the promise on error\n      });\n    });\n\n    // This code now runs after the SSH process has finished\n    await renderSpacer();\n    console.log(\n      `${chalk.hex(colors.green)(\"We hope you enjoyed it! :)\")} ${chalk.hex(\n        colors.blue,\n      )(\"May the Fourth be with you! 🚀\")}`,\n    );\n    await renderSpacer();\n    console.log(chalk.dim(`---`));\n    await renderSpacer();\n    await renderHero();\n  });\n\nasync function renderClear() {\n  console.log(\"\\x1Bc\");\n}\n\nasync function renderSpacer() {\n  console.log(\" \");\n}\n\nasync function renderBanner() {\n  console.log(\n    vice(\n      figlet.textSync(\"LINGO.DEV\", {\n        font: \"ANSI Shadow\",\n        horizontalLayout: \"default\",\n        verticalLayout: \"default\",\n      }),\n    ),\n  );\n}\n\nasync function renderHero() {\n  console.log(\n    `⚡️ ${chalk.hex(colors.green)(\n      \"Lingo.dev\",\n    )} - open-source, AI-powered i18n CLI for web & mobile localization.`,\n  );\n  console.log(\" \");\n  console.log(chalk.hex(colors.blue)(\"📚 Docs: https://lingo.dev/go/docs\"));\n  console.log(\n    chalk.hex(colors.blue)(\"⭐ Star the repo: https://lingo.dev/go/gh\"),\n  );\n  console.log(\n    chalk.hex(colors.blue)(\"🎮 Join Discord: https://lingo.dev/go/discord\"),\n  );\n}\n","{\n  \"name\": \"lingo.dev\",\n  \"version\": \"0.133.11\",\n  \"description\": \"Lingo.dev CLI\",\n  \"private\": false,\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/lingodotdev/lingo.dev.git\",\n    \"directory\": \"packages/cli\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\",\n    \"provenance\": true\n  },\n  \"type\": \"module\",\n  \"sideEffects\": false,\n  \"exports\": {\n    \"./cli\": {\n      \"types\": \"./build/cli.d.ts\",\n      \"import\": \"./build/cli.mjs\",\n      \"require\": \"./build/cli.cjs\"\n    },\n    \"./sdk\": {\n      \"types\": \"./build/sdk.d.ts\",\n      \"import\": \"./build/sdk.mjs\",\n      \"require\": \"./build/sdk.cjs\"\n    },\n    \"./spec\": {\n      \"types\": \"./build/spec.d.ts\",\n      \"import\": \"./build/spec.mjs\",\n      \"require\": \"./build/spec.cjs\"\n    },\n    \"./compiler\": {\n      \"types\": \"./build/compiler.d.ts\",\n      \"import\": \"./build/compiler.mjs\",\n      \"require\": \"./build/compiler.cjs\"\n    },\n    \"./react\": {\n      \"types\": \"./build/react.d.ts\",\n      \"import\": \"./build/react.mjs\",\n      \"require\": \"./build/react.cjs\"\n    },\n    \"./react-client\": {\n      \"types\": \"./build/react/client.d.ts\",\n      \"import\": \"./build/react/client.mjs\",\n      \"require\": \"./build/react/client.cjs\"\n    },\n    \"./react/client\": {\n      \"types\": \"./build/react/client.d.ts\",\n      \"import\": \"./build/react/client.mjs\",\n      \"require\": \"./build/react/client.cjs\"\n    },\n    \"./react-rsc\": {\n      \"types\": \"./build/react/rsc.d.ts\",\n      \"import\": \"./build/react/rsc.mjs\",\n      \"require\": \"./build/react/rsc.cjs\"\n    },\n    \"./react/rsc\": {\n      \"types\": \"./build/react/rsc.d.ts\",\n      \"import\": \"./build/react/rsc.mjs\",\n      \"require\": \"./build/react/rsc.cjs\"\n    },\n    \"./react-router\": {\n      \"types\": \"./build/react/react-router.d.ts\",\n      \"import\": \"./build/react/react-router.mjs\",\n      \"require\": \"./build/react/react-router.cjs\"\n    },\n    \"./react/react-router\": {\n      \"types\": \"./build/react/react-router.d.ts\",\n      \"import\": \"./build/react/react-router.mjs\",\n      \"require\": \"./build/react/react-router.cjs\"\n    },\n    \"./locale-codes\": {\n      \"types\": \"./build/locale-codes.d.ts\",\n      \"import\": \"./build/locale-codes.mjs\",\n      \"require\": \"./build/locale-codes.cjs\"\n    }\n  },\n  \"typesVersions\": {\n    \"*\": {\n      \"sdk\": [\n        \"./build/sdk.d.ts\"\n      ],\n      \"cli\": [\n        \"./build/cli.d.ts\"\n      ],\n      \"spec\": [\n        \"./build/spec.d.ts\"\n      ],\n      \"compiler\": [\n        \"./build/compiler.d.ts\"\n      ],\n      \"react\": [\n        \"./build/react.d.ts\"\n      ],\n      \"react/client\": [\n        \"./build/react/client.d.ts\"\n      ],\n      \"react/rsc\": [\n        \"./build/react/rsc.d.ts\"\n      ],\n      \"react/react-router\": [\n        \"./build/react/react-router.d.ts\"\n      ],\n      \"locale-codes\": [\n        \"./build/locale-codes.d.ts\"\n      ]\n    }\n  },\n  \"bin\": {\n    \"lingo\": \"./bin/cli.mjs\",\n    \"lingo.dev\": \"./bin/cli.mjs\"\n  },\n  \"files\": [\n    \"bin\",\n    \"build\",\n    \"assets\",\n    \"agents.md\"\n  ],\n  \"scripts\": {\n    \"lingo.dev\": \"node --inspect=9229 ./bin/cli.mjs\",\n    \"dev\": \"tsup --watch\",\n    \"build\": \"pnpm typecheck && tsup\",\n    \"typecheck\": \"tsc --noEmit\",\n    \"test\": \"vitest run\",\n    \"test:watch\": \"vitest\",\n    \"clean\": \"rm -rf build\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"Apache-2.0\",\n  \"dependencies\": {\n    \"@ai-sdk/anthropic\": \"3.0.9\",\n    \"@ai-sdk/google\": \"3.0.6\",\n    \"@ai-sdk/mistral\": \"3.0.5\",\n    \"@ai-sdk/openai\": \"3.0.7\",\n    \"@babel/generator\": \"7.28.5\",\n    \"@babel/parser\": \"7.28.5\",\n    \"@babel/traverse\": \"7.28.5\",\n    \"@babel/types\": \"7.28.5\",\n    \"@biomejs/js-api\": \"4.0.0\",\n    \"@biomejs/wasm-nodejs\": \"2.4.6\",\n    \"@datocms/cma-client-node\": \"4.0.1\",\n    \"@gitbeaker/rest\": \"39.34.3\",\n    \"@inkjs/ui\": \"2.0.0\",\n    \"@inquirer/prompts\": \"7.8.0\",\n    \"@lingo.dev/_compiler\": \"workspace:*\",\n    \"@lingo.dev/_locales\": \"workspace:*\",\n    \"@lingo.dev/_react\": \"workspace:*\",\n    \"@lingo.dev/_sdk\": \"workspace:*\",\n    \"@lingo.dev/_spec\": \"workspace:*\",\n    \"@markdoc/markdoc\": \"0.5.4\",\n    \"@modelcontextprotocol/sdk\": \"1.22.0\",\n    \"@openrouter/ai-sdk-provider\": \"6.0.0-alpha.1\",\n    \"@paralleldrive/cuid2\": \"2.2.2\",\n    \"@types/ejs\": \"3.1.5\",\n    \"ai\": \"6.0.25\",\n    \"bitbucket\": \"2.12.0\",\n    \"chalk\": \"5.6.2\",\n    \"chokidar\": \"4.0.3\",\n    \"cli-progress\": \"3.12.0\",\n    \"cli-table3\": \"0.6.5\",\n    \"cors\": \"2.8.5\",\n    \"csv-parse\": \"5.6.0\",\n    \"csv-stringify\": \"6.6.0\",\n    \"date-fns\": \"4.1.0\",\n    \"dedent\": \"1.7.0\",\n    \"diff\": \"7.0.0\",\n    \"dom-serializer\": \"2.0.0\",\n    \"domhandler\": \"5.0.3\",\n    \"domutils\": \"3.2.2\",\n    \"dotenv\": \"16.4.7\",\n    \"ejs\": \"3.1.10\",\n    \"express\": \"5.1.0\",\n    \"external-editor\": \"3.1.0\",\n    \"figlet\": \"1.9.4\",\n    \"flat\": \"6.0.1\",\n    \"gettext-parser\": \"8.0.0\",\n    \"glob\": \"11.1.0\",\n    \"gradient-string\": \"3.0.0\",\n    \"gray-matter\": \"4.0.3\",\n    \"htmlparser2\": \"10.0.0\",\n    \"ini\": \"5.0.0\",\n    \"ink\": \"4.2.0\",\n    \"ink-progress-bar\": \"3.0.0\",\n    \"ink-spinner\": \"5.0.0\",\n    \"inquirer\": \"12.6.0\",\n    \"interactive-commander\": \"0.5.194\",\n    \"is-url\": \"1.2.4\",\n    \"jsdom\": \"25.0.1\",\n    \"json5\": \"2.2.3\",\n    \"jsonc-parser\": \"3.3.1\",\n    \"jsonrepair\": \"3.13.1\",\n    \"listr2\": \"8.3.2\",\n    \"lodash\": \"4.17.21\",\n    \"marked\": \"15.0.6\",\n    \"mdast-util-from-markdown\": \"2.0.2\",\n    \"mdast-util-gfm\": \"3.1.0\",\n    \"micromark-extension-gfm\": \"3.0.0\",\n    \"node-machine-id\": \"1.1.12\",\n    \"node-webvtt\": \"1.9.4\",\n    \"object-hash\": \"3.0.0\",\n    \"octokit\": \"4.0.2\",\n    \"ollama-ai-provider-v2\": \"2.0.0\",\n    \"open\": \"10.2.0\",\n    \"ora\": \"8.1.1\",\n    \"p-limit\": \"6.2.0\",\n    \"php-array-reader\": \"2.1.2\",\n    \"plist\": \"3.1.0\",\n    \"posthog-node\": \"5.14.0\",\n    \"prettier\": \"3.6.2\",\n    \"react\": \"19.2.3\",\n    \"rehype-stringify\": \"10.0.1\",\n    \"remark-disable-tokenizers\": \"1.1.1\",\n    \"remark-frontmatter\": \"5.0.0\",\n    \"remark-gfm\": \"4.0.1\",\n    \"remark-mdx\": \"3.1.1\",\n    \"remark-mdx-frontmatter\": \"5.2.0\",\n    \"remark-parse\": \"11.0.0\",\n    \"remark-rehype\": \"11.1.2\",\n    \"remark-stringify\": \"11.0.0\",\n    \"sax\": \"1.4.3\",\n    \"srt-parser-2\": \"1.2.3\",\n    \"unified\": \"11.0.5\",\n    \"unist-util-visit\": \"5.0.0\",\n    \"vfile\": \"6.0.3\",\n    \"xliff\": \"6.2.2\",\n    \"xml2js\": \"0.6.2\",\n    \"xpath\": \"0.0.34\",\n    \"yaml\": \"2.8.1\",\n    \"zod\": \"4.1.12\"\n  },\n  \"devDependencies\": {\n    \"@types/babel__generator\": \"7.27.0\",\n    \"@types/chokidar\": \"2.1.7\",\n    \"@types/cli-progress\": \"3.11.6\",\n    \"@types/cors\": \"2.8.19\",\n    \"@types/diff\": \"7.0.0\",\n    \"@types/express\": \"5.0.5\",\n    \"@types/figlet\": \"1.7.0\",\n    \"@types/gettext-parser\": \"4.0.4\",\n    \"@types/glob\": \"8.1.0\",\n    \"@types/ini\": \"4.1.1\",\n    \"@types/is-url\": \"1.2.32\",\n    \"@types/jsdom\": \"21.1.7\",\n    \"@types/lodash\": \"4.17.21\",\n    \"@types/mdast\": \"4.0.4\",\n    \"@types/node\": \"22.10.2\",\n    \"@types/node-gettext\": \"3.0.6\",\n    \"@types/object-hash\": \"3.0.6\",\n    \"@types/plist\": \"3.0.5\",\n    \"@types/react\": \"19.2.7\",\n    \"@types/xml2js\": \"0.4.14\",\n    \"tsup\": \"8.5.1\",\n    \"typescript\": \"5.9.3\",\n    \"vitest\": \"3.1.2\"\n  },\n  \"engines\": {\n    \"node\": \">=18\"\n  },\n  \"packageManager\": \"pnpm@9.12.3\"\n}\n","import { Command } from \"interactive-commander\";\nimport _ from \"lodash\";\nimport Ora from \"ora\";\nimport { getConfig } from \"../utils/config\";\nimport { getBuckets } from \"../utils/buckets\";\nimport { resolveOverriddenLocale } from \"@lingo.dev/_spec\";\nimport createBucketLoader from \"../loaders\";\nimport { minimatch } from \"minimatch\";\nimport { safeDecode } from \"../utils/key-matching\";\nimport { confirm } from \"@inquirer/prompts\";\n\ninterface PurgeOptions {\n  bucket?: string[];\n  file?: string[];\n  key?: string;\n  locale?: string[];\n  yesReally?: boolean;\n}\n\nexport default new Command()\n  .command(\"purge\")\n  .description(\n    \"WARNING: Permanently delete translation entries from bucket path patterns defined in i18n.json. This is a destructive operation that cannot be undone. Without any filters, ALL managed keys will be removed from EVERY target locale.\",\n  )\n  .helpOption(\"-h, --help\", \"Show help\")\n  .option(\n    \"--bucket <bucket>\",\n    \"Limit the purge to specific bucket types defined under `buckets` in i18n.json. Repeat the flag to include multiple bucket types. Defaults to all buckets\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--file [files...]\",\n    \"Filter which file paths to purge by matching against path patterns. Only paths containing any of these values will be processed. Examples: --file messages.json --file admin/\",\n  )\n  .option(\n    \"--key <key>\",\n    \"Filter which keys to delete using prefix matching on dot-separated key paths. Example: 'auth.login' matches all keys starting with auth.login. Omit this option to delete ALL keys. Keys marked as locked or ignored in i18n.json are automatically skipped\",\n    (val: string) => encodeURIComponent(val),\n  )\n  .option(\n    \"--locale <locale>\",\n    \"Limit purging to specific target locale codes from i18n.json. Repeat the flag to include multiple locales. Defaults to all configured target locales. Warning: Including the source locale will delete content from it as well.\",\n    (val: string, prev: string[]) => (prev ? [...prev, val] : [val]),\n  )\n  .option(\n    \"--yes-really\",\n    \"Bypass safety confirmations for destructive operations. Use with extreme caution - this will delete translation keys without asking for confirmation. Intended for automated scripts and CI environments only.\",\n  )\n  .action(async function (options: PurgeOptions) {\n    const ora = Ora();\n    try {\n      ora.start(\"Loading configuration...\");\n      const i18nConfig = getConfig();\n      if (!i18nConfig) {\n        throw new Error(\"i18n.json not found. Please run `lingo.dev init`.\");\n      }\n      ora.succeed(\"Configuration loaded\");\n\n      let buckets = getBuckets(i18nConfig);\n      if (options.bucket && options.bucket.length) {\n        buckets = buckets.filter((bucket) =>\n          options.bucket!.includes(bucket.type),\n        );\n      }\n      if (options.file && options.file.length) {\n        buckets = buckets\n          .map((bucket) => {\n            const paths = bucket.paths.filter((bucketPath) =>\n              options.file?.some((f) => bucketPath.pathPattern.includes(f)),\n            );\n            return { ...bucket, paths };\n          })\n          .filter((bucket) => bucket.paths.length > 0);\n        if (buckets.length === 0) {\n          ora.fail(\"All files were filtered out by --file option.\");\n          process.exit(1);\n        }\n      }\n      const sourceLocale = i18nConfig.locale.source;\n      const targetLocales =\n        options.locale && options.locale.length\n          ? options.locale\n          : i18nConfig.locale.targets;\n      let removedAny = false;\n      for (const bucket of buckets) {\n        console.log();\n        ora.info(`Processing bucket: ${bucket.type}`);\n        for (const bucketPath of bucket.paths) {\n          for (const _targetLocale of targetLocales) {\n            const targetLocale = resolveOverriddenLocale(\n              _targetLocale,\n              bucketPath.delimiter,\n            );\n            const bucketOra = Ora({ indent: 2 }).start(\n              `Processing path: ${bucketPath.pathPattern} [${targetLocale}]`,\n            );\n            try {\n              const bucketLoader = createBucketLoader(\n                bucket.type,\n                bucketPath.pathPattern,\n                {\n                  defaultLocale: sourceLocale,\n                  injectLocale: bucket.injectLocale,\n                  formatter: i18nConfig!.formatter,\n                  keyColumn: bucket.keyColumn,\n                },\n                bucket.lockedKeys,\n                bucket.lockedPatterns,\n                bucket.ignoredKeys,\n                bucket.preservedKeys,\n                bucket.localizableKeys,\n              );\n              await bucketLoader.init();\n              bucketLoader.setDefaultLocale(sourceLocale);\n              await bucketLoader.pull(sourceLocale);\n              let targetData = await bucketLoader.pull(targetLocale);\n              if (!targetData || Object.keys(targetData).length === 0) {\n                bucketOra.info(\n                  `No translations found for ${bucketPath.pathPattern} [${targetLocale}]`,\n                );\n                continue;\n              }\n              let newData = { ...targetData };\n              let keysToRemove: string[] = [];\n              if (options.key) {\n                // minimatch for key patterns\n                keysToRemove = Object.keys(newData).filter((k) =>\n                  minimatch(safeDecode(k), safeDecode(options.key!)),\n                );\n              } else {\n                // No key specified: remove all keys\n                keysToRemove = Object.keys(newData);\n              }\n              if (keysToRemove.length > 0) {\n                // Show what will be deleted\n                if (options.key) {\n                  bucketOra.info(\n                    `About to delete ${keysToRemove.length} key(s) matching '${safeDecode(options.key)}' from ${bucketPath.pathPattern} [${targetLocale}]:\\n  ${keysToRemove.slice(0, 10).join(\", \")}${keysToRemove.length > 10 ? \", ...\" : \"\"}`,\n                  );\n                } else {\n                  bucketOra.info(\n                    `About to delete all (${keysToRemove.length}) keys from ${bucketPath.pathPattern} [${targetLocale}]`,\n                  );\n                }\n\n                if (!options.yesReally) {\n                  bucketOra.warn(\n                    \"This is a destructive operation. If you are sure, type 'y' to continue. (Use --yes-really to skip this check.)\",\n                  );\n                  const confirmed = await confirm({\n                    message: `Delete these keys from ${bucketPath.pathPattern} [${targetLocale}]?`,\n                    default: false,\n                  });\n                  if (!confirmed) {\n                    bucketOra.info(\"Skipped by user.\");\n                    continue;\n                  }\n                }\n                for (const key of keysToRemove) {\n                  delete newData[key];\n                }\n                removedAny = true;\n                await bucketLoader.push(targetLocale, newData);\n                if (options.key) {\n                  bucketOra.succeed(\n                    `Removed ${keysToRemove.length} key(s) matching '${safeDecode(options.key)}' from ${bucketPath.pathPattern} [${targetLocale}]`,\n                  );\n                } else {\n                  bucketOra.succeed(\n                    `Removed all keys (${keysToRemove.length}) from ${bucketPath.pathPattern} [${targetLocale}]`,\n                  );\n                }\n              } else if (options.key) {\n                bucketOra.info(\n                  `No keys matching '${safeDecode(options.key)}' found in ${bucketPath.pathPattern} [${targetLocale}]`,\n                );\n              } else {\n                bucketOra.info(\"No keys to remove.\");\n              }\n            } catch (error) {\n              const err = error as Error;\n              bucketOra.fail(`Failed: ${err.message}`);\n            }\n          }\n        }\n      }\n      if (!removedAny) {\n        ora.info(\"No keys were removed.\");\n      } else {\n        ora.succeed(\"Purge completed.\");\n      }\n    } catch (error) {\n      const err = error as Error;\n      ora.fail(err.message);\n      process.exit(1);\n    }\n  });\n"]}