@fluid-app/fluid-cli-localization 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["languageName","PER_PAGE","MAX_PAGES","MAX_PAGES","client"],"sources":["../src/shared/errors.ts","../src/shared/agreements.ts","../src/shared/cli.ts","../src/shared/languages.ts","../src/shared/parsing.ts","../src/shared/client.ts","../src/countries/atlas.ts","../src/countries/api.ts","../src/countries/compliance.ts","../src/countries/prices.ts","../src/countries/plan.ts","../src/countries/seed.ts","../src/countries/status.ts","../src/shared/walk.ts","../src/countries/walk.ts","../src/translations/resources.ts","../src/translations/storefront.ts","../src/translations/auto-languages.ts","../src/translations/labels.ts","../src/translations/machine.ts","../src/translations/menus.ts","../src/translations/theme.ts","../src/translations/sections.ts","../src/translations/report.ts","../src/translations/autotranslate.ts","../src/translations/save.ts","../src/translations/walk.ts","../src/countries/commands.ts","../src/translations/commands.ts","../src/index.ts"],"sourcesContent":["/** A failure the user can act on, printed as a JSON `error` on stderr. */\nexport class LocalizationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LocalizationError\";\n }\n}\n","import type { LocalizationClient } from \"./client.js\";\nimport { LocalizationError } from \"./errors.js\";\n\nexport interface CompanyAgreement {\n id: number;\n title: string;\n active?: boolean;\n company_country_ids?: number[];\n}\n\nconst MAX_AGREEMENT_PAGES = 50;\n\n/** GET /api/agreements, every page. Refuses rather than returning a partial\n * list, which would make an existing agreement look missing. */\nexport async function listAgreements(\n client: LocalizationClient,\n): Promise<CompanyAgreement[]> {\n const all: CompanyAgreement[] = [];\n for (let page = 1; ; page++) {\n const body = await client.get<{\n agreements?: CompanyAgreement[];\n meta?: { pagination?: { total_pages?: number } };\n }>(\"/api/agreements\", { page, per_page: 100 });\n all.push(...(body.agreements ?? []));\n const totalPages = body.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages) break;\n if (page >= MAX_AGREEMENT_PAGES) {\n throw new LocalizationError(\n `The company has more than ${MAX_AGREEMENT_PAGES * 100} agreements, too many to match by title. Check the agreements in Settings > Agreements.`,\n );\n }\n }\n return all;\n}\n\nexport interface AgreementText {\n id: number;\n title: string;\n description: string;\n /** The locale the text was served in; a missing translation falls back to the default. */\n languageIso: string | null;\n active: boolean | null;\n}\n\ninterface AgreementWire {\n id?: number;\n title?: string | null;\n description?: string | null;\n language_iso?: string | null;\n active?: boolean | null;\n}\n\n/** GET /api/agreements/:id, in `lang` when given. */\nexport async function readAgreement(\n client: LocalizationClient,\n id: number,\n lang?: string,\n): Promise<AgreementText> {\n const data = await client.get<{ agreement?: AgreementWire }>(\n `/api/agreements/${id}`,\n lang ? { language_iso: lang } : undefined,\n );\n const wire = data.agreement ?? {};\n return {\n id,\n title: wire.title ?? \"\",\n description: wire.description ?? \"\",\n languageIso: wire.language_iso ?? null,\n active: wire.active ?? null,\n };\n}\n\n/**\n * Whether an agreement has a translation in `iso`. A read in a language\n * with no translation falls back to the default text and says so in\n * `language_iso`, which is how fluid-admin's translation sheet tells them\n * apart. The title falls back silently, so it can't be compared.\n */\nexport async function agreementTranslated(\n client: LocalizationClient,\n id: number,\n iso: string,\n): Promise<boolean> {\n const text = await readAgreement(client, id, iso);\n return (text.languageIso ?? \"\").toLowerCase() === iso.toLowerCase();\n}\n\n/**\n * Save one language's title and text. The API needs both, non-empty. Only\n * these are sent, so the agreement's checkout settings and countries stay\n * as they are.\n */\nexport async function writeAgreementTranslation(\n client: LocalizationClient,\n id: number,\n lang: string,\n text: { title: string; description: string },\n): Promise<void> {\n if (text.title.trim().length === 0 || text.description.trim().length === 0) {\n throw new LocalizationError(\n \"An agreement translation needs both a title and a description.\",\n );\n }\n await client.put(\n `/api/agreements/${id}?language_iso=${encodeURIComponent(lang)}`,\n {\n agreement: { title: text.title, description: text.description },\n },\n );\n}\n","import prompts from \"prompts\";\n\nimport { LocalizationError } from \"./errors.js\";\n\n/**\n * Changes to live configuration or content need an explicit yes. Scripts and\n * agents pass `--yes`; an interactive terminal is asked. With neither,\n * nothing runs.\n */\nexport async function confirmChange(\n question: string,\n yes: boolean,\n interactive: boolean = Boolean(process.stdin.isTTY),\n): Promise<void> {\n if (yes) return;\n if (!interactive)\n throw new LocalizationError(`${question} Pass --yes to confirm.`);\n const answer = await prompts({\n type: \"confirm\",\n name: \"ok\",\n message: question,\n initial: false,\n });\n if (!answer.ok) throw new LocalizationError(\"Cancelled.\");\n}\n\n/**\n * The API's error message. Endpoints answer in three envelopes:\n * `{ error: { message, details } }`, `{ error: \"...\" }`, and\n * `{ error_message, errors }`.\n */\nexport function errorDetail(err: unknown): {\n message: string;\n details?: unknown;\n} {\n const body = (err as { body?: Record<string, unknown> | null } | null)?.body;\n const error = body?.[\"error\"];\n if (error && typeof error === \"object\") {\n const { message, details } = error as {\n message?: unknown;\n details?: unknown;\n };\n if (typeof message === \"string\") {\n return { message, ...(details ? { details } : {}) };\n }\n }\n if (typeof error === \"string\") return { message: error };\n if (typeof body?.[\"error_message\"] === \"string\") {\n return {\n message: body[\"error_message\"] as string,\n ...(body[\"errors\"] ? { details: body[\"errors\"] } : {}),\n };\n }\n if (typeof body?.[\"message\"] === \"string\")\n return { message: body[\"message\"] as string };\n return { message: err instanceof Error ? err.message : String(err) };\n}\n\n/** One line for a per-item failure report: the API's message and status. */\nexport function failureOf(err: unknown): { error: string; status?: number } {\n const status = (err as { status?: unknown } | null)?.status;\n return {\n error: errorDetail(err).message,\n ...(typeof status === \"number\" ? { status } : {}),\n };\n}\n\n/** Print a failure as JSON on stderr and exit non-zero. */\nexport function fail(err: unknown): void {\n const status = (err as { status?: unknown } | null)?.status;\n const { message, details } = errorDetail(err);\n process.stderr.write(\n JSON.stringify({\n error: message,\n ...(typeof status === \"number\" ? { status } : {}),\n ...(details ? { details } : {}),\n }) + \"\\n\",\n );\n process.exitCode = 1;\n}\n\nexport function run(action: () => Promise<void>): Promise<void> {\n return action().catch(fail);\n}\n\n/** Commander collector for a repeatable option. */\nexport const collect = (value: string, previous: string[] = []): string[] => [\n ...previous,\n value,\n];\n\nexport function positiveInt(raw: string, flag: string): number {\n const value = Number(raw);\n if (!Number.isInteger(value) || value <= 0) {\n throw new LocalizationError(\n `${flag} takes a whole number above 0, not \"${raw}\".`,\n );\n }\n return value;\n}\n","import type { LocalizationClient } from \"./client.js\";\nimport { LocalizationError } from \"./errors.js\";\n\nexport interface Language {\n id: number;\n name: string;\n iso: string;\n active: boolean;\n usersCount: number | null;\n}\n\ninterface LanguageWire {\n id: number;\n name?: string;\n iso?: string;\n active_in_company?: boolean;\n company_users_count?: number | null;\n}\n\nconst LANGUAGES_PATH = \"/api/settings/languages\";\n\nfunction toLanguage(wire: LanguageWire): Language {\n return {\n id: wire.id,\n name: wire.name ?? \"\",\n iso: wire.iso ?? \"\",\n active: wire.active_in_company === true,\n usersCount: wire.company_users_count ?? null,\n };\n}\n\n/** Every language Fluid supports, with whether it is on for this company. */\nexport async function listLanguages(\n client: LocalizationClient,\n activeOnly = false,\n): Promise<Language[]> {\n const data = await client.get<{ languages?: LanguageWire[] }>(\n LANGUAGES_PATH,\n activeOnly ? { active: true } : undefined,\n );\n return (data.languages ?? []).map(toLanguage);\n}\n\nexport function normalizeIso(iso: string): string {\n return iso.trim().toLowerCase();\n}\n\nexport function sameIso(a: string, b: string): boolean {\n return normalizeIso(a) === normalizeIso(b);\n}\n\n/** A supported language by ISO code from a list already read, or undefined. */\nexport function languageIn(\n languages: readonly Language[],\n iso: string,\n): Language | undefined {\n return languages.find((language) => sameIso(language.iso, iso));\n}\n\n/** One supported language by ISO code, or a LocalizationError naming close matches. */\nexport async function findLanguage(\n client: LocalizationClient,\n iso: string,\n): Promise<Language> {\n const languages = await listLanguages(client);\n const match = languageIn(languages, iso);\n if (match) return match;\n const prefix = normalizeIso(iso).split(\"-\")[0] ?? normalizeIso(iso);\n const close = languages\n .filter((language) => normalizeIso(language.iso).startsWith(prefix))\n .map((language) => language.iso);\n throw new LocalizationError(\n `\"${iso}\" isn't a language Fluid supports.` +\n (close.length > 0 ? ` Did you mean ${close.join(\", \")}?` : \"\") +\n \" Run `fluid translations languages` to see every code.\",\n );\n}\n\n/** The language, or a LocalizationError if it isn't on for the company. */\nexport async function requireActiveLanguage(\n client: LocalizationClient,\n iso: string,\n): Promise<Language> {\n const language = await findLanguage(client, iso);\n if (!language.active) {\n throw new LocalizationError(\n `${language.name} (${language.iso}) isn't on for this company, so translations can't be written to it. Run \\`fluid translations enable ${language.iso} --yes\\` first.`,\n );\n }\n return language;\n}\n\n/** Turn a language on. The body is the documented and tested top-level `id`. */\nexport async function enableLanguage(\n client: LocalizationClient,\n language: Language,\n): Promise<Language> {\n const data = await client.post<{ language?: LanguageWire }>(LANGUAGES_PATH, {\n id: language.id,\n });\n return data.language\n ? toLanguage(data.language)\n : { ...language, active: true };\n}\n\n/** Turn a language off. Its saved translations are kept. */\nexport async function disableLanguage(\n client: LocalizationClient,\n language: Language,\n): Promise<Language> {\n await client.delete(`${LANGUAGES_PATH}/${language.id}`);\n return { ...language, active: false };\n}\n","import { readFileSync } from \"node:fs\";\n\nimport { LocalizationError } from \"./errors.js\";\n\n/** Keys that would reach an object's prototype instead of an own property. */\nexport const RESERVED_KEYS: ReadonlySet<string> = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\",\n]);\n\n/** Split a dotted path, refusing empty and reserved segments. */\nexport function keyPath(path: string): string[] {\n const parts = path.split(\".\");\n if (parts.some((p) => p.length === 0)) {\n throw new LocalizationError(`\"${path}\" has an empty key segment.`);\n }\n if (parts.some((p) => RESERVED_KEYS.has(p))) {\n throw new LocalizationError(`\"${path}\" uses a reserved key name.`);\n }\n return parts;\n}\n\nfunction splitAssignment(\n assignment: string,\n example: string,\n): [string, string] {\n const at = assignment.indexOf(\"=\");\n if (at <= 0) {\n throw new LocalizationError(\n `\"${assignment}\" isn't key=value, e.g. ${example}.`,\n );\n }\n return [assignment.slice(0, at).trim(), assignment.slice(at + 1)];\n}\n\n/**\n * Parse `--set field=value` text pairs into a flat object. Values are kept as\n * written, so translated text is never reinterpreted; only the first `=`\n * separates the key, and a theme key keeps its dots (`cart.title=Panier`).\n */\nexport function parseTextAssignments(\n assignments: readonly string[],\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const assignment of assignments) {\n const [key, value] = splitAssignment(\n assignment,\n 'title=\"Mélange de betteraves\"',\n );\n keyPath(key);\n out[key] = value;\n }\n return out;\n}\n\n/**\n * Parse `--set key=value` setting overrides. `legal_settings.cooling_off_period_days=30`\n * nests; values that read as JSON (true, 42, [\"a\"], {\"x\":1}) are parsed and\n * anything else stays a string.\n */\nexport function parseSettingAssignments(\n assignments: readonly string[],\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const assignment of assignments) {\n const [key, raw] = splitAssignment(\n assignment,\n \"tax_inclusive_pricing=false\",\n );\n const path = keyPath(key);\n let node = out;\n for (const part of path.slice(0, -1)) {\n const next = Object.hasOwn(node, part) ? node[part] : undefined;\n if (typeof next !== \"object\" || next === null || Array.isArray(next))\n node[part] = {};\n node = node[part] as Record<string, unknown>;\n }\n node[path[path.length - 1]!] = parseValue(raw);\n }\n return out;\n}\n\nfunction parseValue(raw: string): unknown {\n if (/^(true|false|null|-?\\d+(\\.\\d+)?)$/.test(raw) || /^[[{]/.test(raw)) {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n return raw;\n}\n\n/**\n * Read `--file`: a JSON object of field (or key) to string. Long or HTML\n * values are easier to pass this way than on the command line.\n */\nexport function readValuesFile(path: string): Record<string, string> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, \"utf8\"));\n } catch (error) {\n throw new LocalizationError(\n `Couldn't read ${path} as JSON: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new LocalizationError(\n `${path} must hold a JSON object of field to text.`,\n );\n }\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value !== \"string\") {\n throw new LocalizationError(`${path}: \"${key}\" must be a string.`);\n }\n keyPath(key);\n out[key] = value;\n }\n return out;\n}\n\n/** `--set` and `--file` together, `--set` winning on a clash. */\nexport function collectValues(\n sets: readonly string[],\n file?: string,\n): Record<string, string> {\n return {\n ...(file ? readValuesFile(file) : {}),\n ...parseTextAssignments(sets),\n };\n}\n","/** The slice of the CLI's API client the localization commands use. */\nexport interface LocalizationClient {\n get<T>(endpoint: string, params?: Record<string, unknown>): Promise<T>;\n post<T>(endpoint: string, body?: unknown): Promise<T>;\n put<T>(endpoint: string, body?: unknown): Promise<T>;\n patch<T>(endpoint: string, body?: unknown): Promise<T>;\n delete<T>(endpoint: string): Promise<T>;\n}\n\n/** The HTTP status of an API error, if it carries one. */\nexport function statusOf(error: unknown): number | undefined {\n const status = (error as { status?: unknown } | null)?.status;\n return typeof status === \"number\" ? status : undefined;\n}\n","// Fluid's Country Atlas: the certified, per-market setup profile served at\n// GET /api/v202604/country/atlas/{iso}. This mirrors Mist's\n// country-atlas.service (apps/mist-desktop) so the CLI and Mist read the\n// atlas the same way: the same code aliases, the same summary, and the same\n// honesty rule that a market with no atlas is reported as uncovered rather\n// than filled with guesses.\n\nimport { statusOf, type LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\nexport const ATLAS_PATH_PREFIX = \"/api/v202604/country/atlas/\";\n\nexport interface AtlasChecklistItem {\n id: string;\n label: string;\n description: string;\n}\n\nexport interface AtlasMode {\n overview: string;\n launchChecklist: AtlasChecklistItem[];\n}\n\nexport interface AtlasAgreementTranslation {\n title: string;\n description: string;\n language_iso: string;\n}\n\nexport interface AtlasAgreement {\n _localId: string;\n active: boolean;\n translations: AtlasAgreementTranslation[];\n default_checked: boolean;\n show_at_checkout: boolean;\n required_on_checkout: boolean;\n}\n\nexport interface AtlasTaxSettings {\n taxName: string;\n taxInclusive: boolean;\n taxLocalName?: string;\n roundingMethod?: string;\n invoiceRequired?: boolean;\n showTaxBreakdown?: boolean;\n taxThresholdAmount?: number | null;\n taxThresholdEnabled?: boolean;\n taxLocalAbbreviation?: string;\n taxExemptForForeignShipping?: boolean;\n}\n\nexport interface AtlasLegalSettings {\n coolingOffPeriodDays?: number;\n warrantyPeriodMonths?: number;\n cookieConsentRequired?: boolean;\n recurringSubscriptionDisclosureRequired?: boolean;\n}\n\nexport interface AtlasAddressField {\n field: string;\n visible: boolean;\n required: boolean;\n defaultLabel?: string;\n localLabel?: string;\n placeholder?: string;\n columnWidth?: string;\n validation?: string;\n}\n\nexport interface AtlasPaymentMethod {\n name: string;\n enabled: boolean;\n priority: number;\n integration_type: string;\n}\n\nexport interface AtlasEnrollmentField {\n id: string;\n label: string;\n order: number;\n required: boolean;\n component: string;\n description?: string;\n requiredFor?: string[];\n autoTranslate?: boolean;\n}\n\nexport interface CountryAtlas {\n modes: Record<string, AtlasMode>;\n notes: string;\n agreements: AtlasAgreement[];\n requires3ds: boolean;\n taxSettings: AtlasTaxSettings;\n addressFields: AtlasAddressField[];\n legalSettings: AtlasLegalSettings;\n majorLanguages: string[];\n paymentMethods: AtlasPaymentMethod[];\n defaultCurrency: string;\n enrollmentFormFields: AtlasEnrollmentField[];\n}\n\nexport interface CountryAtlasResult {\n /** ISO alpha-2 after alias resolution. */\n countryCode: string;\n /** False when Fluid publishes no atlas for the market. */\n covered: boolean;\n atlas: CountryAtlas | null;\n}\n\n/** The operating modes the atlas describes, as the CLI accepts them. */\nexport const MODES = [\"nfr\", \"otg\", \"usd\"] as const;\nexport type Mode = (typeof MODES)[number];\n\nexport function parseMode(value: string): Mode {\n const mode = value.trim().toLowerCase();\n if (!(MODES as readonly string[]).includes(mode)) {\n throw new LocalizationError(\n `\"${value}\" isn't an operating mode. Use one of: ${MODES.join(\", \")}.`,\n );\n }\n return mode as Mode;\n}\n\n/** Common aliases, as Mist resolves them: people type \"UK\" and alpha-3. */\nconst ALIASES: Record<string, string> = {\n UK: \"GB\",\n DEU: \"DE\",\n FRA: \"FR\",\n GBR: \"GB\",\n CAN: \"CA\",\n MEX: \"MX\",\n AUS: \"AU\",\n JPN: \"JP\",\n USA: \"US\",\n};\n\nexport function normalizeCountryCode(code: string): string {\n const upper = code.trim().toUpperCase();\n return ALIASES[upper] ?? upper;\n}\n\n/** The ISO alpha-2 code, or a LocalizationError for anything else. */\nexport function countryCodeOf(code: string): string {\n const iso = normalizeCountryCode(code);\n if (!/^[A-Z]{2}$/.test(iso)) {\n throw new LocalizationError(\n `\"${code}\" isn't a two-letter ISO country code, e.g. DE or JP.`,\n );\n }\n return iso;\n}\n\n/** Flatten the atlas's simple HTML (overviews, notes) to readable text. */\nexport function htmlToText(html: string): string {\n return html\n .replace(/<\\s*(br|\\/p|\\/li|\\/ul|\\/ol|\\/h[1-6]|\\/blockquote)[^>]*>/gi, \"\\n\")\n .replace(/<\\s*li[^>]*>/gi, \"• \")\n .replace(/<[^>]+>/g, \"\")\n .replace(/&nbsp;/gi, \" \")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#0?39;|&apos;/gi, \"'\")\n .replace(/&amp;/gi, \"&\")\n .replace(/[ \\t]+\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** Whether a response body is an atlas. The endpoint answers 404 for an\n * unknown ISO, and a country with no configuration renders an empty body. */\nfunction isAtlas(body: unknown): body is CountryAtlas {\n return (\n typeof body === \"object\" &&\n body !== null &&\n typeof (body as { modes?: unknown }).modes === \"object\" &&\n (body as { modes?: unknown }).modes !== null\n );\n}\n\n/** Fetch a country's atlas. A 404 or an empty configuration is an uncovered\n * market, not an error. Other failures throw. */\nexport async function fetchAtlas(\n client: LocalizationClient,\n code: string,\n): Promise<CountryAtlasResult> {\n const countryCode = countryCodeOf(code);\n let body: unknown;\n try {\n body = await client.get<unknown>(\n `${ATLAS_PATH_PREFIX}${countryCode.toLowerCase()}`,\n );\n } catch (error) {\n if (statusOf(error) === 404) {\n return { countryCode, covered: false, atlas: null };\n }\n throw error;\n }\n if (!isAtlas(body)) return { countryCode, covered: false, atlas: null };\n return { countryCode, covered: true, atlas: body };\n}\n\n/** The atlas, or a LocalizationError explaining that the market is uncovered. */\nexport async function requireAtlas(\n client: LocalizationClient,\n code: string,\n): Promise<{ countryCode: string; atlas: CountryAtlas }> {\n const result = await fetchAtlas(client, code);\n if (!result.covered || !result.atlas) {\n throw new LocalizationError(\n `Fluid publishes no Country Atlas for ${result.countryCode} yet, so there is nothing to seed from. Set the country up by hand in Settings > Countries.`,\n );\n }\n return { countryCode: result.countryCode, atlas: result.atlas };\n}\n\nconst NOTES_SUMMARY_MAX = 2_000;\n\nexport interface AtlasAgreementSummary {\n localId: string;\n title: string;\n languages: string[];\n requiredOnCheckout: boolean;\n showAtCheckout: boolean;\n defaultChecked: boolean;\n bodyChars: number;\n}\n\n/** The translation an agreement is created from: English, else the first. */\nexport function primaryTranslation(\n agreement: AtlasAgreement,\n): AtlasAgreementTranslation | undefined {\n return (\n agreement.translations.find((t) => t.language_iso === \"en\") ??\n agreement.translations[0]\n );\n}\n\nexport function summarizeAgreement(\n agreement: AtlasAgreement,\n): AtlasAgreementSummary {\n const primary = primaryTranslation(agreement);\n return {\n localId: agreement._localId,\n title: primary?.title ?? agreement._localId,\n languages: agreement.translations.map((t) => t.language_iso),\n requiredOnCheckout: agreement.required_on_checkout,\n showAtCheckout: agreement.show_at_checkout,\n defaultChecked: agreement.default_checked,\n bodyChars: primary?.description.length ?? 0,\n };\n}\n\n/** The atlas without the agreement legal bodies, which run to thousands of\n * characters each; `fluid countries atlas <iso> --agreement <id>` prints one. */\nexport function summarizeAtlas(atlas: CountryAtlas) {\n const notes = htmlToText(atlas.notes ?? \"\");\n const modes: Record<\n string,\n { overview: string; launchChecklist: AtlasChecklistItem[] }\n > = {};\n for (const [key, mode] of Object.entries(atlas.modes ?? {})) {\n modes[key] = {\n overview: htmlToText(mode.overview ?? \"\"),\n launchChecklist: mode.launchChecklist ?? [],\n };\n }\n return {\n defaultCurrency: atlas.defaultCurrency,\n majorLanguages: atlas.majorLanguages ?? [],\n requires3ds: atlas.requires3ds ?? false,\n marketNotes:\n notes.length > NOTES_SUMMARY_MAX\n ? `${notes.slice(0, NOTES_SUMMARY_MAX)}… [truncated]`\n : notes,\n modes,\n taxSettings: atlas.taxSettings,\n legalSettings: atlas.legalSettings ?? {},\n paymentMethods: sortedPaymentMethods(atlas),\n enrollmentFormFields: [...(atlas.enrollmentFormFields ?? [])].sort(\n (a, b) => a.order - b.order,\n ),\n addressFields: atlas.addressFields ?? [],\n agreements: (atlas.agreements ?? []).map(summarizeAgreement),\n };\n}\n\nexport function sortedPaymentMethods(\n atlas: CountryAtlas,\n): AtlasPaymentMethod[] {\n return [...(atlas.paymentMethods ?? [])].sort(\n (a, b) => a.priority - b.priority,\n );\n}\n\n/** The launch checklist for one mode, verbatim. */\nexport function launchChecklist(\n atlas: CountryAtlas,\n mode: Mode,\n): AtlasChecklistItem[] {\n return atlas.modes?.[mode]?.launchChecklist ?? [];\n}\n\n/** One agreement's full translations, by its atlas local id. */\nexport function atlasAgreement(\n atlas: CountryAtlas,\n localId: string,\n): AtlasAgreement {\n const agreement = (atlas.agreements ?? []).find(\n (a) => a._localId === localId,\n );\n if (!agreement) {\n const ids = (atlas.agreements ?? []).map((a) => a._localId);\n throw new LocalizationError(\n `The atlas has no agreement \"${localId}\". It has: ${ids.join(\", \") || \"none\"}.`,\n );\n }\n return agreement;\n}\n","// The Fluid API calls a country setup makes. Each one is an existing\n// endpoint the admin already uses; nothing here needs a new route.\n\nimport { primaryTranslation, type AtlasAgreement } from \"./atlas.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport {\n agreementTranslated,\n writeAgreementTranslation,\n type CompanyAgreement,\n} from \"../shared/agreements.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\nexport interface CatalogCountry {\n id: number;\n iso: string;\n name: string;\n currency_code?: string | null;\n}\n\n/** GET /api/countries: the global country catalog. */\nexport async function listCatalogCountries(\n client: LocalizationClient,\n): Promise<CatalogCountry[]> {\n const body = await client.get<\n CatalogCountry[] | { countries?: CatalogCountry[] }\n >(\"/api/countries\");\n return Array.isArray(body) ? body : (body.countries ?? []);\n}\n\n/** One country from the global catalog. */\nexport async function catalogCountry(\n client: LocalizationClient,\n iso: string,\n): Promise<CatalogCountry> {\n const match = (await listCatalogCountries(client)).find(\n (c) => c.iso?.toUpperCase() === iso,\n );\n if (!match)\n throw new LocalizationError(\n `Fluid's country catalog has no country ${iso}.`,\n );\n return match;\n}\n\nexport interface CompanyCountry {\n id: number;\n currency?: string | null;\n mode?: string | null;\n tax_name?: string | null;\n tax_inclusive_pricing?: boolean | null;\n tax_invoice_required?: boolean | null;\n requires_3ds?: boolean | null;\n payment_method_priority?: string[] | null;\n legal_settings?: Record<string, unknown> | null;\n address_fields?: Array<Record<string, unknown>> | null;\n revision?: string | null;\n test_mode?: boolean | null;\n warehouse?: { id: number; name?: string } | null;\n country?: { id: number; iso: string; name?: string } | null;\n}\n\n/** GET /api/settings/company_countries: the countries the company sells in. */\nexport async function listCompanyCountries(\n client: LocalizationClient,\n): Promise<CompanyCountry[]> {\n const body = await client.get<{ company_countries?: CompanyCountry[] }>(\n \"/api/settings/company_countries\",\n );\n return body.company_countries ?? [];\n}\n\nexport async function findCompanyCountry(\n client: LocalizationClient,\n iso: string,\n): Promise<CompanyCountry | undefined> {\n const list = await listCompanyCountries(client);\n return list.find((cc) => cc.country?.iso?.toUpperCase() === iso);\n}\n\n/** The company country, or a LocalizationError saying to open it first. */\nexport async function requireCompanyCountry(\n client: LocalizationClient,\n iso: string,\n): Promise<CompanyCountry> {\n const found = await findCompanyCountry(client, iso);\n if (!found) {\n throw new LocalizationError(\n `${iso} isn't open for this company yet. Run \\`fluid countries open ${iso} --mode <nfr|otg|usd>\\` first.`,\n );\n }\n return found;\n}\n\n/** POST /api/settings/company_countries. `seedLegacyDefaults: false` skips\n * the server's generic country agreements and forms, so the company gets\n * the atlas selection instead. */\nexport async function createCompanyCountry(\n client: LocalizationClient,\n attributes: Record<string, unknown>,\n seedLegacyDefaults: boolean,\n): Promise<CompanyCountry & { warnings?: unknown }> {\n const body = await client.post<{\n company_country: CompanyCountry;\n warnings?: unknown;\n }>(\"/api/settings/company_countries\", {\n company_country: attributes,\n seed_legacy_defaults: seedLegacyDefaults,\n });\n return {\n ...body.company_country,\n ...(body.warnings ? { warnings: body.warnings } : {}),\n };\n}\n\n/** PUT /api/settings/company_countries/:id, guarded by the revision read\n * just before so a concurrent admin edit answers 409 instead of being lost. */\nexport async function updateCompanyCountry(\n client: LocalizationClient,\n id: number,\n attributes: Record<string, unknown>,\n expectedRevision?: string | null,\n): Promise<CompanyCountry> {\n const { country_id: _ignored, ...rest } = attributes;\n const body = await client.put<{ company_country: CompanyCountry }>(\n `/api/settings/company_countries/${id}`,\n {\n company_country: rest,\n ...(expectedRevision ? { expected_revision: expectedRevision } : {}),\n },\n );\n return body.company_country;\n}\n\nexport interface CreatedAgreement {\n localId: string;\n id: number;\n title: string;\n translations: string[];\n failedTranslations: Array<{ language: string; error: string }>;\n}\n\n/**\n * Create an atlas agreement for a company country. POST writes the base\n * (English) text: the English translation when the atlas has one, else the\n * first. Each translation that is not English is then written in its own\n * locale with PUT ?language_iso=, which needs that language enabled for the\n * company. A translation that fails is reported, not fatal.\n */\nexport async function createAgreementFromAtlas(\n client: LocalizationClient,\n agreement: AtlasAgreement,\n companyCountryId: number,\n): Promise<CreatedAgreement> {\n const primary = primaryTranslation(agreement);\n if (!primary) {\n throw new LocalizationError(\n `Atlas agreement ${agreement._localId} has no text.`,\n );\n }\n const flags = {\n active: true,\n required_on_checkout: agreement.required_on_checkout === true,\n show_at_checkout: agreement.show_at_checkout === true,\n default_checked: agreement.default_checked === true,\n company_country_ids: [companyCountryId],\n };\n const created = await client.post<{ agreement: CompanyAgreement }>(\n \"/api/agreements\",\n {\n agreement: {\n title: primary.title,\n description: primary.description,\n ...flags,\n },\n },\n );\n const id = created.agreement.id;\n const { translations, failedTranslations } = await writeTranslations(\n client,\n id,\n agreement,\n );\n return {\n localId: agreement._localId,\n id,\n title: primary.title,\n translations,\n failedTranslations,\n };\n}\n\n/**\n * Write each of an atlas agreement's translations that isn't English onto a\n * company agreement, in its own locale. Only the title and text are sent, so\n * the agreement's checkout settings and countries are left as they are. A\n * translation that fails is reported, not fatal, and can be written again.\n */\nexport async function writeTranslations(\n client: LocalizationClient,\n agreementId: number,\n agreement: AtlasAgreement,\n): Promise<{\n translations: string[];\n failedTranslations: Array<{\n language: string;\n error: string;\n status?: number;\n }>;\n}> {\n const translations: string[] = [];\n const failedTranslations: Array<{\n language: string;\n error: string;\n status?: number;\n }> = [];\n for (const t of agreement.translations) {\n if (t.language_iso === \"en\") continue;\n try {\n await writeAgreementTranslation(client, agreementId, t.language_iso, t);\n translations.push(t.language_iso);\n } catch (error) {\n failedTranslations.push({\n language: t.language_iso,\n ...failureOf(error),\n });\n }\n }\n return { translations, failedTranslations };\n}\n\n/**\n * The atlas languages an agreement has no translation in, checked the way\n * `fluid translations missing --type agreements` checks them: a read in a\n * language without a translation falls back and says so in `language_iso`.\n * English is the base text an agreement is created from, so it isn't checked.\n */\nexport async function missingTranslations(\n client: LocalizationClient,\n agreementId: number,\n agreement: AtlasAgreement,\n): Promise<string[]> {\n const missing: string[] = [];\n for (const t of agreement.translations) {\n if (t.language_iso === \"en\") continue;\n if (!(await agreementTranslated(client, agreementId, t.language_iso))) {\n missing.push(t.language_iso);\n }\n }\n return missing;\n}\n\nexport interface Warehouse {\n id: number;\n name: string;\n active?: boolean;\n country?: { id?: number; iso?: string } | null;\n}\n\n/** GET /api/settings/warehouses. */\nexport async function listWarehouses(\n client: LocalizationClient,\n): Promise<Warehouse[]> {\n const body = await client.get<{ warehouses?: Warehouse[] }>(\n \"/api/settings/warehouses\",\n );\n return body.warehouses ?? [];\n}\n","// The compliance view of the atlas, as Mist's `country_settings` tool\n// projects it for the mode finalizers and the compliance review. Every value\n// is derived from the atlas; where the atlas is silent the field says so\n// instead of guessing.\n\nimport {\n htmlToText,\n primaryTranslation,\n sortedPaymentMethods,\n type CountryAtlas,\n} from \"./atlas.js\";\n\n/** Privacy regulators matched against the market notes. Not exhaustive: an\n * unmatched market reads \"See market notes\", never an invented name. */\nconst KNOWN_REGULATORS = [\n \"BfDI\",\n \"CNIL\",\n \"ICO\",\n \"Garante\",\n \"AEPD\",\n \"OPC\",\n \"CAI\",\n \"INAI\",\n \"OAIC\",\n \"PPC\",\n \"PDPC\",\n \"FTC\",\n];\n\n/** The first statute-like reference in a legal body, or \"\". */\nexport function extractCitation(bodyText: string): string {\n const patterns = [\n /§\\s?\\d+[a-z]?/i,\n /Art(?:icle)?\\.?\\s?\\d+[-\\w]*/i,\n /Regulation\\s+\\d+/i,\n /\\b\\d{4}[-/]\\d+\\b/,\n ];\n for (const re of patterns) {\n const m = bodyText.match(re);\n if (m) return m[0].trim();\n }\n return \"\";\n}\n\nfunction languageName(iso: string): string {\n try {\n return new Intl.DisplayNames([\"en\"], { type: \"language\" }).of(iso) ?? iso;\n } catch {\n return iso;\n }\n}\n\nfunction unitPriceRule(atlas: CountryAtlas, notes: string) {\n const haystack = [\n notes,\n ...Object.values(atlas.modes ?? {}).flatMap((m) =>\n (m.launchChecklist ?? []).map((c) => `${c.label} ${c.description}`),\n ),\n ]\n .join(\"\\n\")\n .toLowerCase();\n const match = haystack.match(\n /[^\\n]*(unit[\\s-]?pric|price marking|grundpreis)[^\\n]*/,\n );\n return match\n ? { required: true, notes: match[0].trim().slice(0, 200) }\n : { required: false, notes: \"\" };\n}\n\nexport function complianceView(countryCode: string, atlas: CountryAtlas) {\n const notes = htmlToText(atlas.notes ?? \"\");\n const languages = atlas.majorLanguages ?? [];\n const primaryLanguage = languages[0] ?? \"en\";\n const cookieRequired = atlas.legalSettings?.cookieConsentRequired ?? false;\n const regulator = KNOWN_REGULATORS.find((r) =>\n new RegExp(`\\\\b${r}\\\\b`).test(notes),\n );\n\n return {\n countryCode,\n currencyCode: atlas.defaultCurrency,\n languageCode: primaryLanguage,\n languageName: languageName(primaryLanguage),\n widelySpokenLanguages: languages.slice(1),\n // The atlas names the tax; the rate is on GET /api/countries (tax_rates).\n vat: atlas.taxSettings?.taxName\n ? { name: atlas.taxSettings.taxName, standardRatePct: null }\n : null,\n vatInclusiveDisplay: atlas.taxSettings\n ? atlas.taxSettings.taxInclusive\n ? \"inclusive\"\n : \"exclusive_at_sticker\"\n : \"unspecified\",\n legalSettings: atlas.legalSettings ?? {},\n cookieRule: {\n framework: cookieRequired\n ? \"Consent-based cookie law (see market notes)\"\n : \"None documented\",\n requirement: cookieRequired\n ? \"Obtain consent before non-essential cookies; provide an equally-prominent Reject All and log consent.\"\n : \"No cookie-consent mandate documented for this market.\",\n regulator: regulator ?? \"See market notes\",\n },\n unitPriceRule: unitPriceRule(atlas, notes),\n mandatoryDisclosurePages: (atlas.agreements ?? []).map((a) => {\n const primary = primaryTranslation(a);\n const body = htmlToText(primary?.description ?? \"\");\n return {\n localId: a._localId,\n name: primary?.title ?? a._localId,\n description: body.slice(0, 160),\n citation: extractCitation(body),\n };\n }),\n paymentMethods: sortedPaymentMethods(atlas).map((m) => ({\n integrationType: m.integration_type,\n label: m.name,\n recommended: m.enabled,\n })),\n };\n}\n","// Bulk price conversion for a newly opened country: every variant's price in\n// a source country (US by default), multiplied by an exchange rate and\n// rounded to the target currency, saved as the variant's price in the new\n// country. This is the `convert-pricing` step of Mist's open-country\n// workflow, made safe to run unattended:\n//\n// - nothing is written without --write (and --yes, or a prompt);\n// - a variant already priced in the country is skipped unless --overwrite, so\n// a second run never converts a converted price again;\n// - each variant is its own write, and each failure is reported with the\n// API's message instead of stopping the run;\n// - companies whose prices the price editor owns are detected up front.\n// Writes still go through the variants API, which saves them into the\n// price editor's catalog and refuses (422) any it can't place; those are\n// reported as `refusedByPriceEditor`. --overwrite is refused for them, so\n// a price someone set in the editor is never replaced from here.\n\nimport { statusOf, type LocalizationClient } from \"../shared/client.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport type { CompanyCountry } from \"./api.js\";\n\n/** The money fields a conversion can write, as the variants API names them. */\nexport const PRICE_FIELDS = [\n \"price\",\n \"subscription_price\",\n \"compare_price\",\n \"wholesale\",\n \"wholesale_subscription_price\",\n] as const;\nexport type PriceField = (typeof PRICE_FIELDS)[number];\n\n/**\n * What shoppers pay. Wholesale prices are left out by default: they drive\n * member commissions, and an admin token's product reads can show retail in\n * their place, so converting them is opt-in with --fields.\n */\nexport const DEFAULT_PRICE_FIELDS: readonly PriceField[] = [\n \"price\",\n \"subscription_price\",\n \"compare_price\",\n];\n\n/** Currencies Fluid stores with no minor unit (the API rounds the same way). */\nconst ZERO_DECIMAL_CURRENCIES = new Set([\n \"BIF\",\n \"CLP\",\n \"DJF\",\n \"GNF\",\n \"HUF\",\n \"ISK\",\n \"JPY\",\n \"KMF\",\n \"KRW\",\n \"MGA\",\n \"PYG\",\n \"RWF\",\n \"TWD\",\n \"UGX\",\n \"VND\",\n \"VUV\",\n \"XAF\",\n \"XOF\",\n \"XPF\",\n]);\n\nexport function currencyDecimals(currency: string): number {\n return ZERO_DECIMAL_CURRENCIES.has(currency.toUpperCase()) ? 0 : 2;\n}\n\n/** `amount × rate`, rounded half up to the currency's minor unit. */\nexport function convertAmount(\n amount: number,\n rate: number,\n currency: string,\n): number {\n const factor = 10 ** currencyDecimals(currency);\n const scaled = amount * rate * factor;\n // A tiny nudge so 2.675 (stored as 2.67499…) rounds up, as it does on the server.\n return Math.round(scaled + Math.sign(scaled) * 1e-7) / factor;\n}\n\nexport function parseRate(raw: string): number {\n const rate = Number(raw);\n if (!Number.isFinite(rate) || rate <= 0) {\n throw new LocalizationError(\n `--rate takes a positive number, such as 0.92, not \"${raw}\".`,\n );\n }\n return rate;\n}\n\nexport function parsePriceFields(raw: string | undefined): PriceField[] {\n if (!raw) return [...DEFAULT_PRICE_FIELDS];\n const fields = raw\n .split(\",\")\n .map((f) => f.trim())\n .filter(Boolean);\n const unknown = fields.filter(\n (f) => !(PRICE_FIELDS as readonly string[]).includes(f),\n );\n if (unknown.length > 0 || fields.length === 0) {\n throw new LocalizationError(\n `--fields takes a comma-separated list of ${PRICE_FIELDS.join(\", \")}${unknown.length > 0 ? `; not ${unknown.join(\", \")}` : \"\"}.`,\n );\n }\n if (!fields.includes(\"price\")) {\n throw new LocalizationError(\n \"--fields must include price: a variant can't be sold in a country without one.\",\n );\n }\n return [...new Set(fields)] as PriceField[];\n}\n\n/** One variant's prices in one country, as product reads give them. */\nexport interface VariantCountryWire {\n id?: number | null;\n active?: boolean | null;\n country_id?: number | null;\n currency_code?: string | null;\n price?: string | number | null;\n subscription_price?: string | number | null;\n compare_price?: string | number | null;\n wholesale?: string | number | null;\n wholesale_subscription_price?: string | number | null;\n}\n\ninterface VariantWire {\n id: number;\n sku?: string | null;\n title?: string | null;\n /** Keyed by country ISO code. */\n variant_countries?: Record<string, VariantCountryWire> | null;\n}\n\ninterface ProductWire {\n id: number;\n title?: string | null;\n /** A bundle can be priced from its groups, leaving its own price at zero. */\n bundle?: boolean | null;\n status?: string | null;\n variants?: VariantWire[] | null;\n}\n\nfunction amount(value: unknown): number | null {\n if (value === null || value === undefined || value === \"\") return null;\n const n = typeof value === \"number\" ? value : Number(value);\n return Number.isFinite(n) ? n : null;\n}\n\nconst PRODUCTS_PATH = \"/api/company/v1/products\";\n/** The products list renders every variant, so the API caps a page at 50. */\nconst PER_PAGE = 50;\nconst MAX_PAGES = 2000;\n\n/** Every product with its variants, following the list's pages. */\nexport async function listProductsWithVariants(\n client: LocalizationClient,\n statuses: readonly string[],\n): Promise<ProductWire[]> {\n const products: ProductWire[] = [];\n for (let page = 1; page <= MAX_PAGES; page += 1) {\n const data = await client.get<{\n products?: ProductWire[];\n meta?: { pagination?: { total_pages?: number } };\n }>(PRODUCTS_PATH, { page, per_page: PER_PAGE, status: [...statuses] });\n const batch = data.products ?? [];\n for (const product of batch) {\n // The list renders variants; read the product on its own if it didn't.\n if (!Array.isArray(product.variants)) {\n const full = await client.get<{ product?: ProductWire }>(\n `${PRODUCTS_PATH}/${product.id}`,\n );\n products.push(full.product ?? product);\n } else {\n products.push(product);\n }\n }\n const totalPages = data.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages || batch.length === 0) return products;\n }\n throw new LocalizationError(\n `The catalog has more than ${MAX_PAGES * PER_PAGE} products, more than one run can read. Narrow it with --status.`,\n );\n}\n\n/**\n * Whether the price editor owns this company's prices (member-pricing\n * catalog writes), or null when the token can't read the company profile.\n */\nexport async function priceEditorOwnsPrices(\n client: LocalizationClient,\n): Promise<boolean | null> {\n try {\n const data = await client.get<{\n data?: { company?: { member_pricing?: { catalog_writes?: unknown } } };\n }>(\"/api/company/v1/companies/me\");\n return data.data?.company?.member_pricing?.catalog_writes === true;\n } catch (error) {\n if (statusOf(error) === 401 || statusOf(error) === 403) return null;\n throw error;\n }\n}\n\nexport type SkipReason =\n | \"rounds_to_zero\"\n | \"already_priced\"\n | \"priced_in_other_currency\"\n | \"no_source_price\";\n\nexport interface VariantPlan {\n productId: number;\n product: string | null;\n variantId: number;\n sku: string | null;\n /** convert: will be (or was) written; skip: left as it is. */\n action: \"convert\" | \"skip\";\n reason?: SkipReason;\n /** The existing row in the target country, sent back by id so it's updated in place. */\n rowId: number | null;\n active: boolean;\n before: Partial<Record<PriceField, number | null>>;\n after: Partial<Record<PriceField, number>>;\n}\n\nexport interface PlanOptions {\n sourceIso: string;\n targetIso: string;\n targetCountryId: number;\n targetCurrency: string;\n rate: number;\n fields: readonly PriceField[];\n overwrite: boolean;\n activate: boolean;\n}\n\n/**\n * Decide, per variant, what a conversion writes. A variant needs a positive\n * source price. One whose target row already has a positive price is skipped\n * unless `overwrite`, which is what makes a rerun safe: the first run's\n * prices count as \"already priced\". A positive price saved in another\n * currency (the country's currency changed since) is skipped too, and\n * reported apart so it can be reviewed. Opening a country creates a zero-priced,\n * inactive row for every variant, so a zero price counts as unpriced. Only\n * the fields with a positive source amount are sent; the rest of the row is\n * left as it is.\n */\nexport function planConversion(\n products: readonly ProductWire[],\n options: PlanOptions,\n): VariantPlan[] {\n const plans: VariantPlan[] = [];\n for (const product of products) {\n for (const variant of product.variants ?? []) {\n const rows = variant.variant_countries ?? {};\n const source = rowFor(rows, options.sourceIso);\n const target = rowFor(rows, options.targetIso);\n const before = Object.fromEntries(\n options.fields.map((f) => [f, amount(target?.[f])]),\n ) as Partial<Record<PriceField, number | null>>;\n const base = {\n productId: product.id,\n product: product.title ?? null,\n variantId: variant.id,\n sku: variant.sku ?? null,\n rowId: typeof target?.id === \"number\" ? target.id : null,\n active: options.activate || target?.active === true,\n before,\n };\n const sourcePrice = amount(source?.price);\n if (sourcePrice === null || sourcePrice <= 0) {\n plans.push({\n ...base,\n action: \"skip\",\n reason: \"no_source_price\",\n after: {},\n });\n continue;\n }\n if (!options.overwrite && (amount(target?.price) ?? 0) > 0) {\n const currency = target?.currency_code;\n const sameCurrency =\n !currency ||\n currency.toUpperCase() === options.targetCurrency.toUpperCase();\n plans.push({\n ...base,\n action: \"skip\",\n reason: sameCurrency ? \"already_priced\" : \"priced_in_other_currency\",\n after: {},\n });\n continue;\n }\n const after: Partial<Record<PriceField, number>> = {};\n for (const field of options.fields) {\n const value = amount(source?.[field]);\n if (value === null || value <= 0) continue;\n const converted = convertAmount(\n value,\n options.rate,\n options.targetCurrency,\n );\n // A positive price never becomes free: one that rounds to zero is\n // left out, and a variant whose price does is skipped entirely.\n if (converted > 0) after[field] = converted;\n }\n if (after.price === undefined) {\n plans.push({\n ...base,\n action: \"skip\",\n reason: \"rounds_to_zero\",\n after: {},\n });\n continue;\n }\n plans.push({ ...base, action: \"convert\", after });\n }\n }\n return plans;\n}\n\nfunction rowFor(\n rows: Record<string, VariantCountryWire>,\n iso: string,\n): VariantCountryWire | undefined {\n const key = Object.keys(rows).find(\n (k) => k.toUpperCase() === iso.toUpperCase(),\n );\n return key ? rows[key] : undefined;\n}\n\n/** The variants API body that saves one plan's prices in the target country. */\nexport function variantBody(plan: VariantPlan, targetCountryId: number) {\n return {\n variant: {\n variant_countries_attributes: [\n {\n ...(plan.rowId !== null ? { id: plan.rowId } : {}),\n country_id: targetCountryId,\n active: plan.active,\n ...plan.after,\n },\n ],\n },\n };\n}\n\nexport interface WriteFailure {\n variantId: number;\n productId: number;\n sku: string | null;\n error: string;\n status?: number;\n /** The price editor's catalog wouldn't take the price. */\n refusedByPriceEditor: boolean;\n details?: unknown;\n}\n\n/** Messages the price editor's catalog writer refuses a save with. */\nconst PRICE_EDITOR_REFUSAL =\n /could not be written to the catalog|another update to these prices is in progress/i;\n\n/** The variants a fresh read no longer converts, and why. */\nexport interface ChangedSincePreview {\n variantId: number;\n productId: number;\n reason: SkipReason | \"variant_gone\";\n}\n\n/**\n * Save the convertible plans, one variant at a time; a failure never stops\n * the run. Each product is read again just before its variants are written\n * and planned afresh, so a price someone set in the country after the\n * preview is skipped instead of overwritten (`changedSincePreview`).\n */\nexport async function writeConversion(\n client: LocalizationClient,\n plans: readonly VariantPlan[],\n options: PlanOptions,\n): Promise<{\n written: number[];\n failed: WriteFailure[];\n changedSincePreview: ChangedSincePreview[];\n}> {\n const written: number[] = [];\n const failed: WriteFailure[] = [];\n const changedSincePreview: ChangedSincePreview[] = [];\n const byProduct = new Map<number, VariantPlan[]>();\n for (const plan of plans) {\n if (plan.action !== \"convert\") continue;\n byProduct.set(plan.productId, [\n ...(byProduct.get(plan.productId) ?? []),\n plan,\n ]);\n }\n for (const [productId, previewed] of byProduct) {\n let fresh: VariantPlan[];\n try {\n const data = await client.get<{ product?: ProductWire }>(\n `${PRODUCTS_PATH}/${productId}`,\n );\n fresh = data.product ? planConversion([data.product], options) : [];\n } catch (error) {\n for (const plan of previewed) failed.push(failureFor(plan, error));\n continue;\n }\n for (const plan of previewed) {\n const now = fresh.find((p) => p.variantId === plan.variantId);\n if (!now || now.action !== \"convert\") {\n changedSincePreview.push({\n variantId: plan.variantId,\n productId,\n reason: now?.reason ?? \"variant_gone\",\n });\n continue;\n }\n try {\n await client.put(\n `/api/company/v1/variants/${now.variantId}`,\n variantBody(now, options.targetCountryId),\n );\n written.push(now.variantId);\n } catch (error) {\n failed.push(failureFor(now, error));\n }\n }\n }\n return { written, failed, changedSincePreview };\n}\n\nfunction failureFor(plan: VariantPlan, error: unknown): WriteFailure {\n const body = (error as { body?: Record<string, unknown> | null }).body;\n const details = body?.[\"errors\"];\n const { error: message, status } = failureOf(error);\n const text = `${message} ${details ? JSON.stringify(details) : \"\"}`;\n return {\n variantId: plan.variantId,\n productId: plan.productId,\n sku: plan.sku,\n error: message,\n ...(status !== undefined ? { status } : {}),\n refusedByPriceEditor: status === 422 && PRICE_EDITOR_REFUSAL.test(text),\n ...(details ? { details } : {}),\n };\n}\n\n/** Counts and a before→after sample, the summary a dry run prints. */\nexport function summarizePlans(\n plans: readonly VariantPlan[],\n sampleSize: number,\n) {\n const convert = plans.filter((p) => p.action === \"convert\");\n return {\n variants: plans.length,\n toConvert: convert.length,\n skipped: {\n alreadyPriced: plans.filter((p) => p.reason === \"already_priced\").length,\n pricedInOtherCurrency: plans.filter(\n (p) => p.reason === \"priced_in_other_currency\",\n ).length,\n noSourcePrice: plans.filter((p) => p.reason === \"no_source_price\").length,\n roundsToZero: plans.filter((p) => p.reason === \"rounds_to_zero\").length,\n },\n /** Converted variants that will be (or stay) on sale in the country. */\n activeAfterWrite: convert.filter((p) => p.active).length,\n sample: convert.slice(0, sampleSize).map((p) => ({\n productId: p.productId,\n product: p.product,\n variantId: p.variantId,\n sku: p.sku,\n before: p.before,\n after: p.after,\n })),\n };\n}\n\n/** A company country by ISO, from a list already read. */\nexport function companyCountryIn(\n countries: readonly CompanyCountry[],\n iso: string,\n): CompanyCountry | undefined {\n return countries.find(\n (cc) => cc.country?.iso?.toUpperCase() === iso.toUpperCase(),\n );\n}\n\nexport interface ReferenceRate {\n rate: number;\n date: string;\n source: string;\n}\n\n/**\n * A reference rate to start from: the European Central Bank's daily rate,\n * through the Frankfurter API. Fluid has no exchange-rate API. A write never\n * uses this on its own; it needs the rate passed with --rate, so the rate\n * saved is one a person or agent reviewed.\n */\nexport async function referenceRate(\n from: string,\n to: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<ReferenceRate | null> {\n const url = `https://api.frankfurter.app/latest?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`;\n try {\n const response = await fetchImpl(url);\n if (!response.ok) return null;\n const body = (await response.json()) as {\n date?: string;\n rates?: Record<string, number>;\n };\n const rate = body.rates?.[to.toUpperCase()];\n if (typeof rate !== \"number\" || !(rate > 0)) return null;\n return {\n rate,\n date: body.date ?? \"\",\n source: \"European Central Bank reference rate (frankfurter.app)\",\n };\n } catch {\n return null;\n }\n}\n\nexport interface PriceCoverage {\n /** Variants of the products read, bundles left out. */\n variants: number;\n /** Of those, the ones with a positive price in the country. */\n priced: number;\n /** Priced ones on sale in the country. */\n onSale: number;\n /**\n * Active bundles, listed for a person to check. Whether a bundle sells in a\n * country depends on checkout's group pricing and per-group availability,\n * which no read exposes at the retail tier, so they're neither passed nor\n * failed here.\n */\n bundles: Array<{ id: number; title: string | null }>;\n}\n\n/** How many variants have a price in the country and how many are on sale there; bundles listed apart. */\nexport function priceCoverage(\n products: readonly ProductWire[],\n iso: string,\n): PriceCoverage {\n const coverage: PriceCoverage = {\n variants: 0,\n priced: 0,\n onSale: 0,\n bundles: [],\n };\n for (const product of products) {\n if (product.bundle === true) {\n coverage.bundles.push({ id: product.id, title: product.title ?? null });\n continue;\n }\n for (const variant of product.variants ?? []) {\n coverage.variants += 1;\n const row = rowFor(variant.variant_countries ?? {}, iso);\n if ((amount(row?.price) ?? 0) <= 0) continue;\n coverage.priced += 1;\n if (row?.active === true) coverage.onSale += 1;\n }\n }\n return coverage;\n}\n","// What a country still needs, worked out from the atlas and the company's\n// current setup. Pure functions, so `plan`, the write commands, and `status`\n// all agree on the same answer.\n\nimport { summarizeAgreement, type CountryAtlas } from \"./atlas.js\";\nimport { languageIn, type Language } from \"../shared/languages.js\";\nimport type { CompanyAgreement } from \"../shared/agreements.js\";\n\nexport interface LanguageState {\n iso: string;\n name: string | null;\n /** The language's id in Fluid's catalog, null when the catalog lacks it. */\n languageId: number | null;\n enabled: boolean;\n}\n\n/** Each of the market's major languages and whether the company has it on.\n * A language the catalog lacks can't be enabled and is reported as such. */\nexport function languageStates(\n atlas: CountryAtlas,\n languages: readonly Language[],\n): LanguageState[] {\n return (atlas.majorLanguages ?? []).map((iso) => {\n const match = languageIn(languages, iso);\n return {\n iso,\n name: match?.name ?? null,\n languageId: match?.id ?? null,\n enabled: match?.active === true,\n };\n });\n}\n\nexport type AgreementStatus = \"linked\" | \"inactive\" | \"exists\" | \"missing\";\n\nexport interface AgreementState {\n localId: string;\n title: string;\n languages: string[];\n requiredOnCheckout: boolean;\n showAtCheckout: boolean;\n /** linked: an active company agreement with this title is on the country.\n * inactive: one is on the country but switched off, so checkout skips it.\n * exists: one exists but isn't linked to the country.\n * missing: the company has no agreement with this title. */\n status: AgreementStatus;\n agreementId: number | null;\n /** Atlas languages the agreement has no translation in; set by `status`. */\n missingTranslations?: string[];\n}\n\n/** Match atlas agreements to company agreements by title, case-insensitively,\n * so a setup never creates a second agreement with the same title. */\nexport function agreementStates(\n atlas: CountryAtlas,\n agreements: readonly CompanyAgreement[],\n companyCountryId: number | null,\n): AgreementState[] {\n return (atlas.agreements ?? []).map((agreement) => {\n const summary = summarizeAgreement(agreement);\n const titles = new Set(\n agreement.translations.map((t) => t.title.trim().toLowerCase()),\n );\n const matches = agreements.filter((a) =>\n titles.has(a.title?.trim().toLowerCase()),\n );\n const onCountry =\n companyCountryId === null\n ? []\n : matches.filter((a) =>\n (a.company_country_ids ?? []).includes(companyCountryId),\n );\n const linked = onCountry.find((a) => a.active !== false);\n const inactive = linked ? undefined : onCountry[0];\n const match = linked ?? inactive ?? matches[0];\n return {\n localId: summary.localId,\n title: summary.title,\n languages: summary.languages,\n requiredOnCheckout: summary.requiredOnCheckout,\n showAtCheckout: summary.showAtCheckout,\n status: linked\n ? \"linked\"\n : inactive\n ? \"inactive\"\n : match\n ? \"exists\"\n : \"missing\",\n agreementId: match?.id ?? null,\n };\n });\n}\n","// Turn an atlas into the `company_country` body that\n// POST /api/settings/company_countries takes. Seeding happens here, on the\n// client, the way the admin form does it: the atlas is read, its values are\n// offered as the company's own, and the existing settings API saves them.\n// The backend does not seed.\n\nimport { sortedPaymentMethods, type CountryAtlas, type Mode } from \"./atlas.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\n/** Address fields checkout both renders and stores; only these may be\n * required (CompanyCountry::ContractSchema.storable_address_field_names). */\nexport const STORABLE_ADDRESS_FIELDS = [\n \"name\",\n \"address1\",\n \"address2\",\n \"city\",\n \"state\",\n \"postal_code\",\n \"country_code\",\n \"phone\",\n] as const;\n\nconst COLUMN_WIDTHS = [\"full\", \"half\", \"third\"];\n\nexport interface AddressFieldEntry {\n field: string;\n visible: boolean;\n required: boolean;\n local_label?: string;\n column_width?: string;\n placeholder?: string;\n}\n\n/**\n * The atlas address layout in the contract shape the API validates. The\n * atlas marks first and last name required, but checkout cannot collect a\n * required field it does not store, so those keep their layout and become\n * optional. A required field is always visible. Repeats are dropped.\n */\nexport function addressFieldsFromAtlas(\n atlas: CountryAtlas,\n): AddressFieldEntry[] {\n const seen = new Set<string>();\n const entries: AddressFieldEntry[] = [];\n for (const source of atlas.addressFields ?? []) {\n if (\n typeof source.field !== \"string\" ||\n source.field === \"\" ||\n seen.has(source.field)\n ) {\n continue;\n }\n seen.add(source.field);\n const required =\n source.required === true &&\n (STORABLE_ADDRESS_FIELDS as readonly string[]).includes(source.field);\n const entry: AddressFieldEntry = {\n field: source.field,\n visible: required || source.visible !== false,\n required,\n };\n if (typeof source.localLabel === \"string\")\n entry.local_label = source.localLabel;\n if (\n typeof source.columnWidth === \"string\" &&\n COLUMN_WIDTHS.includes(source.columnWidth)\n ) {\n entry.column_width = source.columnWidth;\n }\n if (typeof source.placeholder === \"string\")\n entry.placeholder = source.placeholder;\n entries.push(entry);\n }\n return entries;\n}\n\n/** The atlas legal settings under the API's snake_case keys. Values of the\n * wrong kind are left out rather than sent to be refused. */\nexport function legalSettingsFromAtlas(\n atlas: CountryAtlas,\n): Record<string, unknown> {\n const legal = atlas.legalSettings ?? {};\n const out: Record<string, unknown> = {};\n const count = (v: unknown) =>\n typeof v === \"number\" && Number.isInteger(v) && v >= 0;\n if (count(legal.coolingOffPeriodDays))\n out[\"cooling_off_period_days\"] = legal.coolingOffPeriodDays;\n if (count(legal.warrantyPeriodMonths))\n out[\"warranty_period_months\"] = legal.warrantyPeriodMonths;\n if (typeof legal.cookieConsentRequired === \"boolean\") {\n out[\"cookie_consent_required\"] = legal.cookieConsentRequired;\n }\n if (typeof legal.recurringSubscriptionDisclosureRequired === \"boolean\") {\n out[\"recurring_subscription_disclosure_required\"] =\n legal.recurringSubscriptionDisclosureRequired;\n }\n return out;\n}\n\n/** The atlas's recommended payment methods (enabled ones), in its order, as\n * the integration type names checkout ranks by. */\nexport function paymentPriorityFromAtlas(atlas: CountryAtlas): string[] {\n return sortedPaymentMethods(atlas)\n .filter((m) => m.enabled)\n .map((m) => m.integration_type);\n}\n\nexport interface SeedOptions {\n countryId: number;\n mode: Mode;\n warehouseId?: number;\n businessId?: string;\n entityRegistered?: boolean;\n settlementCurrency?: string;\n}\n\n/** The keys `company_country` accepts on create and update, so a `--set`\n * typo is caught before it reaches the API. */\nexport const WRITABLE_KEYS = [\n \"currency\",\n \"warehouse_id\",\n \"handling_fee\",\n \"default\",\n \"tax_business_id\",\n \"tax_strategy\",\n \"tax_class\",\n \"mode\",\n \"settlement_currency\",\n \"entity_id\",\n \"entity_legally_registered\",\n \"tax_inclusive_pricing\",\n \"tax_name\",\n \"requires_3ds\",\n \"payment_method_priority\",\n \"checkout_requires_terms\",\n \"tax_invoice_required\",\n \"legal_settings\",\n \"address_fields\",\n] as const;\n\n/** The company_country attributes the atlas and the flags give. */\nexport function companyCountryFromAtlas(\n atlas: CountryAtlas,\n options: SeedOptions,\n): Record<string, unknown> {\n const body: Record<string, unknown> = {\n country_id: options.countryId,\n mode: options.mode.toUpperCase(),\n currency: atlas.defaultCurrency,\n requires_3ds: atlas.requires3ds === true,\n };\n const tax = atlas.taxSettings;\n if (tax) {\n // Fluid keeps a tax name only with tax-inclusive (VAT-style) pricing: it\n // clears tax_name whenever tax_inclusive_pricing is false. Sending one for\n // a tax-exclusive country would be silently dropped.\n if (tax.taxInclusive === true && typeof tax.taxName === \"string\" && tax.taxName !== \"\")\n body[\"tax_name\"] = tax.taxName;\n if (typeof tax.taxInclusive === \"boolean\")\n body[\"tax_inclusive_pricing\"] = tax.taxInclusive;\n if (typeof tax.invoiceRequired === \"boolean\")\n body[\"tax_invoice_required\"] = tax.invoiceRequired;\n }\n const priority = paymentPriorityFromAtlas(atlas);\n if (priority.length > 0) body[\"payment_method_priority\"] = priority;\n const legal = legalSettingsFromAtlas(atlas);\n if (Object.keys(legal).length > 0) body[\"legal_settings\"] = legal;\n const address = addressFieldsFromAtlas(atlas);\n if (address.length > 0) body[\"address_fields\"] = address;\n\n if (options.warehouseId !== undefined)\n body[\"warehouse_id\"] = options.warehouseId;\n if (options.businessId !== undefined)\n body[\"tax_business_id\"] = options.businessId;\n if (options.entityRegistered !== undefined) {\n body[\"entity_legally_registered\"] = options.entityRegistered;\n }\n if (options.settlementCurrency !== undefined) {\n body[\"settlement_currency\"] = options.settlementCurrency.toUpperCase();\n }\n return body;\n}\n\n/** Apply `--set` overrides on top of the seeded body, refusing unknown keys. */\nexport function applyOverrides(\n body: Record<string, unknown>,\n overrides: Record<string, unknown>,\n): Record<string, unknown> {\n const unknown = Object.keys(overrides).filter(\n (key) => !(WRITABLE_KEYS as readonly string[]).includes(key),\n );\n if (unknown.length > 0) {\n throw new LocalizationError(\n `Unknown company country setting(s): ${unknown.join(\", \")}. Settable: ${WRITABLE_KEYS.join(\", \")}.`,\n );\n }\n const merged = { ...body };\n for (const [key, value] of Object.entries(overrides)) {\n const current = merged[key];\n merged[key] =\n isPlainObject(current) && isPlainObject(value)\n ? { ...current, ...value }\n : value;\n }\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The body for changing named fields on an open country, leaving the rest as\n * they are. A nested override (`legal_settings.x=…`) merges into the\n * country's current object, so its other keys are kept.\n */\nexport function partialCountryUpdate(\n current: object,\n overrides: Record<string, unknown>,\n): Record<string, unknown> {\n const values = current as Record<string, unknown>;\n if (Object.keys(overrides).length === 0) {\n throw new LocalizationError(\n \"Name at least one setting to change, e.g. --set entity_id=12 or --settlement-currency USD.\",\n );\n }\n const base: Record<string, unknown> = {};\n for (const key of Object.keys(overrides)) {\n if (isPlainObject(values[key])) base[key] = values[key];\n }\n return applyOverrides(base, overrides);\n}\n","// The verification a country setup ends with: what the API says is in place\n// now, compared with the atlas, plus the steps that stay with a person.\n\nimport {\n launchChecklist,\n sortedPaymentMethods,\n type CountryAtlas,\n type Mode,\n} from \"./atlas.js\";\nimport type { CompanyCountry } from \"./api.js\";\nimport type { PriceCoverage } from \"./prices.js\";\nimport type { AgreementState, LanguageState } from \"./plan.js\";\nimport {\n addressFieldsFromAtlas,\n legalSettingsFromAtlas,\n paymentPriorityFromAtlas,\n} from \"./seed.js\";\n\nexport interface Check {\n id: string;\n ok: boolean;\n detail: string;\n}\n\nfunction same(a: unknown, b: unknown): boolean {\n return a === b || (a == null && b == null);\n}\n\n/** Compare the company country with the atlas. A value an admin changed on\n * purpose shows as a difference to review, not as a failure to fix. */\nexport function settingsChecks(\n atlas: CountryAtlas,\n cc: CompanyCountry,\n): Check[] {\n const checks: Check[] = [];\n checks.push({\n id: \"mode\",\n ok: typeof cc.mode === \"string\" && cc.mode !== \"\",\n detail: cc.mode ? `Operating mode ${cc.mode}.` : \"No operating mode set.\",\n });\n checks.push({\n id: \"currency\",\n ok: same(cc.currency, atlas.defaultCurrency),\n detail: `Currency ${cc.currency ?? \"unset\"}; the atlas recommends ${atlas.defaultCurrency}.`,\n });\n const tax = atlas.taxSettings;\n if (tax) {\n // A tax name only applies with tax-inclusive pricing; Fluid clears it\n // otherwise, so a tax-exclusive country has nothing to check.\n const inclusive = cc.tax_inclusive_pricing === true;\n checks.push(\n inclusive\n ? {\n id: \"tax_name\",\n ok: same(cc.tax_name, tax.taxName),\n detail: `Tax name ${cc.tax_name ?? \"unset\"}; the atlas says ${tax.taxName}.`,\n }\n : {\n id: \"tax_name\",\n ok: true,\n detail: `No tax name needed: prices are tax-exclusive, so tax is added at checkout (Fluid stores a tax name only with tax-inclusive pricing).`,\n },\n );\n checks.push({\n id: \"tax_inclusive_pricing\",\n ok: same(cc.tax_inclusive_pricing, tax.taxInclusive),\n detail: `Tax-inclusive prices ${String(cc.tax_inclusive_pricing ?? \"unset\")}; the atlas says ${String(tax.taxInclusive)}.`,\n });\n }\n checks.push({\n id: \"requires_3ds\",\n ok: same(cc.requires_3ds ?? false, atlas.requires3ds ?? false),\n detail: `3-D Secure required ${String(cc.requires_3ds ?? false)}; the atlas says ${String(atlas.requires3ds ?? false)}.`,\n });\n if (tax && typeof tax.invoiceRequired === \"boolean\") {\n checks.push({\n id: \"tax_invoice_required\",\n ok: same(cc.tax_invoice_required, tax.invoiceRequired),\n detail: `Tax invoices required ${String(cc.tax_invoice_required ?? \"unset\")}; the atlas says ${String(tax.invoiceRequired)}.`,\n });\n }\n const priority = paymentPriorityFromAtlas(atlas);\n if (priority.length > 0) {\n const current = cc.payment_method_priority ?? [];\n checks.push({\n id: \"payment_method_priority\",\n ok: JSON.stringify(current) === JSON.stringify(priority),\n detail: `Payment order ${current.length > 0 ? current.join(\", \") : \"unset\"}; the atlas recommends ${priority.join(\", \")}.`,\n });\n }\n const legal = legalSettingsFromAtlas(atlas);\n const legalDiffs = Object.entries(legal)\n .filter(([key, value]) => cc.legal_settings?.[key] !== value)\n .map(([key]) => key);\n checks.push({\n id: \"legal_settings\",\n ok: legalDiffs.length === 0,\n detail:\n legalDiffs.length === 0\n ? \"Legal settings match the atlas.\"\n : `Legal settings differ from the atlas: ${legalDiffs.join(\", \")}.`,\n });\n const requiredFields = addressFieldsFromAtlas(atlas)\n .filter((f) => f.required)\n .map((f) => f.field);\n const current = Array.isArray(cc.address_fields) ? cc.address_fields : null;\n const notRequired = requiredFields.filter(\n (field) =>\n !current?.some((f) => f[\"field\"] === field && f[\"required\"] === true),\n );\n checks.push({\n id: \"address_fields\",\n ok: current !== null && current.length > 0 && notRequired.length === 0,\n detail:\n current === null\n ? \"No address layout on the country; checkout uses the default fields.\"\n : notRequired.length > 0\n ? `The atlas requires ${notRequired.join(\", \")}, which the country doesn't.`\n : `${current.length} address fields set, with every field the atlas requires.`,\n });\n return checks;\n}\n\nexport function statusReport(input: {\n countryCode: string;\n atlas: CountryAtlas;\n companyCountry: CompanyCountry | undefined;\n languages: LanguageState[];\n agreements: AgreementState[];\n /** Price coverage of the company's active products in the country. */\n prices?: PriceCoverage;\n}) {\n const {\n countryCode,\n atlas,\n companyCountry: cc,\n languages,\n agreements,\n } = input;\n if (!cc) {\n return {\n country: countryCode,\n open: false,\n done: false,\n next: `fluid countries open ${countryCode} --mode <nfr|otg|usd>`,\n };\n }\n const priceChecks: Check[] = input.prices\n ? [\n {\n id: \"prices\",\n ok: input.prices.priced === input.prices.variants,\n detail:\n input.prices.priced === input.prices.variants\n ? `All ${input.prices.variants} variants of your active products have a price in ${countryCode}${input.prices.bundles.length > 0 ? ` (bundles aren't counted; see byHand.bundles)` : \"\"}.`\n : `${input.prices.variants - input.prices.priced} of ${input.prices.variants} variants of your active products have no price in ${countryCode}. Run \\`fluid countries prices ${countryCode}\\`.`,\n },\n {\n id: \"on_sale\",\n ok: input.prices.onSale > 0 || input.prices.bundles.length > 0,\n detail:\n input.prices.onSale > 0\n ? `${input.prices.onSale} variants are on sale in ${countryCode}.`\n : input.prices.bundles.length > 0\n ? `No single products are on sale in ${countryCode}; check the bundles under byHand.bundles.`\n : `Nothing is on sale in ${countryCode} yet. Turn on the products you'll sell there, or run prices with --activate.`,\n },\n ]\n : [];\n const checks = [\n ...settingsChecks(atlas, cc),\n ...priceChecks,\n ...languages.map((l) => ({\n id: `language:${l.iso}`,\n ok: l.enabled,\n detail: l.enabled\n ? `${l.name ?? l.iso} is enabled.`\n : l.languageId === null\n ? `${l.iso} isn't in Fluid's language catalog.`\n : `${l.name ?? l.iso} isn't enabled.`,\n })),\n ...agreements.map((a) => ({\n id: `agreement:${a.localId}`,\n ok: a.status === \"linked\" && (a.missingTranslations ?? []).length === 0,\n detail:\n a.status === \"linked\"\n ? (a.missingTranslations ?? []).length > 0\n ? `\"${a.title}\" is on the country but has no ${a.missingTranslations!.join(\", \")} translation. Enable the language, then run agreements --translate.`\n : `\"${a.title}\" is on the country.`\n : a.status === \"inactive\"\n ? `\"${a.title}\" is on the country but inactive, so checkout doesn't show it. Activate it in Settings > Agreements.`\n : a.status === \"exists\"\n ? `\"${a.title}\" exists but isn't on this country. Add the country to it in Settings > Agreements.`\n : `\"${a.title}\" hasn't been created.`,\n })),\n ];\n const mode = (cc.mode ?? \"\").toLowerCase() as Mode;\n return {\n country: countryCode,\n open: true,\n companyCountryId: cc.id,\n testMode: cc.test_mode ?? null,\n done: checks.every((c) => c.ok),\n checks,\n // Translating is `fluid translations`' job; these are the commands that\n // report and fill each market language (the store's own default\n // language needs none, and `auto` says so).\n translations: (atlas.majorLanguages ?? []).map((iso) => ({\n language: iso,\n report: `fluid translations missing ${iso}`,\n translate: `fluid translations auto ${iso} --write --yes`,\n })),\n // Prices in the country's currency come from `fluid countries prices`.\n prices: {\n preview: `fluid countries prices ${countryCode}`,\n convert: `fluid countries prices ${countryCode} --rate <rate> --write --yes`,\n },\n byHand: {\n bundles:\n input.prices && input.prices.bundles.length > 0\n ? {\n how: `Check each bundle's price and availability in ${countryCode} in the bundle editor. Bundles can be priced from their groups, and each group can be switched off per country.`,\n items: input.prices.bundles,\n }\n : null,\n paymentMethods: sortedPaymentMethods(atlas).map((m) => ({\n integrationType: m.integration_type,\n name: m.name,\n recommended: m.enabled,\n })),\n enrollmentFields: [...(atlas.enrollmentFormFields ?? [])]\n .sort((a, b) => a.order - b.order)\n .map((f) => ({\n id: f.id,\n label: f.label,\n component: f.component,\n required: f.required,\n })),\n activatePrices:\n \"Put the products you will sell on sale in the country: run prices with --activate, or activate them per product.\",\n leaveTestMode:\n cc.test_mode === true\n ? \"The country is in test mode. Turn test mode off when payments are configured.\"\n : null,\n },\n launchChecklist: mode ? launchChecklist(atlas, mode) : [],\n };\n}\n","// A small runner for walks that are a fixed sequence of steps: each step asks\n// its questions through `ask`, and `<`, `>`, `?` and Ctrl-C move between\n// steps. Steps record their answers on shared state, so going back shows\n// what was chosen, and every save goes through the same functions as the\n// non-interactive commands.\n\nimport {\n NAV_HELP,\n type Choice,\n type Nav,\n type Prompter,\n type Question,\n} from \"@fluid-app/fluid-cli\";\n\n/** Thrown by `ask` when the person moves instead of answering. */\nexport class NavSignal extends Error {\n constructor(readonly nav: Nav) {\n super(`nav:${nav}`);\n this.name = \"NavSignal\";\n }\n}\n\nexport interface StepContext {\n prompter: Prompter;\n /** Ask one question; navigation is thrown as a NavSignal for the runner. */\n ask(question: Omit<Question, \"canGoBack\" | \"section\">): Promise<string>;\n /** Ask a pick-one question and return the chosen value. */\n choose(message: string, choices: Choice[], current?: string): Promise<string>;\n note(text: string): void;\n}\n\nexport interface WalkStep {\n id: string;\n title: string;\n /** Leave the step out when false, e.g. a warehouse for a USD country. */\n when?: () => boolean;\n run(ctx: StepContext): Promise<void>;\n}\n\nexport interface RunResult {\n /** The steps finished, in order. */\n completed: string[];\n skipped: string[];\n /** True when the person stopped before the last step. */\n stopped: boolean;\n}\n\n/** Run the steps in order, honouring back, skip, the menu and stop. */\nexport async function runSteps(\n prompter: Prompter,\n steps: readonly WalkStep[],\n options: { help?: boolean } = {},\n): Promise<RunResult> {\n const history: number[] = [];\n const completed: string[] = [];\n const skipped: string[] = [];\n if (options.help !== false) prompter.note(NAV_HELP);\n let index = nextActive(steps, 0);\n\n const ctxFor = (step: WalkStep): StepContext => ({\n prompter,\n note: (text) => prompter.note(text),\n ask: async (question) => {\n const reply = await prompter.ask({\n ...question,\n section: step.title,\n canGoBack: history.length > 0,\n } as Question);\n if (\"nav\" in reply) throw new NavSignal(reply.nav);\n return reply.value;\n },\n choose: async (message, choices, current) => {\n const reply = await prompter.ask({\n section: step.title,\n message,\n kind: \"select\",\n choices,\n ...(current !== undefined ? { current } : {}),\n canGoBack: history.length > 0,\n });\n if (\"nav\" in reply) throw new NavSignal(reply.nav);\n return reply.value;\n },\n });\n\n while (index < steps.length) {\n const step = steps[index]!;\n try {\n await step.run(ctxFor(step));\n if (!completed.includes(step.id)) completed.push(step.id);\n history.push(index);\n index = nextActive(steps, index + 1);\n } catch (error) {\n if (!(error instanceof NavSignal)) throw error;\n let nav: Nav | \"goto\" = error.nav;\n let target = -1;\n if (nav === \"menu\") {\n const choice = await prompter.menu(\"What next?\", [\n { title: \"Continue\", value: \"continue\" },\n {\n title: \"Back to the previous step\",\n value: \"back\",\n disabled: history.length === 0,\n },\n { title: \"Skip this step\", value: \"skip\" },\n { title: \"Go to a step\", value: \"goto\" },\n { title: \"Stop for now\", value: \"quit\" },\n ]);\n if (choice === null || choice === \"quit\") nav = \"quit\";\n else if (choice === \"continue\") continue;\n else if (choice === \"goto\") {\n const picked = await prompter.menu(\n \"Which step?\",\n steps\n .map((s, i) => ({ s, i }))\n .filter(({ s }) => !s.when || s.when())\n .map(({ s, i }) => ({\n title: `${completed.includes(s.id) ? \"✓\" : \" \"} ${s.title}`,\n value: String(i),\n })),\n );\n if (picked === null) continue;\n nav = \"goto\";\n target = Number(picked);\n } else nav = choice;\n }\n if (nav === \"quit\") return { completed, skipped, stopped: true };\n if (nav === \"back\") {\n const previous = history.pop();\n if (previous === undefined) prompter.note(\"This is the first step.\");\n else index = previous;\n continue;\n }\n if (nav === \"skip\") {\n if (!skipped.includes(step.id)) skipped.push(step.id);\n history.push(index);\n index = nextActive(steps, index + 1);\n continue;\n }\n if (nav === \"goto\") {\n history.push(index);\n index = target;\n }\n }\n }\n return { completed, skipped, stopped: false };\n}\n\nfunction nextActive(steps: readonly WalkStep[], from: number): number {\n let i = from;\n while (i < steps.length && steps[i]!.when && !steps[i]!.when!()) i += 1;\n return i;\n}\n\nexport const YES_NO: Choice[] = [\n { title: \"Yes\", value: \"yes\" },\n { title: \"No\", value: \"no\" },\n];\n","// `fluid countries walk [iso]`: opening a country as a conversation, the way\n// Mist's Open a Country flow asks it. It picks the country, shows the atlas,\n// asks the mode, the languages, the agreements and each atlas default, then\n// opens the country, offers the price conversion, and hands each new\n// language to the translations walk. Every save goes through the same\n// functions as `open`, `agreements`, `prices` and `translations enable`.\n\nimport type { Prompter } from \"@fluid-app/fluid-cli\";\n\nimport { listAgreements } from \"../shared/agreements.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport {\n enableLanguage,\n listLanguages,\n type Language,\n} from \"../shared/languages.js\";\nimport { runSteps, YES_NO, type WalkStep } from \"../shared/walk.js\";\nimport {\n createAgreementFromAtlas,\n createCompanyCountry,\n findCompanyCountry,\n listCatalogCountries,\n listCompanyCountries,\n listWarehouses,\n updateCompanyCountry,\n writeTranslations,\n type CompanyCountry,\n} from \"./api.js\";\nimport {\n atlasAgreement,\n fetchAtlas,\n htmlToText,\n launchChecklist,\n MODES,\n type CountryAtlas,\n type Mode,\n} from \"./atlas.js\";\nimport { agreementStates, languageStates } from \"./plan.js\";\nimport {\n companyCountryIn,\n currencyDecimals,\n listProductsWithVariants,\n planConversion,\n priceEditorOwnsPrices,\n referenceRate,\n summarizePlans,\n writeConversion,\n DEFAULT_PRICE_FIELDS,\n type PlanOptions,\n} from \"./prices.js\";\nimport { companyCountryFromAtlas } from \"./seed.js\";\n\n/** One line per mode, so the choice reads without the atlas open. */\nexport const MODE_LINES: Record<Mode, string> = {\n nfr: \"Not For Resale: ship cross-border from a warehouse outside the country\",\n otg: \"On The Ground: a local entity, local tax registration, local fulfilment\",\n usd: \"Sell cross-border and charge in US dollars, usually digital goods\",\n};\n\nexport interface CountryDraft {\n iso?: string;\n name?: string;\n countryId?: number;\n atlas?: CountryAtlas;\n existing?: CompanyCountry;\n mode?: Mode;\n warehouseId?: number;\n /** Language codes to turn on. */\n languages: string[];\n /** Atlas agreement ids to create. */\n createAgreements: string[];\n /** Company agreement ids to write the atlas translations onto. */\n translateAgreements: Array<{ localId: string; id: number }>;\n /** Changes to the atlas values, by company_country key. */\n overrides: Record<string, unknown>;\n legacyDefaults: boolean;\n}\n\nexport interface CountryWalkResult {\n country?: string;\n opened: boolean;\n reseeded: boolean;\n enabled: string[];\n agreementsCreated: string[];\n pricesWritten: number;\n stopped: boolean;\n /** Languages handed to the translations walk, or to run later. */\n translate: string[];\n failures: Array<{ step: string; error: string }>;\n}\n\nexport interface CountryWalkDeps {\n api: LocalizationClient;\n prompter: Prompter;\n /** Runs the translations walk for one language; omitted, the command is printed instead. */\n translate?: (\n iso: string,\n ) => Promise<{ failed: Array<{ id: string; error: string }> } | undefined>;\n fetchRate?: typeof referenceRate;\n}\n\nconst yes = (v: string) => v === \"yes\";\n\n/** The company_country body the walk would send: the atlas seed with the person's changes. */\nexport function draftAttributes(draft: CountryDraft): Record<string, unknown> {\n if (!draft.atlas || draft.countryId === undefined || !draft.mode) return {};\n const body = companyCountryFromAtlas(draft.atlas, {\n countryId: draft.countryId,\n mode: draft.mode,\n ...(draft.warehouseId !== undefined\n ? { warehouseId: draft.warehouseId }\n : {}),\n });\n for (const [key, value] of Object.entries(draft.overrides)) {\n if (value === undefined) delete body[key];\n else body[key] = value;\n }\n return body;\n}\n\nexport async function walkCountry(\n deps: CountryWalkDeps,\n options: { iso?: string } = {},\n): Promise<CountryWalkResult> {\n const { api, prompter } = deps;\n const draft: CountryDraft = {\n languages: [],\n createAgreements: [],\n translateAgreements: [],\n overrides: {},\n legacyDefaults: false,\n };\n const result: CountryWalkResult = {\n opened: false,\n reseeded: false,\n enabled: [],\n agreementsCreated: [],\n pricesWritten: 0,\n stopped: false,\n translate: [],\n failures: [],\n };\n let catalog: Array<{ id: number; iso: string; name: string }> | undefined;\n let languages: Language[] = [];\n let reseed = false;\n let confirmed = false;\n\n const setupSteps: WalkStep[] = [\n {\n id: \"country\",\n title: \"Country\",\n run: async (ctx) => {\n catalog ??= await listCatalogCountries(api);\n for (;;) {\n const iso = (\n await ctx.ask({\n message: \"Which country are you opening?\",\n kind: \"search\",\n hint: \"type to filter\",\n choices: catalog.map((c) => ({\n title: `${c.name} (${c.iso})`,\n value: c.iso,\n })),\n ...(draft.iso\n ? { current: draft.iso }\n : options.iso\n ? { current: options.iso.toUpperCase() }\n : {}),\n })\n ).toUpperCase();\n const found = await fetchAtlas(api, iso);\n if (!found.covered || !found.atlas) {\n ctx.note(\n `Fluid has no Country Atlas for ${found.countryCode} yet, so there's nothing to seed it from. Open it by hand in Settings > Countries, or pick another country.`,\n );\n continue;\n }\n if (draft.iso !== found.countryCode) {\n // A new country starts a new draft.\n Object.assign(draft, {\n mode: undefined,\n warehouseId: undefined,\n languages: [],\n createAgreements: [],\n translateAgreements: [],\n overrides: {},\n legacyDefaults: false,\n });\n }\n const entry = catalog.find(\n (c) => c.iso.toUpperCase() === found.countryCode,\n );\n draft.iso = found.countryCode;\n draft.name = entry?.name ?? found.countryCode;\n draft.countryId = entry?.id;\n draft.atlas = found.atlas;\n draft.existing = await findCompanyCountry(api, found.countryCode);\n return;\n }\n },\n },\n {\n id: \"atlas\",\n title: \"Country Atlas\",\n run: async (ctx) => {\n const atlas = draft.atlas!;\n ctx.note(\n [\n `${draft.name}: ${atlas.defaultCurrency}, ${atlas.taxSettings?.taxName ?? \"no tax name\"} ${atlas.taxSettings?.taxInclusive ? \"included in prices\" : \"added at checkout\"}, 3-D Secure ${atlas.requires3ds ? \"required\" : \"not required\"}.`,\n `Languages: ${(atlas.majorLanguages ?? []).join(\", \") || \"none listed\"}. Agreements: ${(atlas.agreements ?? []).length}. Payment methods: ${\n (atlas.paymentMethods ?? [])\n .filter((m) => m.enabled)\n .map((m) => m.name)\n .join(\", \") || \"none recommended\"\n }.`,\n ...(draft.existing\n ? [\n `${draft.iso} is already open for this company (${draft.existing.mode ?? \"no mode\"}, ${draft.existing.currency ?? \"no currency\"}).`,\n ]\n : []),\n ].join(\"\\n\"),\n );\n await ctx.choose(\"Continue with this country?\", [\n { title: \"Continue\", value: \"go\" },\n ]);\n },\n },\n {\n id: \"mode\",\n title: \"Operating mode\",\n run: async (ctx) => {\n const atlas = draft.atlas!;\n const mode = await ctx.choose(\n \"How will you operate there?\",\n MODES.map((m) => ({\n title: `${m.toUpperCase()} — ${MODE_LINES[m]}`,\n value: m,\n description: htmlToText(atlas.modes?.[m]?.overview ?? \"\")\n .split(\"\\n\")[0]\n ?.slice(0, 160),\n })),\n draft.mode ??\n ((draft.existing?.mode ?? \"\").toLowerCase() || undefined),\n );\n draft.mode = mode as Mode;\n },\n },\n {\n id: \"warehouse\",\n title: \"Warehouse\",\n when: () => draft.mode !== \"usd\",\n run: async (ctx) => {\n const warehouses = await listWarehouses(api);\n if (warehouses.length === 0) {\n ctx.note(\n \"This company has no warehouses yet; add one in Settings > Warehouses later.\",\n );\n return;\n }\n const id = await ctx.choose(\n \"Which warehouse fulfils its orders?\",\n [\n ...warehouses.map((w) => ({\n title: `${w.name}${w.country?.iso ? ` (${w.country.iso})` : \"\"}${w.active === false ? \" — inactive\" : \"\"}`,\n value: String(w.id),\n })),\n { title: \"Decide later\", value: \"none\" },\n ],\n draft.warehouseId !== undefined\n ? String(draft.warehouseId)\n : undefined,\n );\n draft.warehouseId = id === \"none\" ? undefined : Number(id);\n },\n },\n {\n id: \"languages\",\n title: \"Languages\",\n run: async (ctx) => {\n languages = await listLanguages(api);\n const states = languageStates(draft.atlas!, languages);\n draft.languages = [];\n for (const state of states) {\n if (state.enabled) {\n ctx.note(`${state.name ?? state.iso} is already on.`);\n continue;\n }\n if (state.languageId === null) {\n ctx.note(\n `${state.iso} isn't in Fluid's language catalog, so it can't be turned on.`,\n );\n continue;\n }\n const answer = await ctx.choose(\n `Turn on ${state.name ?? state.iso} for the whole company?`,\n YES_NO,\n );\n if (yes(answer)) draft.languages.push(state.iso);\n }\n },\n },\n {\n id: \"agreements\",\n title: \"Agreements\",\n run: async (ctx) => {\n const atlas = draft.atlas!;\n const states = agreementStates(\n atlas,\n await listAgreements(api),\n draft.existing?.id ?? null,\n );\n draft.createAgreements = [];\n draft.translateAgreements = [];\n for (const state of states) {\n if (state.status === \"missing\") {\n const answer = await ctx.choose(\n `Create \"${state.title}\"${state.requiredOnCheckout ? \" (required at checkout)\" : \"\"} in ${state.languages.join(\", \")}?`,\n YES_NO,\n );\n if (yes(answer)) draft.createAgreements.push(state.localId);\n } else if (\n (state.status === \"linked\" || state.status === \"inactive\") &&\n state.agreementId !== null\n ) {\n const answer = await ctx.choose(\n `\"${state.title}\" is already on ${draft.iso}. Write the atlas translations onto it?`,\n YES_NO,\n );\n if (yes(answer))\n draft.translateAgreements.push({\n localId: state.localId,\n id: state.agreementId,\n });\n } else {\n ctx.note(\n `\"${state.title}\" exists but isn't on ${draft.iso}; add the country to it in Settings > Agreements.`,\n );\n }\n }\n },\n },\n {\n id: \"defaults\",\n title: \"Atlas defaults\",\n run: async (ctx) => {\n const seeded = draftAttributes({ ...draft, overrides: {} });\n const overrides: Record<string, unknown> = {};\n const keep = async (label: string, shown: string) =>\n (await ctx.choose(`${label}: ${shown}`, [\n { title: \"Keep\", value: \"keep\" },\n { title: \"Change\", value: \"change\" },\n ])) === \"keep\";\n\n const currency = String(seeded[\"currency\"] ?? \"\");\n if (!(await keep(\"Currency\", currency))) {\n for (;;) {\n const value =\n (\n await ctx.ask({\n message: \"Currency code\",\n kind: \"text\",\n current: currency,\n })\n )\n .trim()\n .toUpperCase() || currency;\n if (/^[A-Z]{3}$/.test(value)) {\n overrides[\"currency\"] = value;\n break;\n }\n ctx.note(\n `\"${value}\" isn't a three-letter currency code, such as EUR.`,\n );\n }\n }\n const seededInclusive = seeded[\"tax_inclusive_pricing\"] === true;\n if (!(await keep(\"Prices include tax\", seededInclusive ? \"yes\" : \"no\")))\n overrides[\"tax_inclusive_pricing\"] = !seededInclusive;\n const inclusive =\n (overrides[\"tax_inclusive_pricing\"] ?? seededInclusive) === true;\n // Fluid keeps a tax name only with tax-inclusive pricing, so it's\n // asked for only then.\n if (inclusive) {\n const taxName = String(\n seeded[\"tax_name\"] ?? draft.atlas?.taxSettings?.taxName ?? \"\",\n );\n if (!(await keep(\"Tax name\", taxName || \"(none)\"))) {\n overrides[\"tax_name\"] =\n (\n await ctx.ask({\n message: \"Tax name\",\n kind: \"text\",\n current: taxName,\n })\n ).trim() || taxName;\n } else if (taxName && seeded[\"tax_name\"] === undefined) {\n overrides[\"tax_name\"] = taxName;\n }\n } else {\n delete overrides[\"tax_name\"];\n }\n const threeDs = seeded[\"requires_3ds\"] === true;\n if (\n !(await keep(\"Card payments need 3-D Secure\", threeDs ? \"yes\" : \"no\"))\n )\n overrides[\"requires_3ds\"] = !threeDs;\n const order =\n (seeded[\"payment_method_priority\"] as string[] | undefined) ?? [];\n if (\n !(await keep(\n \"Payment method order\",\n order.join(\", \") || \"checkout's default\",\n ))\n ) {\n const raw = await ctx.ask({\n message:\n \"Payment methods in order, comma-separated (empty for checkout's default)\",\n kind: \"text\",\n current: order.join(\", \"),\n });\n const list = raw\n .split(\",\")\n .map((m) => m.trim())\n .filter(Boolean);\n overrides[\"payment_method_priority\"] =\n list.length > 0 ? list : undefined;\n }\n const fields =\n (seeded[\"address_fields\"] as\n | Array<{ field: string; required: boolean }>\n | undefined) ?? [];\n if (\n fields.length > 0 &&\n !(await keep(\n \"Address layout\",\n fields.map((f) => `${f.field}${f.required ? \"*\" : \"\"}`).join(\", \"),\n ))\n ) {\n overrides[\"address_fields\"] = undefined;\n ctx.note(\n \"Checkout's default address layout will be used; edit it in Settings > Countries.\",\n );\n }\n draft.overrides = overrides;\n },\n },\n {\n id: \"legacy\",\n title: \"Fluid's generic defaults\",\n when: () => !draft.existing,\n run: async (ctx) => {\n const answer = await ctx.choose(\n \"Fluid can also add its generic country agreements and forms.\",\n [\n {\n title: \"Use the atlas agreements and forms only\",\n value: \"atlas\",\n },\n { title: \"Also add Fluid's generic ones\", value: \"legacy\" },\n ],\n draft.legacyDefaults ? \"legacy\" : \"atlas\",\n );\n draft.legacyDefaults = answer === \"legacy\";\n },\n },\n {\n id: \"confirm\",\n title: \"Review\",\n run: async (ctx) => {\n const body = draftAttributes(draft);\n ctx.note(\n [\n `${draft.existing ? \"Update\" : \"Open\"} ${draft.name} (${draft.iso}) in ${draft.mode?.toUpperCase()} mode.`,\n `Currency ${String(body[\"currency\"])}, tax ${String(body[\"tax_name\"] ?? \"none\")}${body[\"tax_inclusive_pricing\"] ? \" (included)\" : \"\"}, 3-D Secure ${body[\"requires_3ds\"] ? \"on\" : \"off\"}.`,\n `Payment order: ${((body[\"payment_method_priority\"] as string[] | undefined) ?? []).join(\", \") || \"checkout's default\"}.`,\n `Languages to turn on: ${draft.languages.join(\", \") || \"none\"}.`,\n `Agreements to create: ${draft.createAgreements.length}; to translate: ${draft.translateAgreements.length}.`,\n ...(draft.existing\n ? []\n : [\n draft.legacyDefaults\n ? \"Fluid's generic country agreements and forms will be added too.\"\n : \"Only the atlas agreements and forms; Fluid's generic ones are left out.\",\n ]),\n ].join(\"\\n\"),\n );\n const choices = draft.existing\n ? [\n {\n title: `Write these settings over ${draft.iso}'s current ones`,\n value: \"reseed\",\n },\n {\n title: `Keep ${draft.iso}'s settings; just do languages and agreements`,\n value: \"keep\",\n },\n ]\n : [{ title: `Open ${draft.iso} now`, value: \"open\" }];\n const answer = await ctx.choose(\"Ready?\", choices);\n reseed = answer === \"reseed\";\n confirmed = true;\n },\n },\n ];\n\n const run = await runSteps(prompter, setupSteps);\n result.country = draft.iso;\n if (run.stopped || !confirmed || !draft.iso) {\n result.stopped = true;\n prompter.note(\"Stopped. Nothing was changed.\");\n return result;\n }\n\n // ---- apply, through the same calls as the commands\n const iso = draft.iso;\n try {\n const body = draftAttributes(draft);\n if (!draft.existing) {\n draft.existing = await createCompanyCountry(\n api,\n body,\n draft.legacyDefaults,\n );\n result.opened = true;\n prompter.note(`Opened ${iso}.`);\n } else if (reseed) {\n draft.existing = await updateCompanyCountry(\n api,\n draft.existing.id,\n body,\n draft.existing.revision,\n );\n result.reseeded = true;\n prompter.note(`Updated ${iso}'s settings.`);\n }\n } catch (error) {\n result.failures.push({ step: \"open\", error: failureOf(error).error });\n prompter.note(\n `${iso} couldn't be saved: ${failureOf(error).error}. Nothing else was changed.`,\n );\n return result;\n }\n\n for (const code of draft.languages) {\n const language = languages.find(\n (l) => l.iso.toLowerCase() === code.toLowerCase(),\n );\n if (!language) continue;\n try {\n await enableLanguage(api, language);\n result.enabled.push(language.iso);\n } catch (error) {\n result.failures.push({\n step: `language:${code}`,\n error: failureOf(error).error,\n });\n }\n }\n if (result.enabled.length > 0)\n prompter.note(`Turned on ${result.enabled.join(\", \")}.`);\n\n for (const localId of draft.createAgreements) {\n try {\n const created = await createAgreementFromAtlas(\n api,\n atlasAgreement(draft.atlas!, localId),\n draft.existing!.id,\n );\n result.agreementsCreated.push(localId);\n for (const f of created.failedTranslations) {\n result.failures.push({\n step: `agreement:${localId}:${f.language}`,\n error: f.error,\n });\n }\n } catch (error) {\n result.failures.push({\n step: `agreement:${localId}`,\n error: failureOf(error).error,\n });\n }\n }\n for (const { localId, id } of draft.translateAgreements) {\n const written = await writeTranslations(\n api,\n id,\n atlasAgreement(draft.atlas!, localId),\n );\n for (const f of written.failedTranslations) {\n result.failures.push({\n step: `agreement:${localId}:${f.language}`,\n error: f.error,\n });\n }\n }\n if (draft.createAgreements.length + draft.translateAgreements.length > 0) {\n prompter.note(\n `Agreements: ${result.agreementsCreated.length} created, ${draft.translateAgreements.length} translated.`,\n );\n }\n for (const failure of result.failures)\n prompter.note(`Not saved (${failure.step}): ${failure.error}`);\n\n // ---- prices\n const priced = await offerPrices(deps, iso);\n result.pricesWritten = priced.written;\n for (const f of priced.failed) {\n result.failures.push({\n step: `price:variant:${f.variantId}`,\n error: f.error,\n });\n }\n\n // ---- translations\n const toTranslate = (draft.atlas!.majorLanguages ?? []).filter((code) => {\n const language = languages.find(\n (l) => l.iso.toLowerCase() === code.toLowerCase(),\n );\n return language?.active || result.enabled.includes(language?.iso ?? \"\");\n });\n for (const code of toTranslate) {\n const answer = await prompter.menu(\n `Translate the storefront into ${code} now?`,\n [\n {\n title: \"Yes, walk through it\",\n value: \"walk\",\n disabled: !deps.translate,\n },\n { title: \"Later\", value: \"later\" },\n ],\n );\n result.translate.push(code);\n if (answer === \"walk\" && deps.translate) {\n const walked = await deps.translate(code);\n for (const f of walked?.failed ?? []) {\n result.failures.push({\n step: `translation:${code}:${f.id}`,\n error: f.error,\n });\n }\n } else prompter.note(`Later: fluid translations walk ${code}`);\n }\n\n const checklist = draft.mode ? launchChecklist(draft.atlas!, draft.mode) : [];\n if (checklist.length > 0) {\n prompter.note(\n `Before you launch (${draft.mode!.toUpperCase()}):\\n${checklist.map((c) => `- ${c.label}`).join(\"\\n\")}`,\n );\n }\n prompter.note(\n `Check the result any time with: fluid countries status ${iso}`,\n );\n return result;\n}\n\n/** Show the conversion preview, ask for the rate, and write on a yes. */\nasync function offerPrices(\n deps: CountryWalkDeps,\n iso: string,\n): Promise<{\n written: number;\n failed: Array<{ variantId: number; error: string }>;\n}> {\n const none = { written: 0, failed: [] };\n const { api, prompter } = deps;\n const countries = await listCompanyCountries(api);\n const target = companyCountryIn(countries, iso);\n const source = companyCountryIn(countries, \"US\");\n if (!target?.country || !source?.currency || !target.currency) {\n prompter.note(\n `No US prices to convert from${source ? \"\" : \" (the company doesn't sell in the US)\"}. Convert from another country later with: fluid countries prices ${iso} --from-country <iso>`,\n );\n return none;\n }\n const from = source.currency.toUpperCase();\n const to = target.currency.toUpperCase();\n const reference =\n from === to\n ? { rate: 1, date: \"\", source: \"same currency\" }\n : await (deps.fetchRate ?? referenceRate)(from, to);\n const editor = await priceEditorOwnsPrices(api);\n const products = await listProductsWithVariants(api, [\"active\"]);\n for (;;) {\n const raw = await prompter.ask({\n section: \"Prices\",\n message: `Exchange rate, ${to} per ${from}${reference ? ` (reference ${reference.rate}${reference.date ? `, ${reference.date}` : \"\"})` : \"\"}`,\n kind: \"text\",\n hint: \"Enter to use it, > to skip prices\",\n ...(reference ? { current: String(reference.rate) } : {}),\n canGoBack: false,\n });\n if (\"nav\" in raw) {\n prompter.note(`Prices skipped. Later: fluid countries prices ${iso}`);\n return none;\n }\n const rate = Number(raw.value.trim() || reference?.rate);\n if (!Number.isFinite(rate) || rate <= 0) {\n prompter.note(\"Type a positive number, such as 0.92.\");\n continue;\n }\n const options: PlanOptions = {\n sourceIso: \"US\",\n targetIso: iso,\n targetCountryId: target.country.id,\n targetCurrency: to,\n rate,\n fields: DEFAULT_PRICE_FIELDS,\n overwrite: false,\n activate: false,\n };\n const plans = planConversion(products, options);\n const summary = summarizePlans(plans, 5);\n prompter.note(\n [\n `At ${rate} ${to} per ${from}, rounded half up to ${currencyDecimals(to)} decimals:`,\n `${summary.toConvert} of ${summary.variants} variants get a price; ${summary.skipped.alreadyPriced} already have one, ${summary.skipped.noSourcePrice} have no US price, ${summary.skipped.roundsToZero} would round to zero.`,\n ...summary.sample.map(\n (s) =>\n ` ${s.product ?? s.productId}${s.sku ? ` (${s.sku})` : \"\"}: ${String(s.before.price ?? \"—\")} → ${String(s.after.price)} ${to}`,\n ),\n ...(editor\n ? [\n \"Your prices are managed in the price editor; any it can't take are listed.\",\n ]\n : []),\n ].join(\"\\n\"),\n );\n if (summary.toConvert === 0) return none;\n const answer = await prompter.menu(\n `Save ${summary.toConvert} prices in ${to}?`,\n [\n { title: \"Save them\", value: \"write\" },\n { title: \"Use a different rate\", value: \"rate\" },\n { title: \"Not now\", value: \"later\" },\n ],\n );\n if (answer === \"rate\") continue;\n if (answer !== \"write\") {\n prompter.note(\n `Prices skipped. Later: fluid countries prices ${iso} --rate ${rate} --write --yes`,\n );\n return none;\n }\n const written = await writeConversion(api, plans, options);\n prompter.note(\n `Saved ${written.written.length} prices${written.failed.length > 0 ? `; ${written.failed.length} refused:\\n${written.failed.map((f) => ` variant ${f.variantId}: ${f.error}`).join(\"\\n\")}` : \".\"}`,\n );\n return {\n written: written.written.length,\n failed: written.failed.map((f) => ({\n variantId: f.variantId,\n error: f.error,\n })),\n };\n }\n}\n","import { LocalizationError } from \"../shared/errors.js\";\n\n/**\n * A storefront resource on the v2026-04 company surface. Each one is\n * translated inline: `PATCH /api/v202604/company/<path>/<id>?lang=<iso>` with\n * the translated fields under `bodyKey`. Every response carries a `languages`\n * array naming the locales the row is translated into.\n */\nexport interface StorefrontType {\n kind: \"storefront\";\n /** The name commands take, e.g. `enrollment-packs`. */\n type: string;\n label: string;\n /** Path segment under `/api/v202604/company/`. */\n path: string;\n /** Key the write body wraps the translated fields in. */\n bodyKey: string;\n /** Key the show response wraps the row in. */\n showKey: string;\n /** Key the list response puts rows under. */\n listKey: string;\n /** Every field a translation can write. */\n fields: readonly string[];\n /** The text fields machine translation fills; the rest are URLs. */\n textFields: readonly string[];\n}\n\nexport interface MenuItemsType {\n kind: \"menu-items\";\n type: \"menu-items\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\nexport interface ThemeType {\n kind: \"theme\";\n type: \"theme\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\n/** Agreements: title and legal text, both saved together. */\nexport interface AgreementsType {\n kind: \"agreements\";\n type: \"agreements\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\n/** Product labels (badges): title only. */\nexport interface LabelsType {\n kind: \"labels\";\n type: \"labels\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\n/** Per-template theme strings that override the locale files on one page. */\nexport interface ThemeSectionsType {\n kind: \"theme-sections\";\n type: \"theme-sections\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\nexport type ResourceType =\n | StorefrontType\n | MenuItemsType\n | ThemeType\n | ThemeSectionsType\n | AgreementsType\n | LabelsType;\n\n/** Types whose source text is in the store's default language (theme types use --from). */\nexport function readsInStoreLanguage(type: ResourceType): boolean {\n return type.kind !== \"theme\" && type.kind !== \"theme-sections\";\n}\n\nfunction storefront(\n type: string,\n label: string,\n bodyKey: string,\n fields: readonly string[],\n urlFields: readonly string[] = [\"image_url\"],\n showKey: string = bodyKey,\n): StorefrontType {\n return {\n kind: \"storefront\",\n type,\n label,\n path: type,\n bodyKey,\n showKey,\n listKey: type.replace(/-/g, \"_\"),\n fields: [...fields, ...urlFields],\n textFields: fields,\n };\n}\n\n/**\n * The field sets match what fluid-admin's translation sheets read and write,\n * which in turn match each v2026-04 write schema's locale-scoped fields.\n * Enrollment packs have no per-locale image on that schema.\n */\nexport const RESOURCE_TYPES: readonly ResourceType[] = [\n storefront(\"products\", \"Products\", \"product\", [\n \"title\",\n \"introduction\",\n \"description\",\n \"feature_text\",\n ]),\n storefront(\"categories\", \"Categories\", \"category\", [\"title\", \"description\"]),\n storefront(\"collections\", \"Collections\", \"collection\", [\n \"title\",\n \"description\",\n ]),\n storefront(\"pages\", \"Pages\", \"page\", [\"title\", \"description\"]),\n storefront(\"posts\", \"Posts\", \"post\", [\"title\", \"description\", \"summary\"]),\n storefront(\n \"media\",\n \"Media\",\n \"medium\",\n [\"title\", \"description\"],\n [\"image_url\", \"video_url\", \"pdf_url\"],\n ),\n // Playlists write under `library` but read back under `playlist`.\n storefront(\n \"playlists\",\n \"Playlists\",\n \"library\",\n [\"title\", \"description\"],\n [\"image_url\"],\n \"playlist\",\n ),\n storefront(\n \"enrollment-packs\",\n \"Enrollment packs\",\n \"enrollment_pack\",\n [\"title\", \"description\"],\n [],\n ),\n {\n kind: \"menu-items\",\n type: \"menu-items\",\n label: \"Navigation menu items\",\n fields: [\"title\"],\n textFields: [\"title\"],\n },\n {\n kind: \"theme\",\n type: \"theme\",\n label: \"Theme strings (locales/<iso>.json)\",\n fields: [\"<key.path>\"],\n textFields: [\"<key.path>\"],\n },\n {\n kind: \"theme-sections\",\n type: \"theme-sections\",\n label: \"Theme page strings (a template's own translations)\",\n fields: [\"<key.path>\"],\n textFields: [\"<key.path>\"],\n },\n {\n kind: \"agreements\",\n type: \"agreements\",\n label: \"Agreements\",\n fields: [\"title\", \"description\"],\n textFields: [\"title\", \"description\"],\n },\n {\n kind: \"labels\",\n type: \"labels\",\n label: \"Product labels\",\n fields: [\"title\"],\n textFields: [\"title\"],\n },\n];\n\nexport const TYPE_NAMES = RESOURCE_TYPES.map((t) => t.type);\n\nexport function resourceType(name: string): ResourceType {\n const wanted = name.trim().toLowerCase();\n const match = RESOURCE_TYPES.find((t) => t.type === wanted);\n if (!match) {\n throw new LocalizationError(\n `\"${name}\" isn't a translatable type. Use one of: ${TYPE_NAMES.join(\", \")}.`,\n );\n }\n return match;\n}\n\n/** The types `--type` selects, or every type when none is given. */\nexport function selectTypes(names: readonly string[]): ResourceType[] {\n if (names.length === 0) return [...RESOURCE_TYPES];\n const picked = names\n .flatMap((name) => name.split(\",\"))\n .filter((name) => name.trim().length > 0)\n .map(resourceType);\n return RESOURCE_TYPES.filter((t) => picked.includes(t));\n}\n\n/** Reject fields a type can't translate, so a typo never reaches the API. */\nexport function checkFields(\n type: StorefrontType | MenuItemsType | AgreementsType | LabelsType,\n fields: Record<string, unknown>,\n): void {\n const unknown = Object.keys(fields).filter((f) => !type.fields.includes(f));\n if (unknown.length > 0) {\n throw new LocalizationError(\n `${type.label} can't translate ${unknown.join(\", \")}. Translatable fields: ${type.fields.join(\", \")}.`,\n );\n }\n if (Object.keys(fields).length === 0) {\n throw new LocalizationError(\n `Pass at least one field, e.g. --set title=... (${type.fields.join(\", \")}).`,\n );\n }\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { statusOf } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport type { StorefrontType } from \"./resources.js\";\n\nexport type Row = Record<string, unknown> & { id: number };\n\ninterface ListWire {\n meta?: { pagination?: { next_cursor?: string | null } };\n [key: string]: unknown;\n}\n\nconst PAGE_LIMIT = 100;\n/** A guard against a cursor that never ends; 1,000 pages is 100,000 rows. */\nconst MAX_PAGES = 1000;\n\nexport function companyPath(type: StorefrontType, id?: number): string {\n return `/api/v202604/company/${type.path}${id === undefined ? \"\" : `/${id}`}`;\n}\n\n/** Every row of a type, following the cursor. Rows are in the default locale. */\nexport async function listAll(\n client: LocalizationClient,\n type: StorefrontType,\n): Promise<Row[]> {\n const rows: Row[] = [];\n let cursor: string | null | undefined;\n for (let page = 0; page < MAX_PAGES; page += 1) {\n const data = await client.get<ListWire>(companyPath(type), {\n \"page[limit]\": PAGE_LIMIT,\n ...(cursor ? { \"page[cursor]\": cursor } : {}),\n });\n const batch = data[type.listKey];\n if (Array.isArray(batch)) rows.push(...(batch as Row[]));\n cursor = data.meta?.pagination?.next_cursor;\n if (!cursor) return rows;\n }\n throw new LocalizationError(\n `${type.label} has more than ${MAX_PAGES * PAGE_LIMIT} rows, more than one run can list. Translate it in fluid-admin or through the API.`,\n );\n}\n\n/** One row, read in `lang` (fields with no translation fall back to `en`). */\nexport async function getRow(\n client: LocalizationClient,\n type: StorefrontType,\n id: number,\n lang?: string,\n): Promise<Row> {\n let data: Record<string, unknown>;\n try {\n data = await client.get<Record<string, unknown>>(\n companyPath(type, id),\n lang ? { lang } : undefined,\n );\n } catch (error) {\n if (statusOf(error) === 404) {\n throw new LocalizationError(\n `No ${type.label.toLowerCase()} row with id ${id}.`,\n );\n }\n throw error;\n }\n const row = data[type.showKey];\n if (!row || typeof row !== \"object\") {\n throw new LocalizationError(\n `The API returned no ${type.showKey} for id ${id}.`,\n );\n }\n return row as Row;\n}\n\n/** The locales a row is translated into, always including the default. */\nexport function rowLanguages(row: Row): string[] {\n const languages = row[\"languages\"];\n return Array.isArray(languages)\n ? languages.filter((l): l is string => typeof l === \"string\")\n : [];\n}\n\nexport function isTranslated(row: Row, iso: string): boolean {\n const wanted = iso.toLowerCase();\n return rowLanguages(row).some((l) => l.toLowerCase() === wanted);\n}\n\n/**\n * Rich-text fields (descriptions on products, media, and enrollment packs)\n * can come back as a string or as `{ body }`. Normalize to a string, or\n * null when there is no value.\n */\nexport function fieldText(value: unknown): string | null {\n if (typeof value === \"string\") return value.length > 0 ? value : null;\n if (value && typeof value === \"object\") {\n const body = (value as { body?: unknown }).body;\n if (typeof body === \"string\") return body.length > 0 ? body : null;\n }\n return null;\n}\n\n/** A row's translatable fields as strings (or null), in `fields` order. */\nexport function pickFields(\n row: Row,\n fields: readonly string[],\n): Record<string, string | null> {\n return Object.fromEntries(fields.map((f) => [f, fieldText(row[f])]));\n}\n\nexport function rowTitle(row: Row): string | null {\n return fieldText(row[\"title\"]);\n}\n\n/**\n * Write one locale's translated fields. A translation PATCH writes only the\n * translated fields on every resource except products, where any other field\n * sent would also apply; the caller sends translated fields only.\n */\nexport async function writeTranslation(\n client: LocalizationClient,\n type: StorefrontType,\n id: number,\n lang: string,\n fields: Record<string, unknown>,\n): Promise<Row> {\n const data = await client.patch<Record<string, unknown>>(\n `${companyPath(type, id)}?lang=${encodeURIComponent(lang)}`,\n { [type.bodyKey]: fields },\n );\n const row = data[type.showKey] ?? data[type.bodyKey];\n return (row && typeof row === \"object\" ? row : { id }) as Row;\n}\n\n/**\n * The store's default language. Every row's `languages` lists it first, so\n * the first row of any storefront type answers; null for an empty catalog.\n */\nexport async function storeDefaultIso(\n client: LocalizationClient,\n types: readonly StorefrontType[],\n): Promise<string | null> {\n for (const type of types) {\n let data: ListWire;\n try {\n data = await client.get<ListWire>(companyPath(type), {\n \"page[limit]\": 1,\n });\n } catch {\n // A token without access to this type can still translate others.\n continue;\n }\n const batch = data[type.listKey];\n const first = Array.isArray(batch)\n ? (batch[0] as Row | undefined)\n : undefined;\n const iso = first ? rowLanguages(first)[0] : undefined;\n if (iso) return iso;\n }\n return null;\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n findLanguage,\n requireActiveLanguage,\n sameIso,\n type Language,\n} from \"../shared/languages.js\";\nimport {\n readsInStoreLanguage,\n RESOURCE_TYPES,\n type ResourceType,\n type StorefrontType,\n} from \"./resources.js\";\nimport { storeDefaultIso } from \"./storefront.js\";\n\nexport interface AutoLanguages {\n /** The language translations go into. */\n target: Language;\n /** The theme's source locale (`--from`). */\n source: Language;\n /** The store's default language, which storefront text is read in. */\n store: Language;\n}\n\n/**\n * The languages a machine translation runs between. Storefront rows, menu\n * items, agreements and labels read back in the store's default language,\n * so that is the language the translator is told they're in; it's read from\n * the rows (selected storefront types first), `storeLanguage` only fills in\n * when they can't be read, and it's never guessed. Theme strings translate\n * from `from`.\n */\nexport async function autoLanguages(\n api: LocalizationClient,\n types: readonly ResourceType[],\n options: {\n iso: string;\n from: string;\n storeLanguage?: string;\n write: boolean;\n },\n): Promise<AutoLanguages> {\n const target = options.write\n ? await requireActiveLanguage(api, options.iso)\n : await findLanguage(api, options.iso);\n const source = await findLanguage(api, options.from);\n let store = source;\n if (types.some(readsInStoreLanguage)) {\n const storefrontTypes = [\n ...types.filter((t): t is StorefrontType => t.kind === \"storefront\"),\n ...RESOURCE_TYPES.filter(\n (t): t is StorefrontType =>\n t.kind === \"storefront\" && !types.includes(t),\n ),\n ];\n const storeIso =\n (await storeDefaultIso(api, storefrontTypes)) ?? options.storeLanguage;\n if (!storeIso) {\n throw new LocalizationError(\n \"Couldn't read the store's default language from its storefront rows. Pass --store-language <iso>.\",\n );\n }\n store = sameIso(storeIso, source.iso)\n ? source\n : await findLanguage(api, storeIso);\n if (sameIso(store.iso, target.iso)) {\n throw new LocalizationError(\n `${target.name} is the store's default language, so storefront content in it is the source, not a translation. Use --type theme --type theme-sections to translate only theme strings.`,\n );\n }\n }\n if (\n types.some((t) => !readsInStoreLanguage(t)) &&\n sameIso(source.iso, target.iso)\n ) {\n throw new LocalizationError(\n `--from and the target are both ${target.iso}; pass --from with the theme's source locale.`,\n );\n }\n return { target, source, store };\n}\n","// Product labels (badges such as \"Vegan\") translate their title. The read in\n// a language falls back to another language's title when it has none, so\n// the API can't say whether a translation exists; a label counts as missing\n// while its title in the language reads the same as in the default.\n\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { statusOf } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\nexport interface Label {\n id: number;\n title: string;\n}\n\ninterface LabelWire {\n id: number;\n title?: string | null;\n}\n\nconst LABELS_PATH = \"/api/company/labels\";\n\n/**\n * The company's labels in the default language. The list isn't paginated and\n * holds only the labels an admin created; Fluid's own labels (\"New\",\n * \"Trending\") are translated by Fluid and can't be changed.\n */\nexport async function listLabels(client: LocalizationClient): Promise<Label[]> {\n const data = await client.get<{ label?: LabelWire[] }>(LABELS_PATH);\n return (data.label ?? []).map((wire) => ({\n id: wire.id,\n title: wire.title ?? \"\",\n }));\n}\n\n/** One label's title in `lang`, or in the default language without one. */\nexport async function getLabelTitle(\n client: LocalizationClient,\n id: number,\n lang?: string,\n): Promise<string> {\n try {\n const data = await client.get<{ label?: LabelWire }>(\n `${LABELS_PATH}/${id}`,\n lang ? { lang } : undefined,\n );\n return data.label?.title ?? \"\";\n } catch (error) {\n if (statusOf(error) === 404) {\n throw new LocalizationError(\n `No label with id ${id}, or it's one of Fluid's own labels, which Fluid translates.`,\n );\n }\n throw error;\n }\n}\n\n/** Whether a label's title in `lang` differs from its default title. */\nexport function labelTranslated(source: string, translated: string): boolean {\n return translated.trim().length > 0 && translated.trim() !== source.trim();\n}\n\nexport async function writeLabelTitle(\n client: LocalizationClient,\n id: number,\n lang: string,\n title: string,\n): Promise<void> {\n await client.put(`${LABELS_PATH}/${id}?lang=${encodeURIComponent(lang)}`, {\n label: { title },\n });\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\n/** Fields per machine-translation request, so one large batch can't time out. */\nexport const BATCH_SIZE = 40;\n\ninterface TranslateWire {\n translations?: Record<string, unknown>;\n usage?: { input_tokens?: number; output_tokens?: number };\n}\n\nexport interface MachineResult {\n translations: Record<string, string>;\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/**\n * Machine-translate id-keyed strings with Fluid AI\n * (`POST /api/v202604/ai/translate`). Languages are named, not coded\n * (\"english\", \"french\"), which is what the endpoint expects. Nothing is\n * saved; the caller writes the results back.\n */\nexport async function machineTranslate(\n client: LocalizationClient,\n sourceLanguage: string,\n targetLanguage: string,\n fields: Record<string, string>,\n model?: string,\n): Promise<MachineResult> {\n const entries = Object.entries(fields).filter(([, v]) => v.trim().length > 0);\n const result: MachineResult = {\n translations: {},\n usage: { inputTokens: 0, outputTokens: 0 },\n };\n for (let start = 0; start < entries.length; start += BATCH_SIZE) {\n const batch = Object.fromEntries(entries.slice(start, start + BATCH_SIZE));\n const data = await client.post<TranslateWire>(\"/api/v202604/ai/translate\", {\n source_language: languageName(sourceLanguage),\n target_language: languageName(targetLanguage),\n fields: batch,\n ...(model ? { model } : {}),\n });\n for (const id of Object.keys(batch)) {\n const value = data.translations?.[id];\n if (typeof value === \"string\" && value.trim().length > 0) {\n result.translations[id] = value;\n }\n }\n result.usage.inputTokens += data.usage?.input_tokens ?? 0;\n result.usage.outputTokens += data.usage?.output_tokens ?? 0;\n }\n return result;\n}\n\n/** \"Spanish\" → \"spanish\"; the endpoint takes lowercase names up to 64 characters. */\nexport function languageName(name: string): string {\n const value = name.trim().toLowerCase();\n if (value.length === 0 || value.length > 64) {\n throw new LocalizationError(`\"${name}\" isn't a usable language name.`);\n }\n return value;\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\ninterface MenuItemWire {\n id: number;\n title?: string | null;\n sub_menu_items?: MenuItemWire[];\n}\n\ninterface MenuWire {\n id: number;\n title?: string | null;\n menu_items?: MenuItemWire[];\n}\n\ninterface MenusWire {\n menus?: MenuWire[];\n meta?: { total_pages?: number };\n}\n\nexport interface MenuItem {\n /** `<menu id>/<item id>`, the id `show` and `set` take for menu items. */\n id: string;\n menuId: number;\n itemId: number;\n menu: string | null;\n title: string | null;\n}\n\nconst PER_PAGE = 100;\nconst MAX_PAGES = 50;\n\n/**\n * Every menu item of every menu, flattened. With `lang`, titles come back\n * without the English fallback, so an item with no translation reads null.\n */\nexport async function listMenuItems(\n client: LocalizationClient,\n lang?: string,\n): Promise<MenuItem[]> {\n const items: MenuItem[] = [];\n for (let page = 1; page <= MAX_PAGES; page += 1) {\n const data = await client.get<MenusWire>(\"/api/menus\", {\n page,\n per_page: PER_PAGE,\n ...(lang ? { lang } : {}),\n });\n const menus = data.menus ?? [];\n for (const menu of menus) {\n flatten(menu, menu.menu_items ?? [], items);\n }\n const totalPages = data.meta?.total_pages ?? 1;\n if (page >= totalPages || menus.length < PER_PAGE) return items;\n }\n throw new LocalizationError(`More than ${MAX_PAGES * PER_PAGE} menus.`);\n}\n\nfunction flatten(menu: MenuWire, nodes: MenuItemWire[], out: MenuItem[]): void {\n for (const node of nodes) {\n out.push({\n id: `${menu.id}/${node.id}`,\n menuId: menu.id,\n itemId: node.id,\n menu: menu.title ?? null,\n title:\n typeof node.title === \"string\" && node.title.length > 0\n ? node.title\n : null,\n });\n flatten(menu, node.sub_menu_items ?? [], out);\n }\n}\n\n/** Parse a `<menu id>/<item id>` reference. */\nexport function parseMenuItemId(ref: string): {\n menuId: number;\n itemId: number;\n} {\n const match = /^(\\d+)\\/(\\d+)$/.exec(ref.trim());\n if (!match) {\n throw new LocalizationError(\n `\"${ref}\" isn't a menu item id. Use <menu id>/<item id>, as \\`fluid translations missing\\` prints it, e.g. 12/340.`,\n );\n }\n return { menuId: Number(match[1]), itemId: Number(match[2]) };\n}\n\n/** One menu item's title in the default locale and, without fallback, in `lang`. */\nexport async function getMenuItem(\n client: LocalizationClient,\n ref: string,\n lang: string,\n): Promise<{ source: string | null; translated: string | null }> {\n const { menuId, itemId } = parseMenuItemId(ref);\n const path = `/api/menus/${menuId}/menu_items/${itemId}`;\n const [source, translated] = await Promise.all([\n client.get<{ menu_item?: MenuItemWire }>(path),\n client.get<{ menu_item?: MenuItemWire }>(path, { lang }),\n ]);\n const text = (wire?: MenuItemWire) =>\n typeof wire?.title === \"string\" && wire.title.length > 0\n ? wire.title\n : null;\n return {\n source: text(source.menu_item),\n translated: text(translated.menu_item),\n };\n}\n\nexport async function writeMenuItemTitle(\n client: LocalizationClient,\n ref: string,\n lang: string,\n title: string,\n): Promise<{ id: string; title: string | null }> {\n const { menuId, itemId } = parseMenuItemId(ref);\n const data = await client.patch<{ menu_item?: MenuItemWire }>(\n `/api/menus/${menuId}/menu_items/${itemId}?lang=${encodeURIComponent(lang)}`,\n { menu_item: { title } },\n );\n return { id: `${menuId}/${itemId}`, title: data.menu_item?.title ?? null };\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { statusOf } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport { keyPath } from \"../shared/parsing.js\";\n\nexport type LocaleDoc = Record<string, unknown>;\n\ninterface ResourceWire {\n key?: string;\n content?: unknown;\n}\n\ninterface ResourcesWire {\n application_theme_resources?: ResourceWire[];\n content_version_sha?: string | null;\n}\n\n/** A theme's locale files, read together with the theme's version. */\nexport interface ThemeLocales {\n themeId: number;\n /** The theme's `content_version_sha` when read; a write sends it back as `base_sha`. */\n sha: string | null;\n /** Parsed `locales/<iso>.json` files, keyed by lowercased ISO code. */\n files: Map<string, { iso: string; doc: LocaleDoc }>;\n}\n\n/** The theme to work on: `--theme <id>`, else the company's active theme. */\nexport async function resolveThemeId(\n client: LocalizationClient,\n themeId?: string,\n): Promise<number> {\n if (themeId !== undefined) {\n const id = Number(themeId);\n if (!Number.isInteger(id) || id <= 0) {\n throw new LocalizationError(`\"${themeId}\" isn't a theme id.`);\n }\n return id;\n }\n try {\n const data = await client.get<{ theme?: { id?: number } }>(\n \"/api/v202604/themes/active\",\n );\n if (typeof data.theme?.id === \"number\") return data.theme.id;\n } catch (error) {\n if (statusOf(error) !== 404) throw error;\n }\n throw new LocalizationError(\n \"This company has no active theme. Pass --theme <id> to work on another theme.\",\n );\n}\n\n/**\n * Every `locales/<iso>.json` file on the theme, plus the theme's\n * `content_version_sha`, from one read of the theme resources API.\n */\nexport async function readThemeLocales(\n client: LocalizationClient,\n themeId: number,\n): Promise<ThemeLocales> {\n let data: ResourcesWire;\n try {\n data = await client.get<ResourcesWire>(\n `/api/application_themes/${themeId}/resources`,\n );\n } catch (error) {\n // The resources list refuses a theme whose member configuration is\n // invalid (422), even though its locale files are fine. Read just the\n // locale files instead; without a version, writes can't be guarded.\n if (statusOf(error) !== 422) throw error;\n return readLocaleTemplates(client, themeId);\n }\n const files = new Map<string, { iso: string; doc: LocaleDoc }>();\n for (const resource of data.application_theme_resources ?? []) {\n const match = /^locales\\/([^/]+)\\.json$/.exec(resource.key ?? \"\");\n if (!match) continue;\n const iso = match[1]!;\n files.set(iso.toLowerCase(), {\n iso,\n doc: parseLocale(resource.content, resource.key!),\n });\n }\n return { themeId, sha: data.content_version_sha ?? null, files };\n}\n\ninterface TemplateSummaryWire {\n id: number;\n name?: string;\n}\n\n/** Locale files through the v2026-04 templates endpoints, with no theme version. */\nasync function readLocaleTemplates(\n client: LocalizationClient,\n themeId: number,\n): Promise<ThemeLocales> {\n const list = await client.get<{ templates?: TemplateSummaryWire[] }>(\n `/api/v202604/themes/${themeId}/templates`,\n { themeable_type: \"locales\" },\n );\n const files = new Map<string, { iso: string; doc: LocaleDoc }>();\n for (const summary of list.templates ?? []) {\n if (!summary.name) continue;\n const data = await client.get<{ template?: { content?: unknown } }>(\n `/api/v202604/themes/${themeId}/templates/${summary.id}`,\n );\n const key = `locales/${summary.name}.json`;\n files.set(summary.name.toLowerCase(), {\n iso: summary.name,\n doc: parseLocale(data.template?.content, key),\n });\n }\n return { themeId, sha: null, files };\n}\n\n/** One locale file from a read, or null when the theme has none. */\nexport function localeOf(locales: ThemeLocales, iso: string): LocaleDoc | null {\n return locales.files.get(iso.toLowerCase())?.doc ?? null;\n}\n\nexport function parseLocale(content: unknown, key: string): LocaleDoc {\n if (content === null || content === undefined || content === \"\") return {};\n if (typeof content === \"object\" && !Array.isArray(content)) {\n return content as LocaleDoc;\n }\n if (typeof content === \"string\") {\n try {\n const parsed: unknown = JSON.parse(content);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as LocaleDoc;\n }\n } catch {\n // fall through\n }\n }\n throw new LocalizationError(\n `${key} isn't a JSON object; fix it before translating.`,\n );\n}\n\n/** Leaf strings keyed by dotted path: `{ cart: { title: \"Cart\" } }` → `cart.title`. */\nexport function flattenLocale(\n doc: LocaleDoc,\n prefix = \"\",\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(doc)) {\n const path = prefix ? `${prefix}.${key}` : key;\n if (typeof value === \"string\") {\n out[path] = value;\n } else if (value && typeof value === \"object\" && !Array.isArray(value)) {\n Object.assign(out, flattenLocale(value as LocaleDoc, path));\n }\n }\n return out;\n}\n\n/** Keys the source locale has that the target lacks or leaves blank. */\nexport function missingKeys(\n source: LocaleDoc,\n target: LocaleDoc | null,\n): Record<string, string> {\n const have = flattenLocale(target ?? {});\n const missing: Record<string, string> = {};\n for (const [key, value] of Object.entries(flattenLocale(source))) {\n if (value.trim().length === 0) continue;\n if ((have[key] ?? \"\").trim().length === 0) missing[key] = value;\n }\n return missing;\n}\n\n/**\n * Keys whose target value is still the source text word for word. Some are\n * right as they are (a brand name, \"OK\", a SKU); the rest were copied and\n * never translated. They aren't counted as missing, so they're listed for\n * review instead.\n */\nexport function sameAsSourceKeys(\n source: LocaleDoc,\n target: LocaleDoc | null,\n): string[] {\n const have = flattenLocale(target ?? {});\n return Object.entries(flattenLocale(source))\n .filter(([key, value]) => /\\p{L}/u.test(value) && have[key] === value)\n .map(([key]) => key);\n}\n\n/** A copy of `doc` with each dotted key set to its value. */\nexport function mergeLocale(\n doc: LocaleDoc,\n updates: Record<string, string>,\n): LocaleDoc {\n const out = structuredClone(doc);\n for (const [path, value] of Object.entries(updates)) {\n const parts = keyPath(path);\n let node: LocaleDoc = out;\n for (const part of parts.slice(0, -1)) {\n const next = Object.hasOwn(node, part) ? node[part] : undefined;\n if (typeof next === \"string\") {\n throw new LocalizationError(\n `\"${path}\" would replace the string at \"${part}\" with an object.`,\n );\n }\n if (!next || typeof next !== \"object\" || Array.isArray(next))\n node[part] = {};\n node = node[part] as LocaleDoc;\n }\n const leaf = parts[parts.length - 1]!;\n if (\n Object.hasOwn(node, leaf) &&\n node[leaf] &&\n typeof node[leaf] === \"object\"\n ) {\n throw new LocalizationError(\n `\"${path}\" is a group of keys, not a string.`,\n );\n }\n node[leaf] = value;\n }\n return out;\n}\n\n/**\n * Save `locales/<iso>.json` through the theme resources API, creating the\n * file if the theme has none. `base_sha` is the version the file was read\n * at, so the server refuses the write (409) if the theme changed since,\n * rather than overwriting someone else's edit. A read with no version\n * (the fallback for an invalid member configuration) can't be saved. The\n * write publishes immediately.\n */\nexport async function writeLocale(\n client: LocalizationClient,\n locales: ThemeLocales,\n iso: string,\n doc: LocaleDoc,\n): Promise<{ key: string; saved: true }> {\n if (!locales.sha) {\n // Without the theme's version the server can't refuse a stale write, so\n // an edit made since the read would be lost. Refuse instead.\n throw new LocalizationError(\n `Theme ${locales.themeId}'s files can't be listed (its member configuration is invalid), so the CLI can read its locale files but can't save them safely. Fix the theme's member configuration, or edit the locale file in the theme editor.`,\n );\n }\n const key = `locales/${locales.files.get(iso.toLowerCase())?.iso ?? iso}.json`;\n try {\n await client.put(`/api/application_themes/${locales.themeId}/resources`, {\n application_theme_resource: {\n key,\n content: JSON.stringify(doc, null, 2),\n },\n base_sha: locales.sha,\n });\n } catch (error) {\n if (statusOf(error) === 409) {\n // Keep the status, so a per-item report can say why.\n throw Object.assign(\n new LocalizationError(\n `Theme ${locales.themeId} changed after it was read, so ${key} wasn't saved. Run the command again to merge onto the latest version.`,\n ),\n { status: 409 },\n );\n }\n throw error;\n }\n return { key, saved: true };\n}\n","// Per-template theme translations: a page template's `translations` holds,\n// per locale, strings that override the theme's locale files on that page\n// (`{ \"fr\": { \"hero\": { \"title\": \"…\" } } }`). The storefront reads them\n// live, so a save shows at once without publishing.\n\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n flattenLocale,\n mergeLocale,\n missingKeys,\n type LocaleDoc,\n} from \"./theme.js\";\n\n/**\n * Template kinds that never render as a page of their own, so their\n * `translations` aren't read: shared sections, blocks and components, the\n * locale files themselves, layouts, and configuration.\n */\nconst NOT_PAGES = new Set([\n \"sections\",\n \"blocks\",\n \"components\",\n \"locales\",\n \"layouts\",\n \"config\",\n]);\n\ninterface TemplateSummaryWire {\n id: number;\n name?: string | null;\n themeable_type?: string | null;\n}\n\ninterface TemplateWire {\n id?: number;\n name?: string | null;\n translations?: unknown;\n}\n\nexport interface TemplateTranslations {\n templateId: number;\n name: string | null;\n translations: Record<string, LocaleDoc>;\n}\n\nfunction asTranslations(value: unknown): Record<string, LocaleDoc> {\n if (typeof value === \"string\") {\n try {\n return asTranslations(JSON.parse(value));\n } catch {\n return {};\n }\n }\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return {};\n const out: Record<string, LocaleDoc> = {};\n for (const [locale, doc] of Object.entries(value)) {\n if (doc && typeof doc === \"object\" && !Array.isArray(doc))\n out[locale] = doc as LocaleDoc;\n }\n return out;\n}\n\n/** One locale's strings from a template's translations, matching the code's casing. */\nexport function localeIn(\n translations: Record<string, LocaleDoc>,\n iso: string,\n): LocaleDoc | null {\n const key = Object.keys(translations).find(\n (k) => k.toLowerCase() === iso.toLowerCase(),\n );\n return key ? (translations[key] ?? null) : null;\n}\n\nexport async function readTemplateTranslations(\n client: LocalizationClient,\n themeId: number,\n templateId: number,\n): Promise<TemplateTranslations> {\n const data = await client.get<{ template?: TemplateWire }>(\n `/api/v202604/themes/${themeId}/templates/${templateId}`,\n );\n return {\n templateId,\n name: data.template?.name ?? null,\n translations: asTranslations(data.template?.translations),\n };\n}\n\n/**\n * Every page template of the theme that has translations. The template list\n * carries no translations, so each page template is read on its own.\n */\nexport async function listTemplateTranslations(\n client: LocalizationClient,\n themeId: number,\n): Promise<TemplateTranslations[]> {\n const list = await client.get<{ templates?: TemplateSummaryWire[] }>(\n `/api/v202604/themes/${themeId}/templates`,\n );\n const out: TemplateTranslations[] = [];\n for (const summary of list.templates ?? []) {\n if (NOT_PAGES.has(summary.themeable_type ?? \"\")) continue;\n const template = await readTemplateTranslations(\n client,\n themeId,\n summary.id,\n );\n if (Object.keys(template.translations).length > 0) out.push(template);\n }\n return out;\n}\n\n/** The keys a template's source locale has and the target lacks, as `<template>/<key>` ids. */\nexport function missingSectionKeys(\n templates: readonly TemplateTranslations[],\n sourceIso: string,\n iso: string,\n): {\n total: number;\n missing: Array<{\n id: string;\n templateId: number;\n key: string;\n source: string;\n }>;\n} {\n let total = 0;\n const missing: Array<{\n id: string;\n templateId: number;\n key: string;\n source: string;\n }> = [];\n for (const template of templates) {\n const source = localeIn(template.translations, sourceIso);\n if (!source) continue;\n total += Object.keys(missingKeys(source, null)).length;\n for (const [key, value] of Object.entries(\n missingKeys(source, localeIn(template.translations, iso)),\n )) {\n missing.push({\n id: `${template.templateId}/${key}`,\n templateId: template.templateId,\n key,\n source: value,\n });\n }\n }\n return { total, missing };\n}\n\n/** Parse a `<template id>/<key.path>` reference. */\nexport function parseSectionRef(ref: string): {\n templateId: number;\n key: string;\n} {\n const match = /^([1-9]\\d*)\\/(.+)$/.exec(ref.trim());\n if (!match) {\n throw new LocalizationError(\n `\"${ref}\" isn't a theme section string. Use <template id>/<key>, as \\`fluid translations missing\\` prints it, e.g. 412/hero.title.`,\n );\n }\n return { templateId: Number(match[1]), key: match[2]! };\n}\n\n/** A section string's source and translated text. */\nexport function sectionText(\n template: TemplateTranslations,\n key: string,\n sourceIso: string,\n iso: string,\n): { source: string | null; translation: string | null } {\n const source =\n flattenLocale(localeIn(template.translations, sourceIso) ?? {})[key] ??\n null;\n const translation =\n flattenLocale(localeIn(template.translations, iso) ?? {})[key] ?? null;\n return {\n source,\n translation:\n translation && translation.trim().length > 0 ? translation : null,\n };\n}\n\n/**\n * Save strings into one template's translations for `iso`. The template is\n * read again just before the write and the strings merged into what it holds\n * then, because the API replaces the whole translations object.\n */\nexport async function writeSectionStrings(\n client: LocalizationClient,\n themeId: number,\n templateId: number,\n iso: string,\n updates: Record<string, string>,\n): Promise<{ templateId: number; keys: string[] }> {\n const fresh = await readTemplateTranslations(client, themeId, templateId);\n const key =\n Object.keys(fresh.translations).find(\n (k) => k.toLowerCase() === iso.toLowerCase(),\n ) ?? iso;\n const merged = mergeLocale(fresh.translations[key] ?? {}, updates);\n await client.patch(`/api/application_theme_templates/${templateId}`, {\n application_theme_template: {\n translations: { ...fresh.translations, [key]: merged },\n },\n });\n return { templateId, keys: Object.keys(updates) };\n}\n","import {\n agreementTranslated,\n listAgreements,\n type CompanyAgreement,\n} from \"../shared/agreements.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n getLabelTitle,\n labelTranslated,\n listLabels,\n type Label,\n} from \"./labels.js\";\nimport { listMenuItems, type MenuItem } from \"./menus.js\";\nimport type { ResourceType, StorefrontType } from \"./resources.js\";\nimport { listTemplateTranslations, missingSectionKeys } from \"./sections.js\";\nimport { isTranslated, listAll, rowTitle, type Row } from \"./storefront.js\";\nimport {\n localeOf,\n missingKeys,\n readThemeLocales,\n resolveThemeId,\n sameAsSourceKeys,\n type ThemeLocales,\n} from \"./theme.js\";\n\nexport interface MissingEntry {\n /** What `show` and `set` take: a row id, `<menu>/<item>`, or a theme key. */\n id: number | string;\n /** The default-locale title, menu item title, or theme string. */\n source: string | null;\n status?: string | null;\n menu?: string | null;\n}\n\nexport interface TypeReport {\n type: string;\n label: string;\n total: number;\n missingCount: number;\n missing: MissingEntry[];\n /** Why a type couldn't be checked, e.g. the company has no active theme. */\n skipped?: string;\n /** Theme keys still word for word the source text, to review by eye. */\n sameAsSource?: { count: number; sample: string[] };\n}\n\nexport interface ReportOptions {\n /** Count only rows a shopper can see (status `published`). */\n publishedOnly: boolean;\n /** The locale the theme's source strings come from, e.g. `en`. */\n sourceIso: string;\n themeId?: string;\n}\n\n/**\n * What still needs translating into `iso`, per type. Storefront rows count as\n * translated once `languages` lists the locale; menu items once their title\n * has a value in the locale; theme keys once `locales/<iso>.json` has a\n * non-blank string for every key the source locale file has; theme page\n * strings the same way within each template's translations; agreements once\n * a read in the locale is served in it; labels once their title in the\n * locale differs from the default.\n */\nexport async function missingReport(\n client: LocalizationClient,\n iso: string,\n types: readonly ResourceType[],\n options: ReportOptions,\n): Promise<{ done: boolean; types: TypeReport[] }> {\n const reports: TypeReport[] = [];\n for (const type of types) {\n if (type.kind === \"storefront\") {\n reports.push(await storefrontReport(client, type, iso, options));\n } else if (type.kind === \"menu-items\") {\n reports.push(await menuReport(client, iso));\n } else if (type.kind === \"theme\") {\n reports.push(\n await unlessNoTheme(type, () => themeReport(client, iso, options)),\n );\n } else if (type.kind === \"theme-sections\") {\n reports.push(\n await unlessNoTheme(type, () => sectionsReport(client, iso, options)),\n );\n } else if (type.kind === \"agreements\") {\n reports.push(await agreementsReport(client, iso, options));\n } else {\n reports.push(await labelsReport(client, iso));\n }\n }\n return {\n done: reports.every((r) => r.missingCount === 0 && !r.skipped),\n types: reports,\n };\n}\n\n/** A theme type on a company with no theme to read is reported as skipped, not as a failure. */\nasync function unlessNoTheme(\n type: ResourceType,\n report: () => Promise<TypeReport>,\n): Promise<TypeReport> {\n try {\n return await report();\n } catch (error) {\n if (\n !(error instanceof LocalizationError) ||\n !/no active theme/.test(error.message)\n )\n throw error;\n return {\n type: type.type,\n label: type.label,\n total: 0,\n missingCount: 0,\n missing: [],\n skipped: error.message,\n };\n }\n}\n\n/** Whether shoppers can see a row: published (when it has a status) and not switched off. */\nexport function shopperVisible(row: Row): boolean {\n const status = row[\"status\"];\n if (typeof status === \"string\" && status !== \"published\") return false;\n return row[\"active\"] !== false;\n}\n\nasync function storefrontReport(\n client: LocalizationClient,\n type: StorefrontType,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const rows = (await listAll(client, type)).filter(\n (row) => !options.publishedOnly || shopperVisible(row),\n );\n const missing = rows\n .filter((row) => !isTranslated(row, iso))\n .map((row) => ({\n id: row.id,\n source: rowTitle(row),\n status:\n typeof row[\"status\"] === \"string\" ? (row[\"status\"] as string) : null,\n }));\n return {\n type: type.type,\n label: type.label,\n total: rows.length,\n missingCount: missing.length,\n missing,\n };\n}\n\n/** Menu items with a default title, and those with no title in `iso`. */\nexport async function missingMenuItems(\n client: LocalizationClient,\n iso: string,\n): Promise<{ titled: MenuItem[]; missing: MenuItem[] }> {\n const [source, translated] = await Promise.all([\n listMenuItems(client),\n listMenuItems(client, iso),\n ]);\n const have = new Map(translated.map((item) => [item.id, item.title]));\n const titled = source.filter((item) => item.title !== null);\n return { titled, missing: titled.filter((item) => !have.get(item.id)) };\n}\n\nasync function menuReport(\n client: LocalizationClient,\n iso: string,\n): Promise<TypeReport> {\n const { titled, missing } = await missingMenuItems(client, iso);\n return {\n type: \"menu-items\",\n label: \"Navigation menu items\",\n total: titled.length,\n missingCount: missing.length,\n missing: missing.map((item) => ({\n id: item.id,\n source: item.title,\n menu: item.menu,\n })),\n };\n}\n\n/** Theme keys the source locale has and `iso` lacks, with the source strings. */\nexport async function missingThemeKeys(\n client: LocalizationClient,\n iso: string,\n options: Pick<ReportOptions, \"sourceIso\" | \"themeId\">,\n): Promise<{\n themeId: number;\n locales: ThemeLocales;\n total: number;\n missing: Record<string, string>;\n sameAsSource: string[];\n skipped?: string;\n}> {\n const themeId = await resolveThemeId(client, options.themeId);\n const locales = await readThemeLocales(client, themeId);\n const source = localeOf(locales, options.sourceIso);\n if (!source) {\n return {\n themeId,\n locales,\n total: 0,\n missing: {},\n sameAsSource: [],\n skipped: `The theme has no locales/${options.sourceIso}.json to translate from. Pass --from with the theme's source locale.`,\n };\n }\n const target = localeOf(locales, iso);\n const missing = missingKeys(source, target);\n const total = Object.keys(missingKeys(source, null)).length;\n return {\n themeId,\n locales,\n total,\n missing,\n sameAsSource: sameAsSourceKeys(source, target),\n };\n}\n\nasync function themeReport(\n client: LocalizationClient,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const result = await missingThemeKeys(client, iso, options);\n return {\n type: \"theme\",\n label: `Theme ${result.themeId} strings (locales/${iso}.json)`,\n total: result.total,\n missingCount: Object.keys(result.missing).length,\n missing: Object.entries(result.missing).map(([key, value]) => ({\n id: key,\n source: value,\n })),\n ...(result.sameAsSource.length > 0\n ? {\n sameAsSource: {\n count: result.sameAsSource.length,\n sample: result.sameAsSource.slice(0, 20),\n },\n }\n : {}),\n ...(result.skipped ? { skipped: result.skipped } : {}),\n };\n}\n\n/** Theme page strings each template's source locale has and `iso` lacks. */\nexport async function missingSections(\n client: LocalizationClient,\n iso: string,\n options: Pick<ReportOptions, \"sourceIso\" | \"themeId\">,\n) {\n const themeId = await resolveThemeId(client, options.themeId);\n const templates = await listTemplateTranslations(client, themeId);\n return { themeId, ...missingSectionKeys(templates, options.sourceIso, iso) };\n}\n\nasync function sectionsReport(\n client: LocalizationClient,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const result = await missingSections(client, iso, options);\n return {\n type: \"theme-sections\",\n label: `Theme ${result.themeId} page strings (template translations)`,\n total: result.total,\n missingCount: result.missing.length,\n missing: result.missing.map((m) => ({ id: m.id, source: m.source })),\n };\n}\n\n/** Agreements with no translation in `iso`; with `publishedOnly`, active ones only. */\nexport async function missingAgreements(\n client: LocalizationClient,\n iso: string,\n publishedOnly: boolean,\n): Promise<{ checked: CompanyAgreement[]; missing: CompanyAgreement[] }> {\n const checked = (await listAgreements(client)).filter(\n (agreement) => !publishedOnly || agreement.active !== false,\n );\n const missing: CompanyAgreement[] = [];\n for (const agreement of checked) {\n if (!(await agreementTranslated(client, agreement.id, iso)))\n missing.push(agreement);\n }\n return { checked, missing };\n}\n\nasync function agreementsReport(\n client: LocalizationClient,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const { checked, missing } = await missingAgreements(\n client,\n iso,\n options.publishedOnly,\n );\n return {\n type: \"agreements\",\n label: \"Agreements\",\n total: checked.length,\n missingCount: missing.length,\n missing: missing.map((a) => ({\n id: a.id,\n source: a.title ?? null,\n status: a.active === false ? \"inactive\" : \"active\",\n })),\n };\n}\n\n/** Labels whose title in `iso` still reads as the default title. */\nexport async function missingLabels(\n client: LocalizationClient,\n iso: string,\n): Promise<{ checked: Label[]; missing: Label[] }> {\n const checked = (await listLabels(client)).filter(\n (label) => label.title.trim().length > 0,\n );\n const missing: Label[] = [];\n for (const label of checked) {\n if (\n !labelTranslated(label.title, await getLabelTitle(client, label.id, iso))\n )\n missing.push(label);\n }\n return { checked, missing };\n}\n\nasync function labelsReport(\n client: LocalizationClient,\n iso: string,\n): Promise<TypeReport> {\n const { checked, missing } = await missingLabels(client, iso);\n return {\n type: \"labels\",\n label: \"Product labels\",\n total: checked.length,\n missingCount: missing.length,\n missing: missing.map((l) => ({ id: l.id, source: l.title })),\n };\n}\n","import {\n readAgreement,\n writeAgreementTranslation,\n} from \"../shared/agreements.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { getLabelTitle, writeLabelTitle } from \"./labels.js\";\nimport { machineTranslate } from \"./machine.js\";\nimport { writeMenuItemTitle } from \"./menus.js\";\nimport {\n missingAgreements,\n missingLabels,\n missingMenuItems,\n missingSections,\n missingThemeKeys,\n shopperVisible,\n} from \"./report.js\";\nimport { writeSectionStrings } from \"./sections.js\";\nimport type { ResourceType, StorefrontType } from \"./resources.js\";\nimport {\n getRow,\n isTranslated,\n listAll,\n pickFields,\n writeTranslation,\n} from \"./storefront.js\";\nimport { localeOf, mergeLocale, writeLocale } from \"./theme.js\";\n\nexport interface AutoOptions {\n /** The target locale's ISO code, as the API spells it. */\n iso: string;\n /**\n * Language names for the translator, e.g. \"English\" and \"French\".\n * Storefront rows and menu items are read in the store's default\n * language (`storeName`); theme strings come from the `sourceIso` file\n * (`sourceName`).\n */\n storeName: string;\n sourceName: string;\n targetName: string;\n sourceIso: string;\n /** The store's default language code, the one storefront text is read in. */\n storeIso: string;\n /** Most rows (or menu items, or theme keys) to translate per type. */\n limit: number;\n /** Save what was translated. Without it, nothing is written. */\n write: boolean;\n publishedOnly: boolean;\n /** `<type>:<id>` items to leave out, e.g. `products:42` or `theme:cart.title`. */\n skip?: ReadonlyArray<string>;\n themeId?: string;\n model?: string;\n}\n\nexport interface AutoEntry {\n id: number | string;\n fields: Record<string, string>;\n /** The text each field was translated from, for review. */\n source?: Record<string, string>;\n saved: boolean;\n}\n\nexport interface ItemFailure {\n id: number | string;\n error: string;\n status?: number;\n}\n\n/** Run one save; a refusal is recorded against the item instead of ending the run. */\nasync function save(\n failed: ItemFailure[],\n id: number | string,\n write: () => Promise<unknown>,\n): Promise<boolean> {\n try {\n await write();\n return true;\n } catch (error) {\n failed.push({ id, ...failureOf(error) });\n return false;\n }\n}\n\nexport interface AutoTypeResult {\n type: string;\n translated: AutoEntry[];\n /** Items the translator returned only some fields for; nothing was saved for them. */\n incomplete: Array<number | string>;\n /** Items whose save the API refused, with its message; counted in `remaining`. */\n failed: ItemFailure[];\n /** Missing items past `--limit`, left for the next run. */\n remaining: number;\n skipped?: string;\n}\n\nexport interface AutoResult {\n written: boolean;\n types: AutoTypeResult[];\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/**\n * Machine-translate what's missing in `iso` and, with `write`, save it.\n * Storefront rows translate their text fields from the default locale\n * (URL fields are left for a person); menu items translate their title;\n * theme keys translate from the source locale file and save in one write.\n */\nexport async function autoTranslate(\n client: LocalizationClient,\n types: readonly ResourceType[],\n options: AutoOptions,\n): Promise<AutoResult> {\n const usage = { inputTokens: 0, outputTokens: 0 };\n const results: AutoTypeResult[] = [];\n const call = async (sourceName: string, fields: Record<string, string>) => {\n const out = await machineTranslate(\n client,\n sourceName,\n options.targetName,\n fields,\n options.model,\n );\n usage.inputTokens += out.usage.inputTokens;\n usage.outputTokens += out.usage.outputTokens;\n return out.translations;\n };\n // Ask once more for any field the first answer left out, so a single\n // omission doesn't leave an item incomplete.\n const translator =\n (sourceName: string) => async (fields: Record<string, string>) => {\n const first = await call(sourceName, fields);\n const omitted = Object.fromEntries(\n Object.entries(fields).filter(\n ([id, v]) => v.trim().length > 0 && first[id] === undefined,\n ),\n );\n if (Object.keys(omitted).length === 0) return first;\n return { ...first, ...(await call(sourceName, omitted)) };\n };\n const skipped = new Set(options.skip ?? []);\n\n for (const type of types) {\n if (type.kind === \"storefront\") {\n results.push(\n await autoStorefront(\n client,\n type,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n } else if (type.kind === \"menu-items\") {\n results.push(\n await autoMenus(\n client,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n } else if (type.kind === \"theme\") {\n results.push(\n await autoTheme(\n client,\n options,\n skipped,\n translator(options.sourceName),\n ),\n );\n } else if (type.kind === \"theme-sections\") {\n results.push(\n await autoSections(\n client,\n options,\n skipped,\n translator(options.sourceName),\n ),\n );\n } else if (type.kind === \"agreements\") {\n results.push(\n await autoAgreements(\n client,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n } else {\n results.push(\n await autoLabels(\n client,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n }\n }\n return { written: options.write, types: results, usage };\n}\n\ntype Translate = (\n fields: Record<string, string>,\n) => Promise<Record<string, string>>;\n\nasync function autoStorefront(\n client: LocalizationClient,\n type: StorefrontType,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const rows = (await listAll(client, type)).filter(\n (row) =>\n !skipped.has(`${type.type}:${row.id}`) &&\n !isTranslated(row, options.iso) &&\n (!options.publishedOnly || shopperVisible(row)),\n );\n const picked = rows.slice(0, options.limit);\n const source: Record<string, string> = {};\n const fieldsOf = new Map<number, string[]>();\n for (const summary of picked) {\n // The list may abbreviate long fields, so read each row in full.\n const row = await getRow(client, type, summary.id);\n for (const [field, value] of Object.entries(\n pickFields(row, type.textFields),\n )) {\n if (value === null || value.trim().length === 0) continue;\n source[`${summary.id}:${field}`] = value;\n fieldsOf.set(summary.id, [...(fieldsOf.get(summary.id) ?? []), field]);\n }\n }\n const translations = await translate(source);\n const entries: AutoEntry[] = [];\n const incomplete: number[] = [];\n const failed: ItemFailure[] = [];\n for (const summary of picked) {\n const wanted = fieldsOf.get(summary.id) ?? [];\n if (wanted.length === 0) continue;\n const fields: Record<string, string> = {};\n for (const field of wanted) {\n const value = translations[`${summary.id}:${field}`];\n if (value !== undefined) fields[field] = value;\n }\n // Saving some fields would add the language to the row's `languages`,\n // and later runs would skip it with the rest still in English.\n if (Object.keys(fields).length < wanted.length) {\n incomplete.push(summary.id);\n continue;\n }\n if (\n options.write &&\n !(await save(failed, summary.id, () =>\n writeTranslation(client, type, summary.id, options.iso, fields),\n ))\n ) {\n continue;\n }\n entries.push({\n id: summary.id,\n fields,\n source: Object.fromEntries(\n wanted.map((f) => [f, source[`${summary.id}:${f}`] ?? \"\"]),\n ),\n saved: options.write,\n });\n }\n return {\n type: type.type,\n translated: entries,\n incomplete,\n failed,\n remaining: rows.length - picked.length + incomplete.length + failed.length,\n };\n}\n\nasync function autoMenus(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const missing = (await missingMenuItems(client, options.iso)).missing.filter(\n (item) => !skipped.has(`menu-items:${item.id}`),\n );\n return autoTitles(\n \"menu-items\",\n missing.map((item) => ({ id: item.id, title: item.title ?? \"\" })),\n options,\n translate,\n (id, title) => writeMenuItemTitle(client, String(id), options.iso, title),\n );\n}\n\nasync function autoLabels(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const { missing } = await missingLabels(client, options.iso);\n const pending = missing.filter((label) => !skipped.has(`labels:${label.id}`));\n // The list is in the caller's language; read each title in the store's own.\n const sources = [];\n for (const label of pending.slice(0, options.limit)) {\n const title = await getLabelTitle(client, label.id, options.storeIso);\n sources.push({ id: label.id, title: title || label.title });\n }\n const result = await autoTitles(\n \"labels\",\n sources,\n options,\n translate,\n (id, title) => writeLabelTitle(client, Number(id), options.iso, title),\n );\n return {\n ...result,\n remaining: result.remaining + pending.length - sources.length,\n };\n}\n\n/** Translate and save a title per item: menu items and labels. */\nasync function autoTitles(\n type: string,\n items: ReadonlyArray<{ id: number | string; title: string }>,\n options: AutoOptions,\n translate: Translate,\n write: (id: number | string, title: string) => Promise<unknown>,\n): Promise<AutoTypeResult> {\n const picked = items.slice(0, options.limit);\n const translations = await translate(\n Object.fromEntries(picked.map((item) => [String(item.id), item.title])),\n );\n const entries: AutoEntry[] = [];\n const incomplete: Array<number | string> = [];\n const failed: ItemFailure[] = [];\n for (const item of picked) {\n if (item.title.trim().length === 0) continue;\n const title = translations[String(item.id)];\n if (title === undefined) {\n incomplete.push(item.id);\n continue;\n }\n if (\n options.write &&\n !(await save(failed, item.id, () => write(item.id, title)))\n )\n continue;\n entries.push({\n id: item.id,\n fields: { title },\n source: { title: item.title },\n saved: options.write,\n });\n }\n return {\n type,\n translated: entries,\n incomplete,\n failed,\n remaining: items.length - picked.length + incomplete.length + failed.length,\n };\n}\n\nasync function autoAgreements(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const { missing } = await missingAgreements(\n client,\n options.iso,\n options.publishedOnly,\n );\n const pending = missing.filter((a) => !skipped.has(`agreements:${a.id}`));\n const picked = pending.slice(0, options.limit);\n const source: Record<string, string> = {};\n for (const agreement of picked) {\n const text = await readAgreement(client, agreement.id, options.storeIso);\n if (text.title.trim()) source[`${agreement.id}:title`] = text.title;\n if (text.description.trim())\n source[`${agreement.id}:description`] = text.description;\n }\n const translations = await translate(source);\n const entries: AutoEntry[] = [];\n const incomplete: number[] = [];\n const failed: ItemFailure[] = [];\n for (const agreement of picked) {\n const title = translations[`${agreement.id}:title`];\n const description = translations[`${agreement.id}:description`];\n // The API saves title and text together; one without the other can't be saved.\n if (title === undefined || description === undefined) {\n incomplete.push(agreement.id);\n continue;\n }\n if (\n options.write &&\n !(await save(failed, agreement.id, () =>\n writeAgreementTranslation(client, agreement.id, options.iso, {\n title,\n description,\n }),\n ))\n ) {\n continue;\n }\n entries.push({\n id: agreement.id,\n fields: { title, description },\n source: {\n title: source[`${agreement.id}:title`] ?? \"\",\n description: source[`${agreement.id}:description`] ?? \"\",\n },\n saved: options.write,\n });\n }\n return {\n type: \"agreements\",\n translated: entries,\n incomplete,\n failed,\n remaining:\n pending.length - picked.length + incomplete.length + failed.length,\n };\n}\n\nasync function autoTheme(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const found = await missingThemeKeys(client, options.iso, options);\n if (found.skipped) {\n return {\n type: \"theme\",\n translated: [],\n incomplete: [],\n failed: [],\n remaining: 0,\n skipped: found.skipped,\n };\n }\n const keys = Object.keys(found.missing).filter(\n (key) => !skipped.has(`theme:${key}`),\n );\n const picked = keys.slice(0, options.limit);\n const translations = await translate(\n Object.fromEntries(picked.map((key) => [key, found.missing[key] ?? \"\"])),\n );\n const updates = Object.fromEntries(\n picked\n .filter((key) => translations[key] !== undefined)\n .map((key) => [key, translations[key] as string]),\n );\n const failed: ItemFailure[] = [];\n let saved = false;\n if (options.write && Object.keys(updates).length > 0) {\n // One write saves every key; base_sha makes the server refuse it if the\n // theme changed since the read, and then every key in it is reported.\n const current = localeOf(found.locales, options.iso) ?? {};\n const attempt: ItemFailure[] = [];\n saved = await save(attempt, \"theme\", () =>\n writeLocale(\n client,\n found.locales,\n options.iso,\n mergeLocale(current, updates),\n ),\n );\n if (!saved) {\n for (const key of Object.keys(updates))\n failed.push({ ...attempt[0]!, id: key });\n }\n }\n const incomplete = picked.filter((key) => translations[key] === undefined);\n return {\n type: \"theme\",\n translated:\n saved || !options.write\n ? Object.entries(updates).map(([key, value]) => ({\n id: key,\n fields: { value },\n source: { value: found.missing[key] ?? \"\" },\n saved,\n }))\n : [],\n incomplete,\n failed,\n remaining: keys.length - picked.length + incomplete.length + failed.length,\n };\n}\n\n/** Theme page strings: translated per key, saved one template at a time. */\nasync function autoSections(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const found = await missingSections(client, options.iso, options);\n const pending = found.missing.filter(\n (m) => !skipped.has(`theme-sections:${m.id}`),\n );\n const picked = pending.slice(0, options.limit);\n const translations = await translate(\n Object.fromEntries(picked.map((m) => [m.id, m.source])),\n );\n const byTemplate = new Map<number, Record<string, string>>();\n const incomplete: string[] = [];\n for (const m of picked) {\n const value = translations[m.id];\n if (value === undefined) {\n incomplete.push(m.id);\n continue;\n }\n byTemplate.set(m.templateId, {\n ...byTemplate.get(m.templateId),\n [m.key]: value,\n });\n }\n const entries: AutoEntry[] = [];\n const failed: ItemFailure[] = [];\n for (const [templateId, updates] of byTemplate) {\n const ok =\n !options.write ||\n (await save(failed, templateId, () =>\n writeSectionStrings(\n client,\n found.themeId,\n templateId,\n options.iso,\n updates,\n ),\n ));\n const keys = Object.entries(updates);\n if (!ok) {\n // A refused template fails every string in it.\n const last = failed[failed.length - 1]!;\n failed.splice(\n failed.length - 1,\n 1,\n ...keys.map(([key]) => ({ ...last, id: `${templateId}/${key}` })),\n );\n continue;\n }\n for (const [key, value] of keys) {\n const id = `${templateId}/${key}`;\n entries.push({\n id,\n fields: { value },\n source: { value: picked.find((m) => m.id === id)?.source ?? \"\" },\n saved: options.write,\n });\n }\n }\n return {\n type: \"theme-sections\",\n translated: entries,\n incomplete,\n failed,\n remaining:\n pending.length - picked.length + incomplete.length + failed.length,\n };\n}\n","// Save one reviewed translation, through the same calls as `set` and `auto`.\n\nimport { writeAgreementTranslation } from \"../shared/agreements.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { writeLabelTitle } from \"./labels.js\";\nimport { writeMenuItemTitle } from \"./menus.js\";\nimport type { ResourceType } from \"./resources.js\";\nimport { parseSectionRef, writeSectionStrings } from \"./sections.js\";\nimport { writeTranslation } from \"./storefront.js\";\nimport {\n localeOf,\n mergeLocale,\n readThemeLocales,\n resolveThemeId,\n writeLocale,\n} from \"./theme.js\";\n\n/**\n * One item's translated fields: a row's fields, a title, or a theme\n * string's `value`. Theme strings are read again just before the write, so\n * the theme's latest version is the one guarded.\n */\nexport async function saveTranslation(\n api: LocalizationClient,\n type: ResourceType,\n id: number | string,\n iso: string,\n fields: Record<string, string>,\n themeId?: string,\n): Promise<void> {\n switch (type.kind) {\n case \"storefront\":\n await writeTranslation(api, type, Number(id), iso, fields);\n return;\n case \"menu-items\":\n await writeMenuItemTitle(api, String(id), iso, fields[\"title\"] ?? \"\");\n return;\n case \"labels\":\n await writeLabelTitle(api, Number(id), iso, fields[\"title\"] ?? \"\");\n return;\n case \"agreements\":\n await writeAgreementTranslation(api, Number(id), iso, {\n title: fields[\"title\"] ?? \"\",\n description: fields[\"description\"] ?? \"\",\n });\n return;\n case \"theme\": {\n const locales = await readThemeLocales(\n api,\n await resolveThemeId(api, themeId),\n );\n const merged = mergeLocale(localeOf(locales, iso) ?? {}, {\n [String(id)]: fields[\"value\"] ?? \"\",\n });\n await writeLocale(api, locales, iso, merged);\n return;\n }\n case \"theme-sections\": {\n const { templateId, key } = parseSectionRef(String(id));\n await writeSectionStrings(\n api,\n await resolveThemeId(api, themeId),\n templateId,\n iso,\n {\n [key]: fields[\"value\"] ?? \"\",\n },\n );\n return;\n }\n }\n}\n","// `fluid translations walk [iso]`: translating a language as a conversation.\n// It shows what's missing per type, machine-translates a batch with Fluid\n// AI, and goes through the proposals one at a time: accept, edit or skip.\n// Each accepted item is saved on its own through the same calls as `set`.\n\nimport type { Prompter } from \"@fluid-app/fluid-cli\";\n\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport {\n enableLanguage,\n listLanguages,\n sameIso,\n type Language,\n} from \"../shared/languages.js\";\nimport { NAV_HELP } from \"@fluid-app/fluid-cli\";\nimport { autoLanguages, type AutoLanguages } from \"./auto-languages.js\";\nimport { autoTranslate, type AutoEntry } from \"./autotranslate.js\";\nimport { missingReport } from \"./report.js\";\nimport { RESOURCE_TYPES, type ResourceType } from \"./resources.js\";\nimport { saveTranslation } from \"./save.js\";\n\nexport interface TranslationWalkDeps {\n api: LocalizationClient;\n prompter: Prompter;\n}\n\nexport interface TranslationWalkOptions {\n iso?: string;\n /** The theme's source locale. */\n from?: string;\n themeId?: string;\n /** Proposals per Fluid AI call. */\n batch?: number;\n}\n\nexport interface TranslationWalkResult {\n language?: string;\n saved: string[];\n edited: string[];\n skipped: string[];\n failed: Array<{ id: string; error: string }>;\n stopped: boolean;\n}\n\nclass Stop extends Error {}\n\nconst FIELD_LABELS: Record<string, string> = { value: \"Text\" };\n\nexport async function walkTranslations(\n deps: TranslationWalkDeps,\n options: TranslationWalkOptions = {},\n): Promise<TranslationWalkResult> {\n const { api, prompter } = deps;\n const from = options.from ?? \"en\";\n const batch = options.batch ?? 10;\n const result: TranslationWalkResult = {\n saved: [],\n edited: [],\n skipped: [],\n failed: [],\n stopped: false,\n };\n prompter.note(NAV_HELP);\n\n try {\n const language = await pickLanguage(deps, options.iso);\n if (!language) {\n result.stopped = true;\n return result;\n }\n result.language = language.iso;\n\n const report = await missingReport(api, language.iso, RESOURCE_TYPES, {\n publishedOnly: false,\n sourceIso: from,\n themeId: options.themeId,\n });\n prompter.note(\n [\n `What isn't in ${language.name} yet:`,\n ...report.types.map(\n (t) =>\n ` ${t.label}: ${t.skipped ? `not checked (${t.skipped})` : `${t.missingCount} of ${t.total} missing`}`,\n ),\n ].join(\"\\n\"),\n );\n const pending = report.types.filter(\n (t) => t.missingCount > 0 && !t.skipped,\n );\n if (pending.length === 0) {\n prompter.note(`Everything is translated into ${language.name}.`);\n return result;\n }\n\n const types = pending.map(\n (t) => RESOURCE_TYPES.find((r) => r.type === t.type)!,\n );\n const languages = await resolveLanguages(deps, types, language.iso, from);\n if (!languages) {\n result.stopped = true;\n return result;\n }\n\n const skip = new Set<string>();\n for (const entry of pending) {\n const type = RESOURCE_TYPES.find((r) => r.type === entry.type)!;\n const start = await prompter.menu(\n `${entry.label}: ${entry.missingCount} missing.`,\n [\n { title: \"Translate with Fluid AI and review each\", value: \"go\" },\n { title: \"Skip this type\", value: \"skip\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (start === null || start === \"stop\") throw new Stop();\n if (start === \"skip\") continue;\n await reviewType(\n deps,\n type,\n languages,\n { ...options, from, batch },\n skip,\n result,\n );\n }\n prompter.note(\n `Saved ${result.saved.length} (${result.edited.length} edited), skipped ${result.skipped.length}, ${result.failed.length} refused. Check with: fluid translations missing ${language.iso}`,\n );\n } catch (error) {\n if (!(error instanceof Stop)) throw error;\n result.stopped = true;\n prompter.note(\"Stopped. Everything accepted so far is saved.\");\n }\n return result;\n}\n\nasync function pickLanguage(\n deps: TranslationWalkDeps,\n iso?: string,\n): Promise<Language | null> {\n const { api, prompter } = deps;\n const all = await listLanguages(api);\n let language = iso ? all.find((l) => sameIso(l.iso, iso)) : undefined;\n if (!language) {\n if (iso) prompter.note(`\"${iso}\" isn't a language Fluid supports.`);\n const reply = await prompter.ask({\n section: \"Language\",\n message: \"Which language are you translating into?\",\n kind: \"search\",\n hint: \"type to filter\",\n choices: [...all]\n .sort((a, b) => Number(b.active) - Number(a.active))\n .map((l) => ({\n title: `${l.name} (${l.iso})${l.active ? \"\" : \" — off\"}`,\n value: l.iso,\n })),\n canGoBack: false,\n });\n if (\"nav\" in reply) return null;\n language = all.find((l) => sameIso(l.iso, reply.value));\n if (!language) return null;\n }\n if (language.active) return language;\n const answer = await prompter.menu(\n `${language.name} isn't on for this company, so translations can't be saved to it.`,\n [\n { title: `Turn on ${language.name}`, value: \"enable\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (answer !== \"enable\") return null;\n return enableLanguage(api, language);\n}\n\n/** The languages to translate between; asks for the store's language when the rows can't say. */\nasync function resolveLanguages(\n deps: TranslationWalkDeps,\n types: readonly ResourceType[],\n iso: string,\n from: string,\n): Promise<AutoLanguages | null> {\n let storeLanguage: string | undefined;\n for (;;) {\n try {\n return await autoLanguages(deps.api, types, {\n iso,\n from,\n storeLanguage,\n write: true,\n });\n } catch (error) {\n if (\n storeLanguage !== undefined ||\n !/store's default language from its storefront rows/.test(\n String((error as Error).message),\n )\n ) {\n deps.prompter.note(failureOf(error).error);\n return null;\n }\n const reply = await deps.prompter.ask({\n section: \"Language\",\n message: \"Which language is your store's own content written in?\",\n kind: \"text\",\n hint: \"a code such as en\",\n canGoBack: false,\n });\n if (\"nav\" in reply) return null;\n storeLanguage = reply.value.trim();\n }\n }\n}\n\n/** Translate a type batch by batch, reviewing each proposal. */\nasync function reviewType(\n deps: TranslationWalkDeps,\n type: ResourceType,\n languages: AutoLanguages,\n options: Required<Pick<TranslationWalkOptions, \"from\" | \"batch\">> &\n TranslationWalkOptions,\n skip: Set<string>,\n result: TranslationWalkResult,\n): Promise<void> {\n const { api, prompter } = deps;\n for (;;) {\n const auto = await autoTranslate(api, [type], {\n iso: languages.target.iso,\n storeName: languages.store.name,\n sourceName: languages.source.name,\n targetName: languages.target.name,\n sourceIso: languages.source.iso,\n storeIso: languages.store.iso,\n limit: options.batch,\n write: false,\n publishedOnly: false,\n themeId: options.themeId,\n skip: [...skip],\n });\n const outcome = auto.types[0]!;\n for (const id of outcome.incomplete) {\n skip.add(`${type.type}:${id}`);\n prompter.note(\n `Fluid AI left part of ${type.label.toLowerCase()} ${id} untranslated; set it with \\`fluid translations set\\`.`,\n );\n }\n if (outcome.translated.length === 0) return;\n\n const moved = await reviewBatch(\n deps,\n type,\n outcome.translated,\n languages,\n options,\n skip,\n result,\n );\n if (moved === \"next-type\") return;\n const left = outcome.remaining - outcome.incomplete.length;\n if (left <= 0) return;\n const more = await prompter.menu(\n `${type.label}: ${left} more to translate.`,\n [\n { title: \"Translate the next batch\", value: \"more\" },\n { title: \"Next type\", value: \"next\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (more === null || more === \"stop\") throw new Stop();\n if (more === \"next\") return;\n }\n}\n\nasync function reviewBatch(\n deps: TranslationWalkDeps,\n type: ResourceType,\n entries: readonly AutoEntry[],\n languages: AutoLanguages,\n options: TranslationWalkOptions,\n skip: Set<string>,\n result: TranslationWalkResult,\n): Promise<\"done\" | \"next-type\"> {\n const { api, prompter } = deps;\n // What was last saved for each item, so going back shows (and re-saves) an edit, not the original proposal.\n const savedFields = new Map<number, Record<string, string>>();\n let i = 0;\n while (i < entries.length) {\n const proposal = entries[i]!;\n const entry = {\n ...proposal,\n fields: savedFields.get(i) ?? proposal.fields,\n };\n const key = `${type.type}:${entry.id}`;\n prompter.note(\n [\n `${type.label} ${entry.id} (${i + 1} of ${entries.length}):`,\n ...Object.entries(entry.fields).map(\n ([field, value]) =>\n ` ${FIELD_LABELS[field] ?? field}: ${clip(entry.source?.[field] ?? \"\")}\\n → ${clip(value)}`,\n ),\n ].join(\"\\n\"),\n );\n const reply = await prompter.ask({\n section: `${type.label} → ${languages.target.name}`,\n message: \"Save this translation?\",\n kind: \"select\",\n choices: [\n { title: \"Accept\", value: \"accept\" },\n { title: \"Edit\", value: \"edit\" },\n { title: \"Skip\", value: \"skip\" },\n ],\n canGoBack: i > 0,\n });\n if (\"nav\" in reply) {\n if (reply.nav === \"quit\") throw new Stop();\n if (reply.nav === \"back\") {\n i = Math.max(0, i - 1);\n continue;\n }\n if (reply.nav === \"skip\") {\n skipItem(key, skip, result);\n i += 1;\n continue;\n }\n const choice = await prompter.menu(\"What next?\", [\n { title: \"Continue\", value: \"continue\" },\n {\n title: `Skip the rest of ${type.label.toLowerCase()}`,\n value: \"next-type\",\n },\n { title: \"Stop\", value: \"stop\" },\n ]);\n if (choice === null || choice === \"stop\") throw new Stop();\n if (choice === \"next-type\") return \"next-type\";\n continue;\n }\n if (reply.value === \"skip\") {\n skipItem(key, skip, result);\n i += 1;\n continue;\n }\n let fields = entry.fields;\n if (reply.value === \"edit\") {\n const edited: Record<string, string> = {};\n for (const [field, value] of Object.entries(entry.fields)) {\n const answer = await prompter.ask({\n section: `${type.label} ${entry.id}`,\n message: FIELD_LABELS[field] ?? field,\n kind: \"text\",\n current: value,\n canGoBack: false,\n });\n if (\"nav\" in answer) {\n if (answer.nav === \"quit\") throw new Stop();\n edited[field] = value;\n } else {\n edited[field] = answer.value.trim() === \"\" ? value : answer.value;\n }\n }\n fields = edited;\n }\n try {\n await saveTranslation(\n api,\n type,\n entry.id,\n languages.target.iso,\n fields,\n options.themeId,\n );\n savedFields.set(i, fields);\n if (!result.saved.includes(key)) result.saved.push(key);\n if (reply.value === \"edit\" && !result.edited.includes(key))\n result.edited.push(key);\n const skippedAt = result.skipped.indexOf(key);\n if (skippedAt >= 0) result.skipped.splice(skippedAt, 1);\n // A retry that saves clears the earlier refusal.\n result.failed = result.failed.filter((f) => f.id !== key);\n skip.delete(key);\n } catch (error) {\n const { error: message } = failureOf(error);\n result.failed.push({ id: key, error: message });\n skip.add(key);\n prompter.note(`Not saved: ${message}`);\n }\n i += 1;\n }\n return \"done\";\n}\n\nfunction skipItem(\n key: string,\n skip: Set<string>,\n result: TranslationWalkResult,\n): void {\n skip.add(key);\n if (!result.skipped.includes(key)) result.skipped.push(key);\n}\n\n/** One line of a long value, for the review. */\nfunction clip(text: string, max = 200): string {\n const flat = text.replace(/\\s+/g, \" \").trim();\n return flat.length > max ? `${flat.slice(0, max)}…` : flat;\n}\n","import type { Command } from \"commander\";\nimport type { CommandContext } from \"@fluid-app/fluid-cli\";\nimport { assertInteractive, terminalPrompter } from \"@fluid-app/fluid-cli\";\n\nimport { listAgreements } from \"../shared/agreements.js\";\nimport { collect, confirmChange, positiveInt, run } from \"../shared/cli.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport { listLanguages } from \"../shared/languages.js\";\nimport { parseSettingAssignments } from \"../shared/parsing.js\";\nimport {\n catalogCountry,\n createAgreementFromAtlas,\n createCompanyCountry,\n findCompanyCountry,\n listCompanyCountries,\n listWarehouses,\n missingTranslations,\n requireCompanyCountry,\n updateCompanyCountry,\n writeTranslations,\n type CreatedAgreement,\n} from \"./api.js\";\nimport {\n atlasAgreement,\n countryCodeOf,\n fetchAtlas,\n launchChecklist,\n parseMode,\n requireAtlas,\n summarizeAtlas,\n type CountryAtlas,\n type Mode,\n} from \"./atlas.js\";\nimport { complianceView } from \"./compliance.js\";\nimport {\n companyCountryIn,\n currencyDecimals,\n DEFAULT_PRICE_FIELDS,\n listProductsWithVariants,\n parsePriceFields,\n parseRate,\n planConversion,\n PRICE_FIELDS,\n priceCoverage,\n priceEditorOwnsPrices,\n referenceRate,\n summarizePlans,\n writeConversion,\n type PlanOptions,\n} from \"./prices.js\";\nimport {\n agreementStates,\n languageStates,\n type AgreementState,\n} from \"./plan.js\";\nimport {\n applyOverrides,\n companyCountryFromAtlas,\n partialCountryUpdate,\n} from \"./seed.js\";\nimport { statusReport } from \"./status.js\";\nimport { walkCountry } from \"./walk.js\";\nimport { walkTranslations } from \"../translations/walk.js\";\n\nasync function client(ctx: CommandContext): Promise<LocalizationClient> {\n return (await ctx.getClient()) as unknown as LocalizationClient;\n}\n\ninterface OpenOptions {\n mode: string;\n warehouseId?: string;\n businessId?: string;\n entityRegistered?: boolean;\n settlementCurrency?: string;\n set: string[];\n}\n\n/** The company_country attributes `plan` shows and `open` sends. */\nasync function seededAttributes(\n api: LocalizationClient,\n iso: string,\n opts: OpenOptions,\n) {\n const mode: Mode = parseMode(opts.mode);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const country = await catalogCountry(api, countryCode);\n const attributes = applyOverrides(\n companyCountryFromAtlas(atlas, {\n countryId: country.id,\n mode,\n ...(opts.warehouseId\n ? { warehouseId: positiveInt(opts.warehouseId, \"--warehouse-id\") }\n : {}),\n ...(opts.businessId ? { businessId: opts.businessId } : {}),\n ...(opts.entityRegistered !== undefined\n ? { entityRegistered: opts.entityRegistered }\n : {}),\n ...(opts.settlementCurrency\n ? { settlementCurrency: opts.settlementCurrency }\n : {}),\n }),\n parseSettingAssignments(opts.set),\n );\n return { countryCode, atlas, country, mode, attributes };\n}\n\nfunction withOpenOptions(command: Command): Command {\n return command\n .requiredOption(\"--mode <mode>\", \"How you will operate: nfr, otg, or usd\")\n .option(\n \"--warehouse-id <id>\",\n \"The warehouse that fulfills orders for the country (NFR and OTG)\",\n )\n .option(\n \"--business-id <id>\",\n \"Your local tax or business registration number (OTG), e.g. a VAT ID\",\n )\n .option(\n \"--entity-registered\",\n \"You already have a legally registered local entity (OTG)\",\n )\n .option(\n \"--settlement-currency <code>\",\n \"Settle in another currency than the one you charge in, e.g. USD\",\n )\n .option(\n \"--set <key=value>\",\n \"Override a seeded value, e.g. tax_inclusive_pricing=false or legal_settings.cooling_off_period_days=30 (repeatable)\",\n collect,\n [],\n );\n}\n\n/** Check each agreement on the country for the atlas translations it lacks. */\nasync function withTranslationGaps(\n api: LocalizationClient,\n atlas: CountryAtlas,\n states: AgreementState[],\n): Promise<AgreementState[]> {\n return Promise.all(\n states.map(async (state) =>\n state.status === \"linked\" && state.agreementId !== null\n ? {\n ...state,\n missingTranslations: await missingTranslations(\n api,\n state.agreementId,\n atlasAgreement(atlas, state.localId),\n ),\n }\n : state,\n ),\n );\n}\n\n/** Price coverage of the active products in a country, bundles listed apart. */\nasync function countryPriceCoverage(api: LocalizationClient, iso: string) {\n return priceCoverage(await listProductsWithVariants(api, [\"active\"]), iso);\n}\n\nexport function registerCountriesCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n parent\n .command(\"atlas <iso>\")\n .description(\n \"Show Fluid's Country Atlas for a market: modes, tax, legal, address, payments, languages, agreements\",\n )\n .option(\n \"--agreement <localId>\",\n \"Print one atlas agreement's full text in every language instead\",\n )\n .action((iso: string, opts: { agreement?: string }) =>\n run(async () => {\n const result = await fetchAtlas(await client(ctx), iso);\n if (!result.covered || !result.atlas) {\n ctx.output({\n country: result.countryCode,\n covered: false,\n note: \"Fluid publishes no Country Atlas for this market yet. Set it up by hand and don't assume tax, legal, or agreement defaults.\",\n });\n return;\n }\n if (opts.agreement) {\n ctx.output({\n country: result.countryCode,\n covered: true,\n agreement: atlasAgreement(result.atlas, opts.agreement),\n });\n return;\n }\n ctx.output({\n country: result.countryCode,\n covered: true,\n ...summarizeAtlas(result.atlas),\n });\n }),\n );\n\n parent\n .command(\"compliance <iso>\")\n .description(\n \"Show the compliance view of the atlas: disclosure pages, cookie rule, tax display, unit pricing\",\n )\n .action((iso: string) =>\n run(async () => {\n const { countryCode, atlas } = await requireAtlas(\n await client(ctx),\n iso,\n );\n ctx.output(complianceView(countryCode, atlas));\n }),\n );\n\n parent\n .command(\"list\")\n .description(\"List the countries this company sells in\")\n .action(() =>\n run(async () => {\n const countries = await listCompanyCountries(await client(ctx));\n ctx.output(\n countries.map((cc) => ({\n companyCountryId: cc.id,\n iso: cc.country?.iso ?? null,\n name: cc.country?.name ?? null,\n mode: cc.mode ?? null,\n currency: cc.currency ?? null,\n taxName: cc.tax_name ?? null,\n taxInclusivePricing: cc.tax_inclusive_pricing ?? null,\n requires3ds: cc.requires_3ds ?? null,\n testMode: cc.test_mode ?? null,\n warehouse: cc.warehouse\n ? { id: cc.warehouse.id, name: cc.warehouse.name ?? null }\n : null,\n })),\n );\n }),\n );\n\n withOpenOptions(\n parent\n .command(\"plan <iso>\")\n .description(\n \"Preview a country setup without changing anything: the seeded settings, languages, agreements, and checklist\",\n ),\n ).action((iso: string, opts: OpenOptions) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas, mode, attributes } = await seededAttributes(\n api,\n iso,\n opts,\n );\n const [existing, languages, agreements, warehouses] = await Promise.all([\n findCompanyCountry(api, countryCode),\n listLanguages(api),\n listAgreements(api),\n listWarehouses(api),\n ]);\n ctx.output({\n country: countryCode,\n mode,\n alreadyOpen: existing\n ? { companyCountryId: existing.id, mode: existing.mode ?? null }\n : false,\n companyCountry: attributes,\n languages: languageStates(atlas, languages),\n agreements: agreementStates(atlas, agreements, existing?.id ?? null),\n warehouses: warehouses.map((w) => ({\n id: w.id,\n name: w.name,\n country: w.country?.iso ?? null,\n active: w.active ?? null,\n })),\n launchChecklist: launchChecklist(atlas, mode),\n });\n }),\n );\n\n withOpenOptions(\n parent\n .command(\"open <iso>\")\n .description(\n \"Add the country to the company, seeded from the atlas: currency, mode, tax, 3-D Secure, legal, address layout, payment order (changes live config)\",\n )\n .option(\n \"--legacy-defaults\",\n \"Also let Fluid add its generic country agreements and forms\",\n false,\n )\n .option(\n \"--reseed\",\n \"If the country is already open, write the atlas values over its current settings\",\n false,\n )\n .option(\"--yes\", \"Confirm without a prompt\", false),\n ).action(\n (\n iso: string,\n opts: OpenOptions & {\n legacyDefaults: boolean;\n reseed: boolean;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, attributes } = await seededAttributes(\n api,\n iso,\n opts,\n );\n const existing = await findCompanyCountry(api, countryCode);\n if (existing && !opts.reseed) {\n ctx.output({\n country: countryCode,\n alreadyOpen: true,\n companyCountryId: existing.id,\n note: \"Nothing changed. Pass --reseed to write the atlas values over the current settings.\",\n });\n return;\n }\n if (existing) {\n await confirmChange(\n `Overwrite ${countryCode}'s settings with the atlas values? Changes an admin made are replaced.`,\n opts.yes,\n );\n const updated = await updateCompanyCountry(\n api,\n existing.id,\n attributes,\n existing.revision,\n );\n ctx.output({\n country: countryCode,\n reseeded: true,\n companyCountry: updated,\n });\n return;\n }\n await confirmChange(\n `Open ${countryCode} for this company in ${String(attributes[\"mode\"])} mode?`,\n opts.yes,\n );\n const created = await createCompanyCountry(\n api,\n attributes,\n opts.legacyDefaults,\n );\n ctx.output({\n country: countryCode,\n opened: true,\n companyCountry: created,\n });\n }),\n );\n\n parent\n .command(\"update <iso>\")\n .description(\n \"Change named settings on an open country and leave the rest as they are, e.g. its entity or settlement currency (changes live config)\",\n )\n .option(\"--warehouse-id <id>\", \"The warehouse that fulfills orders for the country\")\n .option(\"--business-id <id>\", \"The local tax or business registration number\")\n .option(\n \"--entity-registered\",\n \"The company has a legally registered local entity\",\n )\n .option(\"--settlement-currency <code>\", \"Settle in this currency, e.g. USD\")\n .option(\n \"--set <key=value>\",\n \"Set a company country field, e.g. entity_id=12 or legal_settings.cooling_off_period_days=30 (repeatable)\",\n collect,\n [],\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n iso: string,\n opts: Omit<OpenOptions, \"mode\"> & { yes: boolean },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const countryCode = countryCodeOf(iso);\n const existing = await requireCompanyCountry(api, countryCode);\n const body = partialCountryUpdate(existing, {\n ...(opts.warehouseId\n ? { warehouse_id: positiveInt(opts.warehouseId, \"--warehouse-id\") }\n : {}),\n ...(opts.businessId ? { tax_business_id: opts.businessId } : {}),\n ...(opts.entityRegistered ? { entity_legally_registered: true } : {}),\n ...(opts.settlementCurrency\n ? { settlement_currency: opts.settlementCurrency.toUpperCase() }\n : {}),\n ...parseSettingAssignments(opts.set),\n });\n await confirmChange(\n `Change ${Object.keys(body).join(\", \")} on ${countryCode}?`,\n opts.yes,\n );\n ctx.output({\n country: countryCode,\n updated: Object.keys(body),\n companyCountry: await updateCompanyCountry(\n api,\n existing.id,\n body,\n existing.revision,\n ),\n });\n }),\n );\n\n parent\n .command(\"languages <iso>\")\n .description(\n \"Compare the market's major languages with the company's; `fluid translations enable` turns the missing ones on\",\n )\n .action((iso: string) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const states = languageStates(atlas, await listLanguages(api));\n const off = states.filter((s) => !s.enabled && s.languageId !== null);\n ctx.output({\n country: countryCode,\n done: states.every((s) => s.enabled),\n languages: states,\n ...(off.length > 0\n ? {\n next: `fluid translations enable ${off.map((s) => s.iso).join(\" \")} --yes`,\n }\n : {}),\n });\n }),\n );\n\n parent\n .command(\"agreements <iso>\")\n .description(\n \"Match the atlas agreements to the company's; --create adds the missing ones to the country in every atlas language, --translate rewrites the atlas translations onto ones already on it (changes live config)\",\n )\n .option(\n \"--create\",\n \"Create each missing atlas agreement on the country\",\n false,\n )\n .option(\n \"--translate\",\n \"Write the atlas translations onto the agreements already on the country, e.g. after enabling a language\",\n false,\n )\n .option(\n \"--only <localId>\",\n \"With --create or --translate, just this atlas agreement, e.g. agreement_2 (repeatable)\",\n collect,\n [],\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n iso: string,\n opts: {\n create: boolean;\n translate: boolean;\n only: string[];\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const cc = await requireCompanyCountry(api, countryCode);\n const states = agreementStates(\n atlas,\n await listAgreements(api),\n cc.id,\n );\n if (opts.create && opts.translate)\n throw new LocalizationError(\n \"Use --create or --translate, not both.\",\n );\n if (opts.only.length > 0 && !opts.create && !opts.translate) {\n throw new LocalizationError(\n \"--only goes with --create or --translate.\",\n );\n }\n for (const id of opts.only) atlasAgreement(atlas, id);\n const picked = (s: { localId: string }) =>\n opts.only.length === 0 || opts.only.includes(s.localId);\n if (opts.translate) {\n const toTranslate = states.filter(\n (s) =>\n (s.status === \"linked\" || s.status === \"inactive\") &&\n s.agreementId !== null &&\n picked(s),\n );\n const withTranslations = toTranslate.filter((s) =>\n atlasAgreement(atlas, s.localId).translations.some(\n (t) => t.language_iso !== \"en\",\n ),\n );\n if (withTranslations.length === 0) {\n ctx.output({\n country: countryCode,\n translated: [],\n agreements: states,\n });\n return;\n }\n await confirmChange(\n `Write the atlas translations onto ${withTranslations.length} agreement(s)? Translations an admin edited are replaced.`,\n opts.yes,\n );\n const translated = [];\n for (const state of withTranslations) {\n const result = await writeTranslations(\n api,\n state.agreementId!,\n atlasAgreement(atlas, state.localId),\n );\n translated.push({\n localId: state.localId,\n id: state.agreementId,\n ...result,\n });\n }\n ctx.output({ country: countryCode, translated });\n if (translated.some((t) => t.failedTranslations.length > 0))\n process.exitCode = 1;\n return;\n }\n if (!opts.create) {\n ctx.output({\n country: countryCode,\n done: states.every((s) => s.status === \"linked\"),\n agreements: states,\n });\n return;\n }\n const toCreate = states.filter(\n (s) => s.status === \"missing\" && picked(s),\n );\n if (toCreate.length === 0) {\n ctx.output({\n country: countryCode,\n created: [],\n agreements: states,\n });\n return;\n }\n await confirmChange(\n `Create ${toCreate.length} agreement(s) on ${countryCode}: ${toCreate.map((s) => `\"${s.title}\"`).join(\", \")}?`,\n opts.yes,\n );\n const created: CreatedAgreement[] = [];\n const failed: Array<{ localId: string; error: string }> = [];\n for (const state of toCreate) {\n try {\n created.push(\n await createAgreementFromAtlas(\n api,\n atlasAgreement(atlas, state.localId),\n cc.id,\n ),\n );\n } catch (error) {\n failed.push({\n localId: state.localId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n ctx.output({\n country: countryCode,\n created,\n failed,\n agreements: agreementStates(\n atlas,\n await listAgreements(api),\n cc.id,\n ),\n });\n if (\n failed.length > 0 ||\n created.some((c) => c.failedTranslations.length > 0)\n )\n process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"prices <iso>\")\n .description(\n \"Convert every variant's price from another country into this one's currency at an exchange rate. A dry run unless --write (changes live prices)\",\n )\n .option(\n \"--from-country <iso>\",\n \"The country whose prices are converted\",\n \"US\",\n )\n .option(\n \"--rate <rate>\",\n \"Units of the new currency per unit of the source currency, e.g. 0.92; --write needs it\",\n )\n .option(\n \"--fields <list>\",\n `Price fields to convert, comma-separated, from ${PRICE_FIELDS.join(\", \")}`,\n DEFAULT_PRICE_FIELDS.join(\",\"),\n )\n .option(\n \"--status <status>\",\n \"Product status to include (repeatable)\",\n collect,\n [],\n )\n .option(\"--overwrite\", \"Also replace prices the country already has\", false)\n .option(\n \"--activate\",\n \"Put each converted variant on sale in the country\",\n false,\n )\n .option(\"--sample <n>\", \"How many before→after examples to print\", \"10\")\n .option(\n \"--write\",\n \"Save the converted prices; without it nothing is written\",\n false,\n )\n .option(\"--yes\", \"Confirm --write without a prompt\", false)\n .action(\n (\n iso: string,\n opts: {\n fromCountry: string;\n rate?: string;\n fields: string;\n status: string[];\n overwrite: boolean;\n activate: boolean;\n sample: string;\n write: boolean;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const targetIso = countryCodeOf(iso);\n const sourceIso = countryCodeOf(opts.fromCountry);\n if (targetIso === sourceIso)\n throw new LocalizationError(\n \"--from-country is the country being priced.\",\n );\n const fields = parsePriceFields(opts.fields);\n const sample = positiveInt(opts.sample, \"--sample\");\n const statuses = opts.status.length > 0 ? opts.status : [\"active\"];\n const countries = await listCompanyCountries(api);\n const target = companyCountryIn(countries, targetIso);\n if (!target?.country) {\n throw new LocalizationError(\n `${targetIso} isn't open for this company yet. Run \\`fluid countries open ${targetIso} --mode <nfr|otg|usd>\\` first.`,\n );\n }\n const source = companyCountryIn(countries, sourceIso);\n if (!source)\n throw new LocalizationError(\n `${sourceIso} isn't one of this company's countries, so it has no prices to convert.`,\n );\n const targetCurrency = (target.currency ?? \"\").toUpperCase();\n const sourceCurrency = (source.currency ?? \"\").toUpperCase();\n if (!targetCurrency || !sourceCurrency) {\n throw new LocalizationError(\n \"Both countries need a currency. Check them with `fluid countries list`.\",\n );\n }\n\n let rate: { value: number; source: string; date?: string };\n if (opts.rate !== undefined) {\n rate = { value: parseRate(opts.rate), source: \"--rate\" };\n } else if (sourceCurrency === targetCurrency) {\n rate = { value: 1, source: \"same currency\" };\n } else {\n const reference = await referenceRate(\n sourceCurrency,\n targetCurrency,\n );\n if (opts.write) {\n throw new LocalizationError(\n `--write needs the rate you reviewed: pass --rate.${reference ? ` The ${reference.date} reference rate is ${reference.rate} ${targetCurrency} per ${sourceCurrency}.` : \"\"}`,\n );\n }\n if (!reference) {\n throw new LocalizationError(\n `Couldn't look up a ${sourceCurrency} to ${targetCurrency} reference rate. Pass --rate.`,\n );\n }\n rate = {\n value: reference.rate,\n source: reference.source,\n date: reference.date,\n };\n }\n\n const priceEditor = await priceEditorOwnsPrices(api);\n if (priceEditor === null && opts.overwrite) {\n throw new LocalizationError(\n \"Your token can't read the company profile, so it can't tell whether the price editor manages your prices. --overwrite needs to know; run it with a token that can view the company.\",\n );\n }\n if (priceEditor && opts.overwrite) {\n throw new LocalizationError(\n \"This company's prices are managed in the price editor, so --overwrite could replace prices set there. Change existing prices in the price editor; without --overwrite this command fills only prices the country doesn't have.\",\n );\n }\n const planOptions: PlanOptions = {\n sourceIso,\n targetIso,\n targetCountryId: target.country.id,\n targetCurrency,\n rate: rate.value,\n fields,\n overwrite: opts.overwrite,\n activate: opts.activate,\n };\n const plans = planConversion(\n await listProductsWithVariants(api, statuses),\n planOptions,\n );\n const summary = summarizePlans(plans, sample);\n const header = {\n country: targetIso,\n from: { country: sourceIso, currency: sourceCurrency },\n to: { country: targetIso, currency: targetCurrency },\n rate,\n rounding: {\n decimals: currencyDecimals(targetCurrency),\n mode: \"half up\",\n },\n fields,\n statuses,\n priceEditor,\n };\n if (!opts.write || summary.toConvert === 0) {\n ctx.output({\n ...header,\n written: false,\n ...summary,\n ...(summary.toConvert > 0\n ? {\n next: `fluid countries prices ${targetIso} --from-country ${sourceIso} --rate ${rate.value} --fields ${fields.join(\",\")} --write --yes`,\n }\n : {}),\n });\n return;\n }\n await confirmChange(\n `Write ${summary.toConvert} converted price(s) into ${targetIso} (${targetCurrency}) at ${rate.value} ${targetCurrency} per ${sourceCurrency}?`,\n opts.yes,\n );\n const result = await writeConversion(api, plans, planOptions);\n ctx.output({\n ...header,\n written: true,\n ...summary,\n converted: result.written.length,\n changedSincePreview: result.changedSincePreview,\n failed: result.failed,\n refusedByPriceEditor: result.failed.filter(\n (f) => f.refusedByPriceEditor,\n ).length,\n });\n if (result.failed.length > 0) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"walk [iso]\")\n .description(\n \"Open a country by answering questions: the atlas, mode, languages, agreements and defaults, then prices and translations (changes live config)\",\n )\n .action((iso: string | undefined) =>\n run(async () => {\n assertInteractive(\n \"`plan`, `open`, `prices`, `agreements` and `fluid translations enable`\",\n );\n const api = await client(ctx);\n const prompter = terminalPrompter();\n const result = await walkCountry(\n {\n api,\n prompter,\n translate: (language) =>\n walkTranslations({ api, prompter }, { iso: language }),\n },\n iso ? { iso } : {},\n );\n ctx.output(result);\n if (result.failures.length > 0) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"status <iso>\")\n .description(\n \"Check a country's setup against the atlas, including whether your active products have prices in it, and list what's left, ending with the launch checklist\",\n )\n .action((iso: string) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const cc = await findCompanyCountry(api, countryCode);\n const [languages, agreements] = cc\n ? await Promise.all([listLanguages(api), listAgreements(api)])\n : [[], []];\n ctx.output(\n statusReport({\n countryCode,\n atlas,\n companyCountry: cc,\n languages: languageStates(atlas, languages),\n agreements: await withTranslationGaps(\n api,\n atlas,\n agreementStates(atlas, agreements, cc?.id ?? null),\n ),\n ...(cc\n ? {\n prices: await countryPriceCoverage(api, countryCode),\n }\n : {}),\n }),\n );\n }),\n );\n}\n","import type { Command } from \"commander\";\nimport type { CommandContext } from \"@fluid-app/fluid-cli\";\nimport { assertInteractive, terminalPrompter } from \"@fluid-app/fluid-cli\";\n\nimport {\n readAgreement,\n writeAgreementTranslation,\n} from \"../shared/agreements.js\";\n\nimport {\n collect,\n confirmChange,\n failureOf,\n positiveInt,\n run,\n} from \"../shared/cli.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n disableLanguage,\n enableLanguage,\n findLanguage,\n listLanguages,\n requireActiveLanguage,\n type Language,\n} from \"../shared/languages.js\";\nimport { collectValues } from \"../shared/parsing.js\";\nimport { autoLanguages } from \"./auto-languages.js\";\nimport { autoTranslate } from \"./autotranslate.js\";\nimport { walkTranslations } from \"./walk.js\";\nimport { getLabelTitle, labelTranslated, writeLabelTitle } from \"./labels.js\";\nimport { getMenuItem, writeMenuItemTitle } from \"./menus.js\";\nimport { missingReport } from \"./report.js\";\nimport {\n checkFields,\n RESOURCE_TYPES,\n resourceType,\n selectTypes,\n TYPE_NAMES,\n} from \"./resources.js\";\nimport {\n parseSectionRef,\n readTemplateTranslations,\n sectionText,\n writeSectionStrings,\n} from \"./sections.js\";\nimport {\n getRow,\n isTranslated,\n pickFields,\n writeTranslation,\n} from \"./storefront.js\";\nimport {\n flattenLocale,\n localeOf,\n mergeLocale,\n readThemeLocales,\n resolveThemeId,\n writeLocale,\n} from \"./theme.js\";\n\nasync function client(ctx: CommandContext): Promise<LocalizationClient> {\n return (await ctx.getClient()) as unknown as LocalizationClient;\n}\n\nfunction rowId(raw: string | undefined, type: string): number {\n const value = Number(raw);\n if (raw === undefined || !Number.isInteger(value) || value <= 0) {\n throw new LocalizationError(\n `Pass the ${type} row id, as \\`fluid translations missing\\` prints it.`,\n );\n }\n return value;\n}\n\n/** `--skip` items must match a report id exactly: `products:42`, `menu-items:12/340`, `theme:cart.title`. */\nexport function validSkip(item: string): boolean {\n const at = item.indexOf(\":\");\n if (at <= 0) return false;\n const type = item.slice(0, at);\n const id = item.slice(at + 1);\n if (!TYPE_NAMES.includes(type)) return false;\n if (type === \"theme\") return id.length > 0;\n if (type === \"menu-items\") return /^[1-9]\\d*\\/[1-9]\\d*$/.test(id);\n if (type === \"theme-sections\") return /^[1-9]\\d*\\/.+$/.test(id);\n return /^[1-9]\\d*$/.test(id);\n}\n\nfunction requireLang(lang: string | undefined): string {\n if (!lang)\n throw new LocalizationError(\n \"Pass the language with --lang <iso>, e.g. --lang fr.\",\n );\n return lang;\n}\n\nexport function registerTranslationsCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n parent\n .command(\"languages\")\n .description(\n \"List every language Fluid supports and whether it's on for this company\",\n )\n .option(\"--active\", \"Only the languages that are on\", false)\n .action((opts: { active: boolean }) =>\n run(async () =>\n ctx.output(await listLanguages(await client(ctx), opts.active)),\n ),\n );\n\n parent\n .command(\"enable <iso...>\")\n .description(\n \"Turn one or more languages on so shoppers can choose them and translations can be saved (changes live config)\",\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((isos: string[], opts: { yes: boolean }) =>\n run(async () => {\n const api = await client(ctx);\n const all = await listLanguages(api);\n const wanted: Language[] = [];\n for (const iso of isos) {\n const language = all.find(\n (l) => l.iso.toLowerCase() === iso.trim().toLowerCase(),\n );\n // findLanguage names close matches for a code Fluid doesn't have.\n wanted.push(language ?? (await findLanguage(api, iso)));\n }\n const off = wanted.filter(\n (l, i) => !l.active && wanted.indexOf(l) === i,\n );\n const alreadyActive = wanted.filter((l) => l.active).map((l) => l.iso);\n if (off.length === 0) {\n ctx.output({ enabled: [], alreadyActive, failed: [] });\n return;\n }\n await confirmChange(\n `Turn on ${off.map((l) => `${l.name} (${l.iso})`).join(\", \")} for this company?`,\n opts.yes,\n );\n const enabled: string[] = [];\n const failed: Array<{ iso: string; error: string; status?: number }> =\n [];\n for (const language of off) {\n try {\n enabled.push((await enableLanguage(api, language)).iso);\n } catch (error) {\n failed.push({ iso: language.iso, ...failureOf(error) });\n }\n }\n ctx.output({ enabled, alreadyActive, failed });\n if (failed.length > 0) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"disable <iso>\")\n .description(\n \"Turn a language off; its saved translations are kept (changes live config)\",\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((iso: string, opts: { yes: boolean }) =>\n run(async () => {\n const api = await client(ctx);\n const language = await findLanguage(api, iso);\n if (!language.active) {\n ctx.output({ ...language, alreadyInactive: true });\n return;\n }\n await confirmChange(\n `Turn off ${language.name} (${language.iso}) for this company?`,\n opts.yes,\n );\n ctx.output(await disableLanguage(api, language));\n }),\n );\n\n parent\n .command(\"types\")\n .description(\n \"List the translatable types and the fields each one translates\",\n )\n .action(() =>\n run(async () =>\n ctx.output(\n RESOURCE_TYPES.map((t) => ({\n type: t.type,\n label: t.label,\n fields: t.fields,\n machineTranslated: t.textFields,\n })),\n ),\n ),\n );\n\n parent\n .command(\"missing <iso>\")\n .description(\n \"Report what isn't translated into a language yet, per type, so you can fill only the gaps\",\n )\n .option(\n \"--type <type>\",\n `Limit to a type: ${TYPE_NAMES.join(\", \")} (repeatable)`,\n collect,\n [],\n )\n .option(\n \"--published-only\",\n \"Skip drafts, scheduled, and archived rows\",\n false,\n )\n .option(\"--from <iso>\", \"The theme's source locale file\", \"en\")\n .option(\"--theme <id>\", \"Check this theme instead of the active one\")\n .action(\n (\n iso: string,\n opts: {\n type: string[];\n publishedOnly: boolean;\n from: string;\n theme?: string;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const language = await findLanguage(api, iso);\n const report = await missingReport(\n api,\n language.iso,\n selectTypes(opts.type),\n {\n publishedOnly: opts.publishedOnly,\n sourceIso: opts.from,\n themeId: opts.theme,\n },\n );\n ctx.output({\n language: {\n iso: language.iso,\n name: language.name,\n active: language.active,\n },\n done: report.done,\n types: report.types,\n });\n }),\n );\n\n parent\n .command(\"show <type> <id>\")\n .description(\n \"Show one item's default text beside its translation (null when untranslated); for theme, <id> is the key, for theme-sections <template id>/<key>\",\n )\n .option(\"--lang <iso>\", \"The translation to show, e.g. fr\")\n .option(\"--from <iso>\", \"theme and theme-sections: the source locale\", \"en\")\n .option(\n \"--theme <id>\",\n \"theme and theme-sections: a theme other than the active one\",\n )\n .action(\n (\n typeName: string,\n id: string,\n opts: { lang?: string; from: string; theme?: string },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const type = resourceType(typeName);\n const language = await findLanguage(api, requireLang(opts.lang));\n if (type.kind === \"storefront\") {\n const rid = rowId(id, type.type);\n const source = await getRow(api, type, rid);\n const translated = isTranslated(source, language.iso);\n const target = translated\n ? await getRow(api, type, rid, language.iso)\n : null;\n ctx.output({\n type: type.type,\n id: rid,\n lang: language.iso,\n translated,\n source: pickFields(source, type.fields),\n translation: target ? pickFields(target, type.fields) : null,\n });\n } else if (type.kind === \"menu-items\") {\n const item = await getMenuItem(api, id, language.iso);\n ctx.output({\n type: type.type,\n id,\n lang: language.iso,\n translated: item.translated !== null,\n source: { title: item.source },\n translation:\n item.translated === null ? null : { title: item.translated },\n });\n } else if (type.kind === \"agreements\") {\n const aid = rowId(id, type.type);\n const source = await readAgreement(api, aid);\n const target = await readAgreement(api, aid, language.iso);\n const translated =\n (target.languageIso ?? \"\").toLowerCase() ===\n language.iso.toLowerCase();\n ctx.output({\n type: type.type,\n id: aid,\n lang: language.iso,\n translated,\n source: { title: source.title, description: source.description },\n translation: translated\n ? { title: target.title, description: target.description }\n : null,\n });\n } else if (type.kind === \"labels\") {\n const lid = rowId(id, type.type);\n const source = await getLabelTitle(api, lid);\n const target = await getLabelTitle(api, lid, language.iso);\n const translated = labelTranslated(source, target);\n ctx.output({\n type: type.type,\n id: lid,\n lang: language.iso,\n translated,\n source: { title: source },\n translation: translated ? { title: target } : null,\n });\n } else if (type.kind === \"theme-sections\") {\n const { templateId, key } = parseSectionRef(id);\n const themeId = await resolveThemeId(api, opts.theme);\n const template = await readTemplateTranslations(\n api,\n themeId,\n templateId,\n );\n const text = sectionText(template, key, opts.from, language.iso);\n ctx.output({\n type: type.type,\n themeId,\n id: `${templateId}/${key}`,\n template: template.name,\n lang: language.iso,\n translated: text.translation !== null,\n ...text,\n });\n } else {\n const themeId = await resolveThemeId(api, opts.theme);\n const locales = await readThemeLocales(api, themeId);\n const source = flattenLocale(localeOf(locales, opts.from) ?? {});\n const target = flattenLocale(localeOf(locales, language.iso) ?? {});\n ctx.output({\n type: \"theme\",\n themeId,\n key: id,\n lang: language.iso,\n translated: (target[id] ?? \"\").trim().length > 0,\n source: source[id] ?? null,\n translation: target[id] ?? null,\n });\n }\n }),\n );\n\n parent\n .command(\"set <type> [id]\")\n .description(\n \"Save a translation: --set field=value for a row, menu item, agreement or label; --set key.path=value for theme strings, or for theme-sections with the template id (changes live content)\",\n )\n .option(\"--lang <iso>\", \"The language to write, e.g. fr\")\n .option(\n \"--set <field=value>\",\n \"A translated field or theme key (repeatable)\",\n collect,\n [],\n )\n .option(\n \"--file <path>\",\n \"A JSON object of field (or theme key) to text, for long or HTML values\",\n )\n .option(\n \"--theme <id>\",\n \"theme and theme-sections: a theme other than the active one\",\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n typeName: string,\n id: string | undefined,\n opts: {\n lang?: string;\n set: string[];\n file?: string;\n theme?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const type = resourceType(typeName);\n const values = collectValues(opts.set, opts.file);\n const language = await requireActiveLanguage(\n api,\n requireLang(opts.lang),\n );\n if (type.kind === \"storefront\") {\n const rid = rowId(id, type.type);\n checkFields(type, values);\n const fields = Object.fromEntries(\n Object.entries(values).map(([field, value]) => [\n field,\n // URL fields clear back to the default file with null.\n field.endsWith(\"_url\") && (value === \"\" || value === \"null\")\n ? null\n : value,\n ]),\n );\n await confirmChange(\n `Save the ${language.name} translation of ${type.label.toLowerCase()} ${rid}?`,\n opts.yes,\n );\n const row = await writeTranslation(\n api,\n type,\n rid,\n language.iso,\n fields,\n );\n ctx.output({\n type: type.type,\n id: rid,\n lang: language.iso,\n saved: Object.keys(fields),\n languages: Array.isArray(row[\"languages\"])\n ? row[\"languages\"]\n : undefined,\n });\n } else if (type.kind === \"menu-items\") {\n if (!id)\n throw new LocalizationError(\n \"Pass the menu item id as <menu id>/<item id>.\",\n );\n checkFields(type, values);\n const title = values[\"title\"];\n if (!title)\n throw new LocalizationError(\n \"Pass --set title=... for a menu item.\",\n );\n await confirmChange(\n `Save the ${language.name} title of menu item ${id}?`,\n opts.yes,\n );\n ctx.output({\n ...(await writeMenuItemTitle(api, id, language.iso, title)),\n lang: language.iso,\n });\n } else if (type.kind === \"agreements\") {\n const aid = rowId(id, type.type);\n checkFields(type, values);\n // The API saves title and text together. A field left out keeps\n // its current translation; with none yet, both are needed.\n const current = await readAgreement(api, aid, language.iso);\n const translated =\n (current.languageIso ?? \"\").toLowerCase() ===\n language.iso.toLowerCase();\n const title =\n values[\"title\"] ?? (translated ? current.title : undefined);\n const description =\n values[\"description\"] ??\n (translated ? current.description : undefined);\n if (!title || !description) {\n throw new LocalizationError(\n `Agreement ${aid} has no ${language.name} translation yet, so pass both --set title=... and --set description=... (or --file).`,\n );\n }\n await confirmChange(\n `Save the ${language.name} translation of agreement ${aid}?`,\n opts.yes,\n );\n await writeAgreementTranslation(api, aid, language.iso, {\n title,\n description,\n });\n ctx.output({\n type: type.type,\n id: aid,\n lang: language.iso,\n saved: Object.keys(values),\n });\n } else if (type.kind === \"labels\") {\n const lid = rowId(id, type.type);\n checkFields(type, values);\n const title = values[\"title\"];\n if (!title)\n throw new LocalizationError(\"Pass --set title=... for a label.\");\n await confirmChange(\n `Save the ${language.name} title of label ${lid}?`,\n opts.yes,\n );\n await writeLabelTitle(api, lid, language.iso, title);\n ctx.output({\n type: type.type,\n id: lid,\n lang: language.iso,\n saved: [\"title\"],\n });\n } else if (type.kind === \"theme-sections\") {\n const templateId = rowId(id, \"template\");\n if (Object.keys(values).length === 0) {\n throw new LocalizationError(\n \"Pass the page's keys with --set key.path=value or --file.\",\n );\n }\n const themeId = await resolveThemeId(api, opts.theme);\n await confirmChange(\n `Save ${Object.keys(values).length} key(s) to template ${templateId}'s ${language.name} translations? They show on the storefront at once.`,\n opts.yes,\n );\n const saved = await writeSectionStrings(\n api,\n themeId,\n templateId,\n language.iso,\n values,\n );\n ctx.output({\n type: type.type,\n themeId,\n ...saved,\n lang: language.iso,\n });\n } else {\n if (id) {\n throw new LocalizationError(\n \"Theme strings take no id; pass keys with --set key.path=value.\",\n );\n }\n if (Object.keys(values).length === 0) {\n throw new LocalizationError(\n \"Pass theme keys with --set key.path=value or --file.\",\n );\n }\n const themeId = await resolveThemeId(api, opts.theme);\n const locales = await readThemeLocales(api, themeId);\n const merged = mergeLocale(\n localeOf(locales, language.iso) ?? {},\n values,\n );\n await confirmChange(\n `Save ${Object.keys(values).length} key(s) to locales/${language.iso}.json on theme ${themeId}? Theme changes publish immediately.`,\n opts.yes,\n );\n const saved = await writeLocale(api, locales, language.iso, merged);\n ctx.output({\n ...saved,\n themeId,\n lang: language.iso,\n keys: Object.keys(values),\n });\n }\n }),\n );\n\n parent\n .command(\"auto <iso>\")\n .description(\n \"Machine-translate what's missing with Fluid AI and print it; --write saves it (changes live content)\",\n )\n .option(\n \"--type <type>\",\n `Limit to a type: ${TYPE_NAMES.join(\", \")} (repeatable)`,\n collect,\n [],\n )\n .option(\"--limit <n>\", \"Most items to translate per type in this run\", \"25\")\n .option(\n \"--write\",\n \"Save the translations; without it nothing is written\",\n false,\n )\n .option(\n \"--published-only\",\n \"Skip drafts, scheduled, and archived rows\",\n false,\n )\n .option(\n \"--from <iso>\",\n \"The theme's source locale file; storefront rows and menus translate from the store's default language\",\n \"en\",\n )\n .option(\n \"--theme <id>\",\n \"Translate this theme's strings instead of the active theme's\",\n )\n .option(\n \"--model <provider/name>\",\n \"A translation model other than Fluid's default\",\n )\n .option(\n \"--skip <type:id>\",\n \"Leave out one item, e.g. products:42, menu-items:12/340, or theme:cart.title (repeatable)\",\n collect,\n [],\n )\n .option(\n \"--store-language <iso>\",\n \"The store's default language, used only when it can't be read from the storefront rows\",\n )\n .option(\"--yes\", \"Confirm --write without a prompt\", false)\n .action(\n (\n iso: string,\n opts: {\n type: string[];\n limit: string;\n write: boolean;\n publishedOnly: boolean;\n from: string;\n theme?: string;\n model?: string;\n skip: string[];\n storeLanguage?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const limit = positiveInt(opts.limit, \"--limit\");\n for (const item of opts.skip) {\n if (!validSkip(item)) {\n throw new LocalizationError(\n `--skip takes <type>:<id>, e.g. products:42 or menu-items:12/340, not \"${item}\".`,\n );\n }\n }\n const types = selectTypes(opts.type);\n const { target, source, store } = await autoLanguages(api, types, {\n iso,\n from: opts.from,\n storeLanguage: opts.storeLanguage,\n write: opts.write,\n });\n if (opts.write) {\n await confirmChange(\n `Machine-translate and save up to ${limit} item(s) per type into ${target.name}? Theme changes publish immediately.`,\n opts.yes,\n );\n }\n const result = await autoTranslate(api, types, {\n iso: target.iso,\n storeName: store.name,\n sourceName: source.name,\n targetName: target.name,\n sourceIso: source.iso,\n storeIso: store.iso,\n limit,\n write: opts.write,\n publishedOnly: opts.publishedOnly,\n themeId: opts.theme,\n model: opts.model,\n skip: opts.skip,\n });\n ctx.output({\n language: { iso: target.iso, name: target.name },\n ...result,\n });\n if (result.types.some((t) => t.failed.length > 0))\n process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"walk [iso]\")\n .description(\n \"Translate a language by answering questions: what's missing, Fluid AI proposals, and accept, edit or skip each one (changes live content)\",\n )\n .option(\"--from <iso>\", \"The theme's source locale\", \"en\")\n .option(\n \"--theme <id>\",\n \"Translate this theme's strings instead of the active theme's\",\n )\n .option(\"--batch <n>\", \"Proposals per Fluid AI call\", \"10\")\n .action(\n (\n iso: string | undefined,\n opts: { from: string; theme?: string; batch: string },\n ) =>\n run(async () => {\n assertInteractive(\"`missing`, `auto` and `set`\");\n const result = await walkTranslations(\n { api: await client(ctx), prompter: terminalPrompter() },\n {\n ...(iso ? { iso } : {}),\n from: opts.from,\n ...(opts.theme ? { themeId: opts.theme } : {}),\n batch: positiveInt(opts.batch, \"--batch\"),\n },\n );\n ctx.output(result);\n if (result.failed.length > 0) process.exitCode = 1;\n }),\n );\n}\n","import type { FluidPlugin } from \"@fluid-app/fluid-cli\";\nimport { createDomainCommand } from \"@fluid-app/fluid-cli\";\n\nimport { registerCountriesCommands } from \"./countries/commands.js\";\nimport { registerTranslationsCommands } from \"./translations/commands.js\";\n\nexport { registerCountriesCommands } from \"./countries/commands.js\";\nexport { registerTranslationsCommands } from \"./translations/commands.js\";\n\n/**\n * One plugin, two command groups: `fluid countries` opens a market and\n * `fluid translations` translates what its shoppers see. The country flow\n * hands language and translation work to `fluid translations`, so each step\n * has one implementation.\n */\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-localization\",\n version: \"0.1.1\",\n register(ctx) {\n ctx.program.addCommand(\n createDomainCommand(\n \"countries\",\n \"Open a country from Fluid's Country Atlas: settings, prices in its currency, agreements, and a status check\",\n registerCountriesCommands,\n ),\n );\n ctx.program.addCommand(\n createDomainCommand(\n \"translations\",\n \"Add a language and translate what shoppers see: storefront, navigation, theme, agreements, and more\",\n registerTranslationsCommands,\n ),\n );\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["languageName","PER_PAGE","MAX_PAGES","PER_PAGE","MAX_PAGES","MAX_PAGES","client"],"sources":["../src/shared/errors.ts","../src/shared/agreements.ts","../src/shared/cli.ts","../src/shared/languages.ts","../src/shared/parsing.ts","../src/shared/client.ts","../src/countries/atlas.ts","../src/countries/api.ts","../src/countries/compliance.ts","../src/countries/forms.ts","../src/countries/prices.ts","../src/countries/plan.ts","../src/countries/seed.ts","../src/countries/status.ts","../src/shared/walk.ts","../src/countries/walk.ts","../src/translations/resources.ts","../src/translations/storefront.ts","../src/translations/auto-languages.ts","../src/translations/labels.ts","../src/translations/machine.ts","../src/translations/menus.ts","../src/translations/theme.ts","../src/translations/sections.ts","../src/translations/report.ts","../src/translations/autotranslate.ts","../src/translations/save.ts","../src/translations/walk.ts","../src/countries/commands.ts","../src/translations/commands.ts","../src/index.ts"],"sourcesContent":["/** A failure the user can act on, printed as a JSON `error` on stderr. */\nexport class LocalizationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LocalizationError\";\n }\n}\n","import type { LocalizationClient } from \"./client.js\";\nimport { LocalizationError } from \"./errors.js\";\n\nexport interface CompanyAgreement {\n id: number;\n title: string;\n active?: boolean;\n company_country_ids?: number[];\n}\n\nconst MAX_AGREEMENT_PAGES = 50;\n\n/** GET /api/agreements, every page. Refuses rather than returning a partial\n * list, which would make an existing agreement look missing. */\nexport async function listAgreements(\n client: LocalizationClient,\n): Promise<CompanyAgreement[]> {\n const all: CompanyAgreement[] = [];\n for (let page = 1; ; page++) {\n const body = await client.get<{\n agreements?: CompanyAgreement[];\n meta?: { pagination?: { total_pages?: number } };\n }>(\"/api/agreements\", { page, per_page: 100 });\n all.push(...(body.agreements ?? []));\n const totalPages = body.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages) break;\n if (page >= MAX_AGREEMENT_PAGES) {\n throw new LocalizationError(\n `The company has more than ${MAX_AGREEMENT_PAGES * 100} agreements, too many to match by title. Check the agreements in Settings > Agreements.`,\n );\n }\n }\n return all;\n}\n\nexport interface AgreementText {\n id: number;\n title: string;\n description: string;\n /** The locale the text was served in; a missing translation falls back to the default. */\n languageIso: string | null;\n active: boolean | null;\n}\n\ninterface AgreementWire {\n id?: number;\n title?: string | null;\n description?: string | null;\n language_iso?: string | null;\n active?: boolean | null;\n}\n\n/** GET /api/agreements/:id, in `lang` when given. */\nexport async function readAgreement(\n client: LocalizationClient,\n id: number,\n lang?: string,\n): Promise<AgreementText> {\n const data = await client.get<{ agreement?: AgreementWire }>(\n `/api/agreements/${id}`,\n lang ? { language_iso: lang } : undefined,\n );\n const wire = data.agreement ?? {};\n return {\n id,\n title: wire.title ?? \"\",\n description: wire.description ?? \"\",\n languageIso: wire.language_iso ?? null,\n active: wire.active ?? null,\n };\n}\n\n/**\n * Whether an agreement has a translation in `iso`. A read in a language\n * with no translation falls back to the default text and says so in\n * `language_iso`, which is how fluid-admin's translation sheet tells them\n * apart. The title falls back silently, so it can't be compared.\n */\nexport async function agreementTranslated(\n client: LocalizationClient,\n id: number,\n iso: string,\n): Promise<boolean> {\n const text = await readAgreement(client, id, iso);\n return (text.languageIso ?? \"\").toLowerCase() === iso.toLowerCase();\n}\n\n/**\n * Save one language's title and text. The API needs both, non-empty. Only\n * these are sent, so the agreement's checkout settings and countries stay\n * as they are.\n */\nexport async function writeAgreementTranslation(\n client: LocalizationClient,\n id: number,\n lang: string,\n text: { title: string; description: string },\n): Promise<void> {\n if (text.title.trim().length === 0 || text.description.trim().length === 0) {\n throw new LocalizationError(\n \"An agreement translation needs both a title and a description.\",\n );\n }\n await client.put(\n `/api/agreements/${id}?language_iso=${encodeURIComponent(lang)}`,\n {\n agreement: { title: text.title, description: text.description },\n },\n );\n}\n","import prompts from \"prompts\";\n\nimport { LocalizationError } from \"./errors.js\";\n\n/**\n * Changes to live configuration or content need an explicit yes. Scripts and\n * agents pass `--yes`; an interactive terminal is asked. With neither,\n * nothing runs.\n */\nexport async function confirmChange(\n question: string,\n yes: boolean,\n interactive: boolean = Boolean(process.stdin.isTTY),\n): Promise<void> {\n if (yes) return;\n if (!interactive)\n throw new LocalizationError(`${question} Pass --yes to confirm.`);\n const answer = await prompts({\n type: \"confirm\",\n name: \"ok\",\n message: question,\n initial: false,\n });\n if (!answer.ok) throw new LocalizationError(\"Cancelled.\");\n}\n\n/**\n * The API's error message. Endpoints answer in three envelopes:\n * `{ error: { message, details } }`, `{ error: \"...\" }`, and\n * `{ error_message, errors }`.\n */\nexport function errorDetail(err: unknown): {\n message: string;\n details?: unknown;\n} {\n const body = (err as { body?: Record<string, unknown> | null } | null)?.body;\n const error = body?.[\"error\"];\n if (error && typeof error === \"object\") {\n const { message, details } = error as {\n message?: unknown;\n details?: unknown;\n };\n if (typeof message === \"string\") {\n return { message, ...(details ? { details } : {}) };\n }\n }\n if (typeof error === \"string\") return { message: error };\n if (typeof body?.[\"error_message\"] === \"string\") {\n return {\n message: body[\"error_message\"] as string,\n ...(body[\"errors\"] ? { details: body[\"errors\"] } : {}),\n };\n }\n if (typeof body?.[\"message\"] === \"string\")\n return { message: body[\"message\"] as string };\n return { message: err instanceof Error ? err.message : String(err) };\n}\n\n/** One line for a per-item failure report: the API's message and status. */\nexport function failureOf(err: unknown): { error: string; status?: number } {\n const status = (err as { status?: unknown } | null)?.status;\n return {\n error: errorDetail(err).message,\n ...(typeof status === \"number\" ? { status } : {}),\n };\n}\n\n/** Print a failure as JSON on stderr and exit non-zero. */\nexport function fail(err: unknown): void {\n const status = (err as { status?: unknown } | null)?.status;\n const { message, details } = errorDetail(err);\n process.stderr.write(\n JSON.stringify({\n error: message,\n ...(typeof status === \"number\" ? { status } : {}),\n ...(details ? { details } : {}),\n }) + \"\\n\",\n );\n process.exitCode = 1;\n}\n\nexport function run(action: () => Promise<void>): Promise<void> {\n return action().catch(fail);\n}\n\n/** Commander collector for a repeatable option. */\nexport const collect = (value: string, previous: string[] = []): string[] => [\n ...previous,\n value,\n];\n\nexport function positiveInt(raw: string, flag: string): number {\n const value = Number(raw);\n if (!Number.isInteger(value) || value <= 0) {\n throw new LocalizationError(\n `${flag} takes a whole number above 0, not \"${raw}\".`,\n );\n }\n return value;\n}\n","import type { LocalizationClient } from \"./client.js\";\nimport { LocalizationError } from \"./errors.js\";\n\nexport interface Language {\n id: number;\n name: string;\n iso: string;\n active: boolean;\n usersCount: number | null;\n}\n\ninterface LanguageWire {\n id: number;\n name?: string;\n iso?: string;\n active_in_company?: boolean;\n company_users_count?: number | null;\n}\n\nconst LANGUAGES_PATH = \"/api/settings/languages\";\n\nfunction toLanguage(wire: LanguageWire): Language {\n return {\n id: wire.id,\n name: wire.name ?? \"\",\n iso: wire.iso ?? \"\",\n active: wire.active_in_company === true,\n usersCount: wire.company_users_count ?? null,\n };\n}\n\n/** Every language Fluid supports, with whether it is on for this company. */\nexport async function listLanguages(\n client: LocalizationClient,\n activeOnly = false,\n): Promise<Language[]> {\n const data = await client.get<{ languages?: LanguageWire[] }>(\n LANGUAGES_PATH,\n activeOnly ? { active: true } : undefined,\n );\n return (data.languages ?? []).map(toLanguage);\n}\n\nexport function normalizeIso(iso: string): string {\n return iso.trim().toLowerCase();\n}\n\nexport function sameIso(a: string, b: string): boolean {\n return normalizeIso(a) === normalizeIso(b);\n}\n\n/** A supported language by ISO code from a list already read, or undefined. */\nexport function languageIn(\n languages: readonly Language[],\n iso: string,\n): Language | undefined {\n return languages.find((language) => sameIso(language.iso, iso));\n}\n\n/** One supported language by ISO code, or a LocalizationError naming close matches. */\nexport async function findLanguage(\n client: LocalizationClient,\n iso: string,\n): Promise<Language> {\n const languages = await listLanguages(client);\n const match = languageIn(languages, iso);\n if (match) return match;\n const prefix = normalizeIso(iso).split(\"-\")[0] ?? normalizeIso(iso);\n const close = languages\n .filter((language) => normalizeIso(language.iso).startsWith(prefix))\n .map((language) => language.iso);\n throw new LocalizationError(\n `\"${iso}\" isn't a language Fluid supports.` +\n (close.length > 0 ? ` Did you mean ${close.join(\", \")}?` : \"\") +\n \" Run `fluid translations languages` to see every code.\",\n );\n}\n\n/** The language, or a LocalizationError if it isn't on for the company. */\nexport async function requireActiveLanguage(\n client: LocalizationClient,\n iso: string,\n): Promise<Language> {\n const language = await findLanguage(client, iso);\n if (!language.active) {\n throw new LocalizationError(\n `${language.name} (${language.iso}) isn't on for this company, so translations can't be written to it. Run \\`fluid translations enable ${language.iso} --yes\\` first.`,\n );\n }\n return language;\n}\n\n/** Turn a language on. The body is the documented and tested top-level `id`. */\nexport async function enableLanguage(\n client: LocalizationClient,\n language: Language,\n): Promise<Language> {\n const data = await client.post<{ language?: LanguageWire }>(LANGUAGES_PATH, {\n id: language.id,\n });\n return data.language\n ? toLanguage(data.language)\n : { ...language, active: true };\n}\n\n/** Turn a language off. Its saved translations are kept. */\nexport async function disableLanguage(\n client: LocalizationClient,\n language: Language,\n): Promise<Language> {\n await client.delete(`${LANGUAGES_PATH}/${language.id}`);\n return { ...language, active: false };\n}\n","import { readFileSync } from \"node:fs\";\n\nimport { LocalizationError } from \"./errors.js\";\n\n/** Keys that would reach an object's prototype instead of an own property. */\nexport const RESERVED_KEYS: ReadonlySet<string> = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\",\n]);\n\n/** Split a dotted path, refusing empty and reserved segments. */\nexport function keyPath(path: string): string[] {\n const parts = path.split(\".\");\n if (parts.some((p) => p.length === 0)) {\n throw new LocalizationError(`\"${path}\" has an empty key segment.`);\n }\n if (parts.some((p) => RESERVED_KEYS.has(p))) {\n throw new LocalizationError(`\"${path}\" uses a reserved key name.`);\n }\n return parts;\n}\n\nfunction splitAssignment(\n assignment: string,\n example: string,\n): [string, string] {\n const at = assignment.indexOf(\"=\");\n if (at <= 0) {\n throw new LocalizationError(\n `\"${assignment}\" isn't key=value, e.g. ${example}.`,\n );\n }\n return [assignment.slice(0, at).trim(), assignment.slice(at + 1)];\n}\n\n/**\n * Parse `--set field=value` text pairs into a flat object. Values are kept as\n * written, so translated text is never reinterpreted; only the first `=`\n * separates the key, and a theme key keeps its dots (`cart.title=Panier`).\n */\nexport function parseTextAssignments(\n assignments: readonly string[],\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const assignment of assignments) {\n const [key, value] = splitAssignment(\n assignment,\n 'title=\"Mélange de betteraves\"',\n );\n keyPath(key);\n out[key] = value;\n }\n return out;\n}\n\n/**\n * Parse `--set key=value` setting overrides. `legal_settings.cooling_off_period_days=30`\n * nests; values that read as JSON (true, 42, [\"a\"], {\"x\":1}) are parsed and\n * anything else stays a string.\n */\nexport function parseSettingAssignments(\n assignments: readonly string[],\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const assignment of assignments) {\n const [key, raw] = splitAssignment(\n assignment,\n \"tax_inclusive_pricing=false\",\n );\n const path = keyPath(key);\n let node = out;\n for (const part of path.slice(0, -1)) {\n const next = Object.hasOwn(node, part) ? node[part] : undefined;\n if (typeof next !== \"object\" || next === null || Array.isArray(next))\n node[part] = {};\n node = node[part] as Record<string, unknown>;\n }\n node[path[path.length - 1]!] = parseValue(raw);\n }\n return out;\n}\n\nfunction parseValue(raw: string): unknown {\n if (/^(true|false|null|-?\\d+(\\.\\d+)?)$/.test(raw) || /^[[{]/.test(raw)) {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n return raw;\n}\n\n/**\n * Read `--file`: a JSON object of field (or key) to string. Long or HTML\n * values are easier to pass this way than on the command line.\n */\nexport function readValuesFile(path: string): Record<string, string> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, \"utf8\"));\n } catch (error) {\n throw new LocalizationError(\n `Couldn't read ${path} as JSON: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new LocalizationError(\n `${path} must hold a JSON object of field to text.`,\n );\n }\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value !== \"string\") {\n throw new LocalizationError(`${path}: \"${key}\" must be a string.`);\n }\n keyPath(key);\n out[key] = value;\n }\n return out;\n}\n\n/** `--set` and `--file` together, `--set` winning on a clash. */\nexport function collectValues(\n sets: readonly string[],\n file?: string,\n): Record<string, string> {\n return {\n ...(file ? readValuesFile(file) : {}),\n ...parseTextAssignments(sets),\n };\n}\n","/** The slice of the CLI's API client the localization commands use. */\nexport interface LocalizationClient {\n get<T>(endpoint: string, params?: Record<string, unknown>): Promise<T>;\n post<T>(endpoint: string, body?: unknown): Promise<T>;\n put<T>(endpoint: string, body?: unknown): Promise<T>;\n patch<T>(endpoint: string, body?: unknown): Promise<T>;\n delete<T>(endpoint: string): Promise<T>;\n}\n\n/** The HTTP status of an API error, if it carries one. */\nexport function statusOf(error: unknown): number | undefined {\n const status = (error as { status?: unknown } | null)?.status;\n return typeof status === \"number\" ? status : undefined;\n}\n","// Fluid's Country Atlas: the certified, per-market setup profile served at\n// GET /api/v202604/country/atlas/{iso}. This mirrors Mist's\n// country-atlas.service (apps/mist-desktop) so the CLI and Mist read the\n// atlas the same way: the same code aliases, the same summary, and the same\n// honesty rule that a market with no atlas is reported as uncovered rather\n// than filled with guesses.\n\nimport { statusOf, type LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\nexport const ATLAS_PATH_PREFIX = \"/api/v202604/country/atlas/\";\n\nexport interface AtlasChecklistItem {\n id: string;\n label: string;\n description: string;\n}\n\nexport interface AtlasMode {\n overview: string;\n launchChecklist: AtlasChecklistItem[];\n}\n\nexport interface AtlasAgreementTranslation {\n title: string;\n description: string;\n language_iso: string;\n}\n\nexport interface AtlasAgreement {\n _localId: string;\n active: boolean;\n translations: AtlasAgreementTranslation[];\n default_checked: boolean;\n show_at_checkout: boolean;\n required_on_checkout: boolean;\n}\n\nexport interface AtlasTaxSettings {\n taxName: string;\n taxInclusive: boolean;\n taxLocalName?: string;\n roundingMethod?: string;\n invoiceRequired?: boolean;\n showTaxBreakdown?: boolean;\n taxThresholdAmount?: number | null;\n taxThresholdEnabled?: boolean;\n taxLocalAbbreviation?: string;\n taxExemptForForeignShipping?: boolean;\n}\n\nexport interface AtlasLegalSettings {\n coolingOffPeriodDays?: number;\n warrantyPeriodMonths?: number;\n cookieConsentRequired?: boolean;\n recurringSubscriptionDisclosureRequired?: boolean;\n}\n\nexport interface AtlasAddressField {\n field: string;\n visible: boolean;\n required: boolean;\n defaultLabel?: string;\n localLabel?: string;\n placeholder?: string;\n columnWidth?: string;\n validation?: string;\n}\n\nexport interface AtlasPaymentMethod {\n name: string;\n enabled: boolean;\n priority: number;\n integration_type: string;\n}\n\nexport interface AtlasEnrollmentField {\n id: string;\n label: string;\n order: number;\n required: boolean;\n component: string;\n description?: string;\n requiredFor?: string[];\n autoTranslate?: boolean;\n}\n\nexport interface CountryAtlas {\n modes: Record<string, AtlasMode>;\n notes: string;\n agreements: AtlasAgreement[];\n requires3ds: boolean;\n taxSettings: AtlasTaxSettings;\n addressFields: AtlasAddressField[];\n legalSettings: AtlasLegalSettings;\n majorLanguages: string[];\n paymentMethods: AtlasPaymentMethod[];\n defaultCurrency: string;\n enrollmentFormFields: AtlasEnrollmentField[];\n}\n\nexport interface CountryAtlasResult {\n /** ISO alpha-2 after alias resolution. */\n countryCode: string;\n /** False when Fluid publishes no atlas for the market. */\n covered: boolean;\n atlas: CountryAtlas | null;\n}\n\n/** The operating modes the atlas describes, as the CLI accepts them. */\nexport const MODES = [\"nfr\", \"otg\", \"usd\"] as const;\nexport type Mode = (typeof MODES)[number];\n\nexport function parseMode(value: string): Mode {\n const mode = value.trim().toLowerCase();\n if (!(MODES as readonly string[]).includes(mode)) {\n throw new LocalizationError(\n `\"${value}\" isn't an operating mode. Use one of: ${MODES.join(\", \")}.`,\n );\n }\n return mode as Mode;\n}\n\n/** Common aliases, as Mist resolves them: people type \"UK\" and alpha-3. */\nconst ALIASES: Record<string, string> = {\n UK: \"GB\",\n DEU: \"DE\",\n FRA: \"FR\",\n GBR: \"GB\",\n CAN: \"CA\",\n MEX: \"MX\",\n AUS: \"AU\",\n JPN: \"JP\",\n USA: \"US\",\n};\n\nexport function normalizeCountryCode(code: string): string {\n const upper = code.trim().toUpperCase();\n return ALIASES[upper] ?? upper;\n}\n\n/** The ISO alpha-2 code, or a LocalizationError for anything else. */\nexport function countryCodeOf(code: string): string {\n const iso = normalizeCountryCode(code);\n if (!/^[A-Z]{2}$/.test(iso)) {\n throw new LocalizationError(\n `\"${code}\" isn't a two-letter ISO country code, e.g. DE or JP.`,\n );\n }\n return iso;\n}\n\n/** Flatten the atlas's simple HTML (overviews, notes) to readable text. */\nexport function htmlToText(html: string): string {\n return html\n .replace(/<\\s*(br|\\/p|\\/li|\\/ul|\\/ol|\\/h[1-6]|\\/blockquote)[^>]*>/gi, \"\\n\")\n .replace(/<\\s*li[^>]*>/gi, \"• \")\n .replace(/<[^>]+>/g, \"\")\n .replace(/&nbsp;/gi, \" \")\n .replace(/&lt;/gi, \"<\")\n .replace(/&gt;/gi, \">\")\n .replace(/&quot;/gi, '\"')\n .replace(/&#0?39;|&apos;/gi, \"'\")\n .replace(/&amp;/gi, \"&\")\n .replace(/[ \\t]+\\n/g, \"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** Whether a response body is an atlas. The endpoint answers 404 for an\n * unknown ISO, and a country with no configuration renders an empty body. */\nfunction isAtlas(body: unknown): body is CountryAtlas {\n return (\n typeof body === \"object\" &&\n body !== null &&\n typeof (body as { modes?: unknown }).modes === \"object\" &&\n (body as { modes?: unknown }).modes !== null\n );\n}\n\n/** Fetch a country's atlas. A 404 or an empty configuration is an uncovered\n * market, not an error. Other failures throw. */\nexport async function fetchAtlas(\n client: LocalizationClient,\n code: string,\n): Promise<CountryAtlasResult> {\n const countryCode = countryCodeOf(code);\n let body: unknown;\n try {\n body = await client.get<unknown>(\n `${ATLAS_PATH_PREFIX}${countryCode.toLowerCase()}`,\n );\n } catch (error) {\n if (statusOf(error) === 404) {\n return { countryCode, covered: false, atlas: null };\n }\n throw error;\n }\n if (!isAtlas(body)) return { countryCode, covered: false, atlas: null };\n return { countryCode, covered: true, atlas: body };\n}\n\n/** The atlas, or a LocalizationError explaining that the market is uncovered. */\nexport async function requireAtlas(\n client: LocalizationClient,\n code: string,\n): Promise<{ countryCode: string; atlas: CountryAtlas }> {\n const result = await fetchAtlas(client, code);\n if (!result.covered || !result.atlas) {\n throw new LocalizationError(\n `Fluid publishes no Country Atlas for ${result.countryCode} yet, so there is nothing to seed from. Set the country up by hand in Settings > Countries.`,\n );\n }\n return { countryCode: result.countryCode, atlas: result.atlas };\n}\n\nconst NOTES_SUMMARY_MAX = 2_000;\n\nexport interface AtlasAgreementSummary {\n localId: string;\n title: string;\n languages: string[];\n requiredOnCheckout: boolean;\n showAtCheckout: boolean;\n defaultChecked: boolean;\n bodyChars: number;\n}\n\n/** The translation an agreement is created from: English, else the first. */\nexport function primaryTranslation(\n agreement: AtlasAgreement,\n): AtlasAgreementTranslation | undefined {\n return (\n agreement.translations.find((t) => t.language_iso === \"en\") ??\n agreement.translations[0]\n );\n}\n\nexport function summarizeAgreement(\n agreement: AtlasAgreement,\n): AtlasAgreementSummary {\n const primary = primaryTranslation(agreement);\n return {\n localId: agreement._localId,\n title: primary?.title ?? agreement._localId,\n languages: agreement.translations.map((t) => t.language_iso),\n requiredOnCheckout: agreement.required_on_checkout,\n showAtCheckout: agreement.show_at_checkout,\n defaultChecked: agreement.default_checked,\n bodyChars: primary?.description.length ?? 0,\n };\n}\n\n/** The atlas without the agreement legal bodies, which run to thousands of\n * characters each; `fluid countries atlas <iso> --agreement <id>` prints one. */\nexport function summarizeAtlas(atlas: CountryAtlas) {\n const notes = htmlToText(atlas.notes ?? \"\");\n const modes: Record<\n string,\n { overview: string; launchChecklist: AtlasChecklistItem[] }\n > = {};\n for (const [key, mode] of Object.entries(atlas.modes ?? {})) {\n modes[key] = {\n overview: htmlToText(mode.overview ?? \"\"),\n launchChecklist: mode.launchChecklist ?? [],\n };\n }\n return {\n defaultCurrency: atlas.defaultCurrency,\n majorLanguages: atlas.majorLanguages ?? [],\n requires3ds: atlas.requires3ds ?? false,\n marketNotes:\n notes.length > NOTES_SUMMARY_MAX\n ? `${notes.slice(0, NOTES_SUMMARY_MAX)}… [truncated]`\n : notes,\n modes,\n taxSettings: atlas.taxSettings,\n legalSettings: atlas.legalSettings ?? {},\n paymentMethods: sortedPaymentMethods(atlas),\n enrollmentFormFields: [...(atlas.enrollmentFormFields ?? [])].sort(\n (a, b) => a.order - b.order,\n ),\n addressFields: atlas.addressFields ?? [],\n agreements: (atlas.agreements ?? []).map(summarizeAgreement),\n };\n}\n\nexport function sortedPaymentMethods(\n atlas: CountryAtlas,\n): AtlasPaymentMethod[] {\n return [...(atlas.paymentMethods ?? [])].sort(\n (a, b) => a.priority - b.priority,\n );\n}\n\n/** The launch checklist for one mode, verbatim. */\nexport function launchChecklist(\n atlas: CountryAtlas,\n mode: Mode,\n): AtlasChecklistItem[] {\n return atlas.modes?.[mode]?.launchChecklist ?? [];\n}\n\n/** One agreement's full translations, by its atlas local id. */\nexport function atlasAgreement(\n atlas: CountryAtlas,\n localId: string,\n): AtlasAgreement {\n const agreement = (atlas.agreements ?? []).find(\n (a) => a._localId === localId,\n );\n if (!agreement) {\n const ids = (atlas.agreements ?? []).map((a) => a._localId);\n throw new LocalizationError(\n `The atlas has no agreement \"${localId}\". It has: ${ids.join(\", \") || \"none\"}.`,\n );\n }\n return agreement;\n}\n","// The Fluid API calls a country setup makes. Each one is an existing\n// endpoint the admin already uses; nothing here needs a new route.\n\nimport { primaryTranslation, type AtlasAgreement } from \"./atlas.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport {\n agreementTranslated,\n writeAgreementTranslation,\n type CompanyAgreement,\n} from \"../shared/agreements.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\nexport interface CatalogCountry {\n id: number;\n iso: string;\n name: string;\n currency_code?: string | null;\n}\n\n/** GET /api/countries: the global country catalog. */\nexport async function listCatalogCountries(\n client: LocalizationClient,\n): Promise<CatalogCountry[]> {\n const body = await client.get<\n CatalogCountry[] | { countries?: CatalogCountry[] }\n >(\"/api/countries\");\n return Array.isArray(body) ? body : (body.countries ?? []);\n}\n\n/** One country from the global catalog. */\nexport async function catalogCountry(\n client: LocalizationClient,\n iso: string,\n): Promise<CatalogCountry> {\n const match = (await listCatalogCountries(client)).find(\n (c) => c.iso?.toUpperCase() === iso,\n );\n if (!match)\n throw new LocalizationError(\n `Fluid's country catalog has no country ${iso}.`,\n );\n return match;\n}\n\nexport interface CompanyCountry {\n id: number;\n currency?: string | null;\n mode?: string | null;\n tax_name?: string | null;\n tax_inclusive_pricing?: boolean | null;\n tax_invoice_required?: boolean | null;\n requires_3ds?: boolean | null;\n payment_method_priority?: string[] | null;\n legal_settings?: Record<string, unknown> | null;\n address_fields?: Array<Record<string, unknown>> | null;\n revision?: string | null;\n test_mode?: boolean | null;\n warehouse?: { id: number; name?: string } | null;\n country?: { id: number; iso: string; name?: string } | null;\n}\n\n/** GET /api/settings/company_countries: the countries the company sells in. */\nexport async function listCompanyCountries(\n client: LocalizationClient,\n): Promise<CompanyCountry[]> {\n const body = await client.get<{ company_countries?: CompanyCountry[] }>(\n \"/api/settings/company_countries\",\n );\n return body.company_countries ?? [];\n}\n\nexport async function findCompanyCountry(\n client: LocalizationClient,\n iso: string,\n): Promise<CompanyCountry | undefined> {\n const list = await listCompanyCountries(client);\n return list.find((cc) => cc.country?.iso?.toUpperCase() === iso);\n}\n\n/** The company country, or a LocalizationError saying to open it first. */\nexport async function requireCompanyCountry(\n client: LocalizationClient,\n iso: string,\n): Promise<CompanyCountry> {\n const found = await findCompanyCountry(client, iso);\n if (!found) {\n throw new LocalizationError(\n `${iso} isn't open for this company yet. Run \\`fluid countries open ${iso} --mode <nfr|otg|usd>\\` first.`,\n );\n }\n return found;\n}\n\n/** POST /api/settings/company_countries. `seedLegacyDefaults: false` skips\n * the server's generic country agreements and forms, so the company gets\n * the atlas selection instead. */\nexport async function createCompanyCountry(\n client: LocalizationClient,\n attributes: Record<string, unknown>,\n seedLegacyDefaults: boolean,\n): Promise<CompanyCountry & { warnings?: unknown }> {\n const body = await client.post<{\n company_country: CompanyCountry;\n warnings?: unknown;\n }>(\"/api/settings/company_countries\", {\n company_country: attributes,\n seed_legacy_defaults: seedLegacyDefaults,\n });\n return {\n ...body.company_country,\n ...(body.warnings ? { warnings: body.warnings } : {}),\n };\n}\n\n/** PUT /api/settings/company_countries/:id, guarded by the revision read\n * just before so a concurrent admin edit answers 409 instead of being lost. */\nexport async function updateCompanyCountry(\n client: LocalizationClient,\n id: number,\n attributes: Record<string, unknown>,\n expectedRevision?: string | null,\n): Promise<CompanyCountry> {\n const { country_id: _ignored, ...rest } = attributes;\n const body = await client.put<{ company_country: CompanyCountry }>(\n `/api/settings/company_countries/${id}`,\n {\n company_country: rest,\n ...(expectedRevision ? { expected_revision: expectedRevision } : {}),\n },\n );\n return body.company_country;\n}\n\nexport interface CreatedAgreement {\n localId: string;\n id: number;\n title: string;\n translations: string[];\n failedTranslations: Array<{ language: string; error: string }>;\n}\n\n/**\n * Create an atlas agreement for a company country. POST writes the base\n * (English) text: the English translation when the atlas has one, else the\n * first. Each translation that is not English is then written in its own\n * locale with PUT ?language_iso=, which needs that language enabled for the\n * company. A translation that fails is reported, not fatal.\n */\nexport async function createAgreementFromAtlas(\n client: LocalizationClient,\n agreement: AtlasAgreement,\n companyCountryId: number,\n): Promise<CreatedAgreement> {\n const primary = primaryTranslation(agreement);\n if (!primary) {\n throw new LocalizationError(\n `Atlas agreement ${agreement._localId} has no text.`,\n );\n }\n const flags = {\n active: true,\n required_on_checkout: agreement.required_on_checkout === true,\n show_at_checkout: agreement.show_at_checkout === true,\n default_checked: agreement.default_checked === true,\n company_country_ids: [companyCountryId],\n };\n const created = await client.post<{ agreement: CompanyAgreement }>(\n \"/api/agreements\",\n {\n agreement: {\n title: primary.title,\n description: primary.description,\n ...flags,\n },\n },\n );\n const id = created.agreement.id;\n const { translations, failedTranslations } = await writeTranslations(\n client,\n id,\n agreement,\n );\n return {\n localId: agreement._localId,\n id,\n title: primary.title,\n translations,\n failedTranslations,\n };\n}\n\n/**\n * Write each of an atlas agreement's translations that isn't English onto a\n * company agreement, in its own locale. Only the title and text are sent, so\n * the agreement's checkout settings and countries are left as they are. A\n * translation that fails is reported, not fatal, and can be written again.\n */\nexport async function writeTranslations(\n client: LocalizationClient,\n agreementId: number,\n agreement: AtlasAgreement,\n): Promise<{\n translations: string[];\n failedTranslations: Array<{\n language: string;\n error: string;\n status?: number;\n }>;\n}> {\n const translations: string[] = [];\n const failedTranslations: Array<{\n language: string;\n error: string;\n status?: number;\n }> = [];\n for (const t of agreement.translations) {\n if (t.language_iso === \"en\") continue;\n try {\n await writeAgreementTranslation(client, agreementId, t.language_iso, t);\n translations.push(t.language_iso);\n } catch (error) {\n failedTranslations.push({\n language: t.language_iso,\n ...failureOf(error),\n });\n }\n }\n return { translations, failedTranslations };\n}\n\n/**\n * The atlas languages an agreement has no translation in, checked the way\n * `fluid translations missing --type agreements` checks them: a read in a\n * language without a translation falls back and says so in `language_iso`.\n * English is the base text an agreement is created from, so it isn't checked.\n */\nexport async function missingTranslations(\n client: LocalizationClient,\n agreementId: number,\n agreement: AtlasAgreement,\n): Promise<string[]> {\n const missing: string[] = [];\n for (const t of agreement.translations) {\n if (t.language_iso === \"en\") continue;\n if (!(await agreementTranslated(client, agreementId, t.language_iso))) {\n missing.push(t.language_iso);\n }\n }\n return missing;\n}\n\nexport interface Warehouse {\n id: number;\n name: string;\n active?: boolean;\n country?: { id?: number; iso?: string } | null;\n}\n\n/** GET /api/settings/warehouses. */\nexport async function listWarehouses(\n client: LocalizationClient,\n): Promise<Warehouse[]> {\n const body = await client.get<{ warehouses?: Warehouse[] }>(\n \"/api/settings/warehouses\",\n );\n return body.warehouses ?? [];\n}\n","// The compliance view of the atlas, as Mist's `country_settings` tool\n// projects it for the mode finalizers and the compliance review. Every value\n// is derived from the atlas; where the atlas is silent the field says so\n// instead of guessing.\n\nimport {\n htmlToText,\n primaryTranslation,\n sortedPaymentMethods,\n type CountryAtlas,\n} from \"./atlas.js\";\n\n/** Privacy regulators matched against the market notes. Not exhaustive: an\n * unmatched market reads \"See market notes\", never an invented name. */\nconst KNOWN_REGULATORS = [\n \"BfDI\",\n \"CNIL\",\n \"ICO\",\n \"Garante\",\n \"AEPD\",\n \"OPC\",\n \"CAI\",\n \"INAI\",\n \"OAIC\",\n \"PPC\",\n \"PDPC\",\n \"FTC\",\n];\n\n/** The first statute-like reference in a legal body, or \"\". */\nexport function extractCitation(bodyText: string): string {\n const patterns = [\n /§\\s?\\d+[a-z]?/i,\n /Art(?:icle)?\\.?\\s?\\d+[-\\w]*/i,\n /Regulation\\s+\\d+/i,\n /\\b\\d{4}[-/]\\d+\\b/,\n ];\n for (const re of patterns) {\n const m = bodyText.match(re);\n if (m) return m[0].trim();\n }\n return \"\";\n}\n\nfunction languageName(iso: string): string {\n try {\n return new Intl.DisplayNames([\"en\"], { type: \"language\" }).of(iso) ?? iso;\n } catch {\n return iso;\n }\n}\n\nfunction unitPriceRule(atlas: CountryAtlas, notes: string) {\n const haystack = [\n notes,\n ...Object.values(atlas.modes ?? {}).flatMap((m) =>\n (m.launchChecklist ?? []).map((c) => `${c.label} ${c.description}`),\n ),\n ]\n .join(\"\\n\")\n .toLowerCase();\n const match = haystack.match(\n /[^\\n]*(unit[\\s-]?pric|price marking|grundpreis)[^\\n]*/,\n );\n return match\n ? { required: true, notes: match[0].trim().slice(0, 200) }\n : { required: false, notes: \"\" };\n}\n\nexport function complianceView(countryCode: string, atlas: CountryAtlas) {\n const notes = htmlToText(atlas.notes ?? \"\");\n const languages = atlas.majorLanguages ?? [];\n const primaryLanguage = languages[0] ?? \"en\";\n const cookieRequired = atlas.legalSettings?.cookieConsentRequired ?? false;\n const regulator = KNOWN_REGULATORS.find((r) =>\n new RegExp(`\\\\b${r}\\\\b`).test(notes),\n );\n\n return {\n countryCode,\n currencyCode: atlas.defaultCurrency,\n languageCode: primaryLanguage,\n languageName: languageName(primaryLanguage),\n widelySpokenLanguages: languages.slice(1),\n // The atlas names the tax; the rate is on GET /api/countries (tax_rates).\n vat: atlas.taxSettings?.taxName\n ? { name: atlas.taxSettings.taxName, standardRatePct: null }\n : null,\n vatInclusiveDisplay: atlas.taxSettings\n ? atlas.taxSettings.taxInclusive\n ? \"inclusive\"\n : \"exclusive_at_sticker\"\n : \"unspecified\",\n legalSettings: atlas.legalSettings ?? {},\n cookieRule: {\n framework: cookieRequired\n ? \"Consent-based cookie law (see market notes)\"\n : \"None documented\",\n requirement: cookieRequired\n ? \"Obtain consent before non-essential cookies; provide an equally-prominent Reject All and log consent.\"\n : \"No cookie-consent mandate documented for this market.\",\n regulator: regulator ?? \"See market notes\",\n },\n unitPriceRule: unitPriceRule(atlas, notes),\n mandatoryDisclosurePages: (atlas.agreements ?? []).map((a) => {\n const primary = primaryTranslation(a);\n const body = htmlToText(primary?.description ?? \"\");\n return {\n localId: a._localId,\n name: primary?.title ?? a._localId,\n description: body.slice(0, 160),\n citation: extractCitation(body),\n };\n }),\n paymentMethods: sortedPaymentMethods(atlas).map((m) => ({\n integrationType: m.integration_type,\n label: m.name,\n recommended: m.enabled,\n })),\n };\n}\n","// A country's enrollment form: the atlas's enrollment fields, set up as a\n// Post Purchase form scoped to the country. Enrollment checkout shows every\n// active Post Purchase form for the cart's country after payment, merged\n// into one, so a field counts as collected when any of those forms has it.\n//\n// The rules follow the admin's country checklist, so a form made here is the\n// one the admin would make: the same title, the same components, the same\n// fields left out.\n\nimport type { AtlasEnrollmentField, CountryAtlas } from \"./atlas.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { statusOf } from \"../shared/client.js\";\nimport { errorDetail } from \"../shared/cli.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\nexport const FORMS_PATH = \"/api/forms\";\nconst PER_PAGE = 100;\nconst MAX_PAGES = 200;\n\n/** The title of the enrollment form the atlas seeds for a country, the same\n * title the admin gives it. */\nexport const enrollmentFormTitle = (countryName: string) =>\n `Enrollment - ${countryName}`;\n\n/** The member types an atlas field can be required for. */\nconst AUDIENCES = [\"rep\", \"preferred\", \"customer\"] as const;\n\ninterface ComponentMapping {\n element_type: string;\n puck_type: string;\n /** Text inputs carry case_sensitive, as the builder saves them. */\n text?: boolean;\n /** Stored sensitive whatever the label says. */\n sensitive?: boolean;\n}\n\n/**\n * Each atlas `component` (a form-builder component name) and the element it\n * is stored as. Identity fields are `input` elements named by `puck_type`.\n */\nexport const COMPONENT_MAPPINGS: Record<string, ComponentMapping> = {\n LegalName: { element_type: \"input\", puck_type: \"legal_name\", text: true },\n FullName: { element_type: \"input\", puck_type: \"full_name\", text: true },\n Email: { element_type: \"input\", puck_type: \"email\", text: true },\n PhoneNumber: { element_type: \"input\", puck_type: \"phone_number\", text: true },\n DateOfBirth: {\n element_type: \"input\",\n puck_type: \"date_of_birth\",\n sensitive: true,\n },\n ShortTextEntry: {\n element_type: \"short_text_entry\",\n puck_type: \"short_text_entry\",\n text: true,\n },\n LongTextEntry: {\n element_type: \"long_text_entry\",\n puck_type: \"long_text_entry\",\n text: true,\n },\n NumberEntry: {\n element_type: \"number_entry\",\n puck_type: \"number_entry\",\n text: true,\n },\n YesNo: { element_type: \"yes_no_entry\", puck_type: \"yes_no_entry\" },\n MultipleChoice: {\n element_type: \"multiple_choice_entry\",\n puck_type: \"multiple_choice_entry\",\n },\n FileUpload: { element_type: \"file_upload\", puck_type: \"file_upload\" },\n Checkbox: { element_type: \"checkbox\", puck_type: \"checkbox\" },\n};\n\n/** Components an enrollment form must not collect. The repository's review\n * rules treat a form that collects SSNs as critical, so the admin never\n * seeds one either. */\nexport const EXCLUDED_COMPONENTS: ReadonlySet<string> = new Set([\n \"SocialSecurityNumber\",\n]);\n\n/** Identity components: a form with any one of them covers the atlas field,\n * whatever its label. Other components match by label. */\nconst IDENTITY_PUCK_TYPES: ReadonlySet<string> = new Set([\n \"legal_name\",\n \"full_name\",\n \"email\",\n \"phone_number\",\n \"date_of_birth\",\n \"ssn\",\n]);\n\n/** The element types an existing component may be stored as, older names\n * included, each mapped to the `puck_type` this module compares. */\nconst STORED_TYPE_ALIASES: Record<string, string> = {\n legal_name: \"legal_name\",\n full_name: \"full_name\",\n email: \"email\",\n phone_number: \"phone_number\",\n date_of_birth: \"date_of_birth\",\n ssn: \"ssn\",\n short_text_entry: \"short_text_entry\",\n text: \"short_text_entry\",\n long_text_entry: \"long_text_entry\",\n number_entry: \"number_entry\",\n number: \"number_entry\",\n yes_no_entry: \"yes_no_entry\",\n yes_no: \"yes_no_entry\",\n multiple_choice_entry: \"multiple_choice_entry\",\n multiple_choice: \"multiple_choice_entry\",\n file_upload: \"file_upload\",\n checkbox: \"checkbox\",\n};\n\n/**\n * Labels of data an enrollment form must not collect: SSNs, national and tax\n * ID numbers, bank and card details, and passwords. The repository's review\n * rules treat a form that collects any of them as critical, and the atlas\n * names them in the label (a `ShortTextEntry` labelled \"CPF\" or \"IBAN\"), not\n * in the component, so the label is checked too, on every component that\n * takes a value: a respondent types what the label asks for, whatever the\n * element type. A false match only leaves a field to a person.\n */\nconst RESTRICTED_DATA_LABEL =\n /\\b(SSN|EIN|ITIN|CPF|CNPJ|CURP|RFC|RUC|RUT|NIT|NIF|NIE|NIP|TIN|TPIN|PAN|INN|OIB|CUIT|CUIL|NPWP|IRD|UTR|KRA|IBAN|SWIFT|BIC|CVV|CVC|VAT|GST|GSTIN|ABN|TRN|NTN)\\b|tax|fiscal|steuer|social security|national (id|identity|insurance)|identity (number|card|document)|government id|passport|personnummer|id number|bank|routing|sort code|account number|e-?wallet|card number|credit card|debit card|password|passcode/i;\n\n/** Components whose answer is yes or no, so they collect no number\n * whatever their label says: a yes/no about tax registration is fine. Every\n * other component takes a value, and its label decides. */\nconst BOOLEAN_COMPONENTS: ReadonlySet<string> = new Set([\"YesNo\", \"Checkbox\"]);\n\n/** Whether an atlas field asks for data an enrollment form must not\n * collect, by component or by label. */\nexport function collectsRestrictedData(\n field: Pick<AtlasEnrollmentField, \"component\" | \"label\">,\n): boolean {\n return (\n EXCLUDED_COMPONENTS.has(field.component) ||\n (!BOOLEAN_COMPONENTS.has(field.component) &&\n RESTRICTED_DATA_LABEL.test(field.label ?? \"\"))\n );\n}\n\n/** The choices of a MultipleChoice field: strings or `{ label }`. */\nfunction choicesOf(field: AtlasEnrollmentField): string[] {\n const options = (field as unknown as Record<string, unknown>)[\"options\"];\n if (!Array.isArray(options)) return [];\n return options.flatMap((choice) => {\n const label =\n typeof choice === \"string\"\n ? choice\n : (choice as { label?: unknown } | null)?.label;\n return typeof label === \"string\" && label.trim() ? [label.trim()] : [];\n });\n}\n\n/** Whether every member type gets the field. A Post Purchase form is chosen\n * by country only, so a field for some member types would reach everyone. */\nfunction forEveryone(field: AtlasEnrollmentField): boolean {\n const audiences = field.requiredFor ?? [];\n return (\n audiences.length === 0 || AUDIENCES.every((a) => audiences.includes(a))\n );\n}\n\n/** Why the command leaves an atlas field to a person, if it does. */\nexport type Exclusion = \"notCollected\" | \"someMemberTypes\" | \"needsFormBuilder\";\n\nexport interface MappedField {\n id: string;\n label: string;\n component: string;\n required: boolean;\n order: number;\n requiredFor: string[];\n elementType: string | null;\n puckType: string | null;\n sensitive: boolean;\n /** Set when the command doesn't create the field; it stays with a person. */\n excluded: Exclusion | null;\n options: string[];\n}\n\n/** Each atlas enrollment field, in atlas order, with the element it maps to\n * and whether the command may create it. */\nexport function mapAtlasFields(\n fields: readonly AtlasEnrollmentField[],\n): MappedField[] {\n return fields\n .map((field, index) => ({ field, index }))\n .filter(({ field }) => Boolean(field.label?.trim()))\n .sort((a, b) => {\n const oa = a.field.order ?? Number.POSITIVE_INFINITY;\n const ob = b.field.order ?? Number.POSITIVE_INFINITY;\n return oa === ob ? a.index - b.index : oa - ob;\n })\n .map(({ field, index }) => {\n const mapping = COMPONENT_MAPPINGS[field.component];\n const options =\n field.component === \"MultipleChoice\" ? choicesOf(field) : [];\n const excluded: Exclusion | null = collectsRestrictedData(field)\n ? \"notCollected\"\n : !forEveryone(field)\n ? \"someMemberTypes\"\n : !mapping ||\n (field.component === \"MultipleChoice\" && options.length === 0)\n ? \"needsFormBuilder\"\n : null;\n return {\n id: field.id ?? `field-${index}`,\n label: field.label.trim(),\n component: field.component,\n required: field.required === true,\n order: field.order ?? index,\n requiredFor: field.requiredFor ?? [],\n elementType: mapping?.element_type ?? null,\n puckType: mapping?.puck_type ?? null,\n sensitive: mapping?.sensitive === true,\n excluded,\n options,\n };\n });\n}\n\nexport const EXCLUSION_REASONS: Record<Exclusion, string> = {\n notCollected:\n \"Enrollment forms must not collect SSNs, national or tax ID numbers, bank or card details, or passwords. Collect it outside Fluid's forms, e.g. through your tax or payout provider.\",\n someMemberTypes:\n \"The atlas asks only some member types for it, and a Post Purchase form reaches everyone who enrolls in the country. Collect it where you can limit it to those member types.\",\n needsFormBuilder:\n \"The atlas doesn't give enough to build it (a multiple choice with no choices, or a component the form builder doesn't have). Add it in the form builder.\",\n};\n\n/** One component of a form, as `form_components` stores it. */\nexport type FormComponent = Record<string, unknown> & {\n id?: number | string;\n element_type?: string;\n puck_type?: string;\n label?: string;\n sensitive_info?: boolean | string;\n};\n\n/** The component for an atlas field, as the form builder would save it. */\nexport function componentFor(field: MappedField): FormComponent {\n const mapping = COMPONENT_MAPPINGS[field.component];\n if (!mapping || field.excluded) {\n throw new LocalizationError(\n `Atlas field ${field.id} (${field.component}) isn't one this command creates.`,\n );\n }\n return {\n element_type: mapping.element_type,\n puck_type: mapping.puck_type,\n label: field.label,\n placeholder: \"\",\n field_data_value: \"\",\n required: field.required,\n ...(mapping.text ? { case_sensitive: false } : {}),\n sensitive_info: field.sensitive,\n auto_translate: true,\n translations: [],\n show_label: true,\n is_form_field: true,\n ...(field.component === \"MultipleChoice\"\n ? {\n multiple_choice_options: field.options.map((label, index) => ({\n label,\n id: String(index),\n })),\n }\n : {}),\n ...(field.component === \"YesNo\"\n ? {\n yes_no_options: [\n { label: \"Yes\", value: \"yes\" },\n { label: \"No\", value: \"no\" },\n ],\n }\n : {}),\n };\n}\n\nconst normalizeLabel = (label: unknown) =>\n typeof label === \"string\"\n ? label.trim().replace(/\\s+/g, \" \").toLowerCase()\n : \"\";\n\n/** The `puck_type` an existing component compares as, older types included. */\nfunction storedType(component: FormComponent): string | null {\n const raw = (\n typeof component.puck_type === \"string\" && component.puck_type\n ? component.puck_type\n : (component.element_type ?? \"\")\n ).toLowerCase();\n return STORED_TYPE_ALIASES[raw] ?? null;\n}\n\n/** Whether a component already collects an atlas field: the same identity\n * field, or the same kind of field with the same label. */\nexport function componentCovers(\n component: FormComponent,\n field: MappedField,\n): boolean {\n if (!field.puckType) return false;\n const type = storedType(component);\n if (type !== field.puckType) return false;\n if (IDENTITY_PUCK_TYPES.has(type)) return true;\n return normalizeLabel(component.label) === normalizeLabel(field.label);\n}\n\nexport interface CompanyForm {\n id: number;\n title: string;\n status?: string | null;\n form_type?: string | null;\n form_components?: string | null;\n countries?: Array<{ id: number; iso?: string; name?: string }> | null;\n updated_at?: string;\n}\n\n/** A form's components, parsed. Ids are the stored elements' ids. */\nexport function componentsOf(form: CompanyForm): FormComponent[] {\n const raw = form.form_components;\n if (raw == null || raw === \"\") return [];\n let parsed: unknown;\n try {\n parsed = typeof raw === \"string\" ? JSON.parse(raw) : raw;\n } catch {\n throw new LocalizationError(\n `Form ${form.id} (\"${form.title}\") has components that aren't valid JSON, so it can't be read safely. Fix it in the form builder.`,\n );\n }\n return Array.isArray(parsed)\n ? (parsed.filter((c) => c && typeof c === \"object\") as FormComponent[])\n : [];\n}\n\n/** GET /api/forms, every page. */\nexport async function listForms(\n client: LocalizationClient,\n): Promise<CompanyForm[]> {\n const forms: CompanyForm[] = [];\n for (let page = 1; page <= MAX_PAGES; page += 1) {\n const body = await client.get<{\n data?: CompanyForm[];\n pagination?: { next_page?: number | null; total_pages?: number };\n }>(FORMS_PATH, { page, per_page: PER_PAGE });\n const batch = body.data ?? [];\n forms.push(...batch);\n const next = body.pagination?.next_page;\n const total = body.pagination?.total_pages ?? 1;\n if (batch.length === 0 || (next == null && page >= total)) return forms;\n }\n throw new LocalizationError(\n `The company has more than ${MAX_PAGES * PER_PAGE} forms, more than one run reads.`,\n );\n}\n\n/** GET /api/forms/:id, read again just before it's changed. */\nexport async function getForm(\n client: LocalizationClient,\n id: number,\n): Promise<CompanyForm> {\n const body = await client.get<CompanyForm | { form?: CompanyForm }>(\n `${FORMS_PATH}/${id}`,\n );\n const form = \"form\" in body && body.form ? body.form : (body as CompanyForm);\n if (typeof form?.id !== \"number\")\n throw new LocalizationError(`Form ${id} couldn't be read.`);\n return form;\n}\n\nconst isPostPurchase = (form: CompanyForm) =>\n form.form_type === \"post_purchase\";\nconst isActive = (form: CompanyForm) => form.status === \"active\";\nconst countryIdsOf = (form: CompanyForm) =>\n (form.countries ?? []).map((c) => c.id);\n\nexport interface FormSummary {\n id: number;\n title: string;\n status: string | null;\n countries: string[];\n /** Also scoped to other countries, so this command doesn't change it. */\n shared: boolean;\n /** The atlas fields it collects, by atlas id. */\n atlasFields: string[];\n components: number;\n}\n\nexport interface FormsPlan {\n action: \"create\" | \"update\" | \"none\";\n formId: number | null;\n title: string;\n status: \"draft\" | \"active\";\n add: Array<{\n id: string;\n label: string;\n elementType: string;\n puckType: string;\n required: boolean;\n sensitive: boolean;\n }>;\n /** Sensitive fields left out by --skip-sensitive. */\n skippedSensitive: string[];\n activate: boolean;\n}\n\nexport interface FormsReport {\n country: string;\n countryId: number;\n countryName: string;\n /** Every atlas field the command can create is on an active form. */\n done: boolean;\n forms: FormSummary[];\n /** The country's own atlas enrollment form, which --create writes to. */\n target: { id: number; title: string; status: string | null } | null;\n fields: Array<\n Omit<MappedField, \"options\" | \"excluded\"> & {\n state: \"collected\" | \"inDraft\" | \"missing\" | \"byHand\";\n collectedBy: number[];\n reason?: string;\n }\n >;\n missing: string[];\n plan: FormsPlan;\n note?: string;\n /** Set when the country's form is a draft that holds atlas fields. */\n draft?: string;\n next?: string;\n}\n\nexport interface FormsOptions {\n /** A form to add to instead of the atlas-titled one. */\n formId?: number;\n activate?: boolean;\n skipSensitive?: boolean;\n}\n\n/**\n * The form --create writes to: the form named with --form, else the\n * country's atlas form (titled `Enrollment - <Country>`, Post Purchase,\n * scoped to this country alone), active before draft. A form shared with\n * other countries is never a target: a field one country's atlas asks for\n * doesn't belong on another country's enrollment.\n */\nexport function targetForm(\n forms: readonly CompanyForm[],\n country: { id: number; name: string },\n formId?: number,\n): CompanyForm | null {\n const own = (form: CompanyForm) => {\n const ids = countryIdsOf(form);\n return ids.length === 1 && ids[0] === country.id;\n };\n if (formId !== undefined) {\n const form = forms.find((f) => f.id === formId);\n if (!form)\n throw new LocalizationError(`The company has no form ${formId}.`);\n if (!isPostPurchase(form))\n throw new LocalizationError(\n `Form ${formId} (\"${form.title}\") is a ${form.form_type ?? \"untyped\"} form, not Post Purchase, so enrollment checkout doesn't show it.`,\n );\n if (!own(form))\n throw new LocalizationError(\n `Form ${formId} (\"${form.title}\") isn't scoped to ${country.name} alone, so this command won't add country fields to it. Pick a form for ${country.name} only, or leave out --form to use or create \"${enrollmentFormTitle(country.name)}\".`,\n );\n return form;\n }\n const title = enrollmentFormTitle(country.name);\n const candidates = forms.filter(\n (f) => isPostPurchase(f) && own(f) && f.title === title,\n );\n return (\n candidates.find(isActive) ??\n [...candidates].sort((a, b) =>\n (b.updated_at ?? \"\").localeCompare(a.updated_at ?? \"\"),\n )[0] ??\n null\n );\n}\n\n/** What the forms say about a country's atlas enrollment fields, and what\n * --create would send. */\nexport function formsReport(input: {\n countryCode: string;\n country: { id: number; name: string };\n atlas: CountryAtlas;\n forms: readonly CompanyForm[];\n options?: FormsOptions;\n}): FormsReport {\n const { countryCode, country, atlas, options = {} } = input;\n const fields = mapAtlasFields(atlas.enrollmentFormFields ?? []);\n const forCountry = input.forms.filter(\n (f) => isPostPurchase(f) && countryIdsOf(f).includes(country.id),\n );\n const target = targetForm(input.forms, country, options.formId);\n const parsed = new Map(forCountry.map((f) => [f.id, componentsOf(f)]));\n if (target && !parsed.has(target.id))\n parsed.set(target.id, componentsOf(target));\n const covers = (formId: number, field: MappedField) =>\n (parsed.get(formId) ?? []).some((c) => componentCovers(c, field));\n\n const active = forCountry.filter(isActive);\n const rows = fields.map((field) => {\n const collectedBy = active\n .filter((f) => covers(f.id, field))\n .map((f) => f.id);\n const { options: _options, excluded, ...rest } = field;\n const state = excluded\n ? (\"byHand\" as const)\n : collectedBy.length > 0\n ? (\"collected\" as const)\n : target && covers(target.id, field)\n ? (\"inDraft\" as const)\n : (\"missing\" as const);\n return {\n ...rest,\n state,\n collectedBy,\n ...(excluded ? { reason: EXCLUSION_REASONS[excluded] } : {}),\n };\n });\n\n const missing = rows.filter((r) => r.state === \"missing\");\n const toAdd = fields.filter((f) =>\n missing.some(\n (m) => m.id === f.id && !(options.skipSensitive && f.sensitive),\n ),\n );\n const skippedSensitive = options.skipSensitive\n ? missing.filter((m) => m.sensitive).map((m) => m.id)\n : [];\n const title = target?.title ?? enrollmentFormTitle(country.name);\n const wantsActivate =\n options.activate === true && (target ? !isActive(target) : true);\n const action: FormsPlan[\"action\"] =\n toAdd.length > 0\n ? target\n ? \"update\"\n : \"create\"\n : target && wantsActivate\n ? \"update\"\n : \"none\";\n const activate = action !== \"none\" && wantsActivate;\n const plan: FormsPlan = {\n action,\n formId: target?.id ?? null,\n title,\n status:\n (target ? isActive(target) : false) || options.activate\n ? \"active\"\n : \"draft\",\n add: toAdd.map((f) => ({\n id: f.id,\n label: f.label,\n elementType: f.elementType!,\n puckType: f.puckType!,\n required: f.required,\n sensitive: f.sensitive,\n })),\n skippedSensitive,\n activate,\n };\n\n const report: FormsReport = {\n country: countryCode,\n countryId: country.id,\n countryName: country.name,\n done: missing.length === 0 && rows.every((r) => r.state !== \"inDraft\"),\n forms: forCountry.map((f) => ({\n id: f.id,\n title: f.title,\n status: f.status ?? null,\n countries: (f.countries ?? []).map((c) => c.iso ?? String(c.id)),\n shared: countryIdsOf(f).length > 1,\n atlasFields: fields\n .filter((field) => covers(f.id, field))\n .map((x) => x.id),\n components: parsed.get(f.id)?.length ?? 0,\n })),\n target: target\n ? { id: target.id, title: target.title, status: target.status ?? null }\n : null,\n fields: rows,\n missing: missing.map((m) => m.id),\n plan,\n };\n if (fields.length === 0) {\n report.note = `The atlas lists no enrollment fields for ${countryCode}, so there is nothing to add.`;\n return report;\n }\n if (report.forms.some((f) => f.shared)) {\n report.note = `Forms shared with other countries are read, not changed. A field they lack goes on ${countryCode}'s own form, which enrollment shows beside them.`;\n }\n const draftOnly = rows.filter((r) => r.state === \"inDraft\");\n if (draftOnly.length > 0 && target && !isActive(target)) {\n report.draft = `\"${target.title}\" (${target.id}) is a draft, so enrollment doesn't ask for ${draftOnly.map((r) => r.id).join(\", \")} yet. Review it in the form builder and publish it, or rerun with --create --activate --yes.`;\n }\n if (action !== \"none\") {\n report.next = `fluid countries forms ${countryCode} --create${options.formId !== undefined ? ` --form ${options.formId}` : \"\"}${options.activate ? \" --activate\" : \"\"}${options.skipSensitive ? \" --skip-sensitive\" : \"\"} --yes`;\n }\n return report;\n}\n\n/** The message the forms API answers when the caller can't change which\n * fields are sensitive. */\nconst SENSITIVE_DENIED =\n /permission to change which fields are marked sensitive/i;\n\n/** Turn the forms API's sensitive-permission 422 into an actionable error. */\nfunction explainSensitiveDenial(error: unknown, fields: string[]): never {\n if (\n statusOf(error) === 422 &&\n SENSITIVE_DENIED.test(errorDetail(error).message)\n ) {\n throw Object.assign(\n new LocalizationError(\n `Your token can't create fields marked sensitive (${fields.join(\", \")}): that needs the forms manage-sensitive permission. Nothing was saved. Run it with a token whose role has that permission, or rerun with --skip-sensitive to create the rest and leave those fields to someone who has it.`,\n ),\n { status: 422 },\n );\n }\n throw error;\n}\n\nexport interface FormsResult {\n action: \"created\" | \"updated\" | \"none\";\n form: { id: number; title: string; status: string | null } | null;\n added: string[];\n activated: boolean;\n skippedSensitive: string[];\n}\n\n/**\n * Apply a plan. A new form is created with the fields. An existing form is\n * read again first and every one of its components is sent back as it is,\n * ids included, with the new ones after them, so nothing on it is\n * deactivated and no field's sensitivity changes; fields an admin added since\n * the preview aren't added twice.\n */\nexport async function applyFormsPlan(\n client: LocalizationClient,\n report: FormsReport,\n fields: readonly MappedField[],\n): Promise<FormsResult> {\n const { plan } = report;\n const byId = new Map(fields.map((f) => [f.id, f]));\n const toAdd = plan.add.map((a) => byId.get(a.id)!);\n const sensitive = toAdd.filter((f) => f.sensitive).map((f) => f.id);\n const result = (\n action: FormsResult[\"action\"],\n form: CompanyForm | null,\n added: string[],\n activated: boolean,\n ): FormsResult => ({\n action,\n form: form\n ? { id: form.id, title: form.title, status: form.status ?? null }\n : null,\n added,\n activated,\n skippedSensitive: plan.skippedSensitive,\n });\n\n if (plan.action === \"none\") return result(\"none\", null, [], false);\n\n if (plan.action === \"create\") {\n let body: { form?: CompanyForm };\n try {\n body = await client.post<{ form?: CompanyForm }>(FORMS_PATH, {\n form: {\n title: plan.title,\n form_type: \"post_purchase\",\n status: plan.status,\n form_redirect: \"\",\n country_ids: [report.countryId],\n form_components: JSON.stringify(toAdd.map(componentFor)),\n },\n });\n } catch (error) {\n explainSensitiveDenial(error, sensitive);\n }\n const created = body.form ?? null;\n return result(\n \"created\",\n created,\n toAdd.map((f) => f.id),\n plan.status === \"active\",\n );\n }\n\n const current = await getForm(client, plan.formId!);\n const existing = componentsOf(current);\n const stillMissing = toAdd.filter(\n (f) => !existing.some((c) => componentCovers(c, f)),\n );\n if (stillMissing.length === 0 && !plan.activate) {\n return result(\"none\", current, [], false);\n }\n const unidentified = existing.filter((c) => c.id == null || c.id === \"\");\n if (stillMissing.length > 0 && unidentified.length > 0) {\n throw new LocalizationError(\n `Form ${current.id} (\"${current.title}\") has ${unidentified.length} component(s) without an element id, so sending its components back could deactivate them. Open it in the form builder and save it once, then rerun.`,\n );\n }\n let body: { form?: CompanyForm };\n try {\n body = await client.put<{ form?: CompanyForm }>(\n `${FORMS_PATH}/${current.id}`,\n {\n form: {\n ...(stillMissing.length > 0\n ? {\n form_components: JSON.stringify([\n ...existing,\n ...stillMissing.map(componentFor),\n ]),\n }\n : {}),\n ...(plan.activate ? { status: \"active\" } : {}),\n },\n },\n );\n } catch (error) {\n explainSensitiveDenial(\n error,\n stillMissing.filter((f) => f.sensitive).map((f) => f.id),\n );\n }\n return result(\n \"updated\",\n body.form ?? current,\n stillMissing.map((f) => f.id),\n plan.activate,\n );\n}\n","// Bulk price conversion for a newly opened country: every variant's price in\n// a source country (US by default), multiplied by an exchange rate and\n// rounded to the target currency, saved as the variant's price in the new\n// country. This is the `convert-pricing` step of Mist's open-country\n// workflow, made safe to run unattended:\n//\n// - nothing is written without --write (and --yes, or a prompt);\n// - a variant already priced in the country is skipped unless --overwrite, so\n// a second run never converts a converted price again;\n// - each variant is its own write, and each failure is reported with the\n// API's message instead of stopping the run;\n// - companies whose prices the price editor owns are detected up front.\n// Writes still go through the variants API, which saves them into the\n// price editor's catalog and refuses (422) any it can't place; those are\n// reported as `refusedByPriceEditor`. --overwrite is refused for them, so\n// a price someone set in the editor is never replaced from here.\n\nimport { statusOf, type LocalizationClient } from \"../shared/client.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport type { CompanyCountry } from \"./api.js\";\n\n/** The money fields a conversion can write, as the variants API names them. */\nexport const PRICE_FIELDS = [\n \"price\",\n \"subscription_price\",\n \"compare_price\",\n \"wholesale\",\n \"wholesale_subscription_price\",\n] as const;\nexport type PriceField = (typeof PRICE_FIELDS)[number];\n\n/**\n * What shoppers pay. Wholesale prices are left out by default: they drive\n * member commissions, and an admin token's product reads can show retail in\n * their place, so converting them is opt-in with --fields.\n */\nexport const DEFAULT_PRICE_FIELDS: readonly PriceField[] = [\n \"price\",\n \"subscription_price\",\n \"compare_price\",\n];\n\n/** Currencies Fluid stores with no minor unit (the API rounds the same way). */\nconst ZERO_DECIMAL_CURRENCIES = new Set([\n \"BIF\",\n \"CLP\",\n \"DJF\",\n \"GNF\",\n \"HUF\",\n \"ISK\",\n \"JPY\",\n \"KMF\",\n \"KRW\",\n \"MGA\",\n \"PYG\",\n \"RWF\",\n \"TWD\",\n \"UGX\",\n \"VND\",\n \"VUV\",\n \"XAF\",\n \"XOF\",\n \"XPF\",\n]);\n\nexport function currencyDecimals(currency: string): number {\n return ZERO_DECIMAL_CURRENCIES.has(currency.toUpperCase()) ? 0 : 2;\n}\n\n/** `amount × rate`, rounded half up to the currency's minor unit. */\nexport function convertAmount(\n amount: number,\n rate: number,\n currency: string,\n): number {\n const factor = 10 ** currencyDecimals(currency);\n const scaled = amount * rate * factor;\n // A tiny nudge so 2.675 (stored as 2.67499…) rounds up, as it does on the server.\n return Math.round(scaled + Math.sign(scaled) * 1e-7) / factor;\n}\n\nexport function parseRate(raw: string): number {\n const rate = Number(raw);\n if (!Number.isFinite(rate) || rate <= 0) {\n throw new LocalizationError(\n `--rate takes a positive number, such as 0.92, not \"${raw}\".`,\n );\n }\n return rate;\n}\n\nexport function parsePriceFields(raw: string | undefined): PriceField[] {\n if (!raw) return [...DEFAULT_PRICE_FIELDS];\n const fields = raw\n .split(\",\")\n .map((f) => f.trim())\n .filter(Boolean);\n const unknown = fields.filter(\n (f) => !(PRICE_FIELDS as readonly string[]).includes(f),\n );\n if (unknown.length > 0 || fields.length === 0) {\n throw new LocalizationError(\n `--fields takes a comma-separated list of ${PRICE_FIELDS.join(\", \")}${unknown.length > 0 ? `; not ${unknown.join(\", \")}` : \"\"}.`,\n );\n }\n if (!fields.includes(\"price\")) {\n throw new LocalizationError(\n \"--fields must include price: a variant can't be sold in a country without one.\",\n );\n }\n return [...new Set(fields)] as PriceField[];\n}\n\n/** One variant's prices in one country, as product reads give them. */\nexport interface VariantCountryWire {\n id?: number | null;\n active?: boolean | null;\n country_id?: number | null;\n currency_code?: string | null;\n price?: string | number | null;\n subscription_price?: string | number | null;\n compare_price?: string | number | null;\n wholesale?: string | number | null;\n wholesale_subscription_price?: string | number | null;\n}\n\ninterface VariantWire {\n id: number;\n sku?: string | null;\n title?: string | null;\n /** Keyed by country ISO code. */\n variant_countries?: Record<string, VariantCountryWire> | null;\n}\n\ninterface ProductWire {\n id: number;\n title?: string | null;\n /** A bundle can be priced from its groups, leaving its own price at zero. */\n bundle?: boolean | null;\n status?: string | null;\n variants?: VariantWire[] | null;\n}\n\nfunction amount(value: unknown): number | null {\n if (value === null || value === undefined || value === \"\") return null;\n const n = typeof value === \"number\" ? value : Number(value);\n return Number.isFinite(n) ? n : null;\n}\n\nconst PRODUCTS_PATH = \"/api/company/v1/products\";\n/** The products list renders every variant, so the API caps a page at 50. */\nconst PER_PAGE = 50;\nconst MAX_PAGES = 2000;\n\n/** Every product with its variants, following the list's pages. */\nexport async function listProductsWithVariants(\n client: LocalizationClient,\n statuses: readonly string[],\n): Promise<ProductWire[]> {\n const products: ProductWire[] = [];\n for (let page = 1; page <= MAX_PAGES; page += 1) {\n const data = await client.get<{\n products?: ProductWire[];\n meta?: { pagination?: { total_pages?: number } };\n }>(PRODUCTS_PATH, { page, per_page: PER_PAGE, status: [...statuses] });\n const batch = data.products ?? [];\n for (const product of batch) {\n // The list renders variants; read the product on its own if it didn't.\n if (!Array.isArray(product.variants)) {\n const full = await client.get<{ product?: ProductWire }>(\n `${PRODUCTS_PATH}/${product.id}`,\n );\n products.push(full.product ?? product);\n } else {\n products.push(product);\n }\n }\n const totalPages = data.meta?.pagination?.total_pages ?? 1;\n if (page >= totalPages || batch.length === 0) return products;\n }\n throw new LocalizationError(\n `The catalog has more than ${MAX_PAGES * PER_PAGE} products, more than one run can read. Narrow it with --status.`,\n );\n}\n\n/**\n * Whether the price editor owns this company's prices (member-pricing\n * catalog writes), or null when the token can't read the company profile.\n */\nexport async function priceEditorOwnsPrices(\n client: LocalizationClient,\n): Promise<boolean | null> {\n try {\n const data = await client.get<{\n data?: { company?: { member_pricing?: { catalog_writes?: unknown } } };\n }>(\"/api/company/v1/companies/me\");\n return data.data?.company?.member_pricing?.catalog_writes === true;\n } catch (error) {\n if (statusOf(error) === 401 || statusOf(error) === 403) return null;\n throw error;\n }\n}\n\nexport type SkipReason =\n | \"rounds_to_zero\"\n | \"already_priced\"\n | \"priced_in_other_currency\"\n | \"no_source_price\";\n\nexport interface VariantPlan {\n productId: number;\n product: string | null;\n variantId: number;\n sku: string | null;\n /** convert: will be (or was) written; skip: left as it is. */\n action: \"convert\" | \"skip\";\n reason?: SkipReason;\n /** The existing row in the target country, sent back by id so it's updated in place. */\n rowId: number | null;\n active: boolean;\n before: Partial<Record<PriceField, number | null>>;\n after: Partial<Record<PriceField, number>>;\n}\n\nexport interface PlanOptions {\n sourceIso: string;\n targetIso: string;\n targetCountryId: number;\n targetCurrency: string;\n rate: number;\n fields: readonly PriceField[];\n overwrite: boolean;\n activate: boolean;\n}\n\n/**\n * Decide, per variant, what a conversion writes. A variant needs a positive\n * source price. One whose target row already has a positive price is skipped\n * unless `overwrite`, which is what makes a rerun safe: the first run's\n * prices count as \"already priced\". A positive price saved in another\n * currency (the country's currency changed since) is skipped too, and\n * reported apart so it can be reviewed. Opening a country creates a zero-priced,\n * inactive row for every variant, so a zero price counts as unpriced. Only\n * the fields with a positive source amount are sent; the rest of the row is\n * left as it is.\n */\nexport function planConversion(\n products: readonly ProductWire[],\n options: PlanOptions,\n): VariantPlan[] {\n const plans: VariantPlan[] = [];\n for (const product of products) {\n for (const variant of product.variants ?? []) {\n const rows = variant.variant_countries ?? {};\n const source = rowFor(rows, options.sourceIso);\n const target = rowFor(rows, options.targetIso);\n const before = Object.fromEntries(\n options.fields.map((f) => [f, amount(target?.[f])]),\n ) as Partial<Record<PriceField, number | null>>;\n const base = {\n productId: product.id,\n product: product.title ?? null,\n variantId: variant.id,\n sku: variant.sku ?? null,\n rowId: typeof target?.id === \"number\" ? target.id : null,\n active: options.activate || target?.active === true,\n before,\n };\n const sourcePrice = amount(source?.price);\n if (sourcePrice === null || sourcePrice <= 0) {\n plans.push({\n ...base,\n action: \"skip\",\n reason: \"no_source_price\",\n after: {},\n });\n continue;\n }\n if (!options.overwrite && (amount(target?.price) ?? 0) > 0) {\n const currency = target?.currency_code;\n const sameCurrency =\n !currency ||\n currency.toUpperCase() === options.targetCurrency.toUpperCase();\n plans.push({\n ...base,\n action: \"skip\",\n reason: sameCurrency ? \"already_priced\" : \"priced_in_other_currency\",\n after: {},\n });\n continue;\n }\n const after: Partial<Record<PriceField, number>> = {};\n for (const field of options.fields) {\n const value = amount(source?.[field]);\n if (value === null || value <= 0) continue;\n const converted = convertAmount(\n value,\n options.rate,\n options.targetCurrency,\n );\n // A positive price never becomes free: one that rounds to zero is\n // left out, and a variant whose price does is skipped entirely.\n if (converted > 0) after[field] = converted;\n }\n if (after.price === undefined) {\n plans.push({\n ...base,\n action: \"skip\",\n reason: \"rounds_to_zero\",\n after: {},\n });\n continue;\n }\n plans.push({ ...base, action: \"convert\", after });\n }\n }\n return plans;\n}\n\nfunction rowFor(\n rows: Record<string, VariantCountryWire>,\n iso: string,\n): VariantCountryWire | undefined {\n const key = Object.keys(rows).find(\n (k) => k.toUpperCase() === iso.toUpperCase(),\n );\n return key ? rows[key] : undefined;\n}\n\n/** The variants API body that saves one plan's prices in the target country. */\nexport function variantBody(plan: VariantPlan, targetCountryId: number) {\n return {\n variant: {\n variant_countries_attributes: [\n {\n ...(plan.rowId !== null ? { id: plan.rowId } : {}),\n country_id: targetCountryId,\n active: plan.active,\n ...plan.after,\n },\n ],\n },\n };\n}\n\nexport interface WriteFailure {\n variantId: number;\n productId: number;\n sku: string | null;\n error: string;\n status?: number;\n /** The price editor's catalog wouldn't take the price. */\n refusedByPriceEditor: boolean;\n details?: unknown;\n}\n\n/** Messages the price editor's catalog writer refuses a save with. */\nconst PRICE_EDITOR_REFUSAL =\n /could not be written to the catalog|another update to these prices is in progress/i;\n\n/** The variants a fresh read no longer converts, and why. */\nexport interface ChangedSincePreview {\n variantId: number;\n productId: number;\n reason: SkipReason | \"variant_gone\";\n}\n\n/**\n * Save the convertible plans, one variant at a time; a failure never stops\n * the run. Each product is read again just before its variants are written\n * and planned afresh, so a price someone set in the country after the\n * preview is skipped instead of overwritten (`changedSincePreview`).\n */\nexport async function writeConversion(\n client: LocalizationClient,\n plans: readonly VariantPlan[],\n options: PlanOptions,\n): Promise<{\n written: number[];\n failed: WriteFailure[];\n changedSincePreview: ChangedSincePreview[];\n}> {\n const written: number[] = [];\n const failed: WriteFailure[] = [];\n const changedSincePreview: ChangedSincePreview[] = [];\n const byProduct = new Map<number, VariantPlan[]>();\n for (const plan of plans) {\n if (plan.action !== \"convert\") continue;\n byProduct.set(plan.productId, [\n ...(byProduct.get(plan.productId) ?? []),\n plan,\n ]);\n }\n for (const [productId, previewed] of byProduct) {\n let fresh: VariantPlan[];\n try {\n const data = await client.get<{ product?: ProductWire }>(\n `${PRODUCTS_PATH}/${productId}`,\n );\n fresh = data.product ? planConversion([data.product], options) : [];\n } catch (error) {\n for (const plan of previewed) failed.push(failureFor(plan, error));\n continue;\n }\n for (const plan of previewed) {\n const now = fresh.find((p) => p.variantId === plan.variantId);\n if (!now || now.action !== \"convert\") {\n changedSincePreview.push({\n variantId: plan.variantId,\n productId,\n reason: now?.reason ?? \"variant_gone\",\n });\n continue;\n }\n try {\n await client.put(\n `/api/company/v1/variants/${now.variantId}`,\n variantBody(now, options.targetCountryId),\n );\n written.push(now.variantId);\n } catch (error) {\n failed.push(failureFor(now, error));\n }\n }\n }\n return { written, failed, changedSincePreview };\n}\n\nfunction failureFor(plan: VariantPlan, error: unknown): WriteFailure {\n const body = (error as { body?: Record<string, unknown> | null }).body;\n const details = body?.[\"errors\"];\n const { error: message, status } = failureOf(error);\n const text = `${message} ${details ? JSON.stringify(details) : \"\"}`;\n return {\n variantId: plan.variantId,\n productId: plan.productId,\n sku: plan.sku,\n error: message,\n ...(status !== undefined ? { status } : {}),\n refusedByPriceEditor: status === 422 && PRICE_EDITOR_REFUSAL.test(text),\n ...(details ? { details } : {}),\n };\n}\n\n/** Counts and a before→after sample, the summary a dry run prints. */\nexport function summarizePlans(\n plans: readonly VariantPlan[],\n sampleSize: number,\n) {\n const convert = plans.filter((p) => p.action === \"convert\");\n return {\n variants: plans.length,\n toConvert: convert.length,\n skipped: {\n alreadyPriced: plans.filter((p) => p.reason === \"already_priced\").length,\n pricedInOtherCurrency: plans.filter(\n (p) => p.reason === \"priced_in_other_currency\",\n ).length,\n noSourcePrice: plans.filter((p) => p.reason === \"no_source_price\").length,\n roundsToZero: plans.filter((p) => p.reason === \"rounds_to_zero\").length,\n },\n /** Converted variants that will be (or stay) on sale in the country. */\n activeAfterWrite: convert.filter((p) => p.active).length,\n sample: convert.slice(0, sampleSize).map((p) => ({\n productId: p.productId,\n product: p.product,\n variantId: p.variantId,\n sku: p.sku,\n before: p.before,\n after: p.after,\n })),\n };\n}\n\n/** A company country by ISO, from a list already read. */\nexport function companyCountryIn(\n countries: readonly CompanyCountry[],\n iso: string,\n): CompanyCountry | undefined {\n return countries.find(\n (cc) => cc.country?.iso?.toUpperCase() === iso.toUpperCase(),\n );\n}\n\nexport interface ReferenceRate {\n rate: number;\n date: string;\n source: string;\n}\n\n/**\n * A reference rate to start from: the European Central Bank's daily rate,\n * through the Frankfurter API. Fluid has no exchange-rate API. A write never\n * uses this on its own; it needs the rate passed with --rate, so the rate\n * saved is one a person or agent reviewed.\n */\nexport async function referenceRate(\n from: string,\n to: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<ReferenceRate | null> {\n const url = `https://api.frankfurter.app/latest?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`;\n try {\n const response = await fetchImpl(url);\n if (!response.ok) return null;\n const body = (await response.json()) as {\n date?: string;\n rates?: Record<string, number>;\n };\n const rate = body.rates?.[to.toUpperCase()];\n if (typeof rate !== \"number\" || !(rate > 0)) return null;\n return {\n rate,\n date: body.date ?? \"\",\n source: \"European Central Bank reference rate (frankfurter.app)\",\n };\n } catch {\n return null;\n }\n}\n\nexport interface PriceCoverage {\n /** Variants of the products read, bundles left out. */\n variants: number;\n /** Of those, the ones with a positive price in the country. */\n priced: number;\n /** Priced ones on sale in the country. */\n onSale: number;\n /**\n * Active bundles, listed for a person to check. Whether a bundle sells in a\n * country depends on checkout's group pricing and per-group availability,\n * which no read exposes at the retail tier, so they're neither passed nor\n * failed here.\n */\n bundles: Array<{ id: number; title: string | null }>;\n}\n\n/** How many variants have a price in the country and how many are on sale there; bundles listed apart. */\nexport function priceCoverage(\n products: readonly ProductWire[],\n iso: string,\n): PriceCoverage {\n const coverage: PriceCoverage = {\n variants: 0,\n priced: 0,\n onSale: 0,\n bundles: [],\n };\n for (const product of products) {\n if (product.bundle === true) {\n coverage.bundles.push({ id: product.id, title: product.title ?? null });\n continue;\n }\n for (const variant of product.variants ?? []) {\n coverage.variants += 1;\n const row = rowFor(variant.variant_countries ?? {}, iso);\n if ((amount(row?.price) ?? 0) <= 0) continue;\n coverage.priced += 1;\n if (row?.active === true) coverage.onSale += 1;\n }\n }\n return coverage;\n}\n","// What a country still needs, worked out from the atlas and the company's\n// current setup. Pure functions, so `plan`, the write commands, and `status`\n// all agree on the same answer.\n\nimport { summarizeAgreement, type CountryAtlas } from \"./atlas.js\";\nimport { languageIn, type Language } from \"../shared/languages.js\";\nimport type { CompanyAgreement } from \"../shared/agreements.js\";\n\nexport interface LanguageState {\n iso: string;\n name: string | null;\n /** The language's id in Fluid's catalog, null when the catalog lacks it. */\n languageId: number | null;\n enabled: boolean;\n}\n\n/** Each of the market's major languages and whether the company has it on.\n * A language the catalog lacks can't be enabled and is reported as such. */\nexport function languageStates(\n atlas: CountryAtlas,\n languages: readonly Language[],\n): LanguageState[] {\n return (atlas.majorLanguages ?? []).map((iso) => {\n const match = languageIn(languages, iso);\n return {\n iso,\n name: match?.name ?? null,\n languageId: match?.id ?? null,\n enabled: match?.active === true,\n };\n });\n}\n\nexport type AgreementStatus = \"linked\" | \"inactive\" | \"exists\" | \"missing\";\n\nexport interface AgreementState {\n localId: string;\n title: string;\n languages: string[];\n requiredOnCheckout: boolean;\n showAtCheckout: boolean;\n /** linked: an active company agreement with this title is on the country.\n * inactive: one is on the country but switched off, so checkout skips it.\n * exists: one exists but isn't linked to the country.\n * missing: the company has no agreement with this title. */\n status: AgreementStatus;\n agreementId: number | null;\n /** Atlas languages the agreement has no translation in; set by `status`. */\n missingTranslations?: string[];\n}\n\n/** Match atlas agreements to company agreements by title, case-insensitively,\n * so a setup never creates a second agreement with the same title. */\nexport function agreementStates(\n atlas: CountryAtlas,\n agreements: readonly CompanyAgreement[],\n companyCountryId: number | null,\n): AgreementState[] {\n return (atlas.agreements ?? []).map((agreement) => {\n const summary = summarizeAgreement(agreement);\n const titles = new Set(\n agreement.translations.map((t) => t.title.trim().toLowerCase()),\n );\n const matches = agreements.filter((a) =>\n titles.has(a.title?.trim().toLowerCase()),\n );\n const onCountry =\n companyCountryId === null\n ? []\n : matches.filter((a) =>\n (a.company_country_ids ?? []).includes(companyCountryId),\n );\n const linked = onCountry.find((a) => a.active !== false);\n const inactive = linked ? undefined : onCountry[0];\n const match = linked ?? inactive ?? matches[0];\n return {\n localId: summary.localId,\n title: summary.title,\n languages: summary.languages,\n requiredOnCheckout: summary.requiredOnCheckout,\n showAtCheckout: summary.showAtCheckout,\n status: linked\n ? \"linked\"\n : inactive\n ? \"inactive\"\n : match\n ? \"exists\"\n : \"missing\",\n agreementId: match?.id ?? null,\n };\n });\n}\n","// Turn an atlas into the `company_country` body that\n// POST /api/settings/company_countries takes. Seeding happens here, on the\n// client, the way the admin form does it: the atlas is read, its values are\n// offered as the company's own, and the existing settings API saves them.\n// The backend does not seed.\n\nimport { sortedPaymentMethods, type CountryAtlas, type Mode } from \"./atlas.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\n/** Address fields checkout both renders and stores; only these may be\n * required (CompanyCountry::ContractSchema.storable_address_field_names). */\nexport const STORABLE_ADDRESS_FIELDS = [\n \"name\",\n \"address1\",\n \"address2\",\n \"city\",\n \"state\",\n \"postal_code\",\n \"country_code\",\n \"phone\",\n] as const;\n\nconst COLUMN_WIDTHS = [\"full\", \"half\", \"third\"];\n\nexport interface AddressFieldEntry {\n field: string;\n visible: boolean;\n required: boolean;\n local_label?: string;\n column_width?: string;\n placeholder?: string;\n}\n\n/**\n * The atlas address layout in the contract shape the API validates. The\n * atlas marks first and last name required, but checkout cannot collect a\n * required field it does not store, so those keep their layout and become\n * optional. A required field is always visible. Repeats are dropped.\n */\nexport function addressFieldsFromAtlas(\n atlas: CountryAtlas,\n): AddressFieldEntry[] {\n const seen = new Set<string>();\n const entries: AddressFieldEntry[] = [];\n for (const source of atlas.addressFields ?? []) {\n if (\n typeof source.field !== \"string\" ||\n source.field === \"\" ||\n seen.has(source.field)\n ) {\n continue;\n }\n seen.add(source.field);\n const required =\n source.required === true &&\n (STORABLE_ADDRESS_FIELDS as readonly string[]).includes(source.field);\n const entry: AddressFieldEntry = {\n field: source.field,\n visible: required || source.visible !== false,\n required,\n };\n if (typeof source.localLabel === \"string\")\n entry.local_label = source.localLabel;\n if (\n typeof source.columnWidth === \"string\" &&\n COLUMN_WIDTHS.includes(source.columnWidth)\n ) {\n entry.column_width = source.columnWidth;\n }\n if (typeof source.placeholder === \"string\")\n entry.placeholder = source.placeholder;\n entries.push(entry);\n }\n return entries;\n}\n\n/** The atlas legal settings under the API's snake_case keys. Values of the\n * wrong kind are left out rather than sent to be refused. */\nexport function legalSettingsFromAtlas(\n atlas: CountryAtlas,\n): Record<string, unknown> {\n const legal = atlas.legalSettings ?? {};\n const out: Record<string, unknown> = {};\n const count = (v: unknown) =>\n typeof v === \"number\" && Number.isInteger(v) && v >= 0;\n if (count(legal.coolingOffPeriodDays))\n out[\"cooling_off_period_days\"] = legal.coolingOffPeriodDays;\n if (count(legal.warrantyPeriodMonths))\n out[\"warranty_period_months\"] = legal.warrantyPeriodMonths;\n if (typeof legal.cookieConsentRequired === \"boolean\") {\n out[\"cookie_consent_required\"] = legal.cookieConsentRequired;\n }\n if (typeof legal.recurringSubscriptionDisclosureRequired === \"boolean\") {\n out[\"recurring_subscription_disclosure_required\"] =\n legal.recurringSubscriptionDisclosureRequired;\n }\n return out;\n}\n\n/** The atlas's recommended payment methods (enabled ones), in its order, as\n * the integration type names checkout ranks by. */\nexport function paymentPriorityFromAtlas(atlas: CountryAtlas): string[] {\n return sortedPaymentMethods(atlas)\n .filter((m) => m.enabled)\n .map((m) => m.integration_type);\n}\n\nexport interface SeedOptions {\n countryId: number;\n mode: Mode;\n warehouseId?: number;\n businessId?: string;\n entityRegistered?: boolean;\n settlementCurrency?: string;\n}\n\n/** The keys `company_country` accepts on create and update, so a `--set`\n * typo is caught before it reaches the API. */\nexport const WRITABLE_KEYS = [\n \"currency\",\n \"warehouse_id\",\n \"handling_fee\",\n \"default\",\n \"tax_business_id\",\n \"tax_strategy\",\n \"tax_class\",\n \"mode\",\n \"settlement_currency\",\n \"entity_id\",\n \"entity_legally_registered\",\n \"tax_inclusive_pricing\",\n \"tax_name\",\n \"requires_3ds\",\n \"payment_method_priority\",\n \"checkout_requires_terms\",\n \"tax_invoice_required\",\n \"legal_settings\",\n \"address_fields\",\n] as const;\n\n/** The company_country attributes the atlas and the flags give. */\nexport function companyCountryFromAtlas(\n atlas: CountryAtlas,\n options: SeedOptions,\n): Record<string, unknown> {\n const body: Record<string, unknown> = {\n country_id: options.countryId,\n mode: options.mode.toUpperCase(),\n currency: atlas.defaultCurrency,\n requires_3ds: atlas.requires3ds === true,\n };\n const tax = atlas.taxSettings;\n if (tax) {\n // Fluid keeps a tax name only with tax-inclusive (VAT-style) pricing: it\n // clears tax_name whenever tax_inclusive_pricing is false. Sending one for\n // a tax-exclusive country would be silently dropped.\n if (tax.taxInclusive === true && typeof tax.taxName === \"string\" && tax.taxName !== \"\")\n body[\"tax_name\"] = tax.taxName;\n if (typeof tax.taxInclusive === \"boolean\")\n body[\"tax_inclusive_pricing\"] = tax.taxInclusive;\n if (typeof tax.invoiceRequired === \"boolean\")\n body[\"tax_invoice_required\"] = tax.invoiceRequired;\n }\n const priority = paymentPriorityFromAtlas(atlas);\n if (priority.length > 0) body[\"payment_method_priority\"] = priority;\n const legal = legalSettingsFromAtlas(atlas);\n if (Object.keys(legal).length > 0) body[\"legal_settings\"] = legal;\n const address = addressFieldsFromAtlas(atlas);\n if (address.length > 0) body[\"address_fields\"] = address;\n\n if (options.warehouseId !== undefined)\n body[\"warehouse_id\"] = options.warehouseId;\n if (options.businessId !== undefined)\n body[\"tax_business_id\"] = options.businessId;\n if (options.entityRegistered !== undefined) {\n body[\"entity_legally_registered\"] = options.entityRegistered;\n }\n if (options.settlementCurrency !== undefined) {\n body[\"settlement_currency\"] = options.settlementCurrency.toUpperCase();\n }\n return body;\n}\n\n/** Apply `--set` overrides on top of the seeded body, refusing unknown keys. */\nexport function applyOverrides(\n body: Record<string, unknown>,\n overrides: Record<string, unknown>,\n): Record<string, unknown> {\n const unknown = Object.keys(overrides).filter(\n (key) => !(WRITABLE_KEYS as readonly string[]).includes(key),\n );\n if (unknown.length > 0) {\n throw new LocalizationError(\n `Unknown company country setting(s): ${unknown.join(\", \")}. Settable: ${WRITABLE_KEYS.join(\", \")}.`,\n );\n }\n const merged = { ...body };\n for (const [key, value] of Object.entries(overrides)) {\n const current = merged[key];\n merged[key] =\n isPlainObject(current) && isPlainObject(value)\n ? { ...current, ...value }\n : value;\n }\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The body for changing named fields on an open country, leaving the rest as\n * they are. A nested override (`legal_settings.x=…`) merges into the\n * country's current object, so its other keys are kept.\n */\nexport function partialCountryUpdate(\n current: object,\n overrides: Record<string, unknown>,\n): Record<string, unknown> {\n const values = current as Record<string, unknown>;\n if (Object.keys(overrides).length === 0) {\n throw new LocalizationError(\n \"Name at least one setting to change, e.g. --set entity_id=12 or --settlement-currency USD.\",\n );\n }\n const base: Record<string, unknown> = {};\n for (const key of Object.keys(overrides)) {\n if (isPlainObject(values[key])) base[key] = values[key];\n }\n return applyOverrides(base, overrides);\n}\n","// The verification a country setup ends with: what the API says is in place\n// now, compared with the atlas, plus the steps that stay with a person.\n\nimport {\n launchChecklist,\n sortedPaymentMethods,\n type CountryAtlas,\n type Mode,\n} from \"./atlas.js\";\nimport type { CompanyCountry } from \"./api.js\";\nimport type { FormsReport } from \"./forms.js\";\nimport type { PriceCoverage } from \"./prices.js\";\nimport type { AgreementState, LanguageState } from \"./plan.js\";\nimport {\n addressFieldsFromAtlas,\n legalSettingsFromAtlas,\n paymentPriorityFromAtlas,\n} from \"./seed.js\";\n\nexport interface Check {\n id: string;\n ok: boolean;\n detail: string;\n}\n\nfunction same(a: unknown, b: unknown): boolean {\n return a === b || (a == null && b == null);\n}\n\n/** Compare the company country with the atlas. A value an admin changed on\n * purpose shows as a difference to review, not as a failure to fix. */\nexport function settingsChecks(\n atlas: CountryAtlas,\n cc: CompanyCountry,\n): Check[] {\n const checks: Check[] = [];\n checks.push({\n id: \"mode\",\n ok: typeof cc.mode === \"string\" && cc.mode !== \"\",\n detail: cc.mode ? `Operating mode ${cc.mode}.` : \"No operating mode set.\",\n });\n checks.push({\n id: \"currency\",\n ok: same(cc.currency, atlas.defaultCurrency),\n detail: `Currency ${cc.currency ?? \"unset\"}; the atlas recommends ${atlas.defaultCurrency}.`,\n });\n const tax = atlas.taxSettings;\n if (tax) {\n // A tax name only applies with tax-inclusive pricing; Fluid clears it\n // otherwise, so a tax-exclusive country has nothing to check.\n const inclusive = cc.tax_inclusive_pricing === true;\n checks.push(\n inclusive\n ? {\n id: \"tax_name\",\n ok: same(cc.tax_name, tax.taxName),\n detail: `Tax name ${cc.tax_name ?? \"unset\"}; the atlas says ${tax.taxName}.`,\n }\n : {\n id: \"tax_name\",\n ok: true,\n detail: `No tax name needed: prices are tax-exclusive, so tax is added at checkout (Fluid stores a tax name only with tax-inclusive pricing).`,\n },\n );\n checks.push({\n id: \"tax_inclusive_pricing\",\n ok: same(cc.tax_inclusive_pricing, tax.taxInclusive),\n detail: `Tax-inclusive prices ${String(cc.tax_inclusive_pricing ?? \"unset\")}; the atlas says ${String(tax.taxInclusive)}.`,\n });\n }\n checks.push({\n id: \"requires_3ds\",\n ok: same(cc.requires_3ds ?? false, atlas.requires3ds ?? false),\n detail: `3-D Secure required ${String(cc.requires_3ds ?? false)}; the atlas says ${String(atlas.requires3ds ?? false)}.`,\n });\n if (tax && typeof tax.invoiceRequired === \"boolean\") {\n checks.push({\n id: \"tax_invoice_required\",\n ok: same(cc.tax_invoice_required, tax.invoiceRequired),\n detail: `Tax invoices required ${String(cc.tax_invoice_required ?? \"unset\")}; the atlas says ${String(tax.invoiceRequired)}.`,\n });\n }\n const priority = paymentPriorityFromAtlas(atlas);\n if (priority.length > 0) {\n const current = cc.payment_method_priority ?? [];\n checks.push({\n id: \"payment_method_priority\",\n ok: JSON.stringify(current) === JSON.stringify(priority),\n detail: `Payment order ${current.length > 0 ? current.join(\", \") : \"unset\"}; the atlas recommends ${priority.join(\", \")}.`,\n });\n }\n const legal = legalSettingsFromAtlas(atlas);\n const legalDiffs = Object.entries(legal)\n .filter(([key, value]) => cc.legal_settings?.[key] !== value)\n .map(([key]) => key);\n checks.push({\n id: \"legal_settings\",\n ok: legalDiffs.length === 0,\n detail:\n legalDiffs.length === 0\n ? \"Legal settings match the atlas.\"\n : `Legal settings differ from the atlas: ${legalDiffs.join(\", \")}.`,\n });\n const requiredFields = addressFieldsFromAtlas(atlas)\n .filter((f) => f.required)\n .map((f) => f.field);\n const current = Array.isArray(cc.address_fields) ? cc.address_fields : null;\n const notRequired = requiredFields.filter(\n (field) =>\n !current?.some((f) => f[\"field\"] === field && f[\"required\"] === true),\n );\n checks.push({\n id: \"address_fields\",\n ok: current !== null && current.length > 0 && notRequired.length === 0,\n detail:\n current === null\n ? \"No address layout on the country; checkout uses the default fields.\"\n : notRequired.length > 0\n ? `The atlas requires ${notRequired.join(\", \")}, which the country doesn't.`\n : `${current.length} address fields set, with every field the atlas requires.`,\n });\n return checks;\n}\n\nexport function statusReport(input: {\n countryCode: string;\n atlas: CountryAtlas;\n companyCountry: CompanyCountry | undefined;\n languages: LanguageState[];\n agreements: AgreementState[];\n /** Price coverage of the company's active products in the country. */\n prices?: PriceCoverage;\n /** The atlas enrollment fields against the country's Post Purchase forms,\n * or why the forms couldn't be read. */\n enrollment?: FormsReport | { unavailable: string };\n}) {\n const {\n countryCode,\n atlas,\n companyCountry: cc,\n languages,\n agreements,\n } = input;\n if (!cc) {\n return {\n country: countryCode,\n open: false,\n done: false,\n next: `fluid countries open ${countryCode} --mode <nfr|otg|usd>`,\n };\n }\n const priceChecks: Check[] = input.prices\n ? [\n {\n id: \"prices\",\n ok: input.prices.priced === input.prices.variants,\n detail:\n input.prices.priced === input.prices.variants\n ? `All ${input.prices.variants} variants of your active products have a price in ${countryCode}${input.prices.bundles.length > 0 ? ` (bundles aren't counted; see byHand.bundles)` : \"\"}.`\n : `${input.prices.variants - input.prices.priced} of ${input.prices.variants} variants of your active products have no price in ${countryCode}. Run \\`fluid countries prices ${countryCode}\\`.`,\n },\n {\n id: \"on_sale\",\n ok: input.prices.onSale > 0 || input.prices.bundles.length > 0,\n detail:\n input.prices.onSale > 0\n ? `${input.prices.onSale} variants are on sale in ${countryCode}.`\n : input.prices.bundles.length > 0\n ? `No single products are on sale in ${countryCode}; check the bundles under byHand.bundles.`\n : `Nothing is on sale in ${countryCode} yet. Turn on the products you'll sell there, or run prices with --activate.`,\n },\n ]\n : [];\n const enrollment =\n input.enrollment && !(\"unavailable\" in input.enrollment)\n ? input.enrollment\n : null;\n const enrollmentChecks: Check[] = enrollment\n ? [\n {\n id: \"enrollment_form\",\n ok: enrollment.done,\n detail: enrollment.done\n ? enrollment.fields.length === 0\n ? `The atlas lists no enrollment fields for ${countryCode}.`\n : `Enrollment in ${countryCode} asks for every atlas field a Post Purchase form can collect.`\n : enrollment.missing.length > 0\n ? `Enrollment in ${countryCode} doesn't ask for ${enrollment.missing.join(\", \")}. Run \\`fluid countries forms ${countryCode}\\`.`\n : (enrollment.draft ??\n `The enrollment form for ${countryCode} isn't published.`),\n },\n ]\n : input.enrollment && \"unavailable\" in input.enrollment\n ? [\n {\n id: \"enrollment_form\",\n ok: false,\n detail: `Couldn't read the company's forms (${input.enrollment.unavailable}), so the enrollment fields weren't checked.`,\n },\n ]\n : [];\n const checks = [\n ...settingsChecks(atlas, cc),\n ...priceChecks,\n ...enrollmentChecks,\n ...languages.map((l) => ({\n id: `language:${l.iso}`,\n ok: l.enabled,\n detail: l.enabled\n ? `${l.name ?? l.iso} is enabled.`\n : l.languageId === null\n ? `${l.iso} isn't in Fluid's language catalog.`\n : `${l.name ?? l.iso} isn't enabled.`,\n })),\n ...agreements.map((a) => ({\n id: `agreement:${a.localId}`,\n ok: a.status === \"linked\" && (a.missingTranslations ?? []).length === 0,\n detail:\n a.status === \"linked\"\n ? (a.missingTranslations ?? []).length > 0\n ? `\"${a.title}\" is on the country but has no ${a.missingTranslations!.join(\", \")} translation. Enable the language, then run agreements --translate.`\n : `\"${a.title}\" is on the country.`\n : a.status === \"inactive\"\n ? `\"${a.title}\" is on the country but inactive, so checkout doesn't show it. Activate it in Settings > Agreements.`\n : a.status === \"exists\"\n ? `\"${a.title}\" exists but isn't on this country. Add the country to it in Settings > Agreements.`\n : `\"${a.title}\" hasn't been created.`,\n })),\n ];\n const mode = (cc.mode ?? \"\").toLowerCase() as Mode;\n return {\n country: countryCode,\n open: true,\n companyCountryId: cc.id,\n testMode: cc.test_mode ?? null,\n done: checks.every((c) => c.ok),\n checks,\n // Translating is `fluid translations`' job; these are the commands that\n // report and fill each market language (the store's own default\n // language needs none, and `auto` says so).\n translations: (atlas.majorLanguages ?? []).map((iso) => ({\n language: iso,\n report: `fluid translations missing ${iso}`,\n translate: `fluid translations auto ${iso} --write --yes`,\n })),\n // The enrollment form comes from `fluid countries forms`.\n enrollmentForm: {\n report: `fluid countries forms ${countryCode}`,\n create: `fluid countries forms ${countryCode} --create --yes`,\n },\n // Prices in the country's currency come from `fluid countries prices`.\n prices: {\n preview: `fluid countries prices ${countryCode}`,\n convert: `fluid countries prices ${countryCode} --rate <rate> --write --yes`,\n },\n byHand: {\n bundles:\n input.prices && input.prices.bundles.length > 0\n ? {\n how: `Check each bundle's price and availability in ${countryCode} in the bundle editor. Bundles can be priced from their groups, and each group can be switched off per country.`,\n items: input.prices.bundles,\n }\n : null,\n paymentMethods: sortedPaymentMethods(atlas).map((m) => ({\n integrationType: m.integration_type,\n name: m.name,\n recommended: m.enabled,\n })),\n // With the forms read, only the fields `fluid countries forms` leaves\n // to a person; without them, every atlas field.\n enrollmentFields: enrollment\n ? enrollment.fields\n .filter((f) => f.state === \"byHand\")\n .map((f) => ({\n id: f.id,\n label: f.label,\n component: f.component,\n required: f.required,\n ...(f.reason ? { reason: f.reason } : {}),\n }))\n : [...(atlas.enrollmentFormFields ?? [])]\n .sort((a, b) => a.order - b.order)\n .map((f) => ({\n id: f.id,\n label: f.label,\n component: f.component,\n required: f.required,\n })),\n activatePrices:\n \"Put the products you will sell on sale in the country: run prices with --activate, or activate them per product.\",\n leaveTestMode:\n cc.test_mode === true\n ? \"The country is in test mode. Turn test mode off when payments are configured.\"\n : null,\n },\n launchChecklist: mode ? launchChecklist(atlas, mode) : [],\n };\n}\n","// A small runner for walks that are a fixed sequence of steps: each step asks\n// its questions through `ask`, and `<`, `>`, `?` and Ctrl-C move between\n// steps. Steps record their answers on shared state, so going back shows\n// what was chosen, and every save goes through the same functions as the\n// non-interactive commands.\n\nimport {\n NAV_HELP,\n type Choice,\n type Nav,\n type Prompter,\n type Question,\n} from \"@fluid-app/fluid-cli\";\n\n/** Thrown by `ask` when the person moves instead of answering. */\nexport class NavSignal extends Error {\n constructor(readonly nav: Nav) {\n super(`nav:${nav}`);\n this.name = \"NavSignal\";\n }\n}\n\nexport interface StepContext {\n prompter: Prompter;\n /** Ask one question; navigation is thrown as a NavSignal for the runner. */\n ask(question: Omit<Question, \"canGoBack\" | \"section\">): Promise<string>;\n /** Ask a pick-one question and return the chosen value. */\n choose(message: string, choices: Choice[], current?: string): Promise<string>;\n note(text: string): void;\n}\n\nexport interface WalkStep {\n id: string;\n title: string;\n /** Leave the step out when false, e.g. a warehouse for a USD country. */\n when?: () => boolean;\n run(ctx: StepContext): Promise<void>;\n}\n\nexport interface RunResult {\n /** The steps finished, in order. */\n completed: string[];\n skipped: string[];\n /** True when the person stopped before the last step. */\n stopped: boolean;\n}\n\n/** Run the steps in order, honouring back, skip, the menu and stop. */\nexport async function runSteps(\n prompter: Prompter,\n steps: readonly WalkStep[],\n options: { help?: boolean } = {},\n): Promise<RunResult> {\n const history: number[] = [];\n const completed: string[] = [];\n const skipped: string[] = [];\n if (options.help !== false) prompter.note(NAV_HELP);\n let index = nextActive(steps, 0);\n\n const ctxFor = (step: WalkStep): StepContext => ({\n prompter,\n note: (text) => prompter.note(text),\n ask: async (question) => {\n const reply = await prompter.ask({\n ...question,\n section: step.title,\n canGoBack: history.length > 0,\n } as Question);\n if (\"nav\" in reply) throw new NavSignal(reply.nav);\n return reply.value;\n },\n choose: async (message, choices, current) => {\n const reply = await prompter.ask({\n section: step.title,\n message,\n kind: \"select\",\n choices,\n ...(current !== undefined ? { current } : {}),\n canGoBack: history.length > 0,\n });\n if (\"nav\" in reply) throw new NavSignal(reply.nav);\n return reply.value;\n },\n });\n\n while (index < steps.length) {\n const step = steps[index]!;\n try {\n await step.run(ctxFor(step));\n if (!completed.includes(step.id)) completed.push(step.id);\n history.push(index);\n index = nextActive(steps, index + 1);\n } catch (error) {\n if (!(error instanceof NavSignal)) throw error;\n let nav: Nav | \"goto\" = error.nav;\n let target = -1;\n if (nav === \"menu\") {\n const choice = await prompter.menu(\"What next?\", [\n { title: \"Continue\", value: \"continue\" },\n {\n title: \"Back to the previous step\",\n value: \"back\",\n disabled: history.length === 0,\n },\n { title: \"Skip this step\", value: \"skip\" },\n { title: \"Go to a step\", value: \"goto\" },\n { title: \"Stop for now\", value: \"quit\" },\n ]);\n if (choice === null || choice === \"quit\") nav = \"quit\";\n else if (choice === \"continue\") continue;\n else if (choice === \"goto\") {\n const picked = await prompter.menu(\n \"Which step?\",\n steps\n .map((s, i) => ({ s, i }))\n .filter(({ s }) => !s.when || s.when())\n .map(({ s, i }) => ({\n title: `${completed.includes(s.id) ? \"✓\" : \" \"} ${s.title}`,\n value: String(i),\n })),\n );\n if (picked === null) continue;\n nav = \"goto\";\n target = Number(picked);\n } else nav = choice;\n }\n if (nav === \"quit\") return { completed, skipped, stopped: true };\n if (nav === \"back\") {\n const previous = history.pop();\n if (previous === undefined) prompter.note(\"This is the first step.\");\n else index = previous;\n continue;\n }\n if (nav === \"skip\") {\n if (!skipped.includes(step.id)) skipped.push(step.id);\n history.push(index);\n index = nextActive(steps, index + 1);\n continue;\n }\n if (nav === \"goto\") {\n history.push(index);\n index = target;\n }\n }\n }\n return { completed, skipped, stopped: false };\n}\n\nfunction nextActive(steps: readonly WalkStep[], from: number): number {\n let i = from;\n while (i < steps.length && steps[i]!.when && !steps[i]!.when!()) i += 1;\n return i;\n}\n\nexport const YES_NO: Choice[] = [\n { title: \"Yes\", value: \"yes\" },\n { title: \"No\", value: \"no\" },\n];\n","// `fluid countries walk [iso]`: opening a country as a conversation, the way\n// Mist's Open a Country flow asks it. It picks the country, shows the atlas,\n// asks the mode, the languages, the agreements and each atlas default, then\n// opens the country, offers the price conversion, and hands each new\n// language to the translations walk. Every save goes through the same\n// functions as `open`, `agreements`, `prices` and `translations enable`.\n\nimport type { Prompter } from \"@fluid-app/fluid-cli\";\n\nimport { listAgreements } from \"../shared/agreements.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport {\n enableLanguage,\n listLanguages,\n type Language,\n} from \"../shared/languages.js\";\nimport { runSteps, YES_NO, type WalkStep } from \"../shared/walk.js\";\nimport {\n createAgreementFromAtlas,\n createCompanyCountry,\n findCompanyCountry,\n listCatalogCountries,\n listCompanyCountries,\n listWarehouses,\n updateCompanyCountry,\n writeTranslations,\n type CompanyCountry,\n} from \"./api.js\";\nimport {\n atlasAgreement,\n fetchAtlas,\n htmlToText,\n launchChecklist,\n MODES,\n type CountryAtlas,\n type Mode,\n} from \"./atlas.js\";\nimport { agreementStates, languageStates } from \"./plan.js\";\nimport {\n companyCountryIn,\n currencyDecimals,\n listProductsWithVariants,\n planConversion,\n priceEditorOwnsPrices,\n referenceRate,\n summarizePlans,\n writeConversion,\n DEFAULT_PRICE_FIELDS,\n type PlanOptions,\n} from \"./prices.js\";\nimport { companyCountryFromAtlas } from \"./seed.js\";\n\n/** One line per mode, so the choice reads without the atlas open. */\nexport const MODE_LINES: Record<Mode, string> = {\n nfr: \"Not For Resale: ship cross-border from a warehouse outside the country\",\n otg: \"On The Ground: a local entity, local tax registration, local fulfilment\",\n usd: \"Sell cross-border and charge in US dollars, usually digital goods\",\n};\n\nexport interface CountryDraft {\n iso?: string;\n name?: string;\n countryId?: number;\n atlas?: CountryAtlas;\n existing?: CompanyCountry;\n mode?: Mode;\n warehouseId?: number;\n /** Language codes to turn on. */\n languages: string[];\n /** Atlas agreement ids to create. */\n createAgreements: string[];\n /** Company agreement ids to write the atlas translations onto. */\n translateAgreements: Array<{ localId: string; id: number }>;\n /** Changes to the atlas values, by company_country key. */\n overrides: Record<string, unknown>;\n legacyDefaults: boolean;\n}\n\nexport interface CountryWalkResult {\n country?: string;\n opened: boolean;\n reseeded: boolean;\n enabled: string[];\n agreementsCreated: string[];\n pricesWritten: number;\n stopped: boolean;\n /** Languages handed to the translations walk, or to run later. */\n translate: string[];\n failures: Array<{ step: string; error: string }>;\n}\n\nexport interface CountryWalkDeps {\n api: LocalizationClient;\n prompter: Prompter;\n /** Runs the translations walk for one language; omitted, the command is printed instead. */\n translate?: (\n iso: string,\n ) => Promise<{ failed: Array<{ id: string; error: string }> } | undefined>;\n fetchRate?: typeof referenceRate;\n}\n\nconst yes = (v: string) => v === \"yes\";\n\n/** The company_country body the walk would send: the atlas seed with the person's changes. */\nexport function draftAttributes(draft: CountryDraft): Record<string, unknown> {\n if (!draft.atlas || draft.countryId === undefined || !draft.mode) return {};\n const body = companyCountryFromAtlas(draft.atlas, {\n countryId: draft.countryId,\n mode: draft.mode,\n ...(draft.warehouseId !== undefined\n ? { warehouseId: draft.warehouseId }\n : {}),\n });\n for (const [key, value] of Object.entries(draft.overrides)) {\n if (value === undefined) delete body[key];\n else body[key] = value;\n }\n return body;\n}\n\nexport async function walkCountry(\n deps: CountryWalkDeps,\n options: { iso?: string } = {},\n): Promise<CountryWalkResult> {\n const { api, prompter } = deps;\n const draft: CountryDraft = {\n languages: [],\n createAgreements: [],\n translateAgreements: [],\n overrides: {},\n legacyDefaults: false,\n };\n const result: CountryWalkResult = {\n opened: false,\n reseeded: false,\n enabled: [],\n agreementsCreated: [],\n pricesWritten: 0,\n stopped: false,\n translate: [],\n failures: [],\n };\n let catalog: Array<{ id: number; iso: string; name: string }> | undefined;\n let languages: Language[] = [];\n let reseed = false;\n let confirmed = false;\n\n const setupSteps: WalkStep[] = [\n {\n id: \"country\",\n title: \"Country\",\n run: async (ctx) => {\n catalog ??= await listCatalogCountries(api);\n for (;;) {\n const iso = (\n await ctx.ask({\n message: \"Which country are you opening?\",\n kind: \"search\",\n hint: \"type to filter\",\n choices: catalog.map((c) => ({\n title: `${c.name} (${c.iso})`,\n value: c.iso,\n })),\n ...(draft.iso\n ? { current: draft.iso }\n : options.iso\n ? { current: options.iso.toUpperCase() }\n : {}),\n })\n ).toUpperCase();\n const found = await fetchAtlas(api, iso);\n if (!found.covered || !found.atlas) {\n ctx.note(\n `Fluid has no Country Atlas for ${found.countryCode} yet, so there's nothing to seed it from. Open it by hand in Settings > Countries, or pick another country.`,\n );\n continue;\n }\n if (draft.iso !== found.countryCode) {\n // A new country starts a new draft.\n Object.assign(draft, {\n mode: undefined,\n warehouseId: undefined,\n languages: [],\n createAgreements: [],\n translateAgreements: [],\n overrides: {},\n legacyDefaults: false,\n });\n }\n const entry = catalog.find(\n (c) => c.iso.toUpperCase() === found.countryCode,\n );\n draft.iso = found.countryCode;\n draft.name = entry?.name ?? found.countryCode;\n draft.countryId = entry?.id;\n draft.atlas = found.atlas;\n draft.existing = await findCompanyCountry(api, found.countryCode);\n return;\n }\n },\n },\n {\n id: \"atlas\",\n title: \"Country Atlas\",\n run: async (ctx) => {\n const atlas = draft.atlas!;\n ctx.note(\n [\n `${draft.name}: ${atlas.defaultCurrency}, ${atlas.taxSettings?.taxName ?? \"no tax name\"} ${atlas.taxSettings?.taxInclusive ? \"included in prices\" : \"added at checkout\"}, 3-D Secure ${atlas.requires3ds ? \"required\" : \"not required\"}.`,\n `Languages: ${(atlas.majorLanguages ?? []).join(\", \") || \"none listed\"}. Agreements: ${(atlas.agreements ?? []).length}. Payment methods: ${\n (atlas.paymentMethods ?? [])\n .filter((m) => m.enabled)\n .map((m) => m.name)\n .join(\", \") || \"none recommended\"\n }.`,\n ...(draft.existing\n ? [\n `${draft.iso} is already open for this company (${draft.existing.mode ?? \"no mode\"}, ${draft.existing.currency ?? \"no currency\"}).`,\n ]\n : []),\n ].join(\"\\n\"),\n );\n await ctx.choose(\"Continue with this country?\", [\n { title: \"Continue\", value: \"go\" },\n ]);\n },\n },\n {\n id: \"mode\",\n title: \"Operating mode\",\n run: async (ctx) => {\n const atlas = draft.atlas!;\n const mode = await ctx.choose(\n \"How will you operate there?\",\n MODES.map((m) => ({\n title: `${m.toUpperCase()} — ${MODE_LINES[m]}`,\n value: m,\n description: htmlToText(atlas.modes?.[m]?.overview ?? \"\")\n .split(\"\\n\")[0]\n ?.slice(0, 160),\n })),\n draft.mode ??\n ((draft.existing?.mode ?? \"\").toLowerCase() || undefined),\n );\n draft.mode = mode as Mode;\n },\n },\n {\n id: \"warehouse\",\n title: \"Warehouse\",\n when: () => draft.mode !== \"usd\",\n run: async (ctx) => {\n const warehouses = await listWarehouses(api);\n if (warehouses.length === 0) {\n ctx.note(\n \"This company has no warehouses yet; add one in Settings > Warehouses later.\",\n );\n return;\n }\n const id = await ctx.choose(\n \"Which warehouse fulfils its orders?\",\n [\n ...warehouses.map((w) => ({\n title: `${w.name}${w.country?.iso ? ` (${w.country.iso})` : \"\"}${w.active === false ? \" — inactive\" : \"\"}`,\n value: String(w.id),\n })),\n { title: \"Decide later\", value: \"none\" },\n ],\n draft.warehouseId !== undefined\n ? String(draft.warehouseId)\n : undefined,\n );\n draft.warehouseId = id === \"none\" ? undefined : Number(id);\n },\n },\n {\n id: \"languages\",\n title: \"Languages\",\n run: async (ctx) => {\n languages = await listLanguages(api);\n const states = languageStates(draft.atlas!, languages);\n draft.languages = [];\n for (const state of states) {\n if (state.enabled) {\n ctx.note(`${state.name ?? state.iso} is already on.`);\n continue;\n }\n if (state.languageId === null) {\n ctx.note(\n `${state.iso} isn't in Fluid's language catalog, so it can't be turned on.`,\n );\n continue;\n }\n const answer = await ctx.choose(\n `Turn on ${state.name ?? state.iso} for the whole company?`,\n YES_NO,\n );\n if (yes(answer)) draft.languages.push(state.iso);\n }\n },\n },\n {\n id: \"agreements\",\n title: \"Agreements\",\n run: async (ctx) => {\n const atlas = draft.atlas!;\n const states = agreementStates(\n atlas,\n await listAgreements(api),\n draft.existing?.id ?? null,\n );\n draft.createAgreements = [];\n draft.translateAgreements = [];\n for (const state of states) {\n if (state.status === \"missing\") {\n const answer = await ctx.choose(\n `Create \"${state.title}\"${state.requiredOnCheckout ? \" (required at checkout)\" : \"\"} in ${state.languages.join(\", \")}?`,\n YES_NO,\n );\n if (yes(answer)) draft.createAgreements.push(state.localId);\n } else if (\n (state.status === \"linked\" || state.status === \"inactive\") &&\n state.agreementId !== null\n ) {\n const answer = await ctx.choose(\n `\"${state.title}\" is already on ${draft.iso}. Write the atlas translations onto it?`,\n YES_NO,\n );\n if (yes(answer))\n draft.translateAgreements.push({\n localId: state.localId,\n id: state.agreementId,\n });\n } else {\n ctx.note(\n `\"${state.title}\" exists but isn't on ${draft.iso}; add the country to it in Settings > Agreements.`,\n );\n }\n }\n },\n },\n {\n id: \"defaults\",\n title: \"Atlas defaults\",\n run: async (ctx) => {\n const seeded = draftAttributes({ ...draft, overrides: {} });\n const overrides: Record<string, unknown> = {};\n const keep = async (label: string, shown: string) =>\n (await ctx.choose(`${label}: ${shown}`, [\n { title: \"Keep\", value: \"keep\" },\n { title: \"Change\", value: \"change\" },\n ])) === \"keep\";\n\n const currency = String(seeded[\"currency\"] ?? \"\");\n if (!(await keep(\"Currency\", currency))) {\n for (;;) {\n const value =\n (\n await ctx.ask({\n message: \"Currency code\",\n kind: \"text\",\n current: currency,\n })\n )\n .trim()\n .toUpperCase() || currency;\n if (/^[A-Z]{3}$/.test(value)) {\n overrides[\"currency\"] = value;\n break;\n }\n ctx.note(\n `\"${value}\" isn't a three-letter currency code, such as EUR.`,\n );\n }\n }\n const seededInclusive = seeded[\"tax_inclusive_pricing\"] === true;\n if (!(await keep(\"Prices include tax\", seededInclusive ? \"yes\" : \"no\")))\n overrides[\"tax_inclusive_pricing\"] = !seededInclusive;\n const inclusive =\n (overrides[\"tax_inclusive_pricing\"] ?? seededInclusive) === true;\n // Fluid keeps a tax name only with tax-inclusive pricing, so it's\n // asked for only then.\n if (inclusive) {\n const taxName = String(\n seeded[\"tax_name\"] ?? draft.atlas?.taxSettings?.taxName ?? \"\",\n );\n if (!(await keep(\"Tax name\", taxName || \"(none)\"))) {\n overrides[\"tax_name\"] =\n (\n await ctx.ask({\n message: \"Tax name\",\n kind: \"text\",\n current: taxName,\n })\n ).trim() || taxName;\n } else if (taxName && seeded[\"tax_name\"] === undefined) {\n overrides[\"tax_name\"] = taxName;\n }\n } else {\n delete overrides[\"tax_name\"];\n }\n const threeDs = seeded[\"requires_3ds\"] === true;\n if (\n !(await keep(\"Card payments need 3-D Secure\", threeDs ? \"yes\" : \"no\"))\n )\n overrides[\"requires_3ds\"] = !threeDs;\n const order =\n (seeded[\"payment_method_priority\"] as string[] | undefined) ?? [];\n if (\n !(await keep(\n \"Payment method order\",\n order.join(\", \") || \"checkout's default\",\n ))\n ) {\n const raw = await ctx.ask({\n message:\n \"Payment methods in order, comma-separated (empty for checkout's default)\",\n kind: \"text\",\n current: order.join(\", \"),\n });\n const list = raw\n .split(\",\")\n .map((m) => m.trim())\n .filter(Boolean);\n overrides[\"payment_method_priority\"] =\n list.length > 0 ? list : undefined;\n }\n const fields =\n (seeded[\"address_fields\"] as\n | Array<{ field: string; required: boolean }>\n | undefined) ?? [];\n if (\n fields.length > 0 &&\n !(await keep(\n \"Address layout\",\n fields.map((f) => `${f.field}${f.required ? \"*\" : \"\"}`).join(\", \"),\n ))\n ) {\n overrides[\"address_fields\"] = undefined;\n ctx.note(\n \"Checkout's default address layout will be used; edit it in Settings > Countries.\",\n );\n }\n draft.overrides = overrides;\n },\n },\n {\n id: \"legacy\",\n title: \"Fluid's generic defaults\",\n when: () => !draft.existing,\n run: async (ctx) => {\n const answer = await ctx.choose(\n \"Fluid can also add its generic country agreements and forms.\",\n [\n {\n title: \"Use the atlas agreements and forms only\",\n value: \"atlas\",\n },\n { title: \"Also add Fluid's generic ones\", value: \"legacy\" },\n ],\n draft.legacyDefaults ? \"legacy\" : \"atlas\",\n );\n draft.legacyDefaults = answer === \"legacy\";\n },\n },\n {\n id: \"confirm\",\n title: \"Review\",\n run: async (ctx) => {\n const body = draftAttributes(draft);\n ctx.note(\n [\n `${draft.existing ? \"Update\" : \"Open\"} ${draft.name} (${draft.iso}) in ${draft.mode?.toUpperCase()} mode.`,\n `Currency ${String(body[\"currency\"])}, tax ${String(body[\"tax_name\"] ?? \"none\")}${body[\"tax_inclusive_pricing\"] ? \" (included)\" : \"\"}, 3-D Secure ${body[\"requires_3ds\"] ? \"on\" : \"off\"}.`,\n `Payment order: ${((body[\"payment_method_priority\"] as string[] | undefined) ?? []).join(\", \") || \"checkout's default\"}.`,\n `Languages to turn on: ${draft.languages.join(\", \") || \"none\"}.`,\n `Agreements to create: ${draft.createAgreements.length}; to translate: ${draft.translateAgreements.length}.`,\n ...(draft.existing\n ? []\n : [\n draft.legacyDefaults\n ? \"Fluid's generic country agreements and forms will be added too.\"\n : \"Only the atlas agreements and forms; Fluid's generic ones are left out.\",\n ]),\n ].join(\"\\n\"),\n );\n const choices = draft.existing\n ? [\n {\n title: `Write these settings over ${draft.iso}'s current ones`,\n value: \"reseed\",\n },\n {\n title: `Keep ${draft.iso}'s settings; just do languages and agreements`,\n value: \"keep\",\n },\n ]\n : [{ title: `Open ${draft.iso} now`, value: \"open\" }];\n const answer = await ctx.choose(\"Ready?\", choices);\n reseed = answer === \"reseed\";\n confirmed = true;\n },\n },\n ];\n\n const run = await runSteps(prompter, setupSteps);\n result.country = draft.iso;\n if (run.stopped || !confirmed || !draft.iso) {\n result.stopped = true;\n prompter.note(\"Stopped. Nothing was changed.\");\n return result;\n }\n\n // ---- apply, through the same calls as the commands\n const iso = draft.iso;\n try {\n const body = draftAttributes(draft);\n if (!draft.existing) {\n draft.existing = await createCompanyCountry(\n api,\n body,\n draft.legacyDefaults,\n );\n result.opened = true;\n prompter.note(`Opened ${iso}.`);\n } else if (reseed) {\n draft.existing = await updateCompanyCountry(\n api,\n draft.existing.id,\n body,\n draft.existing.revision,\n );\n result.reseeded = true;\n prompter.note(`Updated ${iso}'s settings.`);\n }\n } catch (error) {\n result.failures.push({ step: \"open\", error: failureOf(error).error });\n prompter.note(\n `${iso} couldn't be saved: ${failureOf(error).error}. Nothing else was changed.`,\n );\n return result;\n }\n\n for (const code of draft.languages) {\n const language = languages.find(\n (l) => l.iso.toLowerCase() === code.toLowerCase(),\n );\n if (!language) continue;\n try {\n await enableLanguage(api, language);\n result.enabled.push(language.iso);\n } catch (error) {\n result.failures.push({\n step: `language:${code}`,\n error: failureOf(error).error,\n });\n }\n }\n if (result.enabled.length > 0)\n prompter.note(`Turned on ${result.enabled.join(\", \")}.`);\n\n for (const localId of draft.createAgreements) {\n try {\n const created = await createAgreementFromAtlas(\n api,\n atlasAgreement(draft.atlas!, localId),\n draft.existing!.id,\n );\n result.agreementsCreated.push(localId);\n for (const f of created.failedTranslations) {\n result.failures.push({\n step: `agreement:${localId}:${f.language}`,\n error: f.error,\n });\n }\n } catch (error) {\n result.failures.push({\n step: `agreement:${localId}`,\n error: failureOf(error).error,\n });\n }\n }\n for (const { localId, id } of draft.translateAgreements) {\n const written = await writeTranslations(\n api,\n id,\n atlasAgreement(draft.atlas!, localId),\n );\n for (const f of written.failedTranslations) {\n result.failures.push({\n step: `agreement:${localId}:${f.language}`,\n error: f.error,\n });\n }\n }\n if (draft.createAgreements.length + draft.translateAgreements.length > 0) {\n prompter.note(\n `Agreements: ${result.agreementsCreated.length} created, ${draft.translateAgreements.length} translated.`,\n );\n }\n for (const failure of result.failures)\n prompter.note(`Not saved (${failure.step}): ${failure.error}`);\n\n // ---- prices\n const priced = await offerPrices(deps, iso);\n result.pricesWritten = priced.written;\n for (const f of priced.failed) {\n result.failures.push({\n step: `price:variant:${f.variantId}`,\n error: f.error,\n });\n }\n\n // ---- translations\n const toTranslate = (draft.atlas!.majorLanguages ?? []).filter((code) => {\n const language = languages.find(\n (l) => l.iso.toLowerCase() === code.toLowerCase(),\n );\n return language?.active || result.enabled.includes(language?.iso ?? \"\");\n });\n for (const code of toTranslate) {\n const answer = await prompter.menu(\n `Translate the storefront into ${code} now?`,\n [\n {\n title: \"Yes, walk through it\",\n value: \"walk\",\n disabled: !deps.translate,\n },\n { title: \"Later\", value: \"later\" },\n ],\n );\n result.translate.push(code);\n if (answer === \"walk\" && deps.translate) {\n const walked = await deps.translate(code);\n for (const f of walked?.failed ?? []) {\n result.failures.push({\n step: `translation:${code}:${f.id}`,\n error: f.error,\n });\n }\n } else prompter.note(`Later: fluid translations walk ${code}`);\n }\n\n const checklist = draft.mode ? launchChecklist(draft.atlas!, draft.mode) : [];\n if (checklist.length > 0) {\n prompter.note(\n `Before you launch (${draft.mode!.toUpperCase()}):\\n${checklist.map((c) => `- ${c.label}`).join(\"\\n\")}`,\n );\n }\n prompter.note(\n `Check the result any time with: fluid countries status ${iso}`,\n );\n return result;\n}\n\n/** Show the conversion preview, ask for the rate, and write on a yes. */\nasync function offerPrices(\n deps: CountryWalkDeps,\n iso: string,\n): Promise<{\n written: number;\n failed: Array<{ variantId: number; error: string }>;\n}> {\n const none = { written: 0, failed: [] };\n const { api, prompter } = deps;\n const countries = await listCompanyCountries(api);\n const target = companyCountryIn(countries, iso);\n const source = companyCountryIn(countries, \"US\");\n if (!target?.country || !source?.currency || !target.currency) {\n prompter.note(\n `No US prices to convert from${source ? \"\" : \" (the company doesn't sell in the US)\"}. Convert from another country later with: fluid countries prices ${iso} --from-country <iso>`,\n );\n return none;\n }\n const from = source.currency.toUpperCase();\n const to = target.currency.toUpperCase();\n const reference =\n from === to\n ? { rate: 1, date: \"\", source: \"same currency\" }\n : await (deps.fetchRate ?? referenceRate)(from, to);\n const editor = await priceEditorOwnsPrices(api);\n const products = await listProductsWithVariants(api, [\"active\"]);\n for (;;) {\n const raw = await prompter.ask({\n section: \"Prices\",\n message: `Exchange rate, ${to} per ${from}${reference ? ` (reference ${reference.rate}${reference.date ? `, ${reference.date}` : \"\"})` : \"\"}`,\n kind: \"text\",\n hint: \"Enter to use it, > to skip prices\",\n ...(reference ? { current: String(reference.rate) } : {}),\n canGoBack: false,\n });\n if (\"nav\" in raw) {\n prompter.note(`Prices skipped. Later: fluid countries prices ${iso}`);\n return none;\n }\n const rate = Number(raw.value.trim() || reference?.rate);\n if (!Number.isFinite(rate) || rate <= 0) {\n prompter.note(\"Type a positive number, such as 0.92.\");\n continue;\n }\n const options: PlanOptions = {\n sourceIso: \"US\",\n targetIso: iso,\n targetCountryId: target.country.id,\n targetCurrency: to,\n rate,\n fields: DEFAULT_PRICE_FIELDS,\n overwrite: false,\n activate: false,\n };\n const plans = planConversion(products, options);\n const summary = summarizePlans(plans, 5);\n prompter.note(\n [\n `At ${rate} ${to} per ${from}, rounded half up to ${currencyDecimals(to)} decimals:`,\n `${summary.toConvert} of ${summary.variants} variants get a price; ${summary.skipped.alreadyPriced} already have one, ${summary.skipped.noSourcePrice} have no US price, ${summary.skipped.roundsToZero} would round to zero.`,\n ...summary.sample.map(\n (s) =>\n ` ${s.product ?? s.productId}${s.sku ? ` (${s.sku})` : \"\"}: ${String(s.before.price ?? \"—\")} → ${String(s.after.price)} ${to}`,\n ),\n ...(editor\n ? [\n \"Your prices are managed in the price editor; any it can't take are listed.\",\n ]\n : []),\n ].join(\"\\n\"),\n );\n if (summary.toConvert === 0) return none;\n const answer = await prompter.menu(\n `Save ${summary.toConvert} prices in ${to}?`,\n [\n { title: \"Save them\", value: \"write\" },\n { title: \"Use a different rate\", value: \"rate\" },\n { title: \"Not now\", value: \"later\" },\n ],\n );\n if (answer === \"rate\") continue;\n if (answer !== \"write\") {\n prompter.note(\n `Prices skipped. Later: fluid countries prices ${iso} --rate ${rate} --write --yes`,\n );\n return none;\n }\n const written = await writeConversion(api, plans, options);\n prompter.note(\n `Saved ${written.written.length} prices${written.failed.length > 0 ? `; ${written.failed.length} refused:\\n${written.failed.map((f) => ` variant ${f.variantId}: ${f.error}`).join(\"\\n\")}` : \".\"}`,\n );\n return {\n written: written.written.length,\n failed: written.failed.map((f) => ({\n variantId: f.variantId,\n error: f.error,\n })),\n };\n }\n}\n","import { LocalizationError } from \"../shared/errors.js\";\n\n/**\n * A storefront resource on the v2026-04 company surface. Each one is\n * translated inline: `PATCH /api/v202604/company/<path>/<id>?lang=<iso>` with\n * the translated fields under `bodyKey`. Every response carries a `languages`\n * array naming the locales the row is translated into.\n */\nexport interface StorefrontType {\n kind: \"storefront\";\n /** The name commands take, e.g. `enrollment-packs`. */\n type: string;\n label: string;\n /** Path segment under `/api/v202604/company/`. */\n path: string;\n /** Key the write body wraps the translated fields in. */\n bodyKey: string;\n /** Key the show response wraps the row in. */\n showKey: string;\n /** Key the list response puts rows under. */\n listKey: string;\n /** Every field a translation can write. */\n fields: readonly string[];\n /** The text fields machine translation fills; the rest are URLs. */\n textFields: readonly string[];\n}\n\nexport interface MenuItemsType {\n kind: \"menu-items\";\n type: \"menu-items\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\nexport interface ThemeType {\n kind: \"theme\";\n type: \"theme\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\n/** Agreements: title and legal text, both saved together. */\nexport interface AgreementsType {\n kind: \"agreements\";\n type: \"agreements\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\n/** Product labels (badges): title only. */\nexport interface LabelsType {\n kind: \"labels\";\n type: \"labels\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\n/** Per-template theme strings that override the locale files on one page. */\nexport interface ThemeSectionsType {\n kind: \"theme-sections\";\n type: \"theme-sections\";\n label: string;\n fields: readonly string[];\n textFields: readonly string[];\n}\n\nexport type ResourceType =\n | StorefrontType\n | MenuItemsType\n | ThemeType\n | ThemeSectionsType\n | AgreementsType\n | LabelsType;\n\n/** Types whose source text is in the store's default language (theme types use --from). */\nexport function readsInStoreLanguage(type: ResourceType): boolean {\n return type.kind !== \"theme\" && type.kind !== \"theme-sections\";\n}\n\nfunction storefront(\n type: string,\n label: string,\n bodyKey: string,\n fields: readonly string[],\n urlFields: readonly string[] = [\"image_url\"],\n showKey: string = bodyKey,\n): StorefrontType {\n return {\n kind: \"storefront\",\n type,\n label,\n path: type,\n bodyKey,\n showKey,\n listKey: type.replace(/-/g, \"_\"),\n fields: [...fields, ...urlFields],\n textFields: fields,\n };\n}\n\n/**\n * The field sets match what fluid-admin's translation sheets read and write,\n * which in turn match each v2026-04 write schema's locale-scoped fields.\n * Enrollment packs have no per-locale image on that schema.\n */\nexport const RESOURCE_TYPES: readonly ResourceType[] = [\n storefront(\"products\", \"Products\", \"product\", [\n \"title\",\n \"introduction\",\n \"description\",\n \"feature_text\",\n ]),\n storefront(\"categories\", \"Categories\", \"category\", [\"title\", \"description\"]),\n storefront(\"collections\", \"Collections\", \"collection\", [\n \"title\",\n \"description\",\n ]),\n storefront(\"pages\", \"Pages\", \"page\", [\"title\", \"description\"]),\n storefront(\"posts\", \"Posts\", \"post\", [\"title\", \"description\", \"summary\"]),\n storefront(\n \"media\",\n \"Media\",\n \"medium\",\n [\"title\", \"description\"],\n [\"image_url\", \"video_url\", \"pdf_url\"],\n ),\n // Playlists write under `library` but read back under `playlist`.\n storefront(\n \"playlists\",\n \"Playlists\",\n \"library\",\n [\"title\", \"description\"],\n [\"image_url\"],\n \"playlist\",\n ),\n storefront(\n \"enrollment-packs\",\n \"Enrollment packs\",\n \"enrollment_pack\",\n [\"title\", \"description\"],\n [],\n ),\n {\n kind: \"menu-items\",\n type: \"menu-items\",\n label: \"Navigation menu items\",\n fields: [\"title\"],\n textFields: [\"title\"],\n },\n {\n kind: \"theme\",\n type: \"theme\",\n label: \"Theme strings (locales/<iso>.json)\",\n fields: [\"<key.path>\"],\n textFields: [\"<key.path>\"],\n },\n {\n kind: \"theme-sections\",\n type: \"theme-sections\",\n label: \"Theme page strings (a template's own translations)\",\n fields: [\"<key.path>\"],\n textFields: [\"<key.path>\"],\n },\n {\n kind: \"agreements\",\n type: \"agreements\",\n label: \"Agreements\",\n fields: [\"title\", \"description\"],\n textFields: [\"title\", \"description\"],\n },\n {\n kind: \"labels\",\n type: \"labels\",\n label: \"Product labels\",\n fields: [\"title\"],\n textFields: [\"title\"],\n },\n];\n\nexport const TYPE_NAMES = RESOURCE_TYPES.map((t) => t.type);\n\nexport function resourceType(name: string): ResourceType {\n const wanted = name.trim().toLowerCase();\n const match = RESOURCE_TYPES.find((t) => t.type === wanted);\n if (!match) {\n throw new LocalizationError(\n `\"${name}\" isn't a translatable type. Use one of: ${TYPE_NAMES.join(\", \")}.`,\n );\n }\n return match;\n}\n\n/** The types `--type` selects, or every type when none is given. */\nexport function selectTypes(names: readonly string[]): ResourceType[] {\n if (names.length === 0) return [...RESOURCE_TYPES];\n const picked = names\n .flatMap((name) => name.split(\",\"))\n .filter((name) => name.trim().length > 0)\n .map(resourceType);\n return RESOURCE_TYPES.filter((t) => picked.includes(t));\n}\n\n/** Reject fields a type can't translate, so a typo never reaches the API. */\nexport function checkFields(\n type: StorefrontType | MenuItemsType | AgreementsType | LabelsType,\n fields: Record<string, unknown>,\n): void {\n const unknown = Object.keys(fields).filter((f) => !type.fields.includes(f));\n if (unknown.length > 0) {\n throw new LocalizationError(\n `${type.label} can't translate ${unknown.join(\", \")}. Translatable fields: ${type.fields.join(\", \")}.`,\n );\n }\n if (Object.keys(fields).length === 0) {\n throw new LocalizationError(\n `Pass at least one field, e.g. --set title=... (${type.fields.join(\", \")}).`,\n );\n }\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { statusOf } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport type { StorefrontType } from \"./resources.js\";\n\nexport type Row = Record<string, unknown> & { id: number };\n\ninterface ListWire {\n meta?: { pagination?: { next_cursor?: string | null } };\n [key: string]: unknown;\n}\n\nconst PAGE_LIMIT = 100;\n/** A guard against a cursor that never ends; 1,000 pages is 100,000 rows. */\nconst MAX_PAGES = 1000;\n\nexport function companyPath(type: StorefrontType, id?: number): string {\n return `/api/v202604/company/${type.path}${id === undefined ? \"\" : `/${id}`}`;\n}\n\n/** Every row of a type, following the cursor. Rows are in the default locale. */\nexport async function listAll(\n client: LocalizationClient,\n type: StorefrontType,\n): Promise<Row[]> {\n const rows: Row[] = [];\n let cursor: string | null | undefined;\n for (let page = 0; page < MAX_PAGES; page += 1) {\n const data = await client.get<ListWire>(companyPath(type), {\n \"page[limit]\": PAGE_LIMIT,\n ...(cursor ? { \"page[cursor]\": cursor } : {}),\n });\n const batch = data[type.listKey];\n if (Array.isArray(batch)) rows.push(...(batch as Row[]));\n cursor = data.meta?.pagination?.next_cursor;\n if (!cursor) return rows;\n }\n throw new LocalizationError(\n `${type.label} has more than ${MAX_PAGES * PAGE_LIMIT} rows, more than one run can list. Translate it in fluid-admin or through the API.`,\n );\n}\n\n/** One row, read in `lang` (fields with no translation fall back to `en`). */\nexport async function getRow(\n client: LocalizationClient,\n type: StorefrontType,\n id: number,\n lang?: string,\n): Promise<Row> {\n let data: Record<string, unknown>;\n try {\n data = await client.get<Record<string, unknown>>(\n companyPath(type, id),\n lang ? { lang } : undefined,\n );\n } catch (error) {\n if (statusOf(error) === 404) {\n throw new LocalizationError(\n `No ${type.label.toLowerCase()} row with id ${id}.`,\n );\n }\n throw error;\n }\n const row = data[type.showKey];\n if (!row || typeof row !== \"object\") {\n throw new LocalizationError(\n `The API returned no ${type.showKey} for id ${id}.`,\n );\n }\n return row as Row;\n}\n\n/** The locales a row is translated into, always including the default. */\nexport function rowLanguages(row: Row): string[] {\n const languages = row[\"languages\"];\n return Array.isArray(languages)\n ? languages.filter((l): l is string => typeof l === \"string\")\n : [];\n}\n\nexport function isTranslated(row: Row, iso: string): boolean {\n const wanted = iso.toLowerCase();\n return rowLanguages(row).some((l) => l.toLowerCase() === wanted);\n}\n\n/**\n * Rich-text fields (descriptions on products, media, and enrollment packs)\n * can come back as a string or as `{ body }`. Normalize to a string, or\n * null when there is no value.\n */\nexport function fieldText(value: unknown): string | null {\n if (typeof value === \"string\") return value.length > 0 ? value : null;\n if (value && typeof value === \"object\") {\n const body = (value as { body?: unknown }).body;\n if (typeof body === \"string\") return body.length > 0 ? body : null;\n }\n return null;\n}\n\n/** A row's translatable fields as strings (or null), in `fields` order. */\nexport function pickFields(\n row: Row,\n fields: readonly string[],\n): Record<string, string | null> {\n return Object.fromEntries(fields.map((f) => [f, fieldText(row[f])]));\n}\n\nexport function rowTitle(row: Row): string | null {\n return fieldText(row[\"title\"]);\n}\n\n/**\n * Write one locale's translated fields. A translation PATCH writes only the\n * translated fields on every resource except products, where any other field\n * sent would also apply; the caller sends translated fields only.\n */\nexport async function writeTranslation(\n client: LocalizationClient,\n type: StorefrontType,\n id: number,\n lang: string,\n fields: Record<string, unknown>,\n): Promise<Row> {\n const data = await client.patch<Record<string, unknown>>(\n `${companyPath(type, id)}?lang=${encodeURIComponent(lang)}`,\n { [type.bodyKey]: fields },\n );\n const row = data[type.showKey] ?? data[type.bodyKey];\n return (row && typeof row === \"object\" ? row : { id }) as Row;\n}\n\n/**\n * The store's default language. Every row's `languages` lists it first, so\n * the first row of any storefront type answers; null for an empty catalog.\n */\nexport async function storeDefaultIso(\n client: LocalizationClient,\n types: readonly StorefrontType[],\n): Promise<string | null> {\n for (const type of types) {\n let data: ListWire;\n try {\n data = await client.get<ListWire>(companyPath(type), {\n \"page[limit]\": 1,\n });\n } catch {\n // A token without access to this type can still translate others.\n continue;\n }\n const batch = data[type.listKey];\n const first = Array.isArray(batch)\n ? (batch[0] as Row | undefined)\n : undefined;\n const iso = first ? rowLanguages(first)[0] : undefined;\n if (iso) return iso;\n }\n return null;\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n findLanguage,\n requireActiveLanguage,\n sameIso,\n type Language,\n} from \"../shared/languages.js\";\nimport {\n readsInStoreLanguage,\n RESOURCE_TYPES,\n type ResourceType,\n type StorefrontType,\n} from \"./resources.js\";\nimport { storeDefaultIso } from \"./storefront.js\";\n\nexport interface AutoLanguages {\n /** The language translations go into. */\n target: Language;\n /** The theme's source locale (`--from`). */\n source: Language;\n /** The store's default language, which storefront text is read in. */\n store: Language;\n}\n\n/**\n * The languages a machine translation runs between. Storefront rows, menu\n * items, agreements and labels read back in the store's default language,\n * so that is the language the translator is told they're in; it's read from\n * the rows (selected storefront types first), `storeLanguage` only fills in\n * when they can't be read, and it's never guessed. Theme strings translate\n * from `from`.\n */\nexport async function autoLanguages(\n api: LocalizationClient,\n types: readonly ResourceType[],\n options: {\n iso: string;\n from: string;\n storeLanguage?: string;\n write: boolean;\n },\n): Promise<AutoLanguages> {\n const target = options.write\n ? await requireActiveLanguage(api, options.iso)\n : await findLanguage(api, options.iso);\n const source = await findLanguage(api, options.from);\n let store = source;\n if (types.some(readsInStoreLanguage)) {\n const storefrontTypes = [\n ...types.filter((t): t is StorefrontType => t.kind === \"storefront\"),\n ...RESOURCE_TYPES.filter(\n (t): t is StorefrontType =>\n t.kind === \"storefront\" && !types.includes(t),\n ),\n ];\n const storeIso =\n (await storeDefaultIso(api, storefrontTypes)) ?? options.storeLanguage;\n if (!storeIso) {\n throw new LocalizationError(\n \"Couldn't read the store's default language from its storefront rows. Pass --store-language <iso>.\",\n );\n }\n store = sameIso(storeIso, source.iso)\n ? source\n : await findLanguage(api, storeIso);\n if (sameIso(store.iso, target.iso)) {\n throw new LocalizationError(\n `${target.name} is the store's default language, so storefront content in it is the source, not a translation. Use --type theme --type theme-sections to translate only theme strings.`,\n );\n }\n }\n if (\n types.some((t) => !readsInStoreLanguage(t)) &&\n sameIso(source.iso, target.iso)\n ) {\n throw new LocalizationError(\n `--from and the target are both ${target.iso}; pass --from with the theme's source locale.`,\n );\n }\n return { target, source, store };\n}\n","// Product labels (badges such as \"Vegan\") translate their title. The read in\n// a language falls back to another language's title when it has none, so\n// the API can't say whether a translation exists; a label counts as missing\n// while its title in the language reads the same as in the default.\n\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { statusOf } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\nexport interface Label {\n id: number;\n title: string;\n}\n\ninterface LabelWire {\n id: number;\n title?: string | null;\n}\n\nconst LABELS_PATH = \"/api/company/labels\";\n\n/**\n * The company's labels in the default language. The list isn't paginated and\n * holds only the labels an admin created; Fluid's own labels (\"New\",\n * \"Trending\") are translated by Fluid and can't be changed.\n */\nexport async function listLabels(client: LocalizationClient): Promise<Label[]> {\n const data = await client.get<{ label?: LabelWire[] }>(LABELS_PATH);\n return (data.label ?? []).map((wire) => ({\n id: wire.id,\n title: wire.title ?? \"\",\n }));\n}\n\n/** One label's title in `lang`, or in the default language without one. */\nexport async function getLabelTitle(\n client: LocalizationClient,\n id: number,\n lang?: string,\n): Promise<string> {\n try {\n const data = await client.get<{ label?: LabelWire }>(\n `${LABELS_PATH}/${id}`,\n lang ? { lang } : undefined,\n );\n return data.label?.title ?? \"\";\n } catch (error) {\n if (statusOf(error) === 404) {\n throw new LocalizationError(\n `No label with id ${id}, or it's one of Fluid's own labels, which Fluid translates.`,\n );\n }\n throw error;\n }\n}\n\n/** Whether a label's title in `lang` differs from its default title. */\nexport function labelTranslated(source: string, translated: string): boolean {\n return translated.trim().length > 0 && translated.trim() !== source.trim();\n}\n\nexport async function writeLabelTitle(\n client: LocalizationClient,\n id: number,\n lang: string,\n title: string,\n): Promise<void> {\n await client.put(`${LABELS_PATH}/${id}?lang=${encodeURIComponent(lang)}`, {\n label: { title },\n });\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\n/** Fields per machine-translation request, so one large batch can't time out. */\nexport const BATCH_SIZE = 40;\n\ninterface TranslateWire {\n translations?: Record<string, unknown>;\n usage?: { input_tokens?: number; output_tokens?: number };\n}\n\nexport interface MachineResult {\n translations: Record<string, string>;\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/**\n * Machine-translate id-keyed strings with Fluid AI\n * (`POST /api/v202604/ai/translate`). Languages are named, not coded\n * (\"english\", \"french\"), which is what the endpoint expects. Nothing is\n * saved; the caller writes the results back.\n */\nexport async function machineTranslate(\n client: LocalizationClient,\n sourceLanguage: string,\n targetLanguage: string,\n fields: Record<string, string>,\n model?: string,\n): Promise<MachineResult> {\n const entries = Object.entries(fields).filter(([, v]) => v.trim().length > 0);\n const result: MachineResult = {\n translations: {},\n usage: { inputTokens: 0, outputTokens: 0 },\n };\n for (let start = 0; start < entries.length; start += BATCH_SIZE) {\n const batch = Object.fromEntries(entries.slice(start, start + BATCH_SIZE));\n const data = await client.post<TranslateWire>(\"/api/v202604/ai/translate\", {\n source_language: languageName(sourceLanguage),\n target_language: languageName(targetLanguage),\n fields: batch,\n ...(model ? { model } : {}),\n });\n for (const id of Object.keys(batch)) {\n const value = data.translations?.[id];\n if (typeof value === \"string\" && value.trim().length > 0) {\n result.translations[id] = value;\n }\n }\n result.usage.inputTokens += data.usage?.input_tokens ?? 0;\n result.usage.outputTokens += data.usage?.output_tokens ?? 0;\n }\n return result;\n}\n\n/** \"Spanish\" → \"spanish\"; the endpoint takes lowercase names up to 64 characters. */\nexport function languageName(name: string): string {\n const value = name.trim().toLowerCase();\n if (value.length === 0 || value.length > 64) {\n throw new LocalizationError(`\"${name}\" isn't a usable language name.`);\n }\n return value;\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\n\ninterface MenuItemWire {\n id: number;\n title?: string | null;\n sub_menu_items?: MenuItemWire[];\n}\n\ninterface MenuWire {\n id: number;\n title?: string | null;\n menu_items?: MenuItemWire[];\n}\n\ninterface MenusWire {\n menus?: MenuWire[];\n meta?: { total_pages?: number };\n}\n\nexport interface MenuItem {\n /** `<menu id>/<item id>`, the id `show` and `set` take for menu items. */\n id: string;\n menuId: number;\n itemId: number;\n menu: string | null;\n title: string | null;\n}\n\nconst PER_PAGE = 100;\nconst MAX_PAGES = 50;\n\n/**\n * Every menu item of every menu, flattened. With `lang`, titles come back\n * without the English fallback, so an item with no translation reads null.\n */\nexport async function listMenuItems(\n client: LocalizationClient,\n lang?: string,\n): Promise<MenuItem[]> {\n const items: MenuItem[] = [];\n for (let page = 1; page <= MAX_PAGES; page += 1) {\n const data = await client.get<MenusWire>(\"/api/menus\", {\n page,\n per_page: PER_PAGE,\n ...(lang ? { lang } : {}),\n });\n const menus = data.menus ?? [];\n for (const menu of menus) {\n flatten(menu, menu.menu_items ?? [], items);\n }\n const totalPages = data.meta?.total_pages ?? 1;\n if (page >= totalPages || menus.length < PER_PAGE) return items;\n }\n throw new LocalizationError(`More than ${MAX_PAGES * PER_PAGE} menus.`);\n}\n\nfunction flatten(menu: MenuWire, nodes: MenuItemWire[], out: MenuItem[]): void {\n for (const node of nodes) {\n out.push({\n id: `${menu.id}/${node.id}`,\n menuId: menu.id,\n itemId: node.id,\n menu: menu.title ?? null,\n title:\n typeof node.title === \"string\" && node.title.length > 0\n ? node.title\n : null,\n });\n flatten(menu, node.sub_menu_items ?? [], out);\n }\n}\n\n/** Parse a `<menu id>/<item id>` reference. */\nexport function parseMenuItemId(ref: string): {\n menuId: number;\n itemId: number;\n} {\n const match = /^(\\d+)\\/(\\d+)$/.exec(ref.trim());\n if (!match) {\n throw new LocalizationError(\n `\"${ref}\" isn't a menu item id. Use <menu id>/<item id>, as \\`fluid translations missing\\` prints it, e.g. 12/340.`,\n );\n }\n return { menuId: Number(match[1]), itemId: Number(match[2]) };\n}\n\n/** One menu item's title in the default locale and, without fallback, in `lang`. */\nexport async function getMenuItem(\n client: LocalizationClient,\n ref: string,\n lang: string,\n): Promise<{ source: string | null; translated: string | null }> {\n const { menuId, itemId } = parseMenuItemId(ref);\n const path = `/api/menus/${menuId}/menu_items/${itemId}`;\n const [source, translated] = await Promise.all([\n client.get<{ menu_item?: MenuItemWire }>(path),\n client.get<{ menu_item?: MenuItemWire }>(path, { lang }),\n ]);\n const text = (wire?: MenuItemWire) =>\n typeof wire?.title === \"string\" && wire.title.length > 0\n ? wire.title\n : null;\n return {\n source: text(source.menu_item),\n translated: text(translated.menu_item),\n };\n}\n\nexport async function writeMenuItemTitle(\n client: LocalizationClient,\n ref: string,\n lang: string,\n title: string,\n): Promise<{ id: string; title: string | null }> {\n const { menuId, itemId } = parseMenuItemId(ref);\n const data = await client.patch<{ menu_item?: MenuItemWire }>(\n `/api/menus/${menuId}/menu_items/${itemId}?lang=${encodeURIComponent(lang)}`,\n { menu_item: { title } },\n );\n return { id: `${menuId}/${itemId}`, title: data.menu_item?.title ?? null };\n}\n","import type { LocalizationClient } from \"../shared/client.js\";\nimport { statusOf } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport { keyPath } from \"../shared/parsing.js\";\n\nexport type LocaleDoc = Record<string, unknown>;\n\ninterface ResourceWire {\n key?: string;\n content?: unknown;\n}\n\ninterface ResourcesWire {\n application_theme_resources?: ResourceWire[];\n content_version_sha?: string | null;\n}\n\n/** A theme's locale files, read together with the theme's version. */\nexport interface ThemeLocales {\n themeId: number;\n /** The theme's `content_version_sha` when read; a write sends it back as `base_sha`. */\n sha: string | null;\n /** Parsed `locales/<iso>.json` files, keyed by lowercased ISO code. */\n files: Map<string, { iso: string; doc: LocaleDoc }>;\n}\n\n/** The theme to work on: `--theme <id>`, else the company's active theme. */\nexport async function resolveThemeId(\n client: LocalizationClient,\n themeId?: string,\n): Promise<number> {\n if (themeId !== undefined) {\n const id = Number(themeId);\n if (!Number.isInteger(id) || id <= 0) {\n throw new LocalizationError(`\"${themeId}\" isn't a theme id.`);\n }\n return id;\n }\n try {\n const data = await client.get<{ theme?: { id?: number } }>(\n \"/api/v202604/themes/active\",\n );\n if (typeof data.theme?.id === \"number\") return data.theme.id;\n } catch (error) {\n if (statusOf(error) !== 404) throw error;\n }\n throw new LocalizationError(\n \"This company has no active theme. Pass --theme <id> to work on another theme.\",\n );\n}\n\n/**\n * Every `locales/<iso>.json` file on the theme, plus the theme's\n * `content_version_sha`, from one read of the theme resources API.\n */\nexport async function readThemeLocales(\n client: LocalizationClient,\n themeId: number,\n): Promise<ThemeLocales> {\n let data: ResourcesWire;\n try {\n data = await client.get<ResourcesWire>(\n `/api/application_themes/${themeId}/resources`,\n );\n } catch (error) {\n // The resources list refuses a theme whose member configuration is\n // invalid (422), even though its locale files are fine. Read just the\n // locale files instead; without a version, writes can't be guarded.\n if (statusOf(error) !== 422) throw error;\n return readLocaleTemplates(client, themeId);\n }\n const files = new Map<string, { iso: string; doc: LocaleDoc }>();\n for (const resource of data.application_theme_resources ?? []) {\n const match = /^locales\\/([^/]+)\\.json$/.exec(resource.key ?? \"\");\n if (!match) continue;\n const iso = match[1]!;\n files.set(iso.toLowerCase(), {\n iso,\n doc: parseLocale(resource.content, resource.key!),\n });\n }\n return { themeId, sha: data.content_version_sha ?? null, files };\n}\n\ninterface TemplateSummaryWire {\n id: number;\n name?: string;\n}\n\n/** Locale files through the v2026-04 templates endpoints, with no theme version. */\nasync function readLocaleTemplates(\n client: LocalizationClient,\n themeId: number,\n): Promise<ThemeLocales> {\n const list = await client.get<{ templates?: TemplateSummaryWire[] }>(\n `/api/v202604/themes/${themeId}/templates`,\n { themeable_type: \"locales\" },\n );\n const files = new Map<string, { iso: string; doc: LocaleDoc }>();\n for (const summary of list.templates ?? []) {\n if (!summary.name) continue;\n const data = await client.get<{ template?: { content?: unknown } }>(\n `/api/v202604/themes/${themeId}/templates/${summary.id}`,\n );\n const key = `locales/${summary.name}.json`;\n files.set(summary.name.toLowerCase(), {\n iso: summary.name,\n doc: parseLocale(data.template?.content, key),\n });\n }\n return { themeId, sha: null, files };\n}\n\n/** One locale file from a read, or null when the theme has none. */\nexport function localeOf(locales: ThemeLocales, iso: string): LocaleDoc | null {\n return locales.files.get(iso.toLowerCase())?.doc ?? null;\n}\n\nexport function parseLocale(content: unknown, key: string): LocaleDoc {\n if (content === null || content === undefined || content === \"\") return {};\n if (typeof content === \"object\" && !Array.isArray(content)) {\n return content as LocaleDoc;\n }\n if (typeof content === \"string\") {\n try {\n const parsed: unknown = JSON.parse(content);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as LocaleDoc;\n }\n } catch {\n // fall through\n }\n }\n throw new LocalizationError(\n `${key} isn't a JSON object; fix it before translating.`,\n );\n}\n\n/** Leaf strings keyed by dotted path: `{ cart: { title: \"Cart\" } }` → `cart.title`. */\nexport function flattenLocale(\n doc: LocaleDoc,\n prefix = \"\",\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(doc)) {\n const path = prefix ? `${prefix}.${key}` : key;\n if (typeof value === \"string\") {\n out[path] = value;\n } else if (value && typeof value === \"object\" && !Array.isArray(value)) {\n Object.assign(out, flattenLocale(value as LocaleDoc, path));\n }\n }\n return out;\n}\n\n/** Keys the source locale has that the target lacks or leaves blank. */\nexport function missingKeys(\n source: LocaleDoc,\n target: LocaleDoc | null,\n): Record<string, string> {\n const have = flattenLocale(target ?? {});\n const missing: Record<string, string> = {};\n for (const [key, value] of Object.entries(flattenLocale(source))) {\n if (value.trim().length === 0) continue;\n if ((have[key] ?? \"\").trim().length === 0) missing[key] = value;\n }\n return missing;\n}\n\n/**\n * Keys whose target value is still the source text word for word. Some are\n * right as they are (a brand name, \"OK\", a SKU); the rest were copied and\n * never translated. They aren't counted as missing, so they're listed for\n * review instead.\n */\nexport function sameAsSourceKeys(\n source: LocaleDoc,\n target: LocaleDoc | null,\n): string[] {\n const have = flattenLocale(target ?? {});\n return Object.entries(flattenLocale(source))\n .filter(([key, value]) => /\\p{L}/u.test(value) && have[key] === value)\n .map(([key]) => key);\n}\n\n/** A copy of `doc` with each dotted key set to its value. */\nexport function mergeLocale(\n doc: LocaleDoc,\n updates: Record<string, string>,\n): LocaleDoc {\n const out = structuredClone(doc);\n for (const [path, value] of Object.entries(updates)) {\n const parts = keyPath(path);\n let node: LocaleDoc = out;\n for (const part of parts.slice(0, -1)) {\n const next = Object.hasOwn(node, part) ? node[part] : undefined;\n if (typeof next === \"string\") {\n throw new LocalizationError(\n `\"${path}\" would replace the string at \"${part}\" with an object.`,\n );\n }\n if (!next || typeof next !== \"object\" || Array.isArray(next))\n node[part] = {};\n node = node[part] as LocaleDoc;\n }\n const leaf = parts[parts.length - 1]!;\n if (\n Object.hasOwn(node, leaf) &&\n node[leaf] &&\n typeof node[leaf] === \"object\"\n ) {\n throw new LocalizationError(\n `\"${path}\" is a group of keys, not a string.`,\n );\n }\n node[leaf] = value;\n }\n return out;\n}\n\n/**\n * Save `locales/<iso>.json` through the theme resources API, creating the\n * file if the theme has none. `base_sha` is the version the file was read\n * at, so the server refuses the write (409) if the theme changed since,\n * rather than overwriting someone else's edit. A read with no version\n * (the fallback for an invalid member configuration) can't be saved. The\n * write publishes immediately.\n */\nexport async function writeLocale(\n client: LocalizationClient,\n locales: ThemeLocales,\n iso: string,\n doc: LocaleDoc,\n): Promise<{ key: string; saved: true }> {\n if (!locales.sha) {\n // Without the theme's version the server can't refuse a stale write, so\n // an edit made since the read would be lost. Refuse instead.\n throw new LocalizationError(\n `Theme ${locales.themeId}'s files can't be listed (its member configuration is invalid), so the CLI can read its locale files but can't save them safely. Fix the theme's member configuration, or edit the locale file in the theme editor.`,\n );\n }\n const key = `locales/${locales.files.get(iso.toLowerCase())?.iso ?? iso}.json`;\n try {\n await client.put(`/api/application_themes/${locales.themeId}/resources`, {\n application_theme_resource: {\n key,\n content: JSON.stringify(doc, null, 2),\n },\n base_sha: locales.sha,\n });\n } catch (error) {\n if (statusOf(error) === 409) {\n // Keep the status, so a per-item report can say why.\n throw Object.assign(\n new LocalizationError(\n `Theme ${locales.themeId} changed after it was read, so ${key} wasn't saved. Run the command again to merge onto the latest version.`,\n ),\n { status: 409 },\n );\n }\n throw error;\n }\n return { key, saved: true };\n}\n","// Per-template theme translations: a page template's `translations` holds,\n// per locale, strings that override the theme's locale files on that page\n// (`{ \"fr\": { \"hero\": { \"title\": \"…\" } } }`). The storefront reads them\n// live, so a save shows at once without publishing.\n\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n flattenLocale,\n mergeLocale,\n missingKeys,\n type LocaleDoc,\n} from \"./theme.js\";\n\n/**\n * Template kinds that never render as a page of their own, so their\n * `translations` aren't read: shared sections, blocks and components, the\n * locale files themselves, layouts, and configuration.\n */\nconst NOT_PAGES = new Set([\n \"sections\",\n \"blocks\",\n \"components\",\n \"locales\",\n \"layouts\",\n \"config\",\n]);\n\ninterface TemplateSummaryWire {\n id: number;\n name?: string | null;\n themeable_type?: string | null;\n}\n\ninterface TemplateWire {\n id?: number;\n name?: string | null;\n translations?: unknown;\n}\n\nexport interface TemplateTranslations {\n templateId: number;\n name: string | null;\n translations: Record<string, LocaleDoc>;\n}\n\nfunction asTranslations(value: unknown): Record<string, LocaleDoc> {\n if (typeof value === \"string\") {\n try {\n return asTranslations(JSON.parse(value));\n } catch {\n return {};\n }\n }\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return {};\n const out: Record<string, LocaleDoc> = {};\n for (const [locale, doc] of Object.entries(value)) {\n if (doc && typeof doc === \"object\" && !Array.isArray(doc))\n out[locale] = doc as LocaleDoc;\n }\n return out;\n}\n\n/** One locale's strings from a template's translations, matching the code's casing. */\nexport function localeIn(\n translations: Record<string, LocaleDoc>,\n iso: string,\n): LocaleDoc | null {\n const key = Object.keys(translations).find(\n (k) => k.toLowerCase() === iso.toLowerCase(),\n );\n return key ? (translations[key] ?? null) : null;\n}\n\nexport async function readTemplateTranslations(\n client: LocalizationClient,\n themeId: number,\n templateId: number,\n): Promise<TemplateTranslations> {\n const data = await client.get<{ template?: TemplateWire }>(\n `/api/v202604/themes/${themeId}/templates/${templateId}`,\n );\n return {\n templateId,\n name: data.template?.name ?? null,\n translations: asTranslations(data.template?.translations),\n };\n}\n\n/**\n * Every page template of the theme that has translations. The template list\n * carries no translations, so each page template is read on its own.\n */\nexport async function listTemplateTranslations(\n client: LocalizationClient,\n themeId: number,\n): Promise<TemplateTranslations[]> {\n const list = await client.get<{ templates?: TemplateSummaryWire[] }>(\n `/api/v202604/themes/${themeId}/templates`,\n );\n const out: TemplateTranslations[] = [];\n for (const summary of list.templates ?? []) {\n if (NOT_PAGES.has(summary.themeable_type ?? \"\")) continue;\n const template = await readTemplateTranslations(\n client,\n themeId,\n summary.id,\n );\n if (Object.keys(template.translations).length > 0) out.push(template);\n }\n return out;\n}\n\n/** The keys a template's source locale has and the target lacks, as `<template>/<key>` ids. */\nexport function missingSectionKeys(\n templates: readonly TemplateTranslations[],\n sourceIso: string,\n iso: string,\n): {\n total: number;\n missing: Array<{\n id: string;\n templateId: number;\n key: string;\n source: string;\n }>;\n} {\n let total = 0;\n const missing: Array<{\n id: string;\n templateId: number;\n key: string;\n source: string;\n }> = [];\n for (const template of templates) {\n const source = localeIn(template.translations, sourceIso);\n if (!source) continue;\n total += Object.keys(missingKeys(source, null)).length;\n for (const [key, value] of Object.entries(\n missingKeys(source, localeIn(template.translations, iso)),\n )) {\n missing.push({\n id: `${template.templateId}/${key}`,\n templateId: template.templateId,\n key,\n source: value,\n });\n }\n }\n return { total, missing };\n}\n\n/** Parse a `<template id>/<key.path>` reference. */\nexport function parseSectionRef(ref: string): {\n templateId: number;\n key: string;\n} {\n const match = /^([1-9]\\d*)\\/(.+)$/.exec(ref.trim());\n if (!match) {\n throw new LocalizationError(\n `\"${ref}\" isn't a theme section string. Use <template id>/<key>, as \\`fluid translations missing\\` prints it, e.g. 412/hero.title.`,\n );\n }\n return { templateId: Number(match[1]), key: match[2]! };\n}\n\n/** A section string's source and translated text. */\nexport function sectionText(\n template: TemplateTranslations,\n key: string,\n sourceIso: string,\n iso: string,\n): { source: string | null; translation: string | null } {\n const source =\n flattenLocale(localeIn(template.translations, sourceIso) ?? {})[key] ??\n null;\n const translation =\n flattenLocale(localeIn(template.translations, iso) ?? {})[key] ?? null;\n return {\n source,\n translation:\n translation && translation.trim().length > 0 ? translation : null,\n };\n}\n\n/**\n * Save strings into one template's translations for `iso`. The template is\n * read again just before the write and the strings merged into what it holds\n * then, because the API replaces the whole translations object.\n */\nexport async function writeSectionStrings(\n client: LocalizationClient,\n themeId: number,\n templateId: number,\n iso: string,\n updates: Record<string, string>,\n): Promise<{ templateId: number; keys: string[] }> {\n const fresh = await readTemplateTranslations(client, themeId, templateId);\n const key =\n Object.keys(fresh.translations).find(\n (k) => k.toLowerCase() === iso.toLowerCase(),\n ) ?? iso;\n const merged = mergeLocale(fresh.translations[key] ?? {}, updates);\n await client.patch(`/api/application_theme_templates/${templateId}`, {\n application_theme_template: {\n translations: { ...fresh.translations, [key]: merged },\n },\n });\n return { templateId, keys: Object.keys(updates) };\n}\n","import {\n agreementTranslated,\n listAgreements,\n type CompanyAgreement,\n} from \"../shared/agreements.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n getLabelTitle,\n labelTranslated,\n listLabels,\n type Label,\n} from \"./labels.js\";\nimport { listMenuItems, type MenuItem } from \"./menus.js\";\nimport type { ResourceType, StorefrontType } from \"./resources.js\";\nimport { listTemplateTranslations, missingSectionKeys } from \"./sections.js\";\nimport { isTranslated, listAll, rowTitle, type Row } from \"./storefront.js\";\nimport {\n localeOf,\n missingKeys,\n readThemeLocales,\n resolveThemeId,\n sameAsSourceKeys,\n type ThemeLocales,\n} from \"./theme.js\";\n\nexport interface MissingEntry {\n /** What `show` and `set` take: a row id, `<menu>/<item>`, or a theme key. */\n id: number | string;\n /** The default-locale title, menu item title, or theme string. */\n source: string | null;\n status?: string | null;\n menu?: string | null;\n}\n\nexport interface TypeReport {\n type: string;\n label: string;\n total: number;\n missingCount: number;\n missing: MissingEntry[];\n /** Why a type couldn't be checked, e.g. the company has no active theme. */\n skipped?: string;\n /** Theme keys still word for word the source text, to review by eye. */\n sameAsSource?: { count: number; sample: string[] };\n}\n\nexport interface ReportOptions {\n /** Count only rows a shopper can see (status `published`). */\n publishedOnly: boolean;\n /** The locale the theme's source strings come from, e.g. `en`. */\n sourceIso: string;\n themeId?: string;\n}\n\n/**\n * What still needs translating into `iso`, per type. Storefront rows count as\n * translated once `languages` lists the locale; menu items once their title\n * has a value in the locale; theme keys once `locales/<iso>.json` has a\n * non-blank string for every key the source locale file has; theme page\n * strings the same way within each template's translations; agreements once\n * a read in the locale is served in it; labels once their title in the\n * locale differs from the default.\n */\nexport async function missingReport(\n client: LocalizationClient,\n iso: string,\n types: readonly ResourceType[],\n options: ReportOptions,\n): Promise<{ done: boolean; types: TypeReport[] }> {\n const reports: TypeReport[] = [];\n for (const type of types) {\n if (type.kind === \"storefront\") {\n reports.push(await storefrontReport(client, type, iso, options));\n } else if (type.kind === \"menu-items\") {\n reports.push(await menuReport(client, iso));\n } else if (type.kind === \"theme\") {\n reports.push(\n await unlessNoTheme(type, () => themeReport(client, iso, options)),\n );\n } else if (type.kind === \"theme-sections\") {\n reports.push(\n await unlessNoTheme(type, () => sectionsReport(client, iso, options)),\n );\n } else if (type.kind === \"agreements\") {\n reports.push(await agreementsReport(client, iso, options));\n } else {\n reports.push(await labelsReport(client, iso));\n }\n }\n return {\n done: reports.every((r) => r.missingCount === 0 && !r.skipped),\n types: reports,\n };\n}\n\n/** A theme type on a company with no theme to read is reported as skipped, not as a failure. */\nasync function unlessNoTheme(\n type: ResourceType,\n report: () => Promise<TypeReport>,\n): Promise<TypeReport> {\n try {\n return await report();\n } catch (error) {\n if (\n !(error instanceof LocalizationError) ||\n !/no active theme/.test(error.message)\n )\n throw error;\n return {\n type: type.type,\n label: type.label,\n total: 0,\n missingCount: 0,\n missing: [],\n skipped: error.message,\n };\n }\n}\n\n/** Whether shoppers can see a row: published (when it has a status) and not switched off. */\nexport function shopperVisible(row: Row): boolean {\n const status = row[\"status\"];\n if (typeof status === \"string\" && status !== \"published\") return false;\n return row[\"active\"] !== false;\n}\n\nasync function storefrontReport(\n client: LocalizationClient,\n type: StorefrontType,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const rows = (await listAll(client, type)).filter(\n (row) => !options.publishedOnly || shopperVisible(row),\n );\n const missing = rows\n .filter((row) => !isTranslated(row, iso))\n .map((row) => ({\n id: row.id,\n source: rowTitle(row),\n status:\n typeof row[\"status\"] === \"string\" ? (row[\"status\"] as string) : null,\n }));\n return {\n type: type.type,\n label: type.label,\n total: rows.length,\n missingCount: missing.length,\n missing,\n };\n}\n\n/** Menu items with a default title, and those with no title in `iso`. */\nexport async function missingMenuItems(\n client: LocalizationClient,\n iso: string,\n): Promise<{ titled: MenuItem[]; missing: MenuItem[] }> {\n const [source, translated] = await Promise.all([\n listMenuItems(client),\n listMenuItems(client, iso),\n ]);\n const have = new Map(translated.map((item) => [item.id, item.title]));\n const titled = source.filter((item) => item.title !== null);\n return { titled, missing: titled.filter((item) => !have.get(item.id)) };\n}\n\nasync function menuReport(\n client: LocalizationClient,\n iso: string,\n): Promise<TypeReport> {\n const { titled, missing } = await missingMenuItems(client, iso);\n return {\n type: \"menu-items\",\n label: \"Navigation menu items\",\n total: titled.length,\n missingCount: missing.length,\n missing: missing.map((item) => ({\n id: item.id,\n source: item.title,\n menu: item.menu,\n })),\n };\n}\n\n/** Theme keys the source locale has and `iso` lacks, with the source strings. */\nexport async function missingThemeKeys(\n client: LocalizationClient,\n iso: string,\n options: Pick<ReportOptions, \"sourceIso\" | \"themeId\">,\n): Promise<{\n themeId: number;\n locales: ThemeLocales;\n total: number;\n missing: Record<string, string>;\n sameAsSource: string[];\n skipped?: string;\n}> {\n const themeId = await resolveThemeId(client, options.themeId);\n const locales = await readThemeLocales(client, themeId);\n const source = localeOf(locales, options.sourceIso);\n if (!source) {\n return {\n themeId,\n locales,\n total: 0,\n missing: {},\n sameAsSource: [],\n skipped: `The theme has no locales/${options.sourceIso}.json to translate from. Pass --from with the theme's source locale.`,\n };\n }\n const target = localeOf(locales, iso);\n const missing = missingKeys(source, target);\n const total = Object.keys(missingKeys(source, null)).length;\n return {\n themeId,\n locales,\n total,\n missing,\n sameAsSource: sameAsSourceKeys(source, target),\n };\n}\n\nasync function themeReport(\n client: LocalizationClient,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const result = await missingThemeKeys(client, iso, options);\n return {\n type: \"theme\",\n label: `Theme ${result.themeId} strings (locales/${iso}.json)`,\n total: result.total,\n missingCount: Object.keys(result.missing).length,\n missing: Object.entries(result.missing).map(([key, value]) => ({\n id: key,\n source: value,\n })),\n ...(result.sameAsSource.length > 0\n ? {\n sameAsSource: {\n count: result.sameAsSource.length,\n sample: result.sameAsSource.slice(0, 20),\n },\n }\n : {}),\n ...(result.skipped ? { skipped: result.skipped } : {}),\n };\n}\n\n/** Theme page strings each template's source locale has and `iso` lacks. */\nexport async function missingSections(\n client: LocalizationClient,\n iso: string,\n options: Pick<ReportOptions, \"sourceIso\" | \"themeId\">,\n) {\n const themeId = await resolveThemeId(client, options.themeId);\n const templates = await listTemplateTranslations(client, themeId);\n return { themeId, ...missingSectionKeys(templates, options.sourceIso, iso) };\n}\n\nasync function sectionsReport(\n client: LocalizationClient,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const result = await missingSections(client, iso, options);\n return {\n type: \"theme-sections\",\n label: `Theme ${result.themeId} page strings (template translations)`,\n total: result.total,\n missingCount: result.missing.length,\n missing: result.missing.map((m) => ({ id: m.id, source: m.source })),\n };\n}\n\n/** Agreements with no translation in `iso`; with `publishedOnly`, active ones only. */\nexport async function missingAgreements(\n client: LocalizationClient,\n iso: string,\n publishedOnly: boolean,\n): Promise<{ checked: CompanyAgreement[]; missing: CompanyAgreement[] }> {\n const checked = (await listAgreements(client)).filter(\n (agreement) => !publishedOnly || agreement.active !== false,\n );\n const missing: CompanyAgreement[] = [];\n for (const agreement of checked) {\n if (!(await agreementTranslated(client, agreement.id, iso)))\n missing.push(agreement);\n }\n return { checked, missing };\n}\n\nasync function agreementsReport(\n client: LocalizationClient,\n iso: string,\n options: ReportOptions,\n): Promise<TypeReport> {\n const { checked, missing } = await missingAgreements(\n client,\n iso,\n options.publishedOnly,\n );\n return {\n type: \"agreements\",\n label: \"Agreements\",\n total: checked.length,\n missingCount: missing.length,\n missing: missing.map((a) => ({\n id: a.id,\n source: a.title ?? null,\n status: a.active === false ? \"inactive\" : \"active\",\n })),\n };\n}\n\n/** Labels whose title in `iso` still reads as the default title. */\nexport async function missingLabels(\n client: LocalizationClient,\n iso: string,\n): Promise<{ checked: Label[]; missing: Label[] }> {\n const checked = (await listLabels(client)).filter(\n (label) => label.title.trim().length > 0,\n );\n const missing: Label[] = [];\n for (const label of checked) {\n if (\n !labelTranslated(label.title, await getLabelTitle(client, label.id, iso))\n )\n missing.push(label);\n }\n return { checked, missing };\n}\n\nasync function labelsReport(\n client: LocalizationClient,\n iso: string,\n): Promise<TypeReport> {\n const { checked, missing } = await missingLabels(client, iso);\n return {\n type: \"labels\",\n label: \"Product labels\",\n total: checked.length,\n missingCount: missing.length,\n missing: missing.map((l) => ({ id: l.id, source: l.title })),\n };\n}\n","import {\n readAgreement,\n writeAgreementTranslation,\n} from \"../shared/agreements.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { getLabelTitle, writeLabelTitle } from \"./labels.js\";\nimport { machineTranslate } from \"./machine.js\";\nimport { writeMenuItemTitle } from \"./menus.js\";\nimport {\n missingAgreements,\n missingLabels,\n missingMenuItems,\n missingSections,\n missingThemeKeys,\n shopperVisible,\n} from \"./report.js\";\nimport { writeSectionStrings } from \"./sections.js\";\nimport type { ResourceType, StorefrontType } from \"./resources.js\";\nimport {\n getRow,\n isTranslated,\n listAll,\n pickFields,\n writeTranslation,\n} from \"./storefront.js\";\nimport { localeOf, mergeLocale, writeLocale } from \"./theme.js\";\n\nexport interface AutoOptions {\n /** The target locale's ISO code, as the API spells it. */\n iso: string;\n /**\n * Language names for the translator, e.g. \"English\" and \"French\".\n * Storefront rows and menu items are read in the store's default\n * language (`storeName`); theme strings come from the `sourceIso` file\n * (`sourceName`).\n */\n storeName: string;\n sourceName: string;\n targetName: string;\n sourceIso: string;\n /** The store's default language code, the one storefront text is read in. */\n storeIso: string;\n /** Most rows (or menu items, or theme keys) to translate per type. */\n limit: number;\n /** Save what was translated. Without it, nothing is written. */\n write: boolean;\n publishedOnly: boolean;\n /** `<type>:<id>` items to leave out, e.g. `products:42` or `theme:cart.title`. */\n skip?: ReadonlyArray<string>;\n themeId?: string;\n model?: string;\n}\n\nexport interface AutoEntry {\n id: number | string;\n fields: Record<string, string>;\n /** The text each field was translated from, for review. */\n source?: Record<string, string>;\n saved: boolean;\n}\n\nexport interface ItemFailure {\n id: number | string;\n error: string;\n status?: number;\n}\n\n/** Run one save; a refusal is recorded against the item instead of ending the run. */\nasync function save(\n failed: ItemFailure[],\n id: number | string,\n write: () => Promise<unknown>,\n): Promise<boolean> {\n try {\n await write();\n return true;\n } catch (error) {\n failed.push({ id, ...failureOf(error) });\n return false;\n }\n}\n\nexport interface AutoTypeResult {\n type: string;\n translated: AutoEntry[];\n /** Items the translator returned only some fields for; nothing was saved for them. */\n incomplete: Array<number | string>;\n /** Items whose save the API refused, with its message; counted in `remaining`. */\n failed: ItemFailure[];\n /** Missing items past `--limit`, left for the next run. */\n remaining: number;\n skipped?: string;\n}\n\nexport interface AutoResult {\n written: boolean;\n types: AutoTypeResult[];\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/**\n * Machine-translate what's missing in `iso` and, with `write`, save it.\n * Storefront rows translate their text fields from the default locale\n * (URL fields are left for a person); menu items translate their title;\n * theme keys translate from the source locale file and save in one write.\n */\nexport async function autoTranslate(\n client: LocalizationClient,\n types: readonly ResourceType[],\n options: AutoOptions,\n): Promise<AutoResult> {\n const usage = { inputTokens: 0, outputTokens: 0 };\n const results: AutoTypeResult[] = [];\n const call = async (sourceName: string, fields: Record<string, string>) => {\n const out = await machineTranslate(\n client,\n sourceName,\n options.targetName,\n fields,\n options.model,\n );\n usage.inputTokens += out.usage.inputTokens;\n usage.outputTokens += out.usage.outputTokens;\n return out.translations;\n };\n // Ask once more for any field the first answer left out, so a single\n // omission doesn't leave an item incomplete.\n const translator =\n (sourceName: string) => async (fields: Record<string, string>) => {\n const first = await call(sourceName, fields);\n const omitted = Object.fromEntries(\n Object.entries(fields).filter(\n ([id, v]) => v.trim().length > 0 && first[id] === undefined,\n ),\n );\n if (Object.keys(omitted).length === 0) return first;\n return { ...first, ...(await call(sourceName, omitted)) };\n };\n const skipped = new Set(options.skip ?? []);\n\n for (const type of types) {\n if (type.kind === \"storefront\") {\n results.push(\n await autoStorefront(\n client,\n type,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n } else if (type.kind === \"menu-items\") {\n results.push(\n await autoMenus(\n client,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n } else if (type.kind === \"theme\") {\n results.push(\n await autoTheme(\n client,\n options,\n skipped,\n translator(options.sourceName),\n ),\n );\n } else if (type.kind === \"theme-sections\") {\n results.push(\n await autoSections(\n client,\n options,\n skipped,\n translator(options.sourceName),\n ),\n );\n } else if (type.kind === \"agreements\") {\n results.push(\n await autoAgreements(\n client,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n } else {\n results.push(\n await autoLabels(\n client,\n options,\n skipped,\n translator(options.storeName),\n ),\n );\n }\n }\n return { written: options.write, types: results, usage };\n}\n\ntype Translate = (\n fields: Record<string, string>,\n) => Promise<Record<string, string>>;\n\nasync function autoStorefront(\n client: LocalizationClient,\n type: StorefrontType,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const rows = (await listAll(client, type)).filter(\n (row) =>\n !skipped.has(`${type.type}:${row.id}`) &&\n !isTranslated(row, options.iso) &&\n (!options.publishedOnly || shopperVisible(row)),\n );\n const picked = rows.slice(0, options.limit);\n const source: Record<string, string> = {};\n const fieldsOf = new Map<number, string[]>();\n for (const summary of picked) {\n // The list may abbreviate long fields, so read each row in full.\n const row = await getRow(client, type, summary.id);\n for (const [field, value] of Object.entries(\n pickFields(row, type.textFields),\n )) {\n if (value === null || value.trim().length === 0) continue;\n source[`${summary.id}:${field}`] = value;\n fieldsOf.set(summary.id, [...(fieldsOf.get(summary.id) ?? []), field]);\n }\n }\n const translations = await translate(source);\n const entries: AutoEntry[] = [];\n const incomplete: number[] = [];\n const failed: ItemFailure[] = [];\n for (const summary of picked) {\n const wanted = fieldsOf.get(summary.id) ?? [];\n if (wanted.length === 0) continue;\n const fields: Record<string, string> = {};\n for (const field of wanted) {\n const value = translations[`${summary.id}:${field}`];\n if (value !== undefined) fields[field] = value;\n }\n // Saving some fields would add the language to the row's `languages`,\n // and later runs would skip it with the rest still in English.\n if (Object.keys(fields).length < wanted.length) {\n incomplete.push(summary.id);\n continue;\n }\n if (\n options.write &&\n !(await save(failed, summary.id, () =>\n writeTranslation(client, type, summary.id, options.iso, fields),\n ))\n ) {\n continue;\n }\n entries.push({\n id: summary.id,\n fields,\n source: Object.fromEntries(\n wanted.map((f) => [f, source[`${summary.id}:${f}`] ?? \"\"]),\n ),\n saved: options.write,\n });\n }\n return {\n type: type.type,\n translated: entries,\n incomplete,\n failed,\n remaining: rows.length - picked.length + incomplete.length + failed.length,\n };\n}\n\nasync function autoMenus(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const missing = (await missingMenuItems(client, options.iso)).missing.filter(\n (item) => !skipped.has(`menu-items:${item.id}`),\n );\n return autoTitles(\n \"menu-items\",\n missing.map((item) => ({ id: item.id, title: item.title ?? \"\" })),\n options,\n translate,\n (id, title) => writeMenuItemTitle(client, String(id), options.iso, title),\n );\n}\n\nasync function autoLabels(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const { missing } = await missingLabels(client, options.iso);\n const pending = missing.filter((label) => !skipped.has(`labels:${label.id}`));\n // The list is in the caller's language; read each title in the store's own.\n const sources = [];\n for (const label of pending.slice(0, options.limit)) {\n const title = await getLabelTitle(client, label.id, options.storeIso);\n sources.push({ id: label.id, title: title || label.title });\n }\n const result = await autoTitles(\n \"labels\",\n sources,\n options,\n translate,\n (id, title) => writeLabelTitle(client, Number(id), options.iso, title),\n );\n return {\n ...result,\n remaining: result.remaining + pending.length - sources.length,\n };\n}\n\n/** Translate and save a title per item: menu items and labels. */\nasync function autoTitles(\n type: string,\n items: ReadonlyArray<{ id: number | string; title: string }>,\n options: AutoOptions,\n translate: Translate,\n write: (id: number | string, title: string) => Promise<unknown>,\n): Promise<AutoTypeResult> {\n const picked = items.slice(0, options.limit);\n const translations = await translate(\n Object.fromEntries(picked.map((item) => [String(item.id), item.title])),\n );\n const entries: AutoEntry[] = [];\n const incomplete: Array<number | string> = [];\n const failed: ItemFailure[] = [];\n for (const item of picked) {\n if (item.title.trim().length === 0) continue;\n const title = translations[String(item.id)];\n if (title === undefined) {\n incomplete.push(item.id);\n continue;\n }\n if (\n options.write &&\n !(await save(failed, item.id, () => write(item.id, title)))\n )\n continue;\n entries.push({\n id: item.id,\n fields: { title },\n source: { title: item.title },\n saved: options.write,\n });\n }\n return {\n type,\n translated: entries,\n incomplete,\n failed,\n remaining: items.length - picked.length + incomplete.length + failed.length,\n };\n}\n\nasync function autoAgreements(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const { missing } = await missingAgreements(\n client,\n options.iso,\n options.publishedOnly,\n );\n const pending = missing.filter((a) => !skipped.has(`agreements:${a.id}`));\n const picked = pending.slice(0, options.limit);\n const source: Record<string, string> = {};\n for (const agreement of picked) {\n const text = await readAgreement(client, agreement.id, options.storeIso);\n if (text.title.trim()) source[`${agreement.id}:title`] = text.title;\n if (text.description.trim())\n source[`${agreement.id}:description`] = text.description;\n }\n const translations = await translate(source);\n const entries: AutoEntry[] = [];\n const incomplete: number[] = [];\n const failed: ItemFailure[] = [];\n for (const agreement of picked) {\n const title = translations[`${agreement.id}:title`];\n const description = translations[`${agreement.id}:description`];\n // The API saves title and text together; one without the other can't be saved.\n if (title === undefined || description === undefined) {\n incomplete.push(agreement.id);\n continue;\n }\n if (\n options.write &&\n !(await save(failed, agreement.id, () =>\n writeAgreementTranslation(client, agreement.id, options.iso, {\n title,\n description,\n }),\n ))\n ) {\n continue;\n }\n entries.push({\n id: agreement.id,\n fields: { title, description },\n source: {\n title: source[`${agreement.id}:title`] ?? \"\",\n description: source[`${agreement.id}:description`] ?? \"\",\n },\n saved: options.write,\n });\n }\n return {\n type: \"agreements\",\n translated: entries,\n incomplete,\n failed,\n remaining:\n pending.length - picked.length + incomplete.length + failed.length,\n };\n}\n\nasync function autoTheme(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const found = await missingThemeKeys(client, options.iso, options);\n if (found.skipped) {\n return {\n type: \"theme\",\n translated: [],\n incomplete: [],\n failed: [],\n remaining: 0,\n skipped: found.skipped,\n };\n }\n const keys = Object.keys(found.missing).filter(\n (key) => !skipped.has(`theme:${key}`),\n );\n const picked = keys.slice(0, options.limit);\n const translations = await translate(\n Object.fromEntries(picked.map((key) => [key, found.missing[key] ?? \"\"])),\n );\n const updates = Object.fromEntries(\n picked\n .filter((key) => translations[key] !== undefined)\n .map((key) => [key, translations[key] as string]),\n );\n const failed: ItemFailure[] = [];\n let saved = false;\n if (options.write && Object.keys(updates).length > 0) {\n // One write saves every key; base_sha makes the server refuse it if the\n // theme changed since the read, and then every key in it is reported.\n const current = localeOf(found.locales, options.iso) ?? {};\n const attempt: ItemFailure[] = [];\n saved = await save(attempt, \"theme\", () =>\n writeLocale(\n client,\n found.locales,\n options.iso,\n mergeLocale(current, updates),\n ),\n );\n if (!saved) {\n for (const key of Object.keys(updates))\n failed.push({ ...attempt[0]!, id: key });\n }\n }\n const incomplete = picked.filter((key) => translations[key] === undefined);\n return {\n type: \"theme\",\n translated:\n saved || !options.write\n ? Object.entries(updates).map(([key, value]) => ({\n id: key,\n fields: { value },\n source: { value: found.missing[key] ?? \"\" },\n saved,\n }))\n : [],\n incomplete,\n failed,\n remaining: keys.length - picked.length + incomplete.length + failed.length,\n };\n}\n\n/** Theme page strings: translated per key, saved one template at a time. */\nasync function autoSections(\n client: LocalizationClient,\n options: AutoOptions,\n skipped: ReadonlySet<string>,\n translate: Translate,\n): Promise<AutoTypeResult> {\n const found = await missingSections(client, options.iso, options);\n const pending = found.missing.filter(\n (m) => !skipped.has(`theme-sections:${m.id}`),\n );\n const picked = pending.slice(0, options.limit);\n const translations = await translate(\n Object.fromEntries(picked.map((m) => [m.id, m.source])),\n );\n const byTemplate = new Map<number, Record<string, string>>();\n const incomplete: string[] = [];\n for (const m of picked) {\n const value = translations[m.id];\n if (value === undefined) {\n incomplete.push(m.id);\n continue;\n }\n byTemplate.set(m.templateId, {\n ...byTemplate.get(m.templateId),\n [m.key]: value,\n });\n }\n const entries: AutoEntry[] = [];\n const failed: ItemFailure[] = [];\n for (const [templateId, updates] of byTemplate) {\n const ok =\n !options.write ||\n (await save(failed, templateId, () =>\n writeSectionStrings(\n client,\n found.themeId,\n templateId,\n options.iso,\n updates,\n ),\n ));\n const keys = Object.entries(updates);\n if (!ok) {\n // A refused template fails every string in it.\n const last = failed[failed.length - 1]!;\n failed.splice(\n failed.length - 1,\n 1,\n ...keys.map(([key]) => ({ ...last, id: `${templateId}/${key}` })),\n );\n continue;\n }\n for (const [key, value] of keys) {\n const id = `${templateId}/${key}`;\n entries.push({\n id,\n fields: { value },\n source: { value: picked.find((m) => m.id === id)?.source ?? \"\" },\n saved: options.write,\n });\n }\n }\n return {\n type: \"theme-sections\",\n translated: entries,\n incomplete,\n failed,\n remaining:\n pending.length - picked.length + incomplete.length + failed.length,\n };\n}\n","// Save one reviewed translation, through the same calls as `set` and `auto`.\n\nimport { writeAgreementTranslation } from \"../shared/agreements.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { writeLabelTitle } from \"./labels.js\";\nimport { writeMenuItemTitle } from \"./menus.js\";\nimport type { ResourceType } from \"./resources.js\";\nimport { parseSectionRef, writeSectionStrings } from \"./sections.js\";\nimport { writeTranslation } from \"./storefront.js\";\nimport {\n localeOf,\n mergeLocale,\n readThemeLocales,\n resolveThemeId,\n writeLocale,\n} from \"./theme.js\";\n\n/**\n * One item's translated fields: a row's fields, a title, or a theme\n * string's `value`. Theme strings are read again just before the write, so\n * the theme's latest version is the one guarded.\n */\nexport async function saveTranslation(\n api: LocalizationClient,\n type: ResourceType,\n id: number | string,\n iso: string,\n fields: Record<string, string>,\n themeId?: string,\n): Promise<void> {\n switch (type.kind) {\n case \"storefront\":\n await writeTranslation(api, type, Number(id), iso, fields);\n return;\n case \"menu-items\":\n await writeMenuItemTitle(api, String(id), iso, fields[\"title\"] ?? \"\");\n return;\n case \"labels\":\n await writeLabelTitle(api, Number(id), iso, fields[\"title\"] ?? \"\");\n return;\n case \"agreements\":\n await writeAgreementTranslation(api, Number(id), iso, {\n title: fields[\"title\"] ?? \"\",\n description: fields[\"description\"] ?? \"\",\n });\n return;\n case \"theme\": {\n const locales = await readThemeLocales(\n api,\n await resolveThemeId(api, themeId),\n );\n const merged = mergeLocale(localeOf(locales, iso) ?? {}, {\n [String(id)]: fields[\"value\"] ?? \"\",\n });\n await writeLocale(api, locales, iso, merged);\n return;\n }\n case \"theme-sections\": {\n const { templateId, key } = parseSectionRef(String(id));\n await writeSectionStrings(\n api,\n await resolveThemeId(api, themeId),\n templateId,\n iso,\n {\n [key]: fields[\"value\"] ?? \"\",\n },\n );\n return;\n }\n }\n}\n","// `fluid translations walk [iso]`: translating a language as a conversation.\n// It shows what's missing per type, machine-translates a batch with Fluid\n// AI, and goes through the proposals one at a time: accept, edit or skip.\n// Each accepted item is saved on its own through the same calls as `set`.\n\nimport type { Prompter } from \"@fluid-app/fluid-cli\";\n\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { failureOf } from \"../shared/cli.js\";\nimport {\n enableLanguage,\n listLanguages,\n sameIso,\n type Language,\n} from \"../shared/languages.js\";\nimport { NAV_HELP } from \"@fluid-app/fluid-cli\";\nimport { autoLanguages, type AutoLanguages } from \"./auto-languages.js\";\nimport { autoTranslate, type AutoEntry } from \"./autotranslate.js\";\nimport { missingReport } from \"./report.js\";\nimport { RESOURCE_TYPES, type ResourceType } from \"./resources.js\";\nimport { saveTranslation } from \"./save.js\";\n\nexport interface TranslationWalkDeps {\n api: LocalizationClient;\n prompter: Prompter;\n}\n\nexport interface TranslationWalkOptions {\n iso?: string;\n /** The theme's source locale. */\n from?: string;\n themeId?: string;\n /** Proposals per Fluid AI call. */\n batch?: number;\n}\n\nexport interface TranslationWalkResult {\n language?: string;\n saved: string[];\n edited: string[];\n skipped: string[];\n failed: Array<{ id: string; error: string }>;\n stopped: boolean;\n}\n\nclass Stop extends Error {}\n\nconst FIELD_LABELS: Record<string, string> = { value: \"Text\" };\n\nexport async function walkTranslations(\n deps: TranslationWalkDeps,\n options: TranslationWalkOptions = {},\n): Promise<TranslationWalkResult> {\n const { api, prompter } = deps;\n const from = options.from ?? \"en\";\n const batch = options.batch ?? 10;\n const result: TranslationWalkResult = {\n saved: [],\n edited: [],\n skipped: [],\n failed: [],\n stopped: false,\n };\n prompter.note(NAV_HELP);\n\n try {\n const language = await pickLanguage(deps, options.iso);\n if (!language) {\n result.stopped = true;\n return result;\n }\n result.language = language.iso;\n\n const report = await missingReport(api, language.iso, RESOURCE_TYPES, {\n publishedOnly: false,\n sourceIso: from,\n themeId: options.themeId,\n });\n prompter.note(\n [\n `What isn't in ${language.name} yet:`,\n ...report.types.map(\n (t) =>\n ` ${t.label}: ${t.skipped ? `not checked (${t.skipped})` : `${t.missingCount} of ${t.total} missing`}`,\n ),\n ].join(\"\\n\"),\n );\n const pending = report.types.filter(\n (t) => t.missingCount > 0 && !t.skipped,\n );\n if (pending.length === 0) {\n prompter.note(`Everything is translated into ${language.name}.`);\n return result;\n }\n\n const types = pending.map(\n (t) => RESOURCE_TYPES.find((r) => r.type === t.type)!,\n );\n const languages = await resolveLanguages(deps, types, language.iso, from);\n if (!languages) {\n result.stopped = true;\n return result;\n }\n\n const skip = new Set<string>();\n for (const entry of pending) {\n const type = RESOURCE_TYPES.find((r) => r.type === entry.type)!;\n const start = await prompter.menu(\n `${entry.label}: ${entry.missingCount} missing.`,\n [\n { title: \"Translate with Fluid AI and review each\", value: \"go\" },\n { title: \"Skip this type\", value: \"skip\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (start === null || start === \"stop\") throw new Stop();\n if (start === \"skip\") continue;\n await reviewType(\n deps,\n type,\n languages,\n { ...options, from, batch },\n skip,\n result,\n );\n }\n prompter.note(\n `Saved ${result.saved.length} (${result.edited.length} edited), skipped ${result.skipped.length}, ${result.failed.length} refused. Check with: fluid translations missing ${language.iso}`,\n );\n } catch (error) {\n if (!(error instanceof Stop)) throw error;\n result.stopped = true;\n prompter.note(\"Stopped. Everything accepted so far is saved.\");\n }\n return result;\n}\n\nasync function pickLanguage(\n deps: TranslationWalkDeps,\n iso?: string,\n): Promise<Language | null> {\n const { api, prompter } = deps;\n const all = await listLanguages(api);\n let language = iso ? all.find((l) => sameIso(l.iso, iso)) : undefined;\n if (!language) {\n if (iso) prompter.note(`\"${iso}\" isn't a language Fluid supports.`);\n const reply = await prompter.ask({\n section: \"Language\",\n message: \"Which language are you translating into?\",\n kind: \"search\",\n hint: \"type to filter\",\n choices: [...all]\n .sort((a, b) => Number(b.active) - Number(a.active))\n .map((l) => ({\n title: `${l.name} (${l.iso})${l.active ? \"\" : \" — off\"}`,\n value: l.iso,\n })),\n canGoBack: false,\n });\n if (\"nav\" in reply) return null;\n language = all.find((l) => sameIso(l.iso, reply.value));\n if (!language) return null;\n }\n if (language.active) return language;\n const answer = await prompter.menu(\n `${language.name} isn't on for this company, so translations can't be saved to it.`,\n [\n { title: `Turn on ${language.name}`, value: \"enable\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (answer !== \"enable\") return null;\n return enableLanguage(api, language);\n}\n\n/** The languages to translate between; asks for the store's language when the rows can't say. */\nasync function resolveLanguages(\n deps: TranslationWalkDeps,\n types: readonly ResourceType[],\n iso: string,\n from: string,\n): Promise<AutoLanguages | null> {\n let storeLanguage: string | undefined;\n for (;;) {\n try {\n return await autoLanguages(deps.api, types, {\n iso,\n from,\n storeLanguage,\n write: true,\n });\n } catch (error) {\n if (\n storeLanguage !== undefined ||\n !/store's default language from its storefront rows/.test(\n String((error as Error).message),\n )\n ) {\n deps.prompter.note(failureOf(error).error);\n return null;\n }\n const reply = await deps.prompter.ask({\n section: \"Language\",\n message: \"Which language is your store's own content written in?\",\n kind: \"text\",\n hint: \"a code such as en\",\n canGoBack: false,\n });\n if (\"nav\" in reply) return null;\n storeLanguage = reply.value.trim();\n }\n }\n}\n\n/** Translate a type batch by batch, reviewing each proposal. */\nasync function reviewType(\n deps: TranslationWalkDeps,\n type: ResourceType,\n languages: AutoLanguages,\n options: Required<Pick<TranslationWalkOptions, \"from\" | \"batch\">> &\n TranslationWalkOptions,\n skip: Set<string>,\n result: TranslationWalkResult,\n): Promise<void> {\n const { api, prompter } = deps;\n for (;;) {\n const auto = await autoTranslate(api, [type], {\n iso: languages.target.iso,\n storeName: languages.store.name,\n sourceName: languages.source.name,\n targetName: languages.target.name,\n sourceIso: languages.source.iso,\n storeIso: languages.store.iso,\n limit: options.batch,\n write: false,\n publishedOnly: false,\n themeId: options.themeId,\n skip: [...skip],\n });\n const outcome = auto.types[0]!;\n for (const id of outcome.incomplete) {\n skip.add(`${type.type}:${id}`);\n prompter.note(\n `Fluid AI left part of ${type.label.toLowerCase()} ${id} untranslated; set it with \\`fluid translations set\\`.`,\n );\n }\n if (outcome.translated.length === 0) return;\n\n const moved = await reviewBatch(\n deps,\n type,\n outcome.translated,\n languages,\n options,\n skip,\n result,\n );\n if (moved === \"next-type\") return;\n const left = outcome.remaining - outcome.incomplete.length;\n if (left <= 0) return;\n const more = await prompter.menu(\n `${type.label}: ${left} more to translate.`,\n [\n { title: \"Translate the next batch\", value: \"more\" },\n { title: \"Next type\", value: \"next\" },\n { title: \"Stop\", value: \"stop\" },\n ],\n );\n if (more === null || more === \"stop\") throw new Stop();\n if (more === \"next\") return;\n }\n}\n\nasync function reviewBatch(\n deps: TranslationWalkDeps,\n type: ResourceType,\n entries: readonly AutoEntry[],\n languages: AutoLanguages,\n options: TranslationWalkOptions,\n skip: Set<string>,\n result: TranslationWalkResult,\n): Promise<\"done\" | \"next-type\"> {\n const { api, prompter } = deps;\n // What was last saved for each item, so going back shows (and re-saves) an edit, not the original proposal.\n const savedFields = new Map<number, Record<string, string>>();\n let i = 0;\n while (i < entries.length) {\n const proposal = entries[i]!;\n const entry = {\n ...proposal,\n fields: savedFields.get(i) ?? proposal.fields,\n };\n const key = `${type.type}:${entry.id}`;\n prompter.note(\n [\n `${type.label} ${entry.id} (${i + 1} of ${entries.length}):`,\n ...Object.entries(entry.fields).map(\n ([field, value]) =>\n ` ${FIELD_LABELS[field] ?? field}: ${clip(entry.source?.[field] ?? \"\")}\\n → ${clip(value)}`,\n ),\n ].join(\"\\n\"),\n );\n const reply = await prompter.ask({\n section: `${type.label} → ${languages.target.name}`,\n message: \"Save this translation?\",\n kind: \"select\",\n choices: [\n { title: \"Accept\", value: \"accept\" },\n { title: \"Edit\", value: \"edit\" },\n { title: \"Skip\", value: \"skip\" },\n ],\n canGoBack: i > 0,\n });\n if (\"nav\" in reply) {\n if (reply.nav === \"quit\") throw new Stop();\n if (reply.nav === \"back\") {\n i = Math.max(0, i - 1);\n continue;\n }\n if (reply.nav === \"skip\") {\n skipItem(key, skip, result);\n i += 1;\n continue;\n }\n const choice = await prompter.menu(\"What next?\", [\n { title: \"Continue\", value: \"continue\" },\n {\n title: `Skip the rest of ${type.label.toLowerCase()}`,\n value: \"next-type\",\n },\n { title: \"Stop\", value: \"stop\" },\n ]);\n if (choice === null || choice === \"stop\") throw new Stop();\n if (choice === \"next-type\") return \"next-type\";\n continue;\n }\n if (reply.value === \"skip\") {\n skipItem(key, skip, result);\n i += 1;\n continue;\n }\n let fields = entry.fields;\n if (reply.value === \"edit\") {\n const edited: Record<string, string> = {};\n for (const [field, value] of Object.entries(entry.fields)) {\n const answer = await prompter.ask({\n section: `${type.label} ${entry.id}`,\n message: FIELD_LABELS[field] ?? field,\n kind: \"text\",\n current: value,\n canGoBack: false,\n });\n if (\"nav\" in answer) {\n if (answer.nav === \"quit\") throw new Stop();\n edited[field] = value;\n } else {\n edited[field] = answer.value.trim() === \"\" ? value : answer.value;\n }\n }\n fields = edited;\n }\n try {\n await saveTranslation(\n api,\n type,\n entry.id,\n languages.target.iso,\n fields,\n options.themeId,\n );\n savedFields.set(i, fields);\n if (!result.saved.includes(key)) result.saved.push(key);\n if (reply.value === \"edit\" && !result.edited.includes(key))\n result.edited.push(key);\n const skippedAt = result.skipped.indexOf(key);\n if (skippedAt >= 0) result.skipped.splice(skippedAt, 1);\n // A retry that saves clears the earlier refusal.\n result.failed = result.failed.filter((f) => f.id !== key);\n skip.delete(key);\n } catch (error) {\n const { error: message } = failureOf(error);\n result.failed.push({ id: key, error: message });\n skip.add(key);\n prompter.note(`Not saved: ${message}`);\n }\n i += 1;\n }\n return \"done\";\n}\n\nfunction skipItem(\n key: string,\n skip: Set<string>,\n result: TranslationWalkResult,\n): void {\n skip.add(key);\n if (!result.skipped.includes(key)) result.skipped.push(key);\n}\n\n/** One line of a long value, for the review. */\nfunction clip(text: string, max = 200): string {\n const flat = text.replace(/\\s+/g, \" \").trim();\n return flat.length > max ? `${flat.slice(0, max)}…` : flat;\n}\n","import type { Command } from \"commander\";\nimport type { CommandContext } from \"@fluid-app/fluid-cli\";\nimport { assertInteractive, terminalPrompter } from \"@fluid-app/fluid-cli\";\n\nimport { listAgreements } from \"../shared/agreements.js\";\nimport {\n collect,\n confirmChange,\n failureOf,\n positiveInt,\n run,\n} from \"../shared/cli.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport { listLanguages } from \"../shared/languages.js\";\nimport { parseSettingAssignments } from \"../shared/parsing.js\";\nimport {\n catalogCountry,\n createAgreementFromAtlas,\n createCompanyCountry,\n findCompanyCountry,\n listCompanyCountries,\n listWarehouses,\n missingTranslations,\n requireCompanyCountry,\n updateCompanyCountry,\n writeTranslations,\n type CreatedAgreement,\n} from \"./api.js\";\nimport {\n atlasAgreement,\n countryCodeOf,\n fetchAtlas,\n launchChecklist,\n parseMode,\n requireAtlas,\n summarizeAtlas,\n type CountryAtlas,\n type Mode,\n} from \"./atlas.js\";\nimport { complianceView } from \"./compliance.js\";\nimport {\n applyFormsPlan,\n formsReport,\n listForms,\n mapAtlasFields,\n type FormsReport,\n} from \"./forms.js\";\nimport {\n companyCountryIn,\n currencyDecimals,\n DEFAULT_PRICE_FIELDS,\n listProductsWithVariants,\n parsePriceFields,\n parseRate,\n planConversion,\n PRICE_FIELDS,\n priceCoverage,\n priceEditorOwnsPrices,\n referenceRate,\n summarizePlans,\n writeConversion,\n type PlanOptions,\n} from \"./prices.js\";\nimport {\n agreementStates,\n languageStates,\n type AgreementState,\n} from \"./plan.js\";\nimport {\n applyOverrides,\n companyCountryFromAtlas,\n partialCountryUpdate,\n} from \"./seed.js\";\nimport { statusReport } from \"./status.js\";\nimport { walkCountry } from \"./walk.js\";\nimport { walkTranslations } from \"../translations/walk.js\";\n\nasync function client(ctx: CommandContext): Promise<LocalizationClient> {\n return (await ctx.getClient()) as unknown as LocalizationClient;\n}\n\ninterface OpenOptions {\n mode: string;\n warehouseId?: string;\n businessId?: string;\n entityRegistered?: boolean;\n settlementCurrency?: string;\n set: string[];\n}\n\n/** The company_country attributes `plan` shows and `open` sends. */\nasync function seededAttributes(\n api: LocalizationClient,\n iso: string,\n opts: OpenOptions,\n) {\n const mode: Mode = parseMode(opts.mode);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const country = await catalogCountry(api, countryCode);\n const attributes = applyOverrides(\n companyCountryFromAtlas(atlas, {\n countryId: country.id,\n mode,\n ...(opts.warehouseId\n ? { warehouseId: positiveInt(opts.warehouseId, \"--warehouse-id\") }\n : {}),\n ...(opts.businessId ? { businessId: opts.businessId } : {}),\n ...(opts.entityRegistered !== undefined\n ? { entityRegistered: opts.entityRegistered }\n : {}),\n ...(opts.settlementCurrency\n ? { settlementCurrency: opts.settlementCurrency }\n : {}),\n }),\n parseSettingAssignments(opts.set),\n );\n return { countryCode, atlas, country, mode, attributes };\n}\n\nfunction withOpenOptions(command: Command): Command {\n return command\n .requiredOption(\"--mode <mode>\", \"How you will operate: nfr, otg, or usd\")\n .option(\n \"--warehouse-id <id>\",\n \"The warehouse that fulfills orders for the country (NFR and OTG)\",\n )\n .option(\n \"--business-id <id>\",\n \"Your local tax or business registration number (OTG), e.g. a VAT ID\",\n )\n .option(\n \"--entity-registered\",\n \"You already have a legally registered local entity (OTG)\",\n )\n .option(\n \"--settlement-currency <code>\",\n \"Settle in another currency than the one you charge in, e.g. USD\",\n )\n .option(\n \"--set <key=value>\",\n \"Override a seeded value, e.g. tax_inclusive_pricing=false or legal_settings.cooling_off_period_days=30 (repeatable)\",\n collect,\n [],\n );\n}\n\n/** Check each agreement on the country for the atlas translations it lacks. */\nasync function withTranslationGaps(\n api: LocalizationClient,\n atlas: CountryAtlas,\n states: AgreementState[],\n): Promise<AgreementState[]> {\n return Promise.all(\n states.map(async (state) =>\n state.status === \"linked\" && state.agreementId !== null\n ? {\n ...state,\n missingTranslations: await missingTranslations(\n api,\n state.agreementId,\n atlasAgreement(atlas, state.localId),\n ),\n }\n : state,\n ),\n );\n}\n\n/** Price coverage of the active products in a country, bundles listed apart. */\nasync function countryPriceCoverage(api: LocalizationClient, iso: string) {\n return priceCoverage(await listProductsWithVariants(api, [\"active\"]), iso);\n}\n\n/** The country's enrollment form report, or the reason it couldn't be\n * read (a token without forms access), so status still runs. */\nasync function countryEnrollment(\n api: LocalizationClient,\n countryCode: string,\n atlas: CountryAtlas,\n): Promise<FormsReport | { unavailable: string }> {\n try {\n return formsReport({\n countryCode,\n country: await catalogCountry(api, countryCode),\n atlas,\n forms: await listForms(api),\n });\n } catch (error) {\n return { unavailable: failureOf(error).error };\n }\n}\n\nexport function registerCountriesCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n parent\n .command(\"atlas <iso>\")\n .description(\n \"Show Fluid's Country Atlas for a market: modes, tax, legal, address, payments, languages, agreements\",\n )\n .option(\n \"--agreement <localId>\",\n \"Print one atlas agreement's full text in every language instead\",\n )\n .action((iso: string, opts: { agreement?: string }) =>\n run(async () => {\n const result = await fetchAtlas(await client(ctx), iso);\n if (!result.covered || !result.atlas) {\n ctx.output({\n country: result.countryCode,\n covered: false,\n note: \"Fluid publishes no Country Atlas for this market yet. Set it up by hand and don't assume tax, legal, or agreement defaults.\",\n });\n return;\n }\n if (opts.agreement) {\n ctx.output({\n country: result.countryCode,\n covered: true,\n agreement: atlasAgreement(result.atlas, opts.agreement),\n });\n return;\n }\n ctx.output({\n country: result.countryCode,\n covered: true,\n ...summarizeAtlas(result.atlas),\n });\n }),\n );\n\n parent\n .command(\"compliance <iso>\")\n .description(\n \"Show the compliance view of the atlas: disclosure pages, cookie rule, tax display, unit pricing\",\n )\n .action((iso: string) =>\n run(async () => {\n const { countryCode, atlas } = await requireAtlas(\n await client(ctx),\n iso,\n );\n ctx.output(complianceView(countryCode, atlas));\n }),\n );\n\n parent\n .command(\"list\")\n .description(\"List the countries this company sells in\")\n .action(() =>\n run(async () => {\n const countries = await listCompanyCountries(await client(ctx));\n ctx.output(\n countries.map((cc) => ({\n companyCountryId: cc.id,\n iso: cc.country?.iso ?? null,\n name: cc.country?.name ?? null,\n mode: cc.mode ?? null,\n currency: cc.currency ?? null,\n taxName: cc.tax_name ?? null,\n taxInclusivePricing: cc.tax_inclusive_pricing ?? null,\n requires3ds: cc.requires_3ds ?? null,\n testMode: cc.test_mode ?? null,\n warehouse: cc.warehouse\n ? { id: cc.warehouse.id, name: cc.warehouse.name ?? null }\n : null,\n })),\n );\n }),\n );\n\n withOpenOptions(\n parent\n .command(\"plan <iso>\")\n .description(\n \"Preview a country setup without changing anything: the seeded settings, languages, agreements, and checklist\",\n ),\n ).action((iso: string, opts: OpenOptions) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas, mode, attributes } = await seededAttributes(\n api,\n iso,\n opts,\n );\n const [existing, languages, agreements, warehouses] = await Promise.all([\n findCompanyCountry(api, countryCode),\n listLanguages(api),\n listAgreements(api),\n listWarehouses(api),\n ]);\n ctx.output({\n country: countryCode,\n mode,\n alreadyOpen: existing\n ? { companyCountryId: existing.id, mode: existing.mode ?? null }\n : false,\n companyCountry: attributes,\n languages: languageStates(atlas, languages),\n agreements: agreementStates(atlas, agreements, existing?.id ?? null),\n warehouses: warehouses.map((w) => ({\n id: w.id,\n name: w.name,\n country: w.country?.iso ?? null,\n active: w.active ?? null,\n })),\n launchChecklist: launchChecklist(atlas, mode),\n });\n }),\n );\n\n withOpenOptions(\n parent\n .command(\"open <iso>\")\n .description(\n \"Add the country to the company, seeded from the atlas: currency, mode, tax, 3-D Secure, legal, address layout, payment order (changes live config)\",\n )\n .option(\n \"--legacy-defaults\",\n \"Also let Fluid add its generic country agreements and forms\",\n false,\n )\n .option(\n \"--reseed\",\n \"If the country is already open, write the atlas values over its current settings\",\n false,\n )\n .option(\"--yes\", \"Confirm without a prompt\", false),\n ).action(\n (\n iso: string,\n opts: OpenOptions & {\n legacyDefaults: boolean;\n reseed: boolean;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, attributes } = await seededAttributes(\n api,\n iso,\n opts,\n );\n const existing = await findCompanyCountry(api, countryCode);\n if (existing && !opts.reseed) {\n ctx.output({\n country: countryCode,\n alreadyOpen: true,\n companyCountryId: existing.id,\n note: \"Nothing changed. Pass --reseed to write the atlas values over the current settings.\",\n });\n return;\n }\n if (existing) {\n await confirmChange(\n `Overwrite ${countryCode}'s settings with the atlas values? Changes an admin made are replaced.`,\n opts.yes,\n );\n const updated = await updateCompanyCountry(\n api,\n existing.id,\n attributes,\n existing.revision,\n );\n ctx.output({\n country: countryCode,\n reseeded: true,\n companyCountry: updated,\n });\n return;\n }\n await confirmChange(\n `Open ${countryCode} for this company in ${String(attributes[\"mode\"])} mode?`,\n opts.yes,\n );\n const created = await createCompanyCountry(\n api,\n attributes,\n opts.legacyDefaults,\n );\n ctx.output({\n country: countryCode,\n opened: true,\n companyCountry: created,\n });\n }),\n );\n\n parent\n .command(\"update <iso>\")\n .description(\n \"Change named settings on an open country and leave the rest as they are, e.g. its entity or settlement currency (changes live config)\",\n )\n .option(\n \"--warehouse-id <id>\",\n \"The warehouse that fulfills orders for the country\",\n )\n .option(\n \"--business-id <id>\",\n \"The local tax or business registration number\",\n )\n .option(\n \"--entity-registered\",\n \"The company has a legally registered local entity\",\n )\n .option(\"--settlement-currency <code>\", \"Settle in this currency, e.g. USD\")\n .option(\n \"--set <key=value>\",\n \"Set a company country field, e.g. entity_id=12 or legal_settings.cooling_off_period_days=30 (repeatable)\",\n collect,\n [],\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((iso: string, opts: Omit<OpenOptions, \"mode\"> & { yes: boolean }) =>\n run(async () => {\n const api = await client(ctx);\n const countryCode = countryCodeOf(iso);\n const existing = await requireCompanyCountry(api, countryCode);\n const body = partialCountryUpdate(existing, {\n ...(opts.warehouseId\n ? { warehouse_id: positiveInt(opts.warehouseId, \"--warehouse-id\") }\n : {}),\n ...(opts.businessId ? { tax_business_id: opts.businessId } : {}),\n ...(opts.entityRegistered ? { entity_legally_registered: true } : {}),\n ...(opts.settlementCurrency\n ? { settlement_currency: opts.settlementCurrency.toUpperCase() }\n : {}),\n ...parseSettingAssignments(opts.set),\n });\n await confirmChange(\n `Change ${Object.keys(body).join(\", \")} on ${countryCode}?`,\n opts.yes,\n );\n ctx.output({\n country: countryCode,\n updated: Object.keys(body),\n companyCountry: await updateCompanyCountry(\n api,\n existing.id,\n body,\n existing.revision,\n ),\n });\n }),\n );\n\n parent\n .command(\"languages <iso>\")\n .description(\n \"Compare the market's major languages with the company's; `fluid translations enable` turns the missing ones on\",\n )\n .action((iso: string) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const states = languageStates(atlas, await listLanguages(api));\n const off = states.filter((s) => !s.enabled && s.languageId !== null);\n ctx.output({\n country: countryCode,\n done: states.every((s) => s.enabled),\n languages: states,\n ...(off.length > 0\n ? {\n next: `fluid translations enable ${off.map((s) => s.iso).join(\" \")} --yes`,\n }\n : {}),\n });\n }),\n );\n\n parent\n .command(\"agreements <iso>\")\n .description(\n \"Match the atlas agreements to the company's; --create adds the missing ones to the country in every atlas language, --translate rewrites the atlas translations onto ones already on it (changes live config)\",\n )\n .option(\n \"--create\",\n \"Create each missing atlas agreement on the country\",\n false,\n )\n .option(\n \"--translate\",\n \"Write the atlas translations onto the agreements already on the country, e.g. after enabling a language\",\n false,\n )\n .option(\n \"--only <localId>\",\n \"With --create or --translate, just this atlas agreement, e.g. agreement_2 (repeatable)\",\n collect,\n [],\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n iso: string,\n opts: {\n create: boolean;\n translate: boolean;\n only: string[];\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const cc = await requireCompanyCountry(api, countryCode);\n const states = agreementStates(\n atlas,\n await listAgreements(api),\n cc.id,\n );\n if (opts.create && opts.translate)\n throw new LocalizationError(\n \"Use --create or --translate, not both.\",\n );\n if (opts.only.length > 0 && !opts.create && !opts.translate) {\n throw new LocalizationError(\n \"--only goes with --create or --translate.\",\n );\n }\n for (const id of opts.only) atlasAgreement(atlas, id);\n const picked = (s: { localId: string }) =>\n opts.only.length === 0 || opts.only.includes(s.localId);\n if (opts.translate) {\n const toTranslate = states.filter(\n (s) =>\n (s.status === \"linked\" || s.status === \"inactive\") &&\n s.agreementId !== null &&\n picked(s),\n );\n const withTranslations = toTranslate.filter((s) =>\n atlasAgreement(atlas, s.localId).translations.some(\n (t) => t.language_iso !== \"en\",\n ),\n );\n if (withTranslations.length === 0) {\n ctx.output({\n country: countryCode,\n translated: [],\n agreements: states,\n });\n return;\n }\n await confirmChange(\n `Write the atlas translations onto ${withTranslations.length} agreement(s)? Translations an admin edited are replaced.`,\n opts.yes,\n );\n const translated = [];\n for (const state of withTranslations) {\n const result = await writeTranslations(\n api,\n state.agreementId!,\n atlasAgreement(atlas, state.localId),\n );\n translated.push({\n localId: state.localId,\n id: state.agreementId,\n ...result,\n });\n }\n ctx.output({ country: countryCode, translated });\n if (translated.some((t) => t.failedTranslations.length > 0))\n process.exitCode = 1;\n return;\n }\n if (!opts.create) {\n ctx.output({\n country: countryCode,\n done: states.every((s) => s.status === \"linked\"),\n agreements: states,\n });\n return;\n }\n const toCreate = states.filter(\n (s) => s.status === \"missing\" && picked(s),\n );\n if (toCreate.length === 0) {\n ctx.output({\n country: countryCode,\n created: [],\n agreements: states,\n });\n return;\n }\n await confirmChange(\n `Create ${toCreate.length} agreement(s) on ${countryCode}: ${toCreate.map((s) => `\"${s.title}\"`).join(\", \")}?`,\n opts.yes,\n );\n const created: CreatedAgreement[] = [];\n const failed: Array<{ localId: string; error: string }> = [];\n for (const state of toCreate) {\n try {\n created.push(\n await createAgreementFromAtlas(\n api,\n atlasAgreement(atlas, state.localId),\n cc.id,\n ),\n );\n } catch (error) {\n failed.push({\n localId: state.localId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n ctx.output({\n country: countryCode,\n created,\n failed,\n agreements: agreementStates(\n atlas,\n await listAgreements(api),\n cc.id,\n ),\n });\n if (\n failed.length > 0 ||\n created.some((c) => c.failedTranslations.length > 0)\n )\n process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"forms <iso>\")\n .description(\n \"Compare the atlas enrollment fields with the country's Post Purchase forms; --create sets up its enrollment form, as a draft unless --activate (changes live config)\",\n )\n .option(\n \"--create\",\n \"Create the country's enrollment form, or add the missing atlas fields to it\",\n false,\n )\n .option(\n \"--form <id>\",\n \"With --create, add to this Post Purchase form for the country instead\",\n )\n .option(\n \"--activate\",\n \"With --create, publish the form so enrollment asks for its fields now\",\n false,\n )\n .option(\n \"--skip-sensitive\",\n \"With --create, leave out fields stored as sensitive, which need the forms manage-sensitive permission\",\n false,\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n iso: string,\n opts: {\n create: boolean;\n form?: string;\n activate: boolean;\n skipSensitive: boolean;\n yes: boolean;\n },\n ) =>\n run(async () => {\n if (\n !opts.create &&\n (opts.form || opts.activate || opts.skipSensitive)\n )\n throw new LocalizationError(\n \"--form, --activate and --skip-sensitive go with --create.\",\n );\n const api = await client(ctx);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const country = await catalogCountry(api, countryCode);\n const options = {\n ...(opts.form ? { formId: positiveInt(opts.form, \"--form\") } : {}),\n activate: opts.activate,\n skipSensitive: opts.skipSensitive,\n };\n const report = formsReport({\n countryCode,\n country,\n atlas,\n forms: await listForms(api),\n options,\n });\n if (!opts.create || report.plan.action === \"none\") {\n ctx.output({ ...report, written: false });\n return;\n }\n const { plan } = report;\n const what =\n plan.action === \"create\"\n ? `Create \"${plan.title}\" as a ${plan.status} Post Purchase form for ${countryCode}`\n : `Change form ${plan.formId} (\"${plan.title}\")`;\n const parts = [\n ...(plan.add.length > 0\n ? [\n `with ${plan.add.length} field(s): ${plan.add.map((f) => `\"${f.label}\"${f.sensitive ? \" (sensitive)\" : \"\"}`).join(\", \")}`,\n ]\n : []),\n ...(plan.action === \"update\" && plan.activate\n ? [\"and publish it, so enrollment asks for its fields\"]\n : []),\n ];\n await confirmChange(`${what} ${parts.join(\" \")}?`, opts.yes);\n const result = await applyFormsPlan(\n api,\n report,\n mapAtlasFields(atlas.enrollmentFormFields ?? []),\n );\n const after = formsReport({\n countryCode,\n country,\n atlas,\n forms: await listForms(api),\n ...(options.formId !== undefined\n ? { options: { formId: options.formId } }\n : {}),\n });\n ctx.output({\n ...after,\n written: result.action !== \"none\",\n ...result,\n });\n }),\n );\n\n parent\n .command(\"prices <iso>\")\n .description(\n \"Convert every variant's price from another country into this one's currency at an exchange rate. A dry run unless --write (changes live prices)\",\n )\n .option(\n \"--from-country <iso>\",\n \"The country whose prices are converted\",\n \"US\",\n )\n .option(\n \"--rate <rate>\",\n \"Units of the new currency per unit of the source currency, e.g. 0.92; --write needs it\",\n )\n .option(\n \"--fields <list>\",\n `Price fields to convert, comma-separated, from ${PRICE_FIELDS.join(\", \")}`,\n DEFAULT_PRICE_FIELDS.join(\",\"),\n )\n .option(\n \"--status <status>\",\n \"Product status to include (repeatable)\",\n collect,\n [],\n )\n .option(\"--overwrite\", \"Also replace prices the country already has\", false)\n .option(\n \"--activate\",\n \"Put each converted variant on sale in the country\",\n false,\n )\n .option(\"--sample <n>\", \"How many before→after examples to print\", \"10\")\n .option(\n \"--write\",\n \"Save the converted prices; without it nothing is written\",\n false,\n )\n .option(\"--yes\", \"Confirm --write without a prompt\", false)\n .action(\n (\n iso: string,\n opts: {\n fromCountry: string;\n rate?: string;\n fields: string;\n status: string[];\n overwrite: boolean;\n activate: boolean;\n sample: string;\n write: boolean;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const targetIso = countryCodeOf(iso);\n const sourceIso = countryCodeOf(opts.fromCountry);\n if (targetIso === sourceIso)\n throw new LocalizationError(\n \"--from-country is the country being priced.\",\n );\n const fields = parsePriceFields(opts.fields);\n const sample = positiveInt(opts.sample, \"--sample\");\n const statuses = opts.status.length > 0 ? opts.status : [\"active\"];\n const countries = await listCompanyCountries(api);\n const target = companyCountryIn(countries, targetIso);\n if (!target?.country) {\n throw new LocalizationError(\n `${targetIso} isn't open for this company yet. Run \\`fluid countries open ${targetIso} --mode <nfr|otg|usd>\\` first.`,\n );\n }\n const source = companyCountryIn(countries, sourceIso);\n if (!source)\n throw new LocalizationError(\n `${sourceIso} isn't one of this company's countries, so it has no prices to convert.`,\n );\n const targetCurrency = (target.currency ?? \"\").toUpperCase();\n const sourceCurrency = (source.currency ?? \"\").toUpperCase();\n if (!targetCurrency || !sourceCurrency) {\n throw new LocalizationError(\n \"Both countries need a currency. Check them with `fluid countries list`.\",\n );\n }\n\n let rate: { value: number; source: string; date?: string };\n if (opts.rate !== undefined) {\n rate = { value: parseRate(opts.rate), source: \"--rate\" };\n } else if (sourceCurrency === targetCurrency) {\n rate = { value: 1, source: \"same currency\" };\n } else {\n const reference = await referenceRate(\n sourceCurrency,\n targetCurrency,\n );\n if (opts.write) {\n throw new LocalizationError(\n `--write needs the rate you reviewed: pass --rate.${reference ? ` The ${reference.date} reference rate is ${reference.rate} ${targetCurrency} per ${sourceCurrency}.` : \"\"}`,\n );\n }\n if (!reference) {\n throw new LocalizationError(\n `Couldn't look up a ${sourceCurrency} to ${targetCurrency} reference rate. Pass --rate.`,\n );\n }\n rate = {\n value: reference.rate,\n source: reference.source,\n date: reference.date,\n };\n }\n\n const priceEditor = await priceEditorOwnsPrices(api);\n if (priceEditor === null && opts.overwrite) {\n throw new LocalizationError(\n \"Your token can't read the company profile, so it can't tell whether the price editor manages your prices. --overwrite needs to know; run it with a token that can view the company.\",\n );\n }\n if (priceEditor && opts.overwrite) {\n throw new LocalizationError(\n \"This company's prices are managed in the price editor, so --overwrite could replace prices set there. Change existing prices in the price editor; without --overwrite this command fills only prices the country doesn't have.\",\n );\n }\n const planOptions: PlanOptions = {\n sourceIso,\n targetIso,\n targetCountryId: target.country.id,\n targetCurrency,\n rate: rate.value,\n fields,\n overwrite: opts.overwrite,\n activate: opts.activate,\n };\n const plans = planConversion(\n await listProductsWithVariants(api, statuses),\n planOptions,\n );\n const summary = summarizePlans(plans, sample);\n const header = {\n country: targetIso,\n from: { country: sourceIso, currency: sourceCurrency },\n to: { country: targetIso, currency: targetCurrency },\n rate,\n rounding: {\n decimals: currencyDecimals(targetCurrency),\n mode: \"half up\",\n },\n fields,\n statuses,\n priceEditor,\n };\n if (!opts.write || summary.toConvert === 0) {\n ctx.output({\n ...header,\n written: false,\n ...summary,\n ...(summary.toConvert > 0\n ? {\n next: `fluid countries prices ${targetIso} --from-country ${sourceIso} --rate ${rate.value} --fields ${fields.join(\",\")} --write --yes`,\n }\n : {}),\n });\n return;\n }\n await confirmChange(\n `Write ${summary.toConvert} converted price(s) into ${targetIso} (${targetCurrency}) at ${rate.value} ${targetCurrency} per ${sourceCurrency}?`,\n opts.yes,\n );\n const result = await writeConversion(api, plans, planOptions);\n ctx.output({\n ...header,\n written: true,\n ...summary,\n converted: result.written.length,\n changedSincePreview: result.changedSincePreview,\n failed: result.failed,\n refusedByPriceEditor: result.failed.filter(\n (f) => f.refusedByPriceEditor,\n ).length,\n });\n if (result.failed.length > 0) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"walk [iso]\")\n .description(\n \"Open a country by answering questions: the atlas, mode, languages, agreements and defaults, then prices and translations (changes live config)\",\n )\n .action((iso: string | undefined) =>\n run(async () => {\n assertInteractive(\n \"`plan`, `open`, `prices`, `agreements` and `fluid translations enable`\",\n );\n const api = await client(ctx);\n const prompter = terminalPrompter();\n const result = await walkCountry(\n {\n api,\n prompter,\n translate: (language) =>\n walkTranslations({ api, prompter }, { iso: language }),\n },\n iso ? { iso } : {},\n );\n ctx.output(result);\n if (result.failures.length > 0) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"status <iso>\")\n .description(\n \"Check a country's setup against the atlas, including whether your active products have prices in it, and list what's left, ending with the launch checklist\",\n )\n .action((iso: string) =>\n run(async () => {\n const api = await client(ctx);\n const { countryCode, atlas } = await requireAtlas(api, iso);\n const cc = await findCompanyCountry(api, countryCode);\n const [languages, agreements] = cc\n ? await Promise.all([listLanguages(api), listAgreements(api)])\n : [[], []];\n const enrollment = cc\n ? await countryEnrollment(api, countryCode, atlas)\n : undefined;\n ctx.output(\n statusReport({\n countryCode,\n atlas,\n companyCountry: cc,\n languages: languageStates(atlas, languages),\n agreements: await withTranslationGaps(\n api,\n atlas,\n agreementStates(atlas, agreements, cc?.id ?? null),\n ),\n ...(cc\n ? {\n prices: await countryPriceCoverage(api, countryCode),\n }\n : {}),\n ...(enrollment ? { enrollment } : {}),\n }),\n );\n }),\n );\n}\n","import type { Command } from \"commander\";\nimport type { CommandContext } from \"@fluid-app/fluid-cli\";\nimport { assertInteractive, terminalPrompter } from \"@fluid-app/fluid-cli\";\n\nimport {\n readAgreement,\n writeAgreementTranslation,\n} from \"../shared/agreements.js\";\n\nimport {\n collect,\n confirmChange,\n failureOf,\n positiveInt,\n run,\n} from \"../shared/cli.js\";\nimport type { LocalizationClient } from \"../shared/client.js\";\nimport { LocalizationError } from \"../shared/errors.js\";\nimport {\n disableLanguage,\n enableLanguage,\n findLanguage,\n listLanguages,\n requireActiveLanguage,\n type Language,\n} from \"../shared/languages.js\";\nimport { collectValues } from \"../shared/parsing.js\";\nimport { autoLanguages } from \"./auto-languages.js\";\nimport { autoTranslate } from \"./autotranslate.js\";\nimport { walkTranslations } from \"./walk.js\";\nimport { getLabelTitle, labelTranslated, writeLabelTitle } from \"./labels.js\";\nimport { getMenuItem, writeMenuItemTitle } from \"./menus.js\";\nimport { missingReport } from \"./report.js\";\nimport {\n checkFields,\n RESOURCE_TYPES,\n resourceType,\n selectTypes,\n TYPE_NAMES,\n} from \"./resources.js\";\nimport {\n parseSectionRef,\n readTemplateTranslations,\n sectionText,\n writeSectionStrings,\n} from \"./sections.js\";\nimport {\n getRow,\n isTranslated,\n pickFields,\n writeTranslation,\n} from \"./storefront.js\";\nimport {\n flattenLocale,\n localeOf,\n mergeLocale,\n readThemeLocales,\n resolveThemeId,\n writeLocale,\n} from \"./theme.js\";\n\nasync function client(ctx: CommandContext): Promise<LocalizationClient> {\n return (await ctx.getClient()) as unknown as LocalizationClient;\n}\n\nfunction rowId(raw: string | undefined, type: string): number {\n const value = Number(raw);\n if (raw === undefined || !Number.isInteger(value) || value <= 0) {\n throw new LocalizationError(\n `Pass the ${type} row id, as \\`fluid translations missing\\` prints it.`,\n );\n }\n return value;\n}\n\n/** `--skip` items must match a report id exactly: `products:42`, `menu-items:12/340`, `theme:cart.title`. */\nexport function validSkip(item: string): boolean {\n const at = item.indexOf(\":\");\n if (at <= 0) return false;\n const type = item.slice(0, at);\n const id = item.slice(at + 1);\n if (!TYPE_NAMES.includes(type)) return false;\n if (type === \"theme\") return id.length > 0;\n if (type === \"menu-items\") return /^[1-9]\\d*\\/[1-9]\\d*$/.test(id);\n if (type === \"theme-sections\") return /^[1-9]\\d*\\/.+$/.test(id);\n return /^[1-9]\\d*$/.test(id);\n}\n\nfunction requireLang(lang: string | undefined): string {\n if (!lang)\n throw new LocalizationError(\n \"Pass the language with --lang <iso>, e.g. --lang fr.\",\n );\n return lang;\n}\n\nexport function registerTranslationsCommands(\n parent: Command,\n ctx: CommandContext,\n): void {\n parent\n .command(\"languages\")\n .description(\n \"List every language Fluid supports and whether it's on for this company\",\n )\n .option(\"--active\", \"Only the languages that are on\", false)\n .action((opts: { active: boolean }) =>\n run(async () =>\n ctx.output(await listLanguages(await client(ctx), opts.active)),\n ),\n );\n\n parent\n .command(\"enable <iso...>\")\n .description(\n \"Turn one or more languages on so shoppers can choose them and translations can be saved (changes live config)\",\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((isos: string[], opts: { yes: boolean }) =>\n run(async () => {\n const api = await client(ctx);\n const all = await listLanguages(api);\n const wanted: Language[] = [];\n for (const iso of isos) {\n const language = all.find(\n (l) => l.iso.toLowerCase() === iso.trim().toLowerCase(),\n );\n // findLanguage names close matches for a code Fluid doesn't have.\n wanted.push(language ?? (await findLanguage(api, iso)));\n }\n const off = wanted.filter(\n (l, i) => !l.active && wanted.indexOf(l) === i,\n );\n const alreadyActive = wanted.filter((l) => l.active).map((l) => l.iso);\n if (off.length === 0) {\n ctx.output({ enabled: [], alreadyActive, failed: [] });\n return;\n }\n await confirmChange(\n `Turn on ${off.map((l) => `${l.name} (${l.iso})`).join(\", \")} for this company?`,\n opts.yes,\n );\n const enabled: string[] = [];\n const failed: Array<{ iso: string; error: string; status?: number }> =\n [];\n for (const language of off) {\n try {\n enabled.push((await enableLanguage(api, language)).iso);\n } catch (error) {\n failed.push({ iso: language.iso, ...failureOf(error) });\n }\n }\n ctx.output({ enabled, alreadyActive, failed });\n if (failed.length > 0) process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"disable <iso>\")\n .description(\n \"Turn a language off; its saved translations are kept (changes live config)\",\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action((iso: string, opts: { yes: boolean }) =>\n run(async () => {\n const api = await client(ctx);\n const language = await findLanguage(api, iso);\n if (!language.active) {\n ctx.output({ ...language, alreadyInactive: true });\n return;\n }\n await confirmChange(\n `Turn off ${language.name} (${language.iso}) for this company?`,\n opts.yes,\n );\n ctx.output(await disableLanguage(api, language));\n }),\n );\n\n parent\n .command(\"types\")\n .description(\n \"List the translatable types and the fields each one translates\",\n )\n .action(() =>\n run(async () =>\n ctx.output(\n RESOURCE_TYPES.map((t) => ({\n type: t.type,\n label: t.label,\n fields: t.fields,\n machineTranslated: t.textFields,\n })),\n ),\n ),\n );\n\n parent\n .command(\"missing <iso>\")\n .description(\n \"Report what isn't translated into a language yet, per type, so you can fill only the gaps\",\n )\n .option(\n \"--type <type>\",\n `Limit to a type: ${TYPE_NAMES.join(\", \")} (repeatable)`,\n collect,\n [],\n )\n .option(\n \"--published-only\",\n \"Skip drafts, scheduled, and archived rows\",\n false,\n )\n .option(\"--from <iso>\", \"The theme's source locale file\", \"en\")\n .option(\"--theme <id>\", \"Check this theme instead of the active one\")\n .action(\n (\n iso: string,\n opts: {\n type: string[];\n publishedOnly: boolean;\n from: string;\n theme?: string;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const language = await findLanguage(api, iso);\n const report = await missingReport(\n api,\n language.iso,\n selectTypes(opts.type),\n {\n publishedOnly: opts.publishedOnly,\n sourceIso: opts.from,\n themeId: opts.theme,\n },\n );\n ctx.output({\n language: {\n iso: language.iso,\n name: language.name,\n active: language.active,\n },\n done: report.done,\n types: report.types,\n });\n }),\n );\n\n parent\n .command(\"show <type> <id>\")\n .description(\n \"Show one item's default text beside its translation (null when untranslated); for theme, <id> is the key, for theme-sections <template id>/<key>\",\n )\n .option(\"--lang <iso>\", \"The translation to show, e.g. fr\")\n .option(\"--from <iso>\", \"theme and theme-sections: the source locale\", \"en\")\n .option(\n \"--theme <id>\",\n \"theme and theme-sections: a theme other than the active one\",\n )\n .action(\n (\n typeName: string,\n id: string,\n opts: { lang?: string; from: string; theme?: string },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const type = resourceType(typeName);\n const language = await findLanguage(api, requireLang(opts.lang));\n if (type.kind === \"storefront\") {\n const rid = rowId(id, type.type);\n const source = await getRow(api, type, rid);\n const translated = isTranslated(source, language.iso);\n const target = translated\n ? await getRow(api, type, rid, language.iso)\n : null;\n ctx.output({\n type: type.type,\n id: rid,\n lang: language.iso,\n translated,\n source: pickFields(source, type.fields),\n translation: target ? pickFields(target, type.fields) : null,\n });\n } else if (type.kind === \"menu-items\") {\n const item = await getMenuItem(api, id, language.iso);\n ctx.output({\n type: type.type,\n id,\n lang: language.iso,\n translated: item.translated !== null,\n source: { title: item.source },\n translation:\n item.translated === null ? null : { title: item.translated },\n });\n } else if (type.kind === \"agreements\") {\n const aid = rowId(id, type.type);\n const source = await readAgreement(api, aid);\n const target = await readAgreement(api, aid, language.iso);\n const translated =\n (target.languageIso ?? \"\").toLowerCase() ===\n language.iso.toLowerCase();\n ctx.output({\n type: type.type,\n id: aid,\n lang: language.iso,\n translated,\n source: { title: source.title, description: source.description },\n translation: translated\n ? { title: target.title, description: target.description }\n : null,\n });\n } else if (type.kind === \"labels\") {\n const lid = rowId(id, type.type);\n const source = await getLabelTitle(api, lid);\n const target = await getLabelTitle(api, lid, language.iso);\n const translated = labelTranslated(source, target);\n ctx.output({\n type: type.type,\n id: lid,\n lang: language.iso,\n translated,\n source: { title: source },\n translation: translated ? { title: target } : null,\n });\n } else if (type.kind === \"theme-sections\") {\n const { templateId, key } = parseSectionRef(id);\n const themeId = await resolveThemeId(api, opts.theme);\n const template = await readTemplateTranslations(\n api,\n themeId,\n templateId,\n );\n const text = sectionText(template, key, opts.from, language.iso);\n ctx.output({\n type: type.type,\n themeId,\n id: `${templateId}/${key}`,\n template: template.name,\n lang: language.iso,\n translated: text.translation !== null,\n ...text,\n });\n } else {\n const themeId = await resolveThemeId(api, opts.theme);\n const locales = await readThemeLocales(api, themeId);\n const source = flattenLocale(localeOf(locales, opts.from) ?? {});\n const target = flattenLocale(localeOf(locales, language.iso) ?? {});\n ctx.output({\n type: \"theme\",\n themeId,\n key: id,\n lang: language.iso,\n translated: (target[id] ?? \"\").trim().length > 0,\n source: source[id] ?? null,\n translation: target[id] ?? null,\n });\n }\n }),\n );\n\n parent\n .command(\"set <type> [id]\")\n .description(\n \"Save a translation: --set field=value for a row, menu item, agreement or label; --set key.path=value for theme strings, or for theme-sections with the template id (changes live content)\",\n )\n .option(\"--lang <iso>\", \"The language to write, e.g. fr\")\n .option(\n \"--set <field=value>\",\n \"A translated field or theme key (repeatable)\",\n collect,\n [],\n )\n .option(\n \"--file <path>\",\n \"A JSON object of field (or theme key) to text, for long or HTML values\",\n )\n .option(\n \"--theme <id>\",\n \"theme and theme-sections: a theme other than the active one\",\n )\n .option(\"--yes\", \"Confirm without a prompt\", false)\n .action(\n (\n typeName: string,\n id: string | undefined,\n opts: {\n lang?: string;\n set: string[];\n file?: string;\n theme?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const type = resourceType(typeName);\n const values = collectValues(opts.set, opts.file);\n const language = await requireActiveLanguage(\n api,\n requireLang(opts.lang),\n );\n if (type.kind === \"storefront\") {\n const rid = rowId(id, type.type);\n checkFields(type, values);\n const fields = Object.fromEntries(\n Object.entries(values).map(([field, value]) => [\n field,\n // URL fields clear back to the default file with null.\n field.endsWith(\"_url\") && (value === \"\" || value === \"null\")\n ? null\n : value,\n ]),\n );\n await confirmChange(\n `Save the ${language.name} translation of ${type.label.toLowerCase()} ${rid}?`,\n opts.yes,\n );\n const row = await writeTranslation(\n api,\n type,\n rid,\n language.iso,\n fields,\n );\n ctx.output({\n type: type.type,\n id: rid,\n lang: language.iso,\n saved: Object.keys(fields),\n languages: Array.isArray(row[\"languages\"])\n ? row[\"languages\"]\n : undefined,\n });\n } else if (type.kind === \"menu-items\") {\n if (!id)\n throw new LocalizationError(\n \"Pass the menu item id as <menu id>/<item id>.\",\n );\n checkFields(type, values);\n const title = values[\"title\"];\n if (!title)\n throw new LocalizationError(\n \"Pass --set title=... for a menu item.\",\n );\n await confirmChange(\n `Save the ${language.name} title of menu item ${id}?`,\n opts.yes,\n );\n ctx.output({\n ...(await writeMenuItemTitle(api, id, language.iso, title)),\n lang: language.iso,\n });\n } else if (type.kind === \"agreements\") {\n const aid = rowId(id, type.type);\n checkFields(type, values);\n // The API saves title and text together. A field left out keeps\n // its current translation; with none yet, both are needed.\n const current = await readAgreement(api, aid, language.iso);\n const translated =\n (current.languageIso ?? \"\").toLowerCase() ===\n language.iso.toLowerCase();\n const title =\n values[\"title\"] ?? (translated ? current.title : undefined);\n const description =\n values[\"description\"] ??\n (translated ? current.description : undefined);\n if (!title || !description) {\n throw new LocalizationError(\n `Agreement ${aid} has no ${language.name} translation yet, so pass both --set title=... and --set description=... (or --file).`,\n );\n }\n await confirmChange(\n `Save the ${language.name} translation of agreement ${aid}?`,\n opts.yes,\n );\n await writeAgreementTranslation(api, aid, language.iso, {\n title,\n description,\n });\n ctx.output({\n type: type.type,\n id: aid,\n lang: language.iso,\n saved: Object.keys(values),\n });\n } else if (type.kind === \"labels\") {\n const lid = rowId(id, type.type);\n checkFields(type, values);\n const title = values[\"title\"];\n if (!title)\n throw new LocalizationError(\"Pass --set title=... for a label.\");\n await confirmChange(\n `Save the ${language.name} title of label ${lid}?`,\n opts.yes,\n );\n await writeLabelTitle(api, lid, language.iso, title);\n ctx.output({\n type: type.type,\n id: lid,\n lang: language.iso,\n saved: [\"title\"],\n });\n } else if (type.kind === \"theme-sections\") {\n const templateId = rowId(id, \"template\");\n if (Object.keys(values).length === 0) {\n throw new LocalizationError(\n \"Pass the page's keys with --set key.path=value or --file.\",\n );\n }\n const themeId = await resolveThemeId(api, opts.theme);\n await confirmChange(\n `Save ${Object.keys(values).length} key(s) to template ${templateId}'s ${language.name} translations? They show on the storefront at once.`,\n opts.yes,\n );\n const saved = await writeSectionStrings(\n api,\n themeId,\n templateId,\n language.iso,\n values,\n );\n ctx.output({\n type: type.type,\n themeId,\n ...saved,\n lang: language.iso,\n });\n } else {\n if (id) {\n throw new LocalizationError(\n \"Theme strings take no id; pass keys with --set key.path=value.\",\n );\n }\n if (Object.keys(values).length === 0) {\n throw new LocalizationError(\n \"Pass theme keys with --set key.path=value or --file.\",\n );\n }\n const themeId = await resolveThemeId(api, opts.theme);\n const locales = await readThemeLocales(api, themeId);\n const merged = mergeLocale(\n localeOf(locales, language.iso) ?? {},\n values,\n );\n await confirmChange(\n `Save ${Object.keys(values).length} key(s) to locales/${language.iso}.json on theme ${themeId}? Theme changes publish immediately.`,\n opts.yes,\n );\n const saved = await writeLocale(api, locales, language.iso, merged);\n ctx.output({\n ...saved,\n themeId,\n lang: language.iso,\n keys: Object.keys(values),\n });\n }\n }),\n );\n\n parent\n .command(\"auto <iso>\")\n .description(\n \"Machine-translate what's missing with Fluid AI and print it; --write saves it (changes live content)\",\n )\n .option(\n \"--type <type>\",\n `Limit to a type: ${TYPE_NAMES.join(\", \")} (repeatable)`,\n collect,\n [],\n )\n .option(\"--limit <n>\", \"Most items to translate per type in this run\", \"25\")\n .option(\n \"--write\",\n \"Save the translations; without it nothing is written\",\n false,\n )\n .option(\n \"--published-only\",\n \"Skip drafts, scheduled, and archived rows\",\n false,\n )\n .option(\n \"--from <iso>\",\n \"The theme's source locale file; storefront rows and menus translate from the store's default language\",\n \"en\",\n )\n .option(\n \"--theme <id>\",\n \"Translate this theme's strings instead of the active theme's\",\n )\n .option(\n \"--model <provider/name>\",\n \"A translation model other than Fluid's default\",\n )\n .option(\n \"--skip <type:id>\",\n \"Leave out one item, e.g. products:42, menu-items:12/340, or theme:cart.title (repeatable)\",\n collect,\n [],\n )\n .option(\n \"--store-language <iso>\",\n \"The store's default language, used only when it can't be read from the storefront rows\",\n )\n .option(\"--yes\", \"Confirm --write without a prompt\", false)\n .action(\n (\n iso: string,\n opts: {\n type: string[];\n limit: string;\n write: boolean;\n publishedOnly: boolean;\n from: string;\n theme?: string;\n model?: string;\n skip: string[];\n storeLanguage?: string;\n yes: boolean;\n },\n ) =>\n run(async () => {\n const api = await client(ctx);\n const limit = positiveInt(opts.limit, \"--limit\");\n for (const item of opts.skip) {\n if (!validSkip(item)) {\n throw new LocalizationError(\n `--skip takes <type>:<id>, e.g. products:42 or menu-items:12/340, not \"${item}\".`,\n );\n }\n }\n const types = selectTypes(opts.type);\n const { target, source, store } = await autoLanguages(api, types, {\n iso,\n from: opts.from,\n storeLanguage: opts.storeLanguage,\n write: opts.write,\n });\n if (opts.write) {\n await confirmChange(\n `Machine-translate and save up to ${limit} item(s) per type into ${target.name}? Theme changes publish immediately.`,\n opts.yes,\n );\n }\n const result = await autoTranslate(api, types, {\n iso: target.iso,\n storeName: store.name,\n sourceName: source.name,\n targetName: target.name,\n sourceIso: source.iso,\n storeIso: store.iso,\n limit,\n write: opts.write,\n publishedOnly: opts.publishedOnly,\n themeId: opts.theme,\n model: opts.model,\n skip: opts.skip,\n });\n ctx.output({\n language: { iso: target.iso, name: target.name },\n ...result,\n });\n if (result.types.some((t) => t.failed.length > 0))\n process.exitCode = 1;\n }),\n );\n\n parent\n .command(\"walk [iso]\")\n .description(\n \"Translate a language by answering questions: what's missing, Fluid AI proposals, and accept, edit or skip each one (changes live content)\",\n )\n .option(\"--from <iso>\", \"The theme's source locale\", \"en\")\n .option(\n \"--theme <id>\",\n \"Translate this theme's strings instead of the active theme's\",\n )\n .option(\"--batch <n>\", \"Proposals per Fluid AI call\", \"10\")\n .action(\n (\n iso: string | undefined,\n opts: { from: string; theme?: string; batch: string },\n ) =>\n run(async () => {\n assertInteractive(\"`missing`, `auto` and `set`\");\n const result = await walkTranslations(\n { api: await client(ctx), prompter: terminalPrompter() },\n {\n ...(iso ? { iso } : {}),\n from: opts.from,\n ...(opts.theme ? { themeId: opts.theme } : {}),\n batch: positiveInt(opts.batch, \"--batch\"),\n },\n );\n ctx.output(result);\n if (result.failed.length > 0) process.exitCode = 1;\n }),\n );\n}\n","import type { FluidPlugin } from \"@fluid-app/fluid-cli\";\nimport { createDomainCommand } from \"@fluid-app/fluid-cli\";\n\nimport { registerCountriesCommands } from \"./countries/commands.js\";\nimport { registerTranslationsCommands } from \"./translations/commands.js\";\n\nexport { registerCountriesCommands } from \"./countries/commands.js\";\nexport { registerTranslationsCommands } from \"./translations/commands.js\";\n\n/**\n * One plugin, two command groups: `fluid countries` opens a market and\n * `fluid translations` translates what its shoppers see. The country flow\n * hands language and translation work to `fluid translations`, so each step\n * has one implementation.\n */\nconst plugin: FluidPlugin = {\n name: \"@fluid-app/fluid-cli-localization\",\n version: \"0.1.1\",\n register(ctx) {\n ctx.program.addCommand(\n createDomainCommand(\n \"countries\",\n \"Open a country from Fluid's Country Atlas: settings, prices in its currency, agreements, and a status check\",\n registerCountriesCommands,\n ),\n );\n ctx.program.addCommand(\n createDomainCommand(\n \"translations\",\n \"Add a language and translate what shoppers see: storefront, navigation, theme, agreements, and more\",\n registerTranslationsCommands,\n ),\n );\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}