livelocale 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +230 -5
  2. package/dist/client/index.cjs +178 -70
  3. package/dist/client/index.cjs.map +1 -1
  4. package/dist/client/index.d.cts +36 -22
  5. package/dist/client/index.d.ts +36 -22
  6. package/dist/client/index.js +177 -70
  7. package/dist/client/index.js.map +1 -1
  8. package/dist/codemod/index.cjs +3057 -152
  9. package/dist/codemod/index.cjs.map +1 -1
  10. package/dist/codemod/index.js +3058 -153
  11. package/dist/codemod/index.js.map +1 -1
  12. package/dist/react/index.cjs +185 -77
  13. package/dist/react/index.cjs.map +1 -1
  14. package/dist/react/index.js +185 -77
  15. package/dist/react/index.js.map +1 -1
  16. package/dist/server/index.cjs +108 -66
  17. package/dist/server/index.cjs.map +1 -1
  18. package/dist/server/index.d.cts +13 -3
  19. package/dist/server/index.d.ts +13 -3
  20. package/dist/server/index.js +107 -66
  21. package/dist/server/index.js.map +1 -1
  22. package/dist/studio/assets/index-Bvbtkry-.css +1 -0
  23. package/dist/studio/assets/index-DC_jRsox.js +13 -0
  24. package/dist/studio/assets/inter-latin-300-normal-BVlfKGgI.woff2 +0 -0
  25. package/dist/studio/assets/inter-latin-300-normal-i8F0SvXL.woff +0 -0
  26. package/dist/studio/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  27. package/dist/studio/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  28. package/dist/studio/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  29. package/dist/studio/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  30. package/dist/studio/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
  31. package/dist/studio/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
  32. package/dist/studio/assets/inter-latin-700-normal-BLAVimhd.woff +0 -0
  33. package/dist/studio/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
  34. package/dist/studio/assets/outfit-latin-400-normal-BGsTXAXT.woff2 +0 -0
  35. package/dist/studio/assets/outfit-latin-400-normal-DMwTpYkH.woff +0 -0
  36. package/dist/studio/assets/outfit-latin-500-normal-ClnHRwRh.woff +0 -0
  37. package/dist/studio/assets/outfit-latin-500-normal-DKnIMDSk.woff2 +0 -0
  38. package/dist/studio/assets/outfit-latin-600-normal-B7SfZ07L.woff2 +0 -0
  39. package/dist/studio/assets/outfit-latin-600-normal-BEfTtDA7.woff +0 -0
  40. package/dist/studio/assets/outfit-latin-700-normal-Cu9v6i1X.woff2 +0 -0
  41. package/dist/studio/assets/outfit-latin-700-normal-D4itBLBr.woff +0 -0
  42. package/dist/studio/index.html +3 -6
  43. package/dist/vite/index.cjs +739 -77
  44. package/dist/vite/index.cjs.map +1 -1
  45. package/dist/vite/index.d.cts +22 -2
  46. package/dist/vite/index.d.ts +22 -2
  47. package/dist/vite/index.js +738 -77
  48. package/dist/vite/index.js.map +1 -1
  49. package/dist/{wrap-CsOtHp8r.d.cts → wrap-2qVxLN9Q.d.cts} +10 -18
  50. package/dist/{wrap-CsOtHp8r.d.ts → wrap-2qVxLN9Q.d.ts} +10 -18
  51. package/package.json +4 -1
  52. package/dist/studio/assets/index-D7ifnqy5.js +0 -10
  53. package/dist/studio/assets/index-Zh0KtFUH.css +0 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/vite-plugin.ts","../../src/server/file-mutator.ts","../../src/core/validate.ts","../../src/core/safe-path.ts","../../src/core/json-path.ts","../../src/server/key-scanner.ts","../../src/core/env.ts","../../src/core/gitignore.ts","../../src/server/git-client.ts","../../src/core/placeholders.ts","../../src/server/router.ts","../../src/server/node-body.ts","../../src/core/deep-merge.ts"],"sourcesContent":["import * as path from 'path';\nimport * as fs from 'fs';\nimport { FileMutator } from './file-mutator.js';\nimport { GitClient } from './git-client.js';\nimport { createMutationRouter, MAX_BODY_BYTES } from './router.js';\nimport { readNodeBody } from './node-body.js';\nimport { deepMerge } from '../core/deep-merge.js';\nimport { isLiveLocaleEnabled, BROWSER_FLAG_GLOBAL } from '../core/env.js';\n\nexport interface VitePluginConfig {\n /** Absolute or relative path to the directory containing your locale JSON files. */\n localesPath: string;\n /** Expose git status, commit, and discard to the Studio. Defaults to `true`. */\n git?: boolean;\n /** Bearer token required on every request. Defaults to `LIVELOCALE_TOKEN`. */\n authToken?: string;\n}\n\nconst ROUTE = '/api/livelocale';\n\n/**\n * A Vite plugin that serves the livelocale mutation API for Vite-based SPAs\n * (React, Vue, Svelte, etc.) that do not use Next.js.\n *\n * @example\n * // vite.config.ts\n * import { liveLocaleVite } from 'livelocale/vite';\n *\n * export default defineConfig({\n * plugins: [react(), liveLocaleVite({ localesPath: './src/locales' })],\n * });\n */\nexport function liveLocaleVite(config: VitePluginConfig) {\n const mutator = new FileMutator();\n const router = createMutationRouter(mutator, {\n localesPath: config.localesPath,\n git: config.git === false ? undefined : new GitClient(),\n authToken: config.authToken ?? process.env['LIVELOCALE_TOKEN'],\n });\n\n return {\n name: 'livelocale',\n // Only active during dev server mode; the plugin is a no-op during builds.\n apply: 'serve' as const,\n\n configureServer(server: any) {\n server.middlewares.use(async (req: any, res: any, next: any) => {\n const url: string = req.url ?? '';\n // Exact path only, so app routes such as /api/livelocale-stats stay with the app.\n if (url !== ROUTE && !url.startsWith(`${ROUTE}?`) && !url.startsWith(`${ROUTE}/`)) {\n return next();\n }\n\n try {\n const result = await router.handle({\n method: req.method,\n url: req.url,\n origin: req.headers['origin'] || '',\n contentType: req.headers['content-type'] || '',\n authorization: req.headers['authorization'] || undefined,\n bodyText: req.method === 'POST' ? await readNodeBody(req, MAX_BODY_BYTES) : undefined,\n });\n\n Object.entries(result.headers).forEach(([k, v]) => res.setHeader(k, v));\n res.statusCode = result.status;\n res.end(result.body ?? '');\n } catch (err: any) {\n if (err?.code === 'BODY_TOO_LARGE') {\n res.statusCode = 413;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: err.message, code: 'BODY_TOO_LARGE' }));\n return;\n }\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(\n JSON.stringify({\n error: err.message || 'Internal Server Error',\n code: 'INTERNAL_ERROR',\n })\n );\n }\n });\n },\n\n transformIndexHtml(html: string) {\n // Mirror the server-side LIVELOCALE flag (preview/off) into the page so the client\n // bundle resolves the same mode without bundler-specific env inlining.\n const flag = process.env['LIVELOCALE'];\n const flagScript = flag\n ? `<script>window.${BROWSER_FLAG_GLOBAL} = ${JSON.stringify(flag)};</script>\\n`\n : '';\n if (!isLiveLocaleEnabled()) {\n return flagScript ? html.replace('</head>', `${flagScript}</head>`) : html;\n }\n try {\n const resolvedLocalesPath = path.resolve(config.localesPath);\n const entries = fs.readdirSync(resolvedLocalesPath);\n let fallbackData: Record<string, any> = {};\n for (const entry of entries) {\n if (entry.startsWith('.')) continue;\n const entryPath = path.join(resolvedLocalesPath, entry);\n let entryIsDir = false;\n try {\n entryIsDir = fs.statSync(entryPath).isDirectory();\n } catch {}\n\n if (entryIsDir) {\n try {\n const files = fs.readdirSync(entryPath);\n for (const file of files) {\n if (file.endsWith('.json')) {\n const ns = file.slice(0, -5);\n const raw = fs.readFileSync(path.join(entryPath, file), 'utf-8');\n try {\n const parsed = JSON.parse(raw);\n fallbackData[ns] = deepMerge(fallbackData[ns] || {}, parsed);\n } catch {}\n }\n }\n } catch {}\n } else if (entry.endsWith('.json')) {\n const raw = fs.readFileSync(entryPath, 'utf-8');\n try {\n const parsed = JSON.parse(raw);\n fallbackData = deepMerge(fallbackData, parsed);\n } catch {}\n }\n }\n // RULE SEC-008: XSS-SAFE INJECTION — escape U+2028/U+2029 and \"<\" so\n // translation values containing \"</script>\" cannot break out of the tag.\n const safeJson = JSON.stringify(fallbackData)\n .replace(/</g, '\\\\u003c')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n const script = `<script>window.__livelocale_fallback__ = ${safeJson};</script>`;\n return html.replace('</head>', `${flagScript}${script}\\n</head>`);\n } catch (err) {\n console.warn('[livelocale] Failed to inject fallback data:', err);\n return flagScript ? html.replace('</head>', `${flagScript}</head>`) : html;\n }\n },\n };\n}\n","import { promises as fs } from 'fs';\nimport * as path from 'path';\nimport { isSafeLocaleSegment } from '../core/validate.js';\nimport { LOCALE_RE } from '../core/validate.js';\nimport { DANGEROUS_KEY_SEGMENTS } from '../core/validate.js';\nimport { resolveSafeLocalePath } from '../core/safe-path.js';\nimport { setNestedProperty, detectJsonIndent } from '../core/json-path.js';\nimport { KeyScanner } from './key-scanner.js';\nimport { getDisabledReason } from '../core/env.js';\n\nexport { detectJsonIndent };\n\nfunction assertEnabled(component: string): void {\n const reason = getDisabledReason();\n if (reason !== null) {\n throw new Error(\n `[livelocale] ${component} can only be executed in development mode (${reason}).`\n );\n }\n}\n\nclass FileMutex {\n private queue: Promise<any> = Promise.resolve();\n\n async acquire<T>(fn: () => Promise<T>): Promise<T> {\n const next = this.queue.then(fn);\n // Suppress errors on the main queue chain to prevent locking up the mutex\n this.queue = next.then(\n () => {},\n () => {}\n );\n return next;\n }\n}\n\nexport class FileMutator {\n private fileLocks = new Map<string, FileMutex>();\n private scanner = new KeyScanner();\n\n /**\n * Helper to serialize execution per file path\n */\n private async withLock<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n let lock = this.fileLocks.get(filePath);\n if (!lock) {\n lock = new FileMutex();\n this.fileLocks.set(filePath, lock);\n }\n return lock.acquire(fn);\n }\n\n /**\n * Updates a locale JSON file on disk atomically and safely.\n * Enforces development mode checks, path traversal mitigation, key depth restrictions,\n * structural JSON validation, and concurrency serialization.\n */\n async updateLocaleKey(\n basePath: string,\n locale: string,\n keyPath: string,\n newValue: string\n ): Promise<void> {\n // RULE GEN-001: DEVELOPMENT-ONLY ENFORCEMENT\n assertEnabled('FileMutator');\n\n // RULE SRV-002: PATH TRAVERSAL PREVENTION\n // Reject any locale string containing invalid characters\n if (!isSafeLocaleSegment(locale)) {\n throw new Error(\n `[livelocale] Path traversal attempt detected in locale identifier: \"${locale}\"`\n );\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const localeDir = path.resolve(resolvedBasePath, locale);\n\n let filePath = '';\n let mutationKeyPath = keyPath;\n let isNamespaceDir = false;\n\n try {\n const stats = await fs.stat(localeDir);\n if (stats.isDirectory()) {\n isNamespaceDir = true;\n }\n } catch {}\n\n if (isNamespaceDir) {\n const firstDot = keyPath.indexOf('.');\n if (firstDot !== -1) {\n const namespace = keyPath.substring(0, firstDot);\n if (DANGEROUS_KEY_SEGMENTS.has(namespace)) {\n throw new Error(\n `[livelocale] Forbidden namespace segment in path: \"${namespace}\". Segment names like __proto__, constructor, and prototype are not allowed.`\n );\n }\n mutationKeyPath = keyPath.substring(firstDot + 1);\n filePath = path.resolve(localeDir, `${namespace}.json`);\n } else {\n filePath = path.resolve(localeDir, `${keyPath}.json`);\n mutationKeyPath = '';\n }\n } else {\n filePath = path.resolve(resolvedBasePath, `${locale}.json`);\n }\n\n if (!filePath.startsWith(resolvedBasePath)) {\n throw new Error(\n `[livelocale] Path traversal attempt: Resolved file path must start with basePath.`\n );\n }\n\n // Perform atomic operation within a file lock to guarantee concurrency safety (RULE SRV-006)\n await this.withLock(filePath, async () => {\n let currentData: Record<string, any> = {};\n // Formatting of the existing file, preserved on rewrite (git-friendly diffs).\n let indent = ' ';\n let trailingNewline = '\\n';\n\n try {\n const fileContent = await fs.readFile(filePath, 'utf-8');\n indent = detectJsonIndent(fileContent);\n trailingNewline = fileContent.endsWith('\\n') ? '\\n' : '';\n try {\n const parsed = JSON.parse(fileContent);\n\n // RULE SRV-004: JSON PARSE VALIDATION\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('Root must be an object');\n }\n currentData = parsed;\n } catch (parseError: any) {\n throw new Error(\n `[livelocale] Invalid locale file: root must be an object. Code: INVALID_LOCALE. Original error: ${parseError.message}`\n );\n }\n } catch (err: any) {\n // If file does not exist, initialize it as empty object\n if (err.code !== 'ENOENT') {\n throw new Error(\n `[livelocale] Failed to read locale file at ${filePath}. Operation: read. Original error: ${err.message}`\n );\n }\n }\n\n // Update nested property using safe array traversal (RULE SEC-003 & RULE SRV-003)\n if (mutationKeyPath) {\n setNestedProperty(currentData, mutationKeyPath, newValue);\n } else {\n // Root value mutation inside split JSON files (rare but possible: e.g. key has no dot)\n throw new Error(\n '[livelocale] Invalid namespace key path: must contain at least one nested key.'\n );\n }\n\n // RULE SEC-004: VALIDATE JSON BEFORE WRITE\n let stringified: string;\n try {\n stringified = JSON.stringify(currentData, null, indent);\n JSON.parse(stringified); // Paranoid check\n stringified += trailingNewline;\n } catch (err: any) {\n throw new Error(\n `[livelocale] Produced JSON is invalid or has circular references. Original error: ${err.message}`\n );\n }\n\n // RULE SRV-001: ATOMIC WRITES ONLY\n const tempPath = `${filePath}.tmp`;\n try {\n await fs.writeFile(tempPath, stringified, 'utf-8');\n } catch (err: any) {\n throw new Error(\n `[livelocale] Failed to write temp file at ${tempPath}. Operation: write. Original error: ${err.message}`\n );\n }\n\n try {\n await fs.rename(tempPath, filePath);\n } catch (err: any) {\n // Attempt clean up of temp file\n try {\n await fs.unlink(tempPath);\n } catch {\n // Ignore unlink error to bubble original rename error\n }\n throw new Error(\n `[livelocale] Failed to rename temp file to target at ${filePath}. Operation: rename. Original error: ${err.message}`\n );\n }\n\n // RULE SEC-001: NEVER LOG TRANSLATION VALUES\n console.log(`[livelocale] Updated key '${keyPath}' in locale '${locale}'`);\n });\n }\n\n /**\n * Returns the full dictionary for a locale: a flat `{locale}.json`, or every\n * `*.json` inside the `{locale}/` folder merged under its namespace.\n */\n async readLocale(basePath: string, locale: string): Promise<Record<string, any>> {\n assertEnabled('FileMutator');\n const localePath = path.resolve(basePath, locale);\n let isDir = false;\n try {\n isDir = (await fs.stat(localePath)).isDirectory();\n } catch {}\n\n if (isDir) {\n const merged: Record<string, any> = {};\n for (const file of await fs.readdir(localePath)) {\n if (!file.endsWith('.json')) continue;\n try {\n merged[path.basename(file, '.json')] = JSON.parse(\n await fs.readFile(path.join(localePath, file), 'utf-8')\n );\n } catch {}\n }\n return merged;\n }\n\n const filePath = resolveSafeLocalePath(basePath, locale);\n return JSON.parse(await fs.readFile(filePath, 'utf-8'));\n }\n\n /**\n * Lists all locale codes dynamically scanned from the locales directory.\n */\n async listLocales(basePath: string): Promise<string[]> {\n assertEnabled('FileMutator');\n\n const resolvedBasePath = path.resolve(basePath);\n const items = await fs.readdir(resolvedBasePath, { withFileTypes: true });\n const localesSet = new Set<string>();\n\n for (const item of items) {\n if (item.isDirectory()) {\n if (LOCALE_RE.test(item.name)) {\n localesSet.add(item.name);\n }\n } else if (item.isFile() && item.name.endsWith('.json')) {\n const name = path.basename(item.name, '.json');\n if (LOCALE_RE.test(name)) {\n localesSet.add(name);\n }\n }\n }\n\n return Array.from(localesSet).sort();\n }\n\n /**\n * Adds a new locale directory/file based on current repository structure.\n */\n async addLocale(basePath: string, locale: string): Promise<void> {\n assertEnabled('FileMutator');\n\n if (!isSafeLocaleSegment(locale)) {\n throw new Error(\n `[livelocale] Path traversal attempt detected in locale identifier: \"${locale}\"`\n );\n }\n\n if (!LOCALE_RE.test(locale)) {\n throw new Error(`[livelocale] Invalid locale format: \"${locale}\"`);\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const localePath = path.resolve(resolvedBasePath, locale);\n\n if (!localePath.startsWith(resolvedBasePath)) {\n throw new Error(\n `[livelocale] Path traversal attempt: Resolved file path must start with basePath.`\n );\n }\n\n // Check if directory or file already exists\n let exists = false;\n try {\n await fs.stat(localePath);\n exists = true;\n } catch {}\n try {\n await fs.stat(`${localePath}.json`);\n exists = true;\n } catch {}\n\n if (exists) {\n throw new Error(`[livelocale] Locale \"${locale}\" already exists.`);\n }\n\n // Scan basePath for layout detection: if we find any directories matching locale regex,\n // we use folder-based namespaces.\n const items = await fs.readdir(resolvedBasePath, { withFileTypes: true });\n let referenceDir: string | null = null;\n for (const item of items) {\n if (item.isDirectory() && LOCALE_RE.test(item.name)) {\n referenceDir = item.name;\n break;\n }\n }\n\n if (referenceDir) {\n // Create folder-based layout: create directory and write empty namespace files corresponding\n // to those in referenceDir\n await fs.mkdir(localePath, { recursive: true });\n const refPath = path.resolve(resolvedBasePath, referenceDir);\n const files = await fs.readdir(refPath);\n for (const file of files) {\n if (file.endsWith('.json')) {\n await fs.writeFile(path.join(localePath, file), '{}', 'utf-8');\n }\n }\n } else {\n // Flat layout: write empty json file\n await fs.writeFile(`${localePath}.json`, '{}', 'utf-8');\n }\n\n console.log(`[livelocale] Added locale '${locale}'`);\n }\n\n /**\n * Renames an existing locale (directory or file) to a new name.\n */\n async renameLocale(basePath: string, oldLocale: string, newLocale: string): Promise<void> {\n assertEnabled('FileMutator');\n\n if (!isSafeLocaleSegment(oldLocale) || !isSafeLocaleSegment(newLocale)) {\n throw new Error('[livelocale] Path traversal attempt detected');\n }\n\n if (!LOCALE_RE.test(oldLocale) || !LOCALE_RE.test(newLocale)) {\n throw new Error(`[livelocale] Invalid locale format`);\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const oldPath = path.resolve(resolvedBasePath, oldLocale);\n const newPath = path.resolve(resolvedBasePath, newLocale);\n\n if (!oldPath.startsWith(resolvedBasePath) || !newPath.startsWith(resolvedBasePath)) {\n throw new Error('[livelocale] Path traversal attempt');\n }\n\n let isDir = false;\n let exists = false;\n try {\n const stats = await fs.stat(oldPath);\n isDir = stats.isDirectory();\n exists = true;\n } catch {}\n\n let isFile = false;\n if (!exists) {\n try {\n await fs.stat(`${oldPath}.json`);\n isFile = true;\n exists = true;\n } catch {}\n }\n\n if (!exists) {\n throw new Error(`[livelocale] Locale \"${oldLocale}\" does not exist.`);\n }\n\n // Check if new path already exists\n let newExists = false;\n try {\n await fs.stat(newPath);\n newExists = true;\n } catch {}\n try {\n await fs.stat(`${newPath}.json`);\n newExists = true;\n } catch {}\n\n if (newExists) {\n throw new Error(`[livelocale] Target locale \"${newLocale}\" already exists.`);\n }\n\n if (isDir) {\n await fs.rename(oldPath, newPath);\n } else if (isFile) {\n await fs.rename(`${oldPath}.json`, `${newPath}.json`);\n }\n\n console.log(`[livelocale] Renamed locale '${oldLocale}' to '${newLocale}'`);\n }\n\n /**\n * Deletes an existing locale (directory or file).\n */\n async deleteLocale(basePath: string, locale: string): Promise<void> {\n assertEnabled('FileMutator');\n\n if (!isSafeLocaleSegment(locale)) {\n throw new Error('[livelocale] Path traversal attempt detected');\n }\n\n if (!LOCALE_RE.test(locale)) {\n throw new Error(`[livelocale] Invalid locale format`);\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const localePath = path.resolve(resolvedBasePath, locale);\n\n if (!localePath.startsWith(resolvedBasePath)) {\n throw new Error('[livelocale] Path traversal attempt');\n }\n\n let isDir = false;\n let exists = false;\n try {\n const stats = await fs.stat(localePath);\n isDir = stats.isDirectory();\n exists = true;\n } catch {}\n\n let isFile = false;\n if (!exists) {\n try {\n await fs.stat(`${localePath}.json`);\n isFile = true;\n exists = true;\n } catch {}\n }\n\n if (!exists) {\n throw new Error(`[livelocale] Locale \"${locale}\" does not exist.`);\n }\n\n if (isDir) {\n await fs.rm(localePath, { recursive: true, force: true });\n } else if (isFile) {\n await fs.unlink(`${localePath}.json`);\n }\n\n console.log(`[livelocale] Deleted locale '${locale}'`);\n }\n\n /**\n * Scans codebase directory recursively for references to translation keys.\n * Delegates to the composed KeyScanner (SRP).\n */\n async scanCodebaseKeys(basePath: string): Promise<string[]> {\n return this.scanner.scanCodebaseKeys(basePath);\n }\n\n /**\n * Scans all locale files/folders to compile the set of all keys currently in locales.\n */\n async scanLocalesKeys(basePath: string): Promise<string[]> {\n return this.scanner.scanLocalesKeys(basePath, this.listLocales.bind(this));\n }\n\n /**\n * Returns merged lists of keys from codebase and existing locale files.\n */\n async getKeysMetadata(basePath: string): Promise<{ allKeys: string[]; codeKeys: string[] }> {\n return this.scanner.getKeysMetadata(basePath, this.listLocales.bind(this));\n }\n}\n","/**\n * Shared validation rules for locale codes, translation keys, and values.\n *\n * Platform-agnostic: used by the server router, FileMutator, and the Studio\n * UI so that every layer enforces exactly the same contract.\n */\n\nexport const LOCALE_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,4})?$/;\n\nexport const KEY_RE = /^[a-zA-Z0-9._-]+$/;\n\nexport const MAX_LOCALE_LENGTH = 10;\nexport const MAX_KEY_LENGTH = 200;\nexport const MAX_VALUE_LENGTH = 10000;\n\n/**\n * Object-prototype member names that must never be used as translation key\n * segments. Assigning through these bypasses own-property checks and can\n * pollute `Object.prototype` process-wide (prototype pollution).\n */\nexport const DANGEROUS_KEY_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Returns true if no segment of the dot-separated `key` is a dangerous\n * object-prototype member name.\n */\nexport function isSafeKeyPath(key: unknown): boolean {\n if (typeof key !== 'string') return false;\n return !key.split('.').some((segment) => DANGEROUS_KEY_SEGMENTS.has(segment));\n}\n\n/**\n * Returns true if `locale` is a 2-10 character code matching the loose\n * standard pattern (supports es-419, zh-Hans, eng, etc.).\n */\nexport function isValidLocaleFormat(locale: unknown): boolean {\n return (\n typeof locale === 'string' &&\n locale.length >= 2 &&\n locale.length <= MAX_LOCALE_LENGTH &&\n LOCALE_RE.test(locale)\n );\n}\n\n/**\n * Returns true if `key` is a 1-200 character dot-separated path containing\n * only alphanumerics, dots, underscores, or hyphens.\n */\nexport function isValidKeyFormat(key: unknown): boolean {\n return (\n typeof key === 'string' && key.length >= 1 && key.length <= MAX_KEY_LENGTH && KEY_RE.test(key)\n );\n}\n\n/**\n * Returns true if `value` is a string no longer than MAX_VALUE_LENGTH.\n */\nexport function isValidValueFormat(value: unknown): boolean {\n return typeof value === 'string' && value.length <= MAX_VALUE_LENGTH;\n}\n\n/**\n * Rejects locale identifiers that could escape the locales directory.\n */\nexport function isSafeLocaleSegment(locale: string): boolean {\n return !(\n locale.includes('..') ||\n locale.includes('/') ||\n locale.includes('\\\\') ||\n locale.includes('%') ||\n locale.includes('\\0')\n );\n}\n","import * as path from 'path';\nimport { isSafeLocaleSegment } from './validate.js';\n\n/**\n * Resolves a locale code to a safe `{locale}.json` path inside `basePath`,\n * rejecting path traversal attempts.\n */\nexport function resolveSafeLocalePath(basePath: string, locale: string): string {\n if (!isSafeLocaleSegment(locale)) {\n throw new Error('Path traversal attempt');\n }\n const resolvedBasePath = path.resolve(basePath);\n const filePath = path.resolve(resolvedBasePath, `${locale}.json`);\n if (!filePath.startsWith(resolvedBasePath)) {\n throw new Error('Path traversal attempt');\n }\n return filePath;\n}\n","import { DANGEROUS_KEY_SEGMENTS } from './validate.js';\n\n/** RULE SRV-003: deeper key paths are rejected to keep recursion bounded. */\nexport const MAX_KEY_DEPTH = 30;\n\n/**\n * Sets `value` at the dot-separated `keyPath` inside `obj`, creating\n * intermediate objects as needed. Rejects paths deeper than `MAX_KEY_DEPTH`\n * and any segment that could pollute `Object.prototype` (RULE SEC-005).\n */\nexport function setNestedProperty(obj: Record<string, any>, keyPath: string, value: string): void {\n const keys = keyPath.split('.');\n\n if (keys.length > MAX_KEY_DEPTH) {\n throw new Error(\n `[livelocale] Key depth limit exceeded. Maximum depth is ${MAX_KEY_DEPTH} levels.`\n );\n }\n\n if (keys.some((k) => DANGEROUS_KEY_SEGMENTS.has(k))) {\n throw new Error(\n `[livelocale] Forbidden key segment in path: \"${keyPath}\". Segment names like __proto__, constructor, and prototype are not allowed.`\n );\n }\n\n let current = obj;\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i]!;\n\n if (i === keys.length - 1) {\n current[key] = value;\n return;\n }\n\n if (\n !(key in current) ||\n current[key] === null ||\n typeof current[key] !== 'object' ||\n Array.isArray(current[key])\n ) {\n current[key] = {};\n }\n\n current = current[key];\n }\n}\n\n/**\n * Detects the indentation unit of an existing JSON document so rewrites keep\n * the project's style (tabs, 2 or 4 spaces). Defaults to two spaces.\n */\nexport function detectJsonIndent(content: string): string {\n const match = /^([ \\t]+)\"/m.exec(content);\n if (!match) return ' ';\n const ws = match[1]!;\n return ws.includes('\\t') ? '\\t' : ws;\n}\n\n/**\n * Serializes `data` the way the original `content` was formatted: same\n * indentation unit and the same presence or absence of a trailing newline.\n * New documents get two spaces and a trailing newline.\n */\nexport function stringifyLike(data: unknown, content: string | null): string {\n const indent = content === null ? ' ' : detectJsonIndent(content);\n const trailing = content === null || content.endsWith('\\n') ? '\\n' : '';\n return JSON.stringify(data, null, indent) + trailing;\n}\n\n/** Flattens a nested dictionary into dot-separated leaf keys. */\nexport function flattenKeys(obj: unknown, prefix = '', out: Set<string> = new Set()): Set<string> {\n if (!obj || typeof obj !== 'object') return out;\n for (const key of Object.keys(obj as Record<string, unknown>)) {\n const value = (obj as Record<string, unknown>)[key];\n const full = prefix ? `${prefix}.${key}` : key;\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n flattenKeys(value, full, out);\n } else {\n out.add(full);\n }\n }\n return out;\n}\n","import { promises as fs } from 'fs';\nimport * as path from 'path';\nimport { isSafeLocaleSegment } from '../core/validate.js';\nimport { getDisabledReason } from '../core/env.js';\nimport { parseGitignore, type GitignoreMatcher } from '../core/gitignore.js';\n\nconst IGNORE_DIRS = new Set([\n 'node_modules',\n '.next',\n 'dist',\n 'build',\n '.git',\n 'coverage',\n 'test-results',\n 'out',\n '.idea',\n '.vscode',\n '.turbo',\n '.cache',\n]);\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);\n\n/** Files above this size are almost certainly bundles or fixtures, not components. */\nconst MAX_SOURCE_BYTES = 1_000_000;\n\nconst PROP_RE = /\\bt(?:\\??\\.[a-zA-Z0-9_]+)+/g;\nconst IGNORE_PROPS = new Set([\n 'toString',\n 'length',\n 'constructor',\n 'prototype',\n 'map',\n 'forEach',\n 'filter',\n 'reduce',\n 'call',\n 'apply',\n 'bind',\n 'name',\n 'displayName',\n 'raw',\n 'rich',\n 'markup',\n 'has',\n]);\n\nconst KEY_SHAPE = /^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*$/;\nconst DECL = String.raw`(?:const|let|var)\\s+`;\n/** A string literal in any quote style; read the value with `stringArg(match)`. */\nconst STR = String.raw`(?:'(?<s1>[^']*)'|\"(?<s2>[^\"]*)\"|\\x60(?<s3>[^\\x60]*)\\x60)`;\nconst stringArg = (m: RegExpMatchArray): string | undefined =>\n m.groups?.['s1'] ?? m.groups?.['s2'] ?? m.groups?.['s3'];\n\n/** Translator-method names whose first argument is a key. */\nconst METHODS = String.raw`(?:\\.(?:rich|raw|markup|has))?`;\n\n/**\n * Maps translator variable names to their key prefix (namespace) from the\n * declarations in a file:\n * const t = useTranslations('home') // next-intl (also getTranslations, await)\n * const { t } = useTranslation('auth') // react-i18next\n * const t = wrapTranslationEngine(x, { keyPrefix: 'auth' })\n * const t = wrapNextIntl(x, 'checkout')\n * Scope is not tracked; the last declaration of a name in the file wins.\n */\nfunction namespaceBindings(content: string): Map<string, string> {\n const map = new Map<string, string>();\n\n const hookRe = new RegExp(\n DECL +\n String.raw`(?<name>\\w+)\\s*=\\s*(?:await\\s+)?(?:useTranslations|getTranslations)\\(\\s*(?:` +\n STR +\n String.raw`)?\\s*\\)`,\n 'g'\n );\n for (const m of content.matchAll(hookRe)) map.set(m.groups!['name']!, stringArg(m) ?? '');\n\n const wrapNextIntlRe = new RegExp(\n DECL + String.raw`(?<name>\\w+)\\s*=\\s*wrapNextIntl\\([^;]*?,\\s*` + STR + String.raw`\\s*\\)`,\n 'g'\n );\n for (const m of content.matchAll(wrapNextIntlRe)) {\n map.set(m.groups!['name']!, stringArg(m) ?? '');\n }\n\n const wrapEngineRe = new RegExp(\n DECL + String.raw`(?<name>\\w+)\\s*=\\s*wrapTranslationEngine\\([^;]*?keyPrefix:\\s*` + STR,\n 'g'\n );\n for (const m of content.matchAll(wrapEngineRe)) {\n map.set(m.groups!['name']!, stringArg(m) ?? '');\n }\n\n const i18nextRe = new RegExp(\n DECL + String.raw`\\{(?<bindings>[^}]*)\\}\\s*=\\s*useTranslation\\(\\s*(?:` + STR + String.raw`)?`,\n 'g'\n );\n for (const m of content.matchAll(i18nextRe)) {\n const binding = /\\bt\\s*(?::\\s*(\\w+))?/.exec(m.groups!['bindings']!);\n if (binding) map.set(binding[1] ?? 't', stringArg(m) ?? '');\n }\n\n return map;\n}\n\n/** Extracts translation key references from one source file's text. */\nexport function extractKeys(content: string): string[] {\n const keys = new Set<string>();\n const bindings = namespaceBindings(content);\n const translators = new Set<string>(['t', ...bindings.keys()]);\n\n for (const name of translators) {\n const prefix = bindings.get(name) ?? '';\n const callRe = new RegExp(String.raw`\\b` + name + METHODS + String.raw`\\(\\s*` + STR, 'g');\n for (const m of content.matchAll(callRe)) {\n const key = stringArg(m);\n if (!key || key.includes('${') || !KEY_SHAPE.test(key)) continue;\n keys.add(prefix ? `${prefix}.${key}` : key);\n }\n }\n\n let match: RegExpExecArray | null;\n PROP_RE.lastIndex = 0;\n while ((match = PROP_RE.exec(content)) !== null) {\n const clean = match[0].replace(/\\bt/, '').replace(/\\?/g, '');\n const segments = clean\n .split('.')\n .map((seg) => seg.trim())\n .filter(Boolean);\n if (segments.length > 0 && !segments.some((seg) => IGNORE_PROPS.has(seg))) {\n keys.add(segments.join('.'));\n }\n }\n\n return Array.from(keys).sort();\n}\n\ninterface CachedFile<T> {\n mtimeMs: number;\n size: number;\n value: T;\n}\n\nconst NO_IGNORES: GitignoreMatcher = { ignores: () => false };\n\n/**\n * Read-only analysis of translation keys: scans the codebase for `t(...)`\n * references and compiles the set of keys present in locale files.\n *\n * Scans are incremental: each source file's extracted keys are cached by\n * mtime and size, so a rescan only re-reads files that changed. The project's\n * root `.gitignore` is honoured in addition to the built-in ignore list.\n */\nexport interface KeyScannerOptions {\n /**\n * Refuse to scan unless LiveLocale is enabled (development or preview mode).\n * Defaults to true; read-only tooling such as `check` turns it off so it can run in CI.\n */\n requireEnabled?: boolean;\n}\n\nexport class KeyScanner {\n private fileCache = new Map<string, CachedFile<string[]>>();\n private gitignoreCache: CachedFile<GitignoreMatcher> | null = null;\n private readonly requireEnabled: boolean;\n\n constructor(options: KeyScannerOptions = {}) {\n this.requireEnabled = options.requireEnabled ?? true;\n }\n\n private assertEnabled(): void {\n if (!this.requireEnabled) return;\n const reason = getDisabledReason();\n if (reason !== null) {\n throw new Error(\n `[livelocale] KeyScanner can only be executed in development mode (${reason}).`\n );\n }\n }\n\n private async loadGitignore(projectRoot: string): Promise<GitignoreMatcher> {\n const gitignorePath = path.join(projectRoot, '.gitignore');\n let stat;\n try {\n stat = await fs.stat(gitignorePath);\n } catch {\n this.gitignoreCache = null;\n return NO_IGNORES;\n }\n const hit = this.gitignoreCache;\n if (hit && hit.mtimeMs === stat.mtimeMs && hit.size === stat.size) {\n return hit.value;\n }\n try {\n const content = await fs.readFile(gitignorePath, 'utf-8');\n const matcher = parseGitignore(content);\n this.gitignoreCache = { mtimeMs: stat.mtimeMs, size: stat.size, value: matcher };\n return matcher;\n } catch {\n return NO_IGNORES;\n }\n }\n\n /**\n * Scans the project (the parent directory of `basePath`) for translation key references.\n */\n async scanCodebaseKeys(basePath: string): Promise<string[]> {\n return this.scanSourceKeys(path.dirname(path.resolve(basePath)));\n }\n\n /** Scans every source file under `projectRoot` for translation key references. */\n async scanSourceKeys(projectRoot: string): Promise<string[]> {\n this.assertEnabled();\n\n projectRoot = path.resolve(projectRoot);\n const gitignore = await this.loadGitignore(projectRoot);\n const keysSet = new Set<string>();\n const seen = new Set<string>();\n\n const toRel = (fullPath: string) =>\n path.relative(projectRoot, fullPath).split(path.sep).join('/');\n\n const scanDir = async (dir: string): Promise<void> => {\n let items;\n try {\n items = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const item of items) {\n const fullPath = path.join(dir, item.name);\n\n if (item.isDirectory()) {\n if (IGNORE_DIRS.has(item.name)) continue;\n if (gitignore.ignores(toRel(fullPath), true)) continue;\n await scanDir(fullPath);\n continue;\n }\n\n if (!item.isFile()) continue;\n if (!SOURCE_EXTENSIONS.has(path.extname(item.name))) continue;\n if (item.name.endsWith('.d.ts')) continue;\n if (gitignore.ignores(toRel(fullPath), false)) continue;\n\n let stat;\n try {\n stat = await fs.stat(fullPath);\n } catch {\n continue;\n }\n if (stat.size > MAX_SOURCE_BYTES) continue;\n\n seen.add(fullPath);\n const cached = this.fileCache.get(fullPath);\n if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {\n for (const k of cached.value) keysSet.add(k);\n continue;\n }\n\n try {\n const content = await fs.readFile(fullPath, 'utf-8');\n const keys = extractKeys(content);\n this.fileCache.set(fullPath, { mtimeMs: stat.mtimeMs, size: stat.size, value: keys });\n for (const k of keys) keysSet.add(k);\n } catch {}\n }\n };\n\n await scanDir(projectRoot);\n\n // Forget files that disappeared so the cache cannot grow without bound.\n for (const cachedPath of this.fileCache.keys()) {\n if (!seen.has(cachedPath)) this.fileCache.delete(cachedPath);\n }\n\n return Array.from(keysSet).sort();\n }\n\n /**\n * Scans all locale files/folders to compile the set of all keys currently in locales.\n */\n async scanLocalesKeys(\n basePath: string,\n listLocales: (base: string) => Promise<string[]>\n ): Promise<string[]> {\n this.assertEnabled();\n\n const locales = await listLocales(basePath);\n const keysSet = new Set<string>();\n\n for (const locale of locales) {\n try {\n const resolvedBasePath = path.resolve(basePath);\n const localePath = path.resolve(resolvedBasePath, locale);\n\n let isDir = false;\n try {\n const stats = await fs.stat(localePath);\n isDir = stats.isDirectory();\n } catch {}\n\n let data: Record<string, any> = {};\n\n if (isDir) {\n const files = await fs.readdir(localePath);\n for (const file of files) {\n if (file.endsWith('.json')) {\n const ns = path.basename(file, '.json');\n try {\n const content = await fs.readFile(path.join(localePath, file), 'utf-8');\n data[ns] = JSON.parse(content);\n } catch {}\n }\n }\n } else {\n try {\n if (!isSafeLocaleSegment(locale)) {\n continue;\n }\n const filePath = path.resolve(resolvedBasePath, `${locale}.json`);\n if (filePath.startsWith(resolvedBasePath)) {\n const content = await fs.readFile(filePath, 'utf-8');\n data = JSON.parse(content);\n }\n } catch {}\n }\n\n const flatten = (obj: any, prefix = '') => {\n if (!obj || typeof obj !== 'object') return;\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const newKey = prefix ? `${prefix}.${key}` : key;\n if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {\n flatten(obj[key], newKey);\n } else {\n keysSet.add(newKey);\n }\n }\n }\n };\n\n flatten(data);\n } catch {}\n }\n\n return Array.from(keysSet).sort();\n }\n\n /**\n * Returns merged lists of keys from codebase and existing locale files.\n */\n async getKeysMetadata(\n basePath: string,\n listLocales: (base: string) => Promise<string[]>\n ): Promise<{ allKeys: string[]; codeKeys: string[] }> {\n const codeKeys = await this.scanCodebaseKeys(basePath);\n const localeKeys = await this.scanLocalesKeys(basePath, listLocales);\n const allKeys = Array.from(new Set([...codeKeys, ...localeKeys])).sort();\n return {\n allKeys,\n codeKeys,\n };\n }\n}\n","/**\n * Single source of truth for \"is LiveLocale active right now, and in which mode?\".\n *\n * Modes:\n * - `development`: NODE_ENV is `development` (the default, zero-config mode).\n * - `preview`: explicitly opted in with `LIVELOCALE=preview` (server) or\n * `NEXT_PUBLIC_LIVELOCALE=preview` (client bundles). Works in production builds so a\n * preview deployment can be edited through a hosted Studio. The mutation API then\n * requires a bearer token (see the router).\n * - `off`: everything else.\n *\n * Rules, in order:\n * 1. `LIVELOCALE=off|0|false` (or `NEXT_PUBLIC_LIVELOCALE=off`) always wins → off.\n * 2. `LIVELOCALE=preview` / `NEXT_PUBLIC_LIVELOCALE=preview` → preview.\n * 3. `NODE_ENV` must be `development`, otherwise off.\n * 4. `LIVELOCALE=force` skips rule 5.\n * 5. On the server, a known hosting platform (Vercel preview/production, Netlify,\n * Kubernetes, Render, Fly.io, Railway, Heroku) → off, so a mis-configured staging\n * deploy cannot leak watermarks. A one-time warning is logged.\n *\n * `process.env.NODE_ENV` and `process.env.NEXT_PUBLIC_LIVELOCALE` are referenced as\n * literal member expressions so Next.js and Vite can inline them in browser bundles.\n */\n\nexport type LiveLocaleMode = 'off' | 'development' | 'preview';\n\nconst OFF_VALUES = new Set(['off', '0', 'false']);\n\nlet warnedDeployed = false;\n\n/** Test hook: forget that the deployed-platform warning was already printed. */\nexport function resetEnvWarnings(): void {\n warnedDeployed = false;\n}\n\nfunction readNodeEnv(): string | undefined {\n try {\n return process.env.NODE_ENV;\n } catch {\n return undefined;\n }\n}\n\n/** Set by the Vite plugin (and usable by any host) to mirror the server flag into the page. */\nexport const BROWSER_FLAG_GLOBAL = '__LIVELOCALE_FLAG__';\n\nfunction readPublicFlag(): string | undefined {\n let value: string | undefined;\n try {\n value = process.env.NEXT_PUBLIC_LIVELOCALE;\n } catch {\n value = undefined;\n }\n if (value) return value;\n try {\n const g = globalThis as any;\n const fromGlobal = g.window?.[BROWSER_FLAG_GLOBAL] ?? g[BROWSER_FLAG_GLOBAL];\n return typeof fromGlobal === 'string' ? fromGlobal : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined' || !process.env) return undefined;\n const value = process.env[name];\n return value === '' ? undefined : value;\n}\n\n/** Returns a human-readable platform name when running on a known hosting platform. */\nexport function detectDeployedPlatform(): string | null {\n if (typeof window !== 'undefined') return null; // browser: nothing to detect\n const vercelEnv = readEnv('VERCEL_ENV');\n if (vercelEnv === 'production' || vercelEnv === 'preview')\n return `Vercel (VERCEL_ENV=${vercelEnv})`;\n const netlifyContext = readEnv('CONTEXT');\n if (readEnv('NETLIFY') === 'true' && netlifyContext && netlifyContext !== 'dev') {\n return `Netlify (CONTEXT=${netlifyContext})`;\n }\n if (readEnv('KUBERNETES_SERVICE_HOST')) return 'Kubernetes';\n if (readEnv('RENDER') === 'true') return 'Render';\n if (readEnv('FLY_APP_NAME')) return 'Fly.io';\n if (readEnv('RAILWAY_ENVIRONMENT')) return 'Railway';\n if (readEnv('DYNO')) return 'Heroku';\n return null;\n}\n\nfunction evaluate(): { mode: LiveLocaleMode; reason: string | null } {\n const flag = (readEnv('LIVELOCALE') ?? '').toLowerCase();\n const publicFlag = (readPublicFlag() ?? '').toLowerCase();\n\n if (OFF_VALUES.has(flag)) return { mode: 'off', reason: 'LIVELOCALE kill switch is set' };\n if (OFF_VALUES.has(publicFlag)) {\n return { mode: 'off', reason: 'NEXT_PUBLIC_LIVELOCALE kill switch is set' };\n }\n\n if (flag === 'preview' || publicFlag === 'preview') return { mode: 'preview', reason: null };\n\n const nodeEnv = readNodeEnv();\n if (nodeEnv !== 'development') {\n return {\n mode: 'off',\n reason: `NODE_ENV is \"${nodeEnv ?? 'undefined'}\" (must be \"development\", or set LIVELOCALE=preview)`,\n };\n }\n\n if (flag === 'force') return { mode: 'development', reason: null };\n\n const platform = detectDeployedPlatform();\n if (platform) {\n if (!warnedDeployed) {\n warnedDeployed = true;\n console.warn(\n `[livelocale] Disabled: deployed environment detected (${platform}) while NODE_ENV=development. ` +\n `Set LIVELOCALE=force to override, LIVELOCALE=preview for a hosted preview, or LIVELOCALE=off to silence this warning.`\n );\n }\n return { mode: 'off', reason: `deployed environment detected (${platform})` };\n }\n\n return { mode: 'development', reason: null };\n}\n\n/** The active mode: `off`, `development`, or `preview`. */\nexport function getLiveLocaleMode(): LiveLocaleMode {\n return evaluate().mode;\n}\n\n/**\n * Returns `null` when LiveLocale should be active, otherwise a short reason\n * suitable for error messages and logs.\n */\nexport function getDisabledReason(): string | null {\n return evaluate().reason;\n}\n\n/** True when watermarking, the mutation API, and the client scanner should run. */\nexport function isLiveLocaleEnabled(): boolean {\n return evaluate().mode !== 'off';\n}\n","/**\n * A small, dependency-free `.gitignore` matcher covering the patterns that\n * matter for skipping generated code during key scanning: directory rules\n * (`build/`), anchored rules (`/dist`), globs (`*.log`, `**\\/tmp`,\n * `docs/**\\/*.snap`) and negation (`!keep.log`). Last matching rule wins, and a\n * path whose ancestor directory is ignored is ignored too.\n */\n\nexport interface GitignoreMatcher {\n /** `relPath` is relative to the directory holding the .gitignore, using `/` separators. */\n ignores(relPath: string, isDir: boolean): boolean;\n}\n\ninterface Rule {\n regex: RegExp;\n negated: boolean;\n dirOnly: boolean;\n}\n\nfunction globToRegexSource(pattern: string): string {\n let out = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i]!;\n if (ch === '*') {\n if (pattern[i + 1] === '*') {\n // `**/` → any number of directories (including none); `**` elsewhere → anything\n if (pattern[i + 2] === '/') {\n out += '(?:.*/)?';\n i += 3;\n } else {\n out += '.*';\n i += 2;\n }\n } else {\n out += '[^/]*';\n i += 1;\n }\n } else if (ch === '?') {\n out += '[^/]';\n i += 1;\n } else {\n out += /[.+^${}()|[\\]\\\\]/.test(ch) ? `\\\\${ch}` : ch;\n i += 1;\n }\n }\n return out;\n}\n\nfunction compileRule(rawLine: string): Rule | null {\n let line = rawLine.replace(/\\r$/, '');\n if (!line.trim() || line.trimStart().startsWith('#')) return null;\n // Trailing spaces are ignored unless escaped\n line = line.replace(/(?<!\\\\)\\s+$/, '');\n\n let negated = false;\n if (line.startsWith('!')) {\n negated = true;\n line = line.slice(1);\n }\n\n let dirOnly = false;\n if (line.endsWith('/')) {\n dirOnly = true;\n line = line.slice(0, -1);\n }\n\n let anchored = false;\n if (line.startsWith('/')) {\n anchored = true;\n line = line.slice(1);\n } else if (line.includes('/')) {\n anchored = true;\n }\n\n if (!line) return null;\n\n const body = globToRegexSource(line);\n const regex = anchored ? new RegExp(`^${body}$`) : new RegExp(`(?:^|/)${body}$`);\n return { regex, negated, dirOnly };\n}\n\nexport function parseGitignore(content: string): GitignoreMatcher {\n const rules: Rule[] = [];\n for (const line of content.split('\\n')) {\n const rule = compileRule(line);\n if (rule) rules.push(rule);\n }\n\n const matchOne = (relPath: string, isDir: boolean): boolean => {\n let ignored = false;\n for (const rule of rules) {\n if (rule.dirOnly && !isDir) continue;\n if (rule.regex.test(relPath)) ignored = !rule.negated;\n }\n return ignored;\n };\n\n return {\n ignores(relPath: string, isDir: boolean): boolean {\n if (rules.length === 0) return false;\n const normalized = relPath.replace(/\\\\/g, '/').replace(/^\\.\\//, '').replace(/^\\/+/, '');\n if (!normalized) return false;\n if (matchOne(normalized, isDir)) return true;\n\n // An ignored ancestor directory ignores everything beneath it.\n const segments = normalized.split('/');\n for (let i = 1; i < segments.length; i++) {\n if (matchOne(segments.slice(0, i).join('/'), true)) return true;\n }\n return false;\n },\n };\n}\n","import { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { GitGateway, GitStatus, GitChange, GitChangeStatus } from './router.js';\n\nconst execFileAsync = promisify(execFile);\n\n/** Diffs larger than this are truncated so the Studio never receives megabytes of JSON. */\nconst MAX_DIFF_BYTES = 200_000;\n\nfunction mapStatus(xy: string): GitChangeStatus {\n if (xy === '??') return 'added';\n const code = xy.trim()[0] ?? '';\n switch (code) {\n case 'M':\n return 'modified';\n case 'A':\n return 'added';\n case 'D':\n return 'deleted';\n case 'R':\n return 'renamed';\n default:\n return 'modified';\n }\n}\n\n/**\n * Thin wrapper over the `git` CLI, scoped to the locales directory. Every\n * command receives arguments as an array (no shell), and every path argument\n * is the locales directory relative to the repository root, so the Studio can\n * only ever stage, commit, or discard translation files.\n */\nexport class GitClient implements GitGateway {\n private async git(cwd: string, args: string[]): Promise<string> {\n const { stdout } = await execFileAsync('git', args, {\n cwd,\n maxBuffer: 16 * 1024 * 1024,\n windowsHide: true,\n env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },\n });\n return stdout;\n }\n\n /** Resolves the repository root and the locales path relative to it (forward slashes). */\n private async locate(\n localesPath: string\n ): Promise<{ root: string; rel: string; absolute: string } | null> {\n const absolute = path.resolve(localesPath);\n const cwd = fs.existsSync(absolute) ? absolute : path.dirname(absolute);\n if (!fs.existsSync(cwd)) return null;\n try {\n const root = (await this.git(cwd, ['rev-parse', '--show-toplevel'])).trim();\n if (!root) return null;\n const rel = path.relative(root, absolute).split(path.sep).join('/');\n if (rel.startsWith('..')) return null; // locales directory lives outside the repo\n return { root: path.resolve(root), rel: rel || '.', absolute };\n } catch {\n return null;\n }\n }\n\n async status(localesPath: string): Promise<GitStatus> {\n const loc = await this.locate(localesPath);\n if (!loc) return { isRepo: false, branch: null, changes: [] };\n\n let branch: string | null = null;\n try {\n branch = (await this.git(loc.root, ['rev-parse', '--abbrev-ref', 'HEAD'])).trim() || null;\n } catch {\n branch = null; // fresh repository without commits\n }\n\n const changes: GitChange[] = [];\n try {\n const out = await this.git(loc.root, [\n 'status',\n '--porcelain=v1',\n '--untracked-files=all',\n '--',\n loc.rel,\n ]);\n for (const line of out.split('\\n')) {\n if (line.length < 4) continue;\n const xy = line.slice(0, 2);\n let file = line.slice(3).trim();\n const arrow = file.indexOf(' -> ');\n if (arrow !== -1) file = file.slice(arrow + 4);\n if (file.startsWith('\"') && file.endsWith('\"')) file = file.slice(1, -1);\n changes.push({ path: file, status: mapStatus(xy) });\n }\n } catch {}\n\n return { isRepo: true, branch, changes, mode: 'local', pullRequestUrl: null };\n }\n\n async diff(localesPath: string): Promise<string> {\n const loc = await this.locate(localesPath);\n if (!loc) return '';\n\n let out = '';\n try {\n out = await this.git(loc.root, ['diff', '--no-color', '--', loc.rel]);\n } catch {}\n\n // Untracked files are not part of `git diff`; synthesize an all-additions hunk.\n try {\n const untracked = await this.git(loc.root, [\n 'ls-files',\n '--others',\n '--exclude-standard',\n '--',\n loc.rel,\n ]);\n for (const file of untracked\n .split('\\n')\n .map((l) => l.trim())\n .filter(Boolean)) {\n const full = path.join(loc.root, file);\n let content = '';\n try {\n content = fs.readFileSync(full, 'utf-8');\n } catch {\n continue;\n }\n const lines = content.split('\\n');\n if (lines[lines.length - 1] === '') lines.pop();\n out +=\n `diff --git a/${file} b/${file}\\nnew file mode 100644\\n--- /dev/null\\n+++ b/${file}\\n` +\n `@@ -0,0 +1,${lines.length} @@\\n` +\n lines.map((l) => `+${l}`).join('\\n') +\n '\\n';\n }\n } catch {}\n\n if (Buffer.byteLength(out, 'utf-8') > MAX_DIFF_BYTES) {\n out = out.slice(0, MAX_DIFF_BYTES) + '\\n… diff truncated …\\n';\n }\n return out;\n }\n\n async commit(localesPath: string, message: string): Promise<{ hash: string }> {\n const loc = await this.locate(localesPath);\n if (!loc) throw new Error('Not a git repository.');\n\n const status = await this.status(localesPath);\n if (status.changes.length === 0) {\n throw new Error(`No changes to commit under ${loc.rel}.`);\n }\n\n await this.git(loc.root, ['add', '-A', '--', loc.rel]);\n await this.git(loc.root, ['commit', '-q', '-m', message, '--', loc.rel]);\n const hash = (await this.git(loc.root, ['rev-parse', '--short', 'HEAD'])).trim();\n return { hash };\n }\n\n async discard(localesPath: string): Promise<void> {\n const loc = await this.locate(localesPath);\n if (!loc) throw new Error('Not a git repository.');\n\n try {\n await this.git(loc.root, ['checkout', '--', loc.rel]);\n } catch {\n // No tracked files under the locales directory yet; nothing to restore.\n }\n await this.git(loc.root, ['clean', '-fd', '--', loc.rel]);\n }\n}\n","/**\n * Placeholder analysis for translation messages.\n *\n * Understands the formats the supported libraries use:\n * - ICU MessageFormat (next-intl, react-intl, FormatJS): `{name}`, `{count, number}`,\n * `{count, plural, one {…} other {…}}`, apostrophe quoting.\n * - i18next: `{{name}}`, `{{count, number}}`, nesting `$t(key)`.\n * - printf-style: `%s`, `%d`, `%1$s`.\n * - rich-text tags (react-intl, next-intl `t.rich`, i18next `<Trans>`): `<b>…</b>`, `<0>…</0>`.\n *\n * A placeholder token is a canonical string such as `{name}`, `{{name}}`, `%s` or `<b>`,\n * so two messages can be compared as sets.\n */\n\nconst PLURAL_TYPES = new Set(['plural', 'select', 'selectordinal']);\n\ninterface ParseResult {\n args: Set<string>;\n error: string | null;\n}\n\n/**\n * Removes ICU apostrophe-quoted spans (`'{'`, `'{literal}'`) and turns `''`\n * into a plain apostrophe so the structural scan only sees real syntax.\n */\nfunction unquoteIcu(message: string): string {\n let out = '';\n let i = 0;\n while (i < message.length) {\n const ch = message[i]!;\n if (ch !== \"'\") {\n out += ch;\n i += 1;\n continue;\n }\n if (message[i + 1] === \"'\") {\n out += \"'\";\n i += 2;\n continue;\n }\n const next = message[i + 1];\n if (next === '{' || next === '}' || next === '#' || next === '|') {\n // Quoted span: skip to the closing single apostrophe (doubled ones are literal).\n let j = i + 1;\n while (j < message.length) {\n if (message[j] === \"'\") {\n if (message[j + 1] === \"'\") {\n j += 2;\n continue;\n }\n break;\n }\n j += 1;\n }\n i = j + 1;\n continue;\n }\n out += ch;\n i += 1;\n }\n return out;\n}\n\n/** Recursive-descent walk over ICU structure collecting argument names and the first error. */\nfunction parseIcu(message: string): ParseResult {\n const args = new Set<string>();\n let pos = 0;\n let error: string | null = null;\n\n const fail = (msg: string) => {\n if (!error) error = msg;\n };\n\n const parseMessage = (depth: number): void => {\n while (pos < message.length) {\n const ch = message[pos]!;\n if (ch === '{') {\n pos += 1;\n parseArgument(depth + 1);\n } else if (ch === '}') {\n if (depth === 0) {\n fail('Unbalanced braces: unexpected \"}\"');\n pos += 1;\n } else {\n return; // caller consumes the brace\n }\n } else {\n pos += 1;\n }\n }\n if (depth > 0) fail('Unbalanced braces: missing \"}\"');\n };\n\n const readUntil = (stops: string[]): string => {\n let out = '';\n while (pos < message.length && !stops.includes(message[pos]!)) {\n out += message[pos]!;\n pos += 1;\n }\n return out;\n };\n\n const parseArgument = (depth: number): void => {\n const name = readUntil([',', '}']).trim();\n if (name && !name.includes('{')) args.add(name);\n if (pos >= message.length) {\n fail('Unbalanced braces: missing \"}\"');\n return;\n }\n if (message[pos] === '}') {\n pos += 1;\n return;\n }\n pos += 1; // ','\n const type = readUntil([',', '}']).trim();\n if (pos >= message.length) {\n fail('Unbalanced braces: missing \"}\"');\n return;\n }\n if (message[pos] === '}') {\n pos += 1;\n return;\n }\n pos += 1; // ','\n\n if (PLURAL_TYPES.has(type)) {\n let hasOther = false;\n while (pos < message.length) {\n // skip whitespace\n while (pos < message.length && /\\s/.test(message[pos]!)) pos += 1;\n if (message[pos] === '}') {\n pos += 1;\n if (!hasOther) fail(`\"${name}\" (${type}) has no \"other\" branch`);\n return;\n }\n const selector = readUntil(['{', '}', ' ', '\\t', '\\n']).trim();\n while (pos < message.length && /\\s/.test(message[pos]!)) pos += 1;\n if (message[pos] !== '{') {\n fail(`\"${name}\" (${type}): expected \"{\" after \"${selector}\"`);\n return;\n }\n if (selector === 'other') hasOther = true;\n pos += 1;\n parseMessage(depth + 1);\n if (message[pos] === '}') pos += 1;\n }\n fail('Unbalanced braces: missing \"}\"');\n return;\n }\n\n // number/date/time style, or unknown: skip to the matching brace\n let level = 1;\n while (pos < message.length && level > 0) {\n if (message[pos] === '{') level += 1;\n else if (message[pos] === '}') level -= 1;\n pos += 1;\n }\n if (level > 0) fail('Unbalanced braces: missing \"}\"');\n };\n\n parseMessage(0);\n return { args, error };\n}\n\nconst I18NEXT_RE = /\\{\\{\\s*([^{}]*?)\\s*\\}\\}/g;\nconst NESTING_RE = /\\$t\\([^)]*\\)/g;\nconst PRINTF_RE = /%(?:\\d+\\$)?[-+ 0#]*\\d*(?:\\.\\d+)?[sdifgxXoc]/g;\nconst TAG_RE = /<\\/?([A-Za-z][\\w-]*|\\d+)\\b[^<>]*>/g;\n\n/** Returns the sorted, de-duplicated placeholder tokens found in `message`. */\nexport function extractPlaceholders(message: string): string[] {\n if (typeof message !== 'string' || message === '') return [];\n const tokens = new Set<string>();\n\n // i18next interpolation first, then blank it out so the ICU scan does not see `{{`.\n const withoutI18next = message.replace(I18NEXT_RE, (_m, inner: string) => {\n const name = inner.split(',')[0]!.trim();\n if (name) tokens.add(`{{${name}}}`);\n return ' ';\n });\n\n for (const m of withoutI18next.match(NESTING_RE) ?? []) tokens.add(m);\n for (const m of withoutI18next.replace(/%%/g, '').match(PRINTF_RE) ?? []) tokens.add(m);\n for (const m of withoutI18next.matchAll(TAG_RE)) tokens.add(`<${m[1]}>`);\n\n const icu = parseIcu(unquoteIcu(withoutI18next.replace(TAG_RE, ' ')));\n for (const arg of icu.args) tokens.add(`{${arg}}`);\n\n return Array.from(tokens).sort();\n}\n\nexport interface PlaceholderComparison {\n ok: boolean;\n /** Present in the reference but not in the value. */\n missing: string[];\n /** Present in the value but not in the reference. */\n extra: string[];\n}\n\n/** Compares placeholder sets; an empty reference is compatible with anything. */\nexport function comparePlaceholders(\n reference: string | undefined | null,\n value: string\n): PlaceholderComparison {\n if (!reference) return { ok: true, missing: [], extra: [] };\n const ref = new Set(extractPlaceholders(reference));\n const val = new Set(extractPlaceholders(value));\n const missing = Array.from(ref).filter((t) => !val.has(t));\n const extra = Array.from(val).filter((t) => !ref.has(t));\n return { ok: missing.length === 0 && extra.length === 0, missing, extra };\n}\n\n/** Returns a description of the first ICU structural problem, or null when the message is sound. */\nexport function validateMessageSyntax(message: string): string | null {\n if (typeof message !== 'string') return null;\n const prepared = unquoteIcu(message.replace(I18NEXT_RE, ' ').replace(TAG_RE, ' '));\n return parseIcu(prepared).error;\n}\n","import { getDisabledReason, getLiveLocaleMode } from '../core/env.js';\nimport { comparePlaceholders, validateMessageSyntax } from '../core/placeholders.js';\nimport {\n isValidKeyFormat,\n isValidLocaleFormat,\n isValidValueFormat,\n isSafeKeyPath,\n} from '../core/validate.js';\n\n/**\n * Platform-agnostic port for translation file operations. The Next.js and\n * Vite adapters both depend on this narrow contract (DIP) instead of the\n * concrete FileMutator implementation.\n */\nexport interface MutationGateway {\n updateLocaleKey(basePath: string, locale: string, key: string, value: string): Promise<void>;\n listLocales(basePath: string): Promise<string[]>;\n /** Full dictionary for one locale (namespace folders merged). Rejects when missing. */\n readLocale(basePath: string, locale: string): Promise<Record<string, any>>;\n addLocale(basePath: string, locale: string): Promise<void>;\n renameLocale(basePath: string, locale: string, newLocale: string): Promise<void>;\n deleteLocale(basePath: string, locale: string): Promise<void>;\n getKeysMetadata(basePath: string): Promise<{ allKeys: string[]; codeKeys: string[] }>;\n}\n\nexport type GitChangeStatus = 'modified' | 'added' | 'deleted' | 'renamed';\n\nexport interface GitChange {\n /** Path relative to the repository root, forward slashes. */\n path: string;\n status: GitChangeStatus;\n}\n\nexport interface GitStatus {\n isRepo: boolean;\n branch: string | null;\n changes: GitChange[];\n /** `local` = git CLI on the developer machine, `github` = branch + pull request via API. */\n mode?: 'local' | 'github';\n /** Open pull request for the branch, when the gateway knows about one. */\n pullRequestUrl?: string | null;\n}\n\n/**\n * Optional port for version-control operations scoped to the locales\n * directory. Implemented by `GitClient`; absent when git integration is off.\n */\nexport interface GitGateway {\n status(localesPath: string): Promise<GitStatus>;\n diff(localesPath: string): Promise<string>;\n commit(localesPath: string, message: string): Promise<{ hash: string }>;\n discard(localesPath: string): Promise<void>;\n /**\n * Optional self-test of credentials and permissions. Returns human-readable\n * problems (empty when everything needed is in place). Reported by `?action=info`.\n */\n checkAccess?(): Promise<string[]>;\n}\n\nexport interface RouterRequest {\n method: string;\n url: string;\n origin: string;\n /** Raw request body for body-carrying methods; parsed by the router. */\n bodyText?: string;\n /** Value of the Authorization header, if any. */\n authorization?: string;\n /** Value of the Content-Type header (lowercased), if any. */\n contentType?: string;\n}\n\nexport interface RouterResponse {\n status: number;\n headers: Record<string, string>;\n body: string | null;\n}\n\nexport interface MutationRouterOptions {\n localesPath: string;\n allowedOrigins?: string[];\n /** Enables the `git` read action and the `commit` / `discard` write actions. */\n git?: GitGateway;\n /**\n * When set, every request except CORS preflight must carry\n * `Authorization: Bearer <authToken>`. Mandatory in preview mode.\n */\n authToken?: string;\n}\n\nexport interface MutationRouter {\n handle(request: RouterRequest): Promise<RouterResponse>;\n}\n\nconst DEFAULT_ALLOWED_ORIGINS = ['http://localhost:3010', 'http://127.0.0.1:3010'];\n\n/** Maximum accepted request body size (1 MB) — far above any realistic payload. */\nexport const MAX_BODY_BYTES = 1_000_000;\n\n/** Commit messages longer than this are rejected. */\nexport const MAX_COMMIT_MESSAGE_LENGTH = 500;\n\n/** Any localhost origin (any port) is trusted — remote websites are not. */\nconst LOCALHOST_ORIGIN_RE = /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/;\n\n/** Constant-time string comparison so token checks do not leak length or prefix. */\nfunction safeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\nfunction bearerToken(authorization: string | undefined): string | null {\n if (!authorization) return null;\n const match = /^Bearer\\s+(.+)$/i.exec(authorization.trim());\n return match ? match[1]!.trim() : null;\n}\n\nconst json = (\n status: number,\n headers: Record<string, string>,\n payload: unknown\n): RouterResponse => ({\n status,\n headers,\n body: JSON.stringify(payload),\n});\n\n/**\n * The single implementation of the livelocale mutation API.\n *\n * Both the Next.js route handler and the Vite dev-server middleware are thin\n * adapters over this router, so validation, CORS, and error handling live in\n * exactly one place (OCP).\n */\nexport function createMutationRouter(\n mutator: MutationGateway,\n options: MutationRouterOptions\n): MutationRouter {\n // Extra origins may be supplied at run time (GitHub Codespaces, Gitpod,\n // custom HTTPS dev hostnames) without touching code:\n // LIVELOCALE_ALLOWED_ORIGINS=\"https://abc-3010.app.github.dev,https://studio.dev.local\"\n const envOrigins = (process.env['LIVELOCALE_ALLOWED_ORIGINS'] || '')\n .split(',')\n .map((o) => o.trim())\n .filter(Boolean);\n const allowedOrigins = [...(options.allowedOrigins || DEFAULT_ALLOWED_ORIGINS), ...envOrigins];\n const localesPath = options.localesPath;\n const git = options.git;\n const authToken = options.authToken && options.authToken.length > 0 ? options.authToken : null;\n\n const isOriginAllowed = (origin: string): boolean =>\n allowedOrigins.includes(origin) || LOCALHOST_ORIGIN_RE.test(origin);\n\n const resolveAllowedOrigin = (origin: string): string => {\n return isOriginAllowed(origin) ? origin : allowedOrigins[0] || '';\n };\n\n const corsHeaders = (origin: string): Record<string, string> => ({\n 'Access-Control-Allow-Origin': resolveAllowedOrigin(origin),\n 'Access-Control-Allow-Methods': 'POST, OPTIONS, GET',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n 'Content-Type': 'application/json',\n });\n\n // A passing access check is remembered; a failing one is re-run on every\n // `info` call so fixing the token takes effect without a restart.\n let accessOk = false;\n async function accessProblems(): Promise<string[]> {\n if (!git?.checkAccess || accessOk) return [];\n try {\n const problems = await git.checkAccess();\n accessOk = problems.length === 0;\n return problems;\n } catch (err: any) {\n return [`Could not verify repository access: ${err?.message || err}`];\n }\n }\n\n // ─── GET: Fetch locale dictionary, list locales, or fetch keys metadata ───\n\n async function handleGet(\n urlObj: URL,\n basePath: string,\n headers: Record<string, string>\n ): Promise<RouterResponse> {\n const locale = urlObj.searchParams.get('locale');\n const action = urlObj.searchParams.get('action');\n\n if (action === 'info') {\n return json(200, headers, {\n mode: getLiveLocaleMode(),\n authRequired: authToken !== null,\n git: Boolean(git),\n problems: await accessProblems(),\n });\n }\n\n if (action === 'git') {\n if (!git) {\n return json(200, headers, {\n available: false,\n isRepo: false,\n branch: null,\n changes: [],\n diff: '',\n });\n }\n try {\n const status = await git.status(basePath);\n const diff = status.isRepo && status.changes.length > 0 ? await git.diff(basePath) : '';\n return json(200, headers, { available: true, ...status, diff });\n } catch (err: any) {\n return json(500, headers, {\n error: `Failed to read git status: ${err.message}`,\n code: 'GIT_STATUS_FAILED',\n });\n }\n }\n\n if (action === 'keys') {\n try {\n const metadata = await mutator.getKeysMetadata(basePath);\n return json(200, headers, metadata);\n } catch (err: any) {\n return json(500, headers, {\n error: `Failed to fetch keys metadata: ${err.message}`,\n code: 'KEYS_METADATA_FAILED',\n });\n }\n }\n\n if (!locale) {\n try {\n const localesList = await mutator.listLocales(basePath);\n return json(200, headers, localesList);\n } catch (err: any) {\n return json(500, headers, {\n error: `Failed to list locales: ${err.message}`,\n code: 'LIST_LOCALES_FAILED',\n });\n }\n }\n\n if (!isValidLocaleFormat(locale)) {\n return json(400, headers, {\n error: 'Invalid locale string parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n\n try {\n return json(200, headers, await mutator.readLocale(basePath, locale));\n } catch (readErr: any) {\n return json(404, headers, {\n error: `Failed to load locale: ${readErr.message}`,\n code: 'LOCALE_LOAD_FAILED',\n });\n }\n }\n\n // ─── POST: Mutate a translation key or manage locales ────────────────────\n\n async function handlePost(\n bodyText: string | undefined,\n basePath: string,\n headers: Record<string, string>\n ): Promise<RouterResponse> {\n let body: any;\n try {\n body = JSON.parse(bodyText ?? '');\n } catch {\n return json(400, headers, {\n error: 'Invalid JSON request body',\n code: 'BAD_REQUEST',\n });\n }\n\n const { locale, key, value, action, newLocale, message, reference, force } = body || {};\n\n if (action === 'commit' || action === 'discard') {\n if (!git) {\n return json(501, headers, {\n error: 'Git integration is not enabled for this handler.',\n code: 'GIT_UNAVAILABLE',\n });\n }\n if (action === 'commit') {\n const text = typeof message === 'string' ? message.trim() : '';\n if (!text || text.length > MAX_COMMIT_MESSAGE_LENGTH) {\n return json(400, headers, {\n error: `Commit message must be 1-${MAX_COMMIT_MESSAGE_LENGTH} characters.`,\n code: 'INVALID_COMMIT_MESSAGE',\n });\n }\n try {\n const result = await git.commit(basePath, text);\n return json(200, headers, { success: true, hash: result.hash });\n } catch (err: any) {\n return json(409, headers, { error: err.message, code: 'GIT_COMMIT_FAILED' });\n }\n }\n try {\n await git.discard(basePath);\n return json(200, headers, { success: true });\n } catch (err: any) {\n return json(500, headers, { error: err.message, code: 'GIT_DISCARD_FAILED' });\n }\n }\n\n if (action === 'addLocale') {\n if (!isValidLocaleFormat(locale)) {\n return json(400, headers, {\n error: 'Invalid locale string parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n await mutator.addLocale(basePath, locale);\n return json(200, headers, { success: true, locale });\n }\n\n if (action === 'renameLocale') {\n if (!isValidLocaleFormat(locale) || !isValidLocaleFormat(newLocale)) {\n return json(400, headers, {\n error: 'Invalid oldLocale or newLocale parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n await mutator.renameLocale(basePath, locale, newLocale);\n return json(200, headers, { success: true, locale, newLocale });\n }\n\n if (action === 'deleteLocale') {\n if (!isValidLocaleFormat(locale)) {\n return json(400, headers, {\n error: 'Invalid locale string parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n await mutator.deleteLocale(basePath, locale);\n return json(200, headers, { success: true, locale });\n }\n\n if (!isValidLocaleFormat(locale)) {\n return json(400, headers, {\n error: 'Invalid locale string parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n\n if (!isValidKeyFormat(key)) {\n return json(400, headers, {\n error:\n 'Invalid key path. Must be 1-200 characters containing alphanumeric, dots, underscores, or hyphens.',\n code: 'INVALID_KEY_FORMAT',\n });\n }\n\n if (!isSafeKeyPath(key)) {\n return json(400, headers, {\n error:\n 'Forbidden key path: segments like __proto__, constructor, or prototype are not allowed.',\n code: 'INVALID_KEY_FORMAT',\n });\n }\n\n if (!isValidValueFormat(value)) {\n return json(400, headers, {\n error: 'Invalid value. Must be a string less than 10000 characters.',\n code: 'INVALID_VALUE_FORMAT',\n });\n }\n\n // RULE SRV-007: PLACEHOLDER SAFETY — a translation must keep the placeholders\n // of its reference and stay structurally valid, unless the author forces it.\n if (force !== true) {\n const syntaxError = validateMessageSyntax(value);\n if (syntaxError) {\n return json(422, headers, {\n error: `Message syntax: ${syntaxError}.`,\n code: 'MESSAGE_SYNTAX',\n });\n }\n if (typeof reference === 'string') {\n const comparison = comparePlaceholders(reference, value);\n if (!comparison.ok) {\n const parts: string[] = [];\n if (comparison.missing.length) parts.push(`missing ${comparison.missing.join(', ')}`);\n if (comparison.extra.length) parts.push(`unexpected ${comparison.extra.join(', ')}`);\n return json(422, headers, {\n error: `Placeholders differ from the reference: ${parts.join('; ')}.`,\n code: 'PLACEHOLDER_MISMATCH',\n details: { missing: comparison.missing, extra: comparison.extra },\n });\n }\n }\n }\n\n await mutator.updateLocaleKey(basePath, locale, key, value);\n\n return json(200, headers, { success: true, key, locale });\n }\n\n return {\n async handle(request: RouterRequest): Promise<RouterResponse> {\n const headers = corsHeaders(request.origin);\n\n // RULE GEN-001: DEVELOPMENT-ONLY ENFORCEMENT\n const disabledReason = getDisabledReason();\n if (disabledReason !== null) {\n return json(403, headers, {\n error: `Forbidden: LiveLocale is disabled — ${disabledReason}. It only runs in development mode.`,\n code: 'FORBIDDEN',\n });\n }\n\n // Preflight never carries credentials; let it through so the browser can\n // learn that Authorization is an allowed header.\n if (request.method === 'OPTIONS') {\n return { status: 204, headers, body: null };\n }\n\n // RULE SEC-011: PREVIEW MODE REQUIRES A TOKEN — the API is reachable from the\n // internet in this mode, so refuse to serve anything without one configured.\n if (getLiveLocaleMode() === 'preview' && !authToken) {\n return json(403, headers, {\n error:\n 'Preview mode requires an access token. Set LIVELOCALE_TOKEN (or pass authToken) on the server.',\n code: 'PREVIEW_TOKEN_REQUIRED',\n });\n }\n\n // RULE SEC-012: BEARER AUTH — when a token is configured, every request needs it.\n if (authToken) {\n const presented = bearerToken(request.authorization);\n if (!presented || !safeEqual(presented, authToken)) {\n return json(401, headers, {\n error: 'Unauthorized: a valid Authorization: Bearer token is required.',\n code: 'UNAUTHORIZED',\n });\n }\n }\n\n // RULE SEC-006: CSRF PREVENTION — hard-reject writes from disallowed origins\n // (browsers always send Origin on cross-origin POSTs, including no-cors ones).\n if (request.method === 'POST' && request.origin && !isOriginAllowed(request.origin)) {\n return json(403, headers, {\n error: 'Forbidden: request origin is not allowed.',\n code: 'FORBIDDEN_ORIGIN',\n });\n }\n\n // RULE SEC-006: require a JSON content type on writes so the no-cors\n // \"text/plain\" CSRF vector cannot bypass validation via simple requests.\n if (\n request.method === 'POST' &&\n request.contentType &&\n !request.contentType.includes('application/json')\n ) {\n return json(415, headers, {\n error: 'Content-Type must be application/json.',\n code: 'UNSUPPORTED_MEDIA_TYPE',\n });\n }\n\n try {\n if (request.method === 'GET') {\n const urlObj = new URL(request.url, 'http://localhost');\n return await handleGet(urlObj, localesPath, headers);\n }\n\n if (request.method === 'POST') {\n return await handlePost(request.bodyText, localesPath, headers);\n }\n\n // Unsupported method\n return json(405, headers, {\n error: 'Method Not Allowed',\n code: 'METHOD_NOT_ALLOWED',\n });\n } catch (err: any) {\n return json(500, headers, {\n error: err.message || 'Internal Server Error',\n code: 'INTERNAL_ERROR',\n });\n }\n },\n };\n}\n","/**\n * Minimal structural type for a Node `http.IncomingMessage`-like request.\n * Used by the Pages Router adapter and the Vite middleware so neither needs\n * to import `next` or `vite` types.\n */\nexport interface NodeRequestLike {\n method?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n on(event: 'data', cb: (chunk: Buffer | string) => void): unknown;\n on(event: 'end', cb: () => void): unknown;\n on(event: 'error', cb: (err: Error) => void): unknown;\n destroy?(): void;\n}\n\n/** Minimal structural type for a Node `http.ServerResponse`-like response. */\nexport interface NodeResponseLike {\n statusCode: number;\n setHeader(name: string, value: string): unknown;\n end(chunk?: string): unknown;\n}\n\nexport class BodyTooLargeError extends Error {\n readonly code = 'BODY_TOO_LARGE';\n constructor() {\n super('Request body too large.');\n this.name = 'BodyTooLargeError';\n }\n}\n\n/**\n * Buffers a Node request body as UTF-8 text, rejecting with\n * `BodyTooLargeError` as soon as `maxBytes` is exceeded (RULE SEC-007).\n */\nexport function readNodeBody(req: NodeRequestLike, maxBytes: number): Promise<string> {\n return new Promise((resolve, reject) => {\n let bodyText = '';\n let settled = false;\n req.on('data', (chunk) => {\n if (settled) return;\n bodyText += chunk.toString();\n if (Buffer.byteLength(bodyText, 'utf-8') > maxBytes) {\n settled = true;\n reject(new BodyTooLargeError());\n req.destroy?.();\n }\n });\n req.on('end', () => {\n if (settled) return;\n settled = true;\n resolve(bodyText);\n });\n req.on('error', (err) => {\n if (settled) return;\n settled = true;\n reject(err);\n });\n });\n}\n\n/** Returns the first value of a possibly multi-valued Node header, lowercased name expected. */\nexport function headerValue(\n headers: Record<string, string | string[] | undefined>,\n name: string\n): string {\n const raw = headers[name];\n if (Array.isArray(raw)) return raw[0] ?? '';\n return raw ?? '';\n}\n","/**\n * Recursively merges `source` into `target`.\n *\n * Arrays and primitives in `source` replace `target` values; plain objects\n * are merged depth-first.\n */\nexport function deepMerge(target: any, source: any): any {\n if (typeof target !== 'object' || target === null) return source;\n if (typeof source !== 'object' || source === null) return target;\n\n const result = { ...target };\n for (const key of Object.keys(source)) {\n if (key in target) {\n result[key] = deepMerge(target[key], source[key]);\n } else {\n result[key] = source[key];\n }\n }\n return result;\n}\n"],"mappings":";AAAA,YAAYA,WAAU;AACtB,YAAYC,SAAQ;;;ACDpB,SAAS,YAAYC,WAAU;AAC/B,YAAYC,WAAU;;;ACMf,IAAM,YAAY;AAElB,IAAM,SAAS;AAEf,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AAOzB,IAAM,yBAAyB,oBAAI,IAAI,CAAC,aAAa,eAAe,WAAW,CAAC;AAMhF,SAAS,cAAc,KAAuB;AACnD,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,SAAO,CAAC,IAAI,MAAM,GAAG,EAAE,KAAK,CAAC,YAAY,uBAAuB,IAAI,OAAO,CAAC;AAC9E;AAMO,SAAS,oBAAoB,QAA0B;AAC5D,SACE,OAAO,WAAW,YAClB,OAAO,UAAU,KACjB,OAAO,UAAU,qBACjB,UAAU,KAAK,MAAM;AAEzB;AAMO,SAAS,iBAAiB,KAAuB;AACtD,SACE,OAAO,QAAQ,YAAY,IAAI,UAAU,KAAK,IAAI,UAAU,kBAAkB,OAAO,KAAK,GAAG;AAEjG;AAKO,SAAS,mBAAmB,OAAyB;AAC1D,SAAO,OAAO,UAAU,YAAY,MAAM,UAAU;AACtD;AAKO,SAAS,oBAAoB,QAAyB;AAC3D,SAAO,EACL,OAAO,SAAS,IAAI,KACpB,OAAO,SAAS,GAAG,KACnB,OAAO,SAAS,IAAI,KACpB,OAAO,SAAS,GAAG,KACnB,OAAO,SAAS,IAAI;AAExB;;;ACxEA,YAAY,UAAU;AAOf,SAAS,sBAAsB,UAAkB,QAAwB;AAC9E,MAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,QAAM,mBAAwB,aAAQ,QAAQ;AAC9C,QAAM,WAAgB,aAAQ,kBAAkB,GAAG,MAAM,OAAO;AAChE,MAAI,CAAC,SAAS,WAAW,gBAAgB,GAAG;AAC1C,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO;AACT;;;ACdO,IAAM,gBAAgB;AAOtB,SAAS,kBAAkB,KAA0B,SAAiB,OAAqB;AAChG,QAAM,OAAO,QAAQ,MAAM,GAAG;AAE9B,MAAI,KAAK,SAAS,eAAe;AAC/B,UAAM,IAAI;AAAA,MACR,2DAA2D,aAAa;AAAA,IAC1E;AAAA,EACF;AAEA,MAAI,KAAK,KAAK,CAAC,MAAM,uBAAuB,IAAI,CAAC,CAAC,GAAG;AACnD,UAAM,IAAI;AAAA,MACR,gDAAgD,OAAO;AAAA,IACzD;AAAA,EACF;AAEA,MAAI,UAAU;AACd,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAElB,QAAI,MAAM,KAAK,SAAS,GAAG;AACzB,cAAQ,GAAG,IAAI;AACf;AAAA,IACF;AAEA,QACE,EAAE,OAAO,YACT,QAAQ,GAAG,MAAM,QACjB,OAAO,QAAQ,GAAG,MAAM,YACxB,MAAM,QAAQ,QAAQ,GAAG,CAAC,GAC1B;AACA,cAAQ,GAAG,IAAI,CAAC;AAAA,IAClB;AAEA,cAAU,QAAQ,GAAG;AAAA,EACvB;AACF;AAMO,SAAS,iBAAiB,SAAyB;AACxD,QAAM,QAAQ,cAAc,KAAK,OAAO;AACxC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,KAAK,MAAM,CAAC;AAClB,SAAO,GAAG,SAAS,GAAI,IAAI,MAAO;AACpC;;;ACxDA,SAAS,YAAY,UAAU;AAC/B,YAAYC,WAAU;;;ACyBtB,IAAM,aAAa,oBAAI,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC;AAEhD,IAAI,iBAAiB;AAOrB,SAAS,cAAkC;AACzC,MAAI;AACF,WAAO,QAAQ,IAAI;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,IAAM,sBAAsB;AAEnC,SAAS,iBAAqC;AAC5C,MAAI;AACJ,MAAI;AACF,YAAQ,QAAQ,IAAI;AAAA,EACtB,QAAQ;AACN,YAAQ;AAAA,EACV;AACA,MAAI,MAAO,QAAO;AAClB,MAAI;AACF,UAAM,IAAI;AACV,UAAM,aAAa,EAAE,SAAS,mBAAmB,KAAK,EAAE,mBAAmB;AAC3E,WAAO,OAAO,eAAe,WAAW,aAAa;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,MAAkC;AACjD,MAAI,OAAO,YAAY,eAAe,CAAC,QAAQ,IAAK,QAAO;AAC3D,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,SAAO,UAAU,KAAK,SAAY;AACpC;AAGO,SAAS,yBAAwC;AACtD,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,YAAY,QAAQ,YAAY;AACtC,MAAI,cAAc,gBAAgB,cAAc;AAC9C,WAAO,sBAAsB,SAAS;AACxC,QAAM,iBAAiB,QAAQ,SAAS;AACxC,MAAI,QAAQ,SAAS,MAAM,UAAU,kBAAkB,mBAAmB,OAAO;AAC/E,WAAO,oBAAoB,cAAc;AAAA,EAC3C;AACA,MAAI,QAAQ,yBAAyB,EAAG,QAAO;AAC/C,MAAI,QAAQ,QAAQ,MAAM,OAAQ,QAAO;AACzC,MAAI,QAAQ,cAAc,EAAG,QAAO;AACpC,MAAI,QAAQ,qBAAqB,EAAG,QAAO;AAC3C,MAAI,QAAQ,MAAM,EAAG,QAAO;AAC5B,SAAO;AACT;AAEA,SAAS,WAA4D;AACnE,QAAM,QAAQ,QAAQ,YAAY,KAAK,IAAI,YAAY;AACvD,QAAM,cAAc,eAAe,KAAK,IAAI,YAAY;AAExD,MAAI,WAAW,IAAI,IAAI,EAAG,QAAO,EAAE,MAAM,OAAO,QAAQ,gCAAgC;AACxF,MAAI,WAAW,IAAI,UAAU,GAAG;AAC9B,WAAO,EAAE,MAAM,OAAO,QAAQ,4CAA4C;AAAA,EAC5E;AAEA,MAAI,SAAS,aAAa,eAAe,UAAW,QAAO,EAAE,MAAM,WAAW,QAAQ,KAAK;AAE3F,QAAM,UAAU,YAAY;AAC5B,MAAI,YAAY,eAAe;AAC7B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,gBAAgB,WAAW,WAAW;AAAA,IAChD;AAAA,EACF;AAEA,MAAI,SAAS,QAAS,QAAO,EAAE,MAAM,eAAe,QAAQ,KAAK;AAEjE,QAAM,WAAW,uBAAuB;AACxC,MAAI,UAAU;AACZ,QAAI,CAAC,gBAAgB;AACnB,uBAAiB;AACjB,cAAQ;AAAA,QACN,yDAAyD,QAAQ;AAAA,MAEnE;AAAA,IACF;AACA,WAAO,EAAE,MAAM,OAAO,QAAQ,kCAAkC,QAAQ,IAAI;AAAA,EAC9E;AAEA,SAAO,EAAE,MAAM,eAAe,QAAQ,KAAK;AAC7C;AAGO,SAAS,oBAAoC;AAClD,SAAO,SAAS,EAAE;AACpB;AAMO,SAAS,oBAAmC;AACjD,SAAO,SAAS,EAAE;AACpB;AAGO,SAAS,sBAA+B;AAC7C,SAAO,SAAS,EAAE,SAAS;AAC7B;;;ACxHA,SAAS,kBAAkB,SAAyB;AAClD,MAAI,MAAM;AACV,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AACzB,UAAM,KAAK,QAAQ,CAAC;AACpB,QAAI,OAAO,KAAK;AACd,UAAI,QAAQ,IAAI,CAAC,MAAM,KAAK;AAE1B,YAAI,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1B,iBAAO;AACP,eAAK;AAAA,QACP,OAAO;AACL,iBAAO;AACP,eAAK;AAAA,QACP;AAAA,MACF,OAAO;AACL,eAAO;AACP,aAAK;AAAA,MACP;AAAA,IACF,WAAW,OAAO,KAAK;AACrB,aAAO;AACP,WAAK;AAAA,IACP,OAAO;AACL,aAAO,mBAAmB,KAAK,EAAE,IAAI,KAAK,EAAE,KAAK;AACjD,WAAK;AAAA,IACP;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,SAA8B;AACjD,MAAI,OAAO,QAAQ,QAAQ,OAAO,EAAE;AACpC,MAAI,CAAC,KAAK,KAAK,KAAK,KAAK,UAAU,EAAE,WAAW,GAAG,EAAG,QAAO;AAE7D,SAAO,KAAK,QAAQ,eAAe,EAAE;AAErC,MAAI,UAAU;AACd,MAAI,KAAK,WAAW,GAAG,GAAG;AACxB,cAAU;AACV,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB;AAEA,MAAI,UAAU;AACd,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,cAAU;AACV,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB;AAEA,MAAI,WAAW;AACf,MAAI,KAAK,WAAW,GAAG,GAAG;AACxB,eAAW;AACX,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB,WAAW,KAAK,SAAS,GAAG,GAAG;AAC7B,eAAW;AAAA,EACb;AAEA,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,OAAO,kBAAkB,IAAI;AACnC,QAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,IAAI,GAAG,IAAI,IAAI,OAAO,UAAU,IAAI,GAAG;AAC/E,SAAO,EAAE,OAAO,SAAS,QAAQ;AACnC;AAEO,SAAS,eAAe,SAAmC;AAChE,QAAM,QAAgB,CAAC;AACvB,aAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,UAAM,OAAO,YAAY,IAAI;AAC7B,QAAI,KAAM,OAAM,KAAK,IAAI;AAAA,EAC3B;AAEA,QAAM,WAAW,CAAC,SAAiB,UAA4B;AAC7D,QAAI,UAAU;AACd,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,WAAW,CAAC,MAAO;AAC5B,UAAI,KAAK,MAAM,KAAK,OAAO,EAAG,WAAU,CAAC,KAAK;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,QAAQ,SAAiB,OAAyB;AAChD,UAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,YAAM,aAAa,QAAQ,QAAQ,OAAO,GAAG,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,QAAQ,EAAE;AACtF,UAAI,CAAC,WAAY,QAAO;AACxB,UAAI,SAAS,YAAY,KAAK,EAAG,QAAO;AAGxC,YAAM,WAAW,WAAW,MAAM,GAAG;AACrC,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAI,SAAS,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,GAAG,IAAI,EAAG,QAAO;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AF3GA,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,oBAAoB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,MAAM,CAAC;AAGhF,IAAM,mBAAmB;AAEzB,IAAM,UAAU;AAChB,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,YAAY;AAClB,IAAM,OAAO,OAAO;AAEpB,IAAM,MAAM,OAAO;AACnB,IAAM,YAAY,CAAC,MACjB,EAAE,SAAS,IAAI,KAAK,EAAE,SAAS,IAAI,KAAK,EAAE,SAAS,IAAI;AAGzD,IAAM,UAAU,OAAO;AAWvB,SAAS,kBAAkB,SAAsC;AAC/D,QAAM,MAAM,oBAAI,IAAoB;AAEpC,QAAM,SAAS,IAAI;AAAA,IACjB,OACE,OAAO,mFACP,MACA,OAAO;AAAA,IACT;AAAA,EACF;AACA,aAAW,KAAK,QAAQ,SAAS,MAAM,EAAG,KAAI,IAAI,EAAE,OAAQ,MAAM,GAAI,UAAU,CAAC,KAAK,EAAE;AAExF,QAAM,iBAAiB,IAAI;AAAA,IACzB,OAAO,OAAO,mDAAmD,MAAM,OAAO;AAAA,IAC9E;AAAA,EACF;AACA,aAAW,KAAK,QAAQ,SAAS,cAAc,GAAG;AAChD,QAAI,IAAI,EAAE,OAAQ,MAAM,GAAI,UAAU,CAAC,KAAK,EAAE;AAAA,EAChD;AAEA,QAAM,eAAe,IAAI;AAAA,IACvB,OAAO,OAAO,qEAAqE;AAAA,IACnF;AAAA,EACF;AACA,aAAW,KAAK,QAAQ,SAAS,YAAY,GAAG;AAC9C,QAAI,IAAI,EAAE,OAAQ,MAAM,GAAI,UAAU,CAAC,KAAK,EAAE;AAAA,EAChD;AAEA,QAAM,YAAY,IAAI;AAAA,IACpB,OAAO,OAAO,2DAA2D,MAAM,OAAO;AAAA,IACtF;AAAA,EACF;AACA,aAAW,KAAK,QAAQ,SAAS,SAAS,GAAG;AAC3C,UAAM,UAAU,uBAAuB,KAAK,EAAE,OAAQ,UAAU,CAAE;AAClE,QAAI,QAAS,KAAI,IAAI,QAAQ,CAAC,KAAK,KAAK,UAAU,CAAC,KAAK,EAAE;AAAA,EAC5D;AAEA,SAAO;AACT;AAGO,SAAS,YAAY,SAA2B;AACrD,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,WAAW,kBAAkB,OAAO;AAC1C,QAAM,cAAc,oBAAI,IAAY,CAAC,KAAK,GAAG,SAAS,KAAK,CAAC,CAAC;AAE7D,aAAW,QAAQ,aAAa;AAC9B,UAAM,SAAS,SAAS,IAAI,IAAI,KAAK;AACrC,UAAM,SAAS,IAAI,OAAO,OAAO,UAAU,OAAO,UAAU,OAAO,aAAa,KAAK,GAAG;AACxF,eAAW,KAAK,QAAQ,SAAS,MAAM,GAAG;AACxC,YAAM,MAAM,UAAU,CAAC;AACvB,UAAI,CAAC,OAAO,IAAI,SAAS,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAG;AACxD,WAAK,IAAI,SAAS,GAAG,MAAM,IAAI,GAAG,KAAK,GAAG;AAAA,IAC5C;AAAA,EACF;AAEA,MAAI;AACJ,UAAQ,YAAY;AACpB,UAAQ,QAAQ,QAAQ,KAAK,OAAO,OAAO,MAAM;AAC/C,UAAM,QAAQ,MAAM,CAAC,EAAE,QAAQ,OAAO,EAAE,EAAE,QAAQ,OAAO,EAAE;AAC3D,UAAM,WAAW,MACd,MAAM,GAAG,EACT,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,EACvB,OAAO,OAAO;AACjB,QAAI,SAAS,SAAS,KAAK,CAAC,SAAS,KAAK,CAAC,QAAQ,aAAa,IAAI,GAAG,CAAC,GAAG;AACzE,WAAK,IAAI,SAAS,KAAK,GAAG,CAAC;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI,EAAE,KAAK;AAC/B;AAQA,IAAM,aAA+B,EAAE,SAAS,MAAM,MAAM;AAkBrD,IAAM,aAAN,MAAiB;AAAA,EACd,YAAY,oBAAI,IAAkC;AAAA,EAClD,iBAAsD;AAAA,EAC7C;AAAA,EAEjB,YAAY,UAA6B,CAAC,GAAG;AAC3C,SAAK,iBAAiB,QAAQ,kBAAkB;AAAA,EAClD;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,CAAC,KAAK,eAAgB;AAC1B,UAAM,SAAS,kBAAkB;AACjC,QAAI,WAAW,MAAM;AACnB,YAAM,IAAI;AAAA,QACR,qEAAqE,MAAM;AAAA,MAC7E;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,aAAgD;AAC1E,UAAM,gBAAqB,WAAK,aAAa,YAAY;AACzD,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,GAAG,KAAK,aAAa;AAAA,IACpC,QAAQ;AACN,WAAK,iBAAiB;AACtB,aAAO;AAAA,IACT;AACA,UAAM,MAAM,KAAK;AACjB,QAAI,OAAO,IAAI,YAAY,KAAK,WAAW,IAAI,SAAS,KAAK,MAAM;AACjE,aAAO,IAAI;AAAA,IACb;AACA,QAAI;AACF,YAAM,UAAU,MAAM,GAAG,SAAS,eAAe,OAAO;AACxD,YAAM,UAAU,eAAe,OAAO;AACtC,WAAK,iBAAiB,EAAE,SAAS,KAAK,SAAS,MAAM,KAAK,MAAM,OAAO,QAAQ;AAC/E,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,UAAqC;AAC1D,WAAO,KAAK,eAAoB,cAAa,cAAQ,QAAQ,CAAC,CAAC;AAAA,EACjE;AAAA;AAAA,EAGA,MAAM,eAAe,aAAwC;AAC3D,SAAK,cAAc;AAEnB,kBAAmB,cAAQ,WAAW;AACtC,UAAM,YAAY,MAAM,KAAK,cAAc,WAAW;AACtD,UAAM,UAAU,oBAAI,IAAY;AAChC,UAAM,OAAO,oBAAI,IAAY;AAE7B,UAAM,QAAQ,CAAC,aACR,eAAS,aAAa,QAAQ,EAAE,MAAW,SAAG,EAAE,KAAK,GAAG;AAE/D,UAAM,UAAU,OAAO,QAA+B;AACpD,UAAI;AACJ,UAAI;AACF,gBAAQ,MAAM,GAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,MACvD,QAAQ;AACN;AAAA,MACF;AAEA,iBAAW,QAAQ,OAAO;AACxB,cAAM,WAAgB,WAAK,KAAK,KAAK,IAAI;AAEzC,YAAI,KAAK,YAAY,GAAG;AACtB,cAAI,YAAY,IAAI,KAAK,IAAI,EAAG;AAChC,cAAI,UAAU,QAAQ,MAAM,QAAQ,GAAG,IAAI,EAAG;AAC9C,gBAAM,QAAQ,QAAQ;AACtB;AAAA,QACF;AAEA,YAAI,CAAC,KAAK,OAAO,EAAG;AACpB,YAAI,CAAC,kBAAkB,IAAS,cAAQ,KAAK,IAAI,CAAC,EAAG;AACrD,YAAI,KAAK,KAAK,SAAS,OAAO,EAAG;AACjC,YAAI,UAAU,QAAQ,MAAM,QAAQ,GAAG,KAAK,EAAG;AAE/C,YAAI;AACJ,YAAI;AACF,iBAAO,MAAM,GAAG,KAAK,QAAQ;AAAA,QAC/B,QAAQ;AACN;AAAA,QACF;AACA,YAAI,KAAK,OAAO,iBAAkB;AAElC,aAAK,IAAI,QAAQ;AACjB,cAAM,SAAS,KAAK,UAAU,IAAI,QAAQ;AAC1C,YAAI,UAAU,OAAO,YAAY,KAAK,WAAW,OAAO,SAAS,KAAK,MAAM;AAC1E,qBAAW,KAAK,OAAO,MAAO,SAAQ,IAAI,CAAC;AAC3C;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,UAAU,MAAM,GAAG,SAAS,UAAU,OAAO;AACnD,gBAAM,OAAO,YAAY,OAAO;AAChC,eAAK,UAAU,IAAI,UAAU,EAAE,SAAS,KAAK,SAAS,MAAM,KAAK,MAAM,OAAO,KAAK,CAAC;AACpF,qBAAW,KAAK,KAAM,SAAQ,IAAI,CAAC;AAAA,QACrC,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AAEA,UAAM,QAAQ,WAAW;AAGzB,eAAW,cAAc,KAAK,UAAU,KAAK,GAAG;AAC9C,UAAI,CAAC,KAAK,IAAI,UAAU,EAAG,MAAK,UAAU,OAAO,UAAU;AAAA,IAC7D;AAEA,WAAO,MAAM,KAAK,OAAO,EAAE,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,UACA,aACmB;AACnB,SAAK,cAAc;AAEnB,UAAM,UAAU,MAAM,YAAY,QAAQ;AAC1C,UAAM,UAAU,oBAAI,IAAY;AAEhC,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,cAAM,mBAAwB,cAAQ,QAAQ;AAC9C,cAAM,aAAkB,cAAQ,kBAAkB,MAAM;AAExD,YAAI,QAAQ;AACZ,YAAI;AACF,gBAAM,QAAQ,MAAM,GAAG,KAAK,UAAU;AACtC,kBAAQ,MAAM,YAAY;AAAA,QAC5B,QAAQ;AAAA,QAAC;AAET,YAAI,OAA4B,CAAC;AAEjC,YAAI,OAAO;AACT,gBAAM,QAAQ,MAAM,GAAG,QAAQ,UAAU;AACzC,qBAAW,QAAQ,OAAO;AACxB,gBAAI,KAAK,SAAS,OAAO,GAAG;AAC1B,oBAAM,KAAU,eAAS,MAAM,OAAO;AACtC,kBAAI;AACF,sBAAM,UAAU,MAAM,GAAG,SAAc,WAAK,YAAY,IAAI,GAAG,OAAO;AACtE,qBAAK,EAAE,IAAI,KAAK,MAAM,OAAO;AAAA,cAC/B,QAAQ;AAAA,cAAC;AAAA,YACX;AAAA,UACF;AAAA,QACF,OAAO;AACL,cAAI;AACF,gBAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC;AAAA,YACF;AACA,kBAAM,WAAgB,cAAQ,kBAAkB,GAAG,MAAM,OAAO;AAChE,gBAAI,SAAS,WAAW,gBAAgB,GAAG;AACzC,oBAAM,UAAU,MAAM,GAAG,SAAS,UAAU,OAAO;AACnD,qBAAO,KAAK,MAAM,OAAO;AAAA,YAC3B;AAAA,UACF,QAAQ;AAAA,UAAC;AAAA,QACX;AAEA,cAAM,UAAU,CAAC,KAAU,SAAS,OAAO;AACzC,cAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AACrC,qBAAW,OAAO,KAAK;AACrB,gBAAI,OAAO,UAAU,eAAe,KAAK,KAAK,GAAG,GAAG;AAClD,oBAAM,SAAS,SAAS,GAAG,MAAM,IAAI,GAAG,KAAK;AAC7C,kBAAI,OAAO,IAAI,GAAG,MAAM,YAAY,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,QAAQ,IAAI,GAAG,CAAC,GAAG;AACjF,wBAAQ,IAAI,GAAG,GAAG,MAAM;AAAA,cAC1B,OAAO;AACL,wBAAQ,IAAI,MAAM;AAAA,cACpB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,gBAAQ,IAAI;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,IACX;AAEA,WAAO,MAAM,KAAK,OAAO,EAAE,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,UACA,aACoD;AACpD,UAAM,WAAW,MAAM,KAAK,iBAAiB,QAAQ;AACrD,UAAM,aAAa,MAAM,KAAK,gBAAgB,UAAU,WAAW;AACnE,UAAM,UAAU,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC,EAAE,KAAK;AACvE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AJjWA,SAAS,cAAc,WAAyB;AAC9C,QAAM,SAAS,kBAAkB;AACjC,MAAI,WAAW,MAAM;AACnB,UAAM,IAAI;AAAA,MACR,gBAAgB,SAAS,8CAA8C,MAAM;AAAA,IAC/E;AAAA,EACF;AACF;AAEA,IAAM,YAAN,MAAgB;AAAA,EACN,QAAsB,QAAQ,QAAQ;AAAA,EAE9C,MAAM,QAAW,IAAkC;AACjD,UAAM,OAAO,KAAK,MAAM,KAAK,EAAE;AAE/B,SAAK,QAAQ,KAAK;AAAA,MAChB,MAAM;AAAA,MAAC;AAAA,MACP,MAAM;AAAA,MAAC;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,cAAN,MAAkB;AAAA,EACf,YAAY,oBAAI,IAAuB;AAAA,EACvC,UAAU,IAAI,WAAW;AAAA;AAAA;AAAA;AAAA,EAKjC,MAAc,SAAY,UAAkB,IAAkC;AAC5E,QAAI,OAAO,KAAK,UAAU,IAAI,QAAQ;AACtC,QAAI,CAAC,MAAM;AACT,aAAO,IAAI,UAAU;AACrB,WAAK,UAAU,IAAI,UAAU,IAAI;AAAA,IACnC;AACA,WAAO,KAAK,QAAQ,EAAE;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,gBACJ,UACA,QACA,SACA,UACe;AAEf,kBAAc,aAAa;AAI3B,QAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,uEAAuE,MAAM;AAAA,MAC/E;AAAA,IACF;AAEA,UAAM,mBAAwB,cAAQ,QAAQ;AAC9C,UAAM,YAAiB,cAAQ,kBAAkB,MAAM;AAEvD,QAAI,WAAW;AACf,QAAI,kBAAkB;AACtB,QAAI,iBAAiB;AAErB,QAAI;AACF,YAAM,QAAQ,MAAMC,IAAG,KAAK,SAAS;AACrC,UAAI,MAAM,YAAY,GAAG;AACvB,yBAAiB;AAAA,MACnB;AAAA,IACF,QAAQ;AAAA,IAAC;AAET,QAAI,gBAAgB;AAClB,YAAM,WAAW,QAAQ,QAAQ,GAAG;AACpC,UAAI,aAAa,IAAI;AACnB,cAAM,YAAY,QAAQ,UAAU,GAAG,QAAQ;AAC/C,YAAI,uBAAuB,IAAI,SAAS,GAAG;AACzC,gBAAM,IAAI;AAAA,YACR,sDAAsD,SAAS;AAAA,UACjE;AAAA,QACF;AACA,0BAAkB,QAAQ,UAAU,WAAW,CAAC;AAChD,mBAAgB,cAAQ,WAAW,GAAG,SAAS,OAAO;AAAA,MACxD,OAAO;AACL,mBAAgB,cAAQ,WAAW,GAAG,OAAO,OAAO;AACpD,0BAAkB;AAAA,MACpB;AAAA,IACF,OAAO;AACL,iBAAgB,cAAQ,kBAAkB,GAAG,MAAM,OAAO;AAAA,IAC5D;AAEA,QAAI,CAAC,SAAS,WAAW,gBAAgB,GAAG;AAC1C,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,UAAM,KAAK,SAAS,UAAU,YAAY;AACxC,UAAI,cAAmC,CAAC;AAExC,UAAI,SAAS;AACb,UAAI,kBAAkB;AAEtB,UAAI;AACF,cAAM,cAAc,MAAMA,IAAG,SAAS,UAAU,OAAO;AACvD,iBAAS,iBAAiB,WAAW;AACrC,0BAAkB,YAAY,SAAS,IAAI,IAAI,OAAO;AACtD,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,WAAW;AAGrC,cAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,kBAAM,IAAI,MAAM,wBAAwB;AAAA,UAC1C;AACA,wBAAc;AAAA,QAChB,SAAS,YAAiB;AACxB,gBAAM,IAAI;AAAA,YACR,mGAAmG,WAAW,OAAO;AAAA,UACvH;AAAA,QACF;AAAA,MACF,SAAS,KAAU;AAEjB,YAAI,IAAI,SAAS,UAAU;AACzB,gBAAM,IAAI;AAAA,YACR,8CAA8C,QAAQ,sCAAsC,IAAI,OAAO;AAAA,UACzG;AAAA,QACF;AAAA,MACF;AAGA,UAAI,iBAAiB;AACnB,0BAAkB,aAAa,iBAAiB,QAAQ;AAAA,MAC1D,OAAO;AAEL,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,UAAI;AACJ,UAAI;AACF,sBAAc,KAAK,UAAU,aAAa,MAAM,MAAM;AACtD,aAAK,MAAM,WAAW;AACtB,uBAAe;AAAA,MACjB,SAAS,KAAU;AACjB,cAAM,IAAI;AAAA,UACR,qFAAqF,IAAI,OAAO;AAAA,QAClG;AAAA,MACF;AAGA,YAAM,WAAW,GAAG,QAAQ;AAC5B,UAAI;AACF,cAAMA,IAAG,UAAU,UAAU,aAAa,OAAO;AAAA,MACnD,SAAS,KAAU;AACjB,cAAM,IAAI;AAAA,UACR,6CAA6C,QAAQ,uCAAuC,IAAI,OAAO;AAAA,QACzG;AAAA,MACF;AAEA,UAAI;AACF,cAAMA,IAAG,OAAO,UAAU,QAAQ;AAAA,MACpC,SAAS,KAAU;AAEjB,YAAI;AACF,gBAAMA,IAAG,OAAO,QAAQ;AAAA,QAC1B,QAAQ;AAAA,QAER;AACA,cAAM,IAAI;AAAA,UACR,wDAAwD,QAAQ,wCAAwC,IAAI,OAAO;AAAA,QACrH;AAAA,MACF;AAGA,cAAQ,IAAI,6BAA6B,OAAO,gBAAgB,MAAM,GAAG;AAAA,IAC3E,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,UAAkB,QAA8C;AAC/E,kBAAc,aAAa;AAC3B,UAAM,aAAkB,cAAQ,UAAU,MAAM;AAChD,QAAI,QAAQ;AACZ,QAAI;AACF,eAAS,MAAMA,IAAG,KAAK,UAAU,GAAG,YAAY;AAAA,IAClD,QAAQ;AAAA,IAAC;AAET,QAAI,OAAO;AACT,YAAM,SAA8B,CAAC;AACrC,iBAAW,QAAQ,MAAMA,IAAG,QAAQ,UAAU,GAAG;AAC/C,YAAI,CAAC,KAAK,SAAS,OAAO,EAAG;AAC7B,YAAI;AACF,iBAAY,eAAS,MAAM,OAAO,CAAC,IAAI,KAAK;AAAA,YAC1C,MAAMA,IAAG,SAAc,WAAK,YAAY,IAAI,GAAG,OAAO;AAAA,UACxD;AAAA,QACF,QAAQ;AAAA,QAAC;AAAA,MACX;AACA,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,sBAAsB,UAAU,MAAM;AACvD,WAAO,KAAK,MAAM,MAAMA,IAAG,SAAS,UAAU,OAAO,CAAC;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,UAAqC;AACrD,kBAAc,aAAa;AAE3B,UAAM,mBAAwB,cAAQ,QAAQ;AAC9C,UAAM,QAAQ,MAAMA,IAAG,QAAQ,kBAAkB,EAAE,eAAe,KAAK,CAAC;AACxE,UAAM,aAAa,oBAAI,IAAY;AAEnC,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,YAAY,GAAG;AACtB,YAAI,UAAU,KAAK,KAAK,IAAI,GAAG;AAC7B,qBAAW,IAAI,KAAK,IAAI;AAAA,QAC1B;AAAA,MACF,WAAW,KAAK,OAAO,KAAK,KAAK,KAAK,SAAS,OAAO,GAAG;AACvD,cAAM,OAAY,eAAS,KAAK,MAAM,OAAO;AAC7C,YAAI,UAAU,KAAK,IAAI,GAAG;AACxB,qBAAW,IAAI,IAAI;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,MAAM,KAAK,UAAU,EAAE,KAAK;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,UAAkB,QAA+B;AAC/D,kBAAc,aAAa;AAE3B,QAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,uEAAuE,MAAM;AAAA,MAC/E;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,KAAK,MAAM,GAAG;AAC3B,YAAM,IAAI,MAAM,wCAAwC,MAAM,GAAG;AAAA,IACnE;AAEA,UAAM,mBAAwB,cAAQ,QAAQ;AAC9C,UAAM,aAAkB,cAAQ,kBAAkB,MAAM;AAExD,QAAI,CAAC,WAAW,WAAW,gBAAgB,GAAG;AAC5C,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAGA,QAAI,SAAS;AACb,QAAI;AACF,YAAMA,IAAG,KAAK,UAAU;AACxB,eAAS;AAAA,IACX,QAAQ;AAAA,IAAC;AACT,QAAI;AACF,YAAMA,IAAG,KAAK,GAAG,UAAU,OAAO;AAClC,eAAS;AAAA,IACX,QAAQ;AAAA,IAAC;AAET,QAAI,QAAQ;AACV,YAAM,IAAI,MAAM,wBAAwB,MAAM,mBAAmB;AAAA,IACnE;AAIA,UAAM,QAAQ,MAAMA,IAAG,QAAQ,kBAAkB,EAAE,eAAe,KAAK,CAAC;AACxE,QAAI,eAA8B;AAClC,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,YAAY,KAAK,UAAU,KAAK,KAAK,IAAI,GAAG;AACnD,uBAAe,KAAK;AACpB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,cAAc;AAGhB,YAAMA,IAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC9C,YAAM,UAAe,cAAQ,kBAAkB,YAAY;AAC3D,YAAM,QAAQ,MAAMA,IAAG,QAAQ,OAAO;AACtC,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,SAAS,OAAO,GAAG;AAC1B,gBAAMA,IAAG,UAAe,WAAK,YAAY,IAAI,GAAG,MAAM,OAAO;AAAA,QAC/D;AAAA,MACF;AAAA,IACF,OAAO;AAEL,YAAMA,IAAG,UAAU,GAAG,UAAU,SAAS,MAAM,OAAO;AAAA,IACxD;AAEA,YAAQ,IAAI,8BAA8B,MAAM,GAAG;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,UAAkB,WAAmB,WAAkC;AACxF,kBAAc,aAAa;AAE3B,QAAI,CAAC,oBAAoB,SAAS,KAAK,CAAC,oBAAoB,SAAS,GAAG;AACtE,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAEA,QAAI,CAAC,UAAU,KAAK,SAAS,KAAK,CAAC,UAAU,KAAK,SAAS,GAAG;AAC5D,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,UAAM,mBAAwB,cAAQ,QAAQ;AAC9C,UAAM,UAAe,cAAQ,kBAAkB,SAAS;AACxD,UAAM,UAAe,cAAQ,kBAAkB,SAAS;AAExD,QAAI,CAAC,QAAQ,WAAW,gBAAgB,KAAK,CAAC,QAAQ,WAAW,gBAAgB,GAAG;AAClF,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,QAAI;AACF,YAAM,QAAQ,MAAMA,IAAG,KAAK,OAAO;AACnC,cAAQ,MAAM,YAAY;AAC1B,eAAS;AAAA,IACX,QAAQ;AAAA,IAAC;AAET,QAAI,SAAS;AACb,QAAI,CAAC,QAAQ;AACX,UAAI;AACF,cAAMA,IAAG,KAAK,GAAG,OAAO,OAAO;AAC/B,iBAAS;AACT,iBAAS;AAAA,MACX,QAAQ;AAAA,MAAC;AAAA,IACX;AAEA,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,wBAAwB,SAAS,mBAAmB;AAAA,IACtE;AAGA,QAAI,YAAY;AAChB,QAAI;AACF,YAAMA,IAAG,KAAK,OAAO;AACrB,kBAAY;AAAA,IACd,QAAQ;AAAA,IAAC;AACT,QAAI;AACF,YAAMA,IAAG,KAAK,GAAG,OAAO,OAAO;AAC/B,kBAAY;AAAA,IACd,QAAQ;AAAA,IAAC;AAET,QAAI,WAAW;AACb,YAAM,IAAI,MAAM,+BAA+B,SAAS,mBAAmB;AAAA,IAC7E;AAEA,QAAI,OAAO;AACT,YAAMA,IAAG,OAAO,SAAS,OAAO;AAAA,IAClC,WAAW,QAAQ;AACjB,YAAMA,IAAG,OAAO,GAAG,OAAO,SAAS,GAAG,OAAO,OAAO;AAAA,IACtD;AAEA,YAAQ,IAAI,gCAAgC,SAAS,SAAS,SAAS,GAAG;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,UAAkB,QAA+B;AAClE,kBAAc,aAAa;AAE3B,QAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAEA,QAAI,CAAC,UAAU,KAAK,MAAM,GAAG;AAC3B,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,UAAM,mBAAwB,cAAQ,QAAQ;AAC9C,UAAM,aAAkB,cAAQ,kBAAkB,MAAM;AAExD,QAAI,CAAC,WAAW,WAAW,gBAAgB,GAAG;AAC5C,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,QAAI;AACF,YAAM,QAAQ,MAAMA,IAAG,KAAK,UAAU;AACtC,cAAQ,MAAM,YAAY;AAC1B,eAAS;AAAA,IACX,QAAQ;AAAA,IAAC;AAET,QAAI,SAAS;AACb,QAAI,CAAC,QAAQ;AACX,UAAI;AACF,cAAMA,IAAG,KAAK,GAAG,UAAU,OAAO;AAClC,iBAAS;AACT,iBAAS;AAAA,MACX,QAAQ;AAAA,MAAC;AAAA,IACX;AAEA,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,wBAAwB,MAAM,mBAAmB;AAAA,IACnE;AAEA,QAAI,OAAO;AACT,YAAMA,IAAG,GAAG,YAAY,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IAC1D,WAAW,QAAQ;AACjB,YAAMA,IAAG,OAAO,GAAG,UAAU,OAAO;AAAA,IACtC;AAEA,YAAQ,IAAI,gCAAgC,MAAM,GAAG;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBAAiB,UAAqC;AAC1D,WAAO,KAAK,QAAQ,iBAAiB,QAAQ;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,UAAqC;AACzD,WAAO,KAAK,QAAQ,gBAAgB,UAAU,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,UAAsE;AAC1F,WAAO,KAAK,QAAQ,gBAAgB,UAAU,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,EAC3E;AACF;;;AO5cA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAGtB,IAAM,gBAAgB,UAAU,QAAQ;AAGxC,IAAM,iBAAiB;AAEvB,SAAS,UAAU,IAA6B;AAC9C,MAAI,OAAO,KAAM,QAAO;AACxB,QAAM,OAAO,GAAG,KAAK,EAAE,CAAC,KAAK;AAC7B,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAQO,IAAM,YAAN,MAAsC;AAAA,EAC3C,MAAc,IAAI,KAAa,MAAiC;AAC9D,UAAM,EAAE,OAAO,IAAI,MAAM,cAAc,OAAO,MAAM;AAAA,MAClD;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,MACvB,aAAa;AAAA,MACb,KAAK,EAAE,GAAG,QAAQ,KAAK,qBAAqB,IAAI;AAAA,IAClD,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,OACZ,aACiE;AACjE,UAAM,WAAgB,cAAQ,WAAW;AACzC,UAAM,MAAS,eAAW,QAAQ,IAAI,WAAgB,cAAQ,QAAQ;AACtE,QAAI,CAAI,eAAW,GAAG,EAAG,QAAO;AAChC,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,CAAC,aAAa,iBAAiB,CAAC,GAAG,KAAK;AAC1E,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,MAAW,eAAS,MAAM,QAAQ,EAAE,MAAW,SAAG,EAAE,KAAK,GAAG;AAClE,UAAI,IAAI,WAAW,IAAI,EAAG,QAAO;AACjC,aAAO,EAAE,MAAW,cAAQ,IAAI,GAAG,KAAK,OAAO,KAAK,SAAS;AAAA,IAC/D,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,aAAyC;AACpD,UAAM,MAAM,MAAM,KAAK,OAAO,WAAW;AACzC,QAAI,CAAC,IAAK,QAAO,EAAE,QAAQ,OAAO,QAAQ,MAAM,SAAS,CAAC,EAAE;AAE5D,QAAI,SAAwB;AAC5B,QAAI;AACF,gBAAU,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,aAAa,gBAAgB,MAAM,CAAC,GAAG,KAAK,KAAK;AAAA,IACvF,QAAQ;AACN,eAAS;AAAA,IACX;AAEA,UAAM,UAAuB,CAAC;AAC9B,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,IAAI,IAAI,MAAM;AAAA,QACnC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,IAAI;AAAA,MACN,CAAC;AACD,iBAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,YAAI,KAAK,SAAS,EAAG;AACrB,cAAM,KAAK,KAAK,MAAM,GAAG,CAAC;AAC1B,YAAI,OAAO,KAAK,MAAM,CAAC,EAAE,KAAK;AAC9B,cAAM,QAAQ,KAAK,QAAQ,MAAM;AACjC,YAAI,UAAU,GAAI,QAAO,KAAK,MAAM,QAAQ,CAAC;AAC7C,YAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,EAAG,QAAO,KAAK,MAAM,GAAG,EAAE;AACvE,gBAAQ,KAAK,EAAE,MAAM,MAAM,QAAQ,UAAU,EAAE,EAAE,CAAC;AAAA,MACpD;AAAA,IACF,QAAQ;AAAA,IAAC;AAET,WAAO,EAAE,QAAQ,MAAM,QAAQ,SAAS,MAAM,SAAS,gBAAgB,KAAK;AAAA,EAC9E;AAAA,EAEA,MAAM,KAAK,aAAsC;AAC/C,UAAM,MAAM,MAAM,KAAK,OAAO,WAAW;AACzC,QAAI,CAAC,IAAK,QAAO;AAEjB,QAAI,MAAM;AACV,QAAI;AACF,YAAM,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,cAAc,MAAM,IAAI,GAAG,CAAC;AAAA,IACtE,QAAQ;AAAA,IAAC;AAGT,QAAI;AACF,YAAM,YAAY,MAAM,KAAK,IAAI,IAAI,MAAM;AAAA,QACzC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,IAAI;AAAA,MACN,CAAC;AACD,iBAAW,QAAQ,UAChB,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,GAAG;AAClB,cAAM,OAAY,WAAK,IAAI,MAAM,IAAI;AACrC,YAAI,UAAU;AACd,YAAI;AACF,oBAAa,iBAAa,MAAM,OAAO;AAAA,QACzC,QAAQ;AACN;AAAA,QACF;AACA,cAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,YAAI,MAAM,MAAM,SAAS,CAAC,MAAM,GAAI,OAAM,IAAI;AAC9C,eACE,gBAAgB,IAAI,MAAM,IAAI;AAAA;AAAA;AAAA,QAAgD,IAAI;AAAA,aACpE,MAAM,MAAM;AAAA,IAC1B,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI,IACnC;AAAA,MACJ;AAAA,IACF,QAAQ;AAAA,IAAC;AAET,QAAI,OAAO,WAAW,KAAK,OAAO,IAAI,gBAAgB;AACpD,YAAM,IAAI,MAAM,GAAG,cAAc,IAAI;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,aAAqB,SAA4C;AAC5E,UAAM,MAAM,MAAM,KAAK,OAAO,WAAW;AACzC,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uBAAuB;AAEjD,UAAM,SAAS,MAAM,KAAK,OAAO,WAAW;AAC5C,QAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,YAAM,IAAI,MAAM,8BAA8B,IAAI,GAAG,GAAG;AAAA,IAC1D;AAEA,UAAM,KAAK,IAAI,IAAI,MAAM,CAAC,OAAO,MAAM,MAAM,IAAI,GAAG,CAAC;AACrD,UAAM,KAAK,IAAI,IAAI,MAAM,CAAC,UAAU,MAAM,MAAM,SAAS,MAAM,IAAI,GAAG,CAAC;AACvE,UAAM,QAAQ,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,aAAa,WAAW,MAAM,CAAC,GAAG,KAAK;AAC/E,WAAO,EAAE,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,QAAQ,aAAoC;AAChD,UAAM,MAAM,MAAM,KAAK,OAAO,WAAW;AACzC,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uBAAuB;AAEjD,QAAI;AACF,YAAM,KAAK,IAAI,IAAI,MAAM,CAAC,YAAY,MAAM,IAAI,GAAG,CAAC;AAAA,IACtD,QAAQ;AAAA,IAER;AACA,UAAM,KAAK,IAAI,IAAI,MAAM,CAAC,SAAS,OAAO,MAAM,IAAI,GAAG,CAAC;AAAA,EAC1D;AACF;;;AC1JA,IAAM,eAAe,oBAAI,IAAI,CAAC,UAAU,UAAU,eAAe,CAAC;AAWlE,SAAS,WAAW,SAAyB;AAC3C,MAAI,MAAM;AACV,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AACzB,UAAM,KAAK,QAAQ,CAAC;AACpB,QAAI,OAAO,KAAK;AACd,aAAO;AACP,WAAK;AACL;AAAA,IACF;AACA,QAAI,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1B,aAAO;AACP,WAAK;AACL;AAAA,IACF;AACA,UAAM,OAAO,QAAQ,IAAI,CAAC;AAC1B,QAAI,SAAS,OAAO,SAAS,OAAO,SAAS,OAAO,SAAS,KAAK;AAEhE,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,QAAQ,QAAQ;AACzB,YAAI,QAAQ,CAAC,MAAM,KAAK;AACtB,cAAI,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1B,iBAAK;AACL;AAAA,UACF;AACA;AAAA,QACF;AACA,aAAK;AAAA,MACP;AACA,UAAI,IAAI;AACR;AAAA,IACF;AACA,WAAO;AACP,SAAK;AAAA,EACP;AACA,SAAO;AACT;AAGA,SAAS,SAAS,SAA8B;AAC9C,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,MAAM;AACV,MAAI,QAAuB;AAE3B,QAAM,OAAO,CAAC,QAAgB;AAC5B,QAAI,CAAC,MAAO,SAAQ;AAAA,EACtB;AAEA,QAAM,eAAe,CAAC,UAAwB;AAC5C,WAAO,MAAM,QAAQ,QAAQ;AAC3B,YAAM,KAAK,QAAQ,GAAG;AACtB,UAAI,OAAO,KAAK;AACd,eAAO;AACP,sBAAc,QAAQ,CAAC;AAAA,MACzB,WAAW,OAAO,KAAK;AACrB,YAAI,UAAU,GAAG;AACf,eAAK,mCAAmC;AACxC,iBAAO;AAAA,QACT,OAAO;AACL;AAAA,QACF;AAAA,MACF,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AACA,QAAI,QAAQ,EAAG,MAAK,gCAAgC;AAAA,EACtD;AAEA,QAAM,YAAY,CAAC,UAA4B;AAC7C,QAAI,MAAM;AACV,WAAO,MAAM,QAAQ,UAAU,CAAC,MAAM,SAAS,QAAQ,GAAG,CAAE,GAAG;AAC7D,aAAO,QAAQ,GAAG;AAClB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,CAAC,UAAwB;AAC7C,UAAM,OAAO,UAAU,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK;AACxC,QAAI,QAAQ,CAAC,KAAK,SAAS,GAAG,EAAG,MAAK,IAAI,IAAI;AAC9C,QAAI,OAAO,QAAQ,QAAQ;AACzB,WAAK,gCAAgC;AACrC;AAAA,IACF;AACA,QAAI,QAAQ,GAAG,MAAM,KAAK;AACxB,aAAO;AACP;AAAA,IACF;AACA,WAAO;AACP,UAAM,OAAO,UAAU,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK;AACxC,QAAI,OAAO,QAAQ,QAAQ;AACzB,WAAK,gCAAgC;AACrC;AAAA,IACF;AACA,QAAI,QAAQ,GAAG,MAAM,KAAK;AACxB,aAAO;AACP;AAAA,IACF;AACA,WAAO;AAEP,QAAI,aAAa,IAAI,IAAI,GAAG;AAC1B,UAAI,WAAW;AACf,aAAO,MAAM,QAAQ,QAAQ;AAE3B,eAAO,MAAM,QAAQ,UAAU,KAAK,KAAK,QAAQ,GAAG,CAAE,EAAG,QAAO;AAChE,YAAI,QAAQ,GAAG,MAAM,KAAK;AACxB,iBAAO;AACP,cAAI,CAAC,SAAU,MAAK,IAAI,IAAI,MAAM,IAAI,yBAAyB;AAC/D;AAAA,QACF;AACA,cAAM,WAAW,UAAU,CAAC,KAAK,KAAK,KAAK,KAAM,IAAI,CAAC,EAAE,KAAK;AAC7D,eAAO,MAAM,QAAQ,UAAU,KAAK,KAAK,QAAQ,GAAG,CAAE,EAAG,QAAO;AAChE,YAAI,QAAQ,GAAG,MAAM,KAAK;AACxB,eAAK,IAAI,IAAI,MAAM,IAAI,0BAA0B,QAAQ,GAAG;AAC5D;AAAA,QACF;AACA,YAAI,aAAa,QAAS,YAAW;AACrC,eAAO;AACP,qBAAa,QAAQ,CAAC;AACtB,YAAI,QAAQ,GAAG,MAAM,IAAK,QAAO;AAAA,MACnC;AACA,WAAK,gCAAgC;AACrC;AAAA,IACF;AAGA,QAAI,QAAQ;AACZ,WAAO,MAAM,QAAQ,UAAU,QAAQ,GAAG;AACxC,UAAI,QAAQ,GAAG,MAAM,IAAK,UAAS;AAAA,eAC1B,QAAQ,GAAG,MAAM,IAAK,UAAS;AACxC,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,EAAG,MAAK,gCAAgC;AAAA,EACtD;AAEA,eAAa,CAAC;AACd,SAAO,EAAE,MAAM,MAAM;AACvB;AAEA,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,YAAY;AAClB,IAAM,SAAS;AAGR,SAAS,oBAAoB,SAA2B;AAC7D,MAAI,OAAO,YAAY,YAAY,YAAY,GAAI,QAAO,CAAC;AAC3D,QAAM,SAAS,oBAAI,IAAY;AAG/B,QAAM,iBAAiB,QAAQ,QAAQ,YAAY,CAAC,IAAI,UAAkB;AACxE,UAAM,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC,EAAG,KAAK;AACvC,QAAI,KAAM,QAAO,IAAI,KAAK,IAAI,IAAI;AAClC,WAAO;AAAA,EACT,CAAC;AAED,aAAW,KAAK,eAAe,MAAM,UAAU,KAAK,CAAC,EAAG,QAAO,IAAI,CAAC;AACpE,aAAW,KAAK,eAAe,QAAQ,OAAO,EAAE,EAAE,MAAM,SAAS,KAAK,CAAC,EAAG,QAAO,IAAI,CAAC;AACtF,aAAW,KAAK,eAAe,SAAS,MAAM,EAAG,QAAO,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG;AAEvE,QAAM,MAAM,SAAS,WAAW,eAAe,QAAQ,QAAQ,GAAG,CAAC,CAAC;AACpE,aAAW,OAAO,IAAI,KAAM,QAAO,IAAI,IAAI,GAAG,GAAG;AAEjD,SAAO,MAAM,KAAK,MAAM,EAAE,KAAK;AACjC;AAWO,SAAS,oBACd,WACA,OACuB;AACvB,MAAI,CAAC,UAAW,QAAO,EAAE,IAAI,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,EAAE;AAC1D,QAAM,MAAM,IAAI,IAAI,oBAAoB,SAAS,CAAC;AAClD,QAAM,MAAM,IAAI,IAAI,oBAAoB,KAAK,CAAC;AAC9C,QAAM,UAAU,MAAM,KAAK,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;AACzD,QAAM,QAAQ,MAAM,KAAK,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;AACvD,SAAO,EAAE,IAAI,QAAQ,WAAW,KAAK,MAAM,WAAW,GAAG,SAAS,MAAM;AAC1E;AAGO,SAAS,sBAAsB,SAAgC;AACpE,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,QAAM,WAAW,WAAW,QAAQ,QAAQ,YAAY,GAAG,EAAE,QAAQ,QAAQ,GAAG,CAAC;AACjF,SAAO,SAAS,QAAQ,EAAE;AAC5B;;;AC5HA,IAAM,0BAA0B,CAAC,yBAAyB,uBAAuB;AAG1E,IAAM,iBAAiB;AAGvB,IAAM,4BAA4B;AAGzC,IAAM,sBAAsB;AAG5B,SAAS,UAAU,GAAW,GAAoB;AAChD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,SAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC;AAC3E,SAAO,SAAS;AAClB;AAEA,SAAS,YAAY,eAAkD;AACrE,MAAI,CAAC,cAAe,QAAO;AAC3B,QAAM,QAAQ,mBAAmB,KAAK,cAAc,KAAK,CAAC;AAC1D,SAAO,QAAQ,MAAM,CAAC,EAAG,KAAK,IAAI;AACpC;AAEA,IAAM,OAAO,CACX,QACA,SACA,aACoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA,MAAM,KAAK,UAAU,OAAO;AAC9B;AASO,SAAS,qBACd,SACA,SACgB;AAIhB,QAAM,cAAc,QAAQ,IAAI,4BAA4B,KAAK,IAC9D,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACjB,QAAM,iBAAiB,CAAC,GAAI,QAAQ,kBAAkB,yBAA0B,GAAG,UAAU;AAC7F,QAAM,cAAc,QAAQ;AAC5B,QAAM,MAAM,QAAQ;AACpB,QAAM,YAAY,QAAQ,aAAa,QAAQ,UAAU,SAAS,IAAI,QAAQ,YAAY;AAE1F,QAAM,kBAAkB,CAAC,WACvB,eAAe,SAAS,MAAM,KAAK,oBAAoB,KAAK,MAAM;AAEpE,QAAM,uBAAuB,CAAC,WAA2B;AACvD,WAAO,gBAAgB,MAAM,IAAI,SAAS,eAAe,CAAC,KAAK;AAAA,EACjE;AAEA,QAAM,cAAc,CAAC,YAA4C;AAAA,IAC/D,+BAA+B,qBAAqB,MAAM;AAAA,IAC1D,gCAAgC;AAAA,IAChC,gCAAgC;AAAA,IAChC,gBAAgB;AAAA,EAClB;AAIA,MAAI,WAAW;AACf,iBAAe,iBAAoC;AACjD,QAAI,CAAC,KAAK,eAAe,SAAU,QAAO,CAAC;AAC3C,QAAI;AACF,YAAM,WAAW,MAAM,IAAI,YAAY;AACvC,iBAAW,SAAS,WAAW;AAC/B,aAAO;AAAA,IACT,SAAS,KAAU;AACjB,aAAO,CAAC,uCAAuC,KAAK,WAAW,GAAG,EAAE;AAAA,IACtE;AAAA,EACF;AAIA,iBAAe,UACb,QACA,UACA,SACyB;AACzB,UAAM,SAAS,OAAO,aAAa,IAAI,QAAQ;AAC/C,UAAM,SAAS,OAAO,aAAa,IAAI,QAAQ;AAE/C,QAAI,WAAW,QAAQ;AACrB,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,MAAM,kBAAkB;AAAA,QACxB,cAAc,cAAc;AAAA,QAC5B,KAAK,QAAQ,GAAG;AAAA,QAChB,UAAU,MAAM,eAAe;AAAA,MACjC,CAAC;AAAA,IACH;AAEA,QAAI,WAAW,OAAO;AACpB,UAAI,CAAC,KAAK;AACR,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,WAAW;AAAA,UACX,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS,CAAC;AAAA,UACV,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,UAAI;AACF,cAAM,SAAS,MAAM,IAAI,OAAO,QAAQ;AACxC,cAAM,OAAO,OAAO,UAAU,OAAO,QAAQ,SAAS,IAAI,MAAM,IAAI,KAAK,QAAQ,IAAI;AACrF,eAAO,KAAK,KAAK,SAAS,EAAE,WAAW,MAAM,GAAG,QAAQ,KAAK,CAAC;AAAA,MAChE,SAAS,KAAU;AACjB,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO,8BAA8B,IAAI,OAAO;AAAA,UAChD,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,WAAW,QAAQ;AACrB,UAAI;AACF,cAAM,WAAW,MAAM,QAAQ,gBAAgB,QAAQ;AACvD,eAAO,KAAK,KAAK,SAAS,QAAQ;AAAA,MACpC,SAAS,KAAU;AACjB,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO,kCAAkC,IAAI,OAAO;AAAA,UACpD,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ;AACX,UAAI;AACF,cAAM,cAAc,MAAM,QAAQ,YAAY,QAAQ;AACtD,eAAO,KAAK,KAAK,SAAS,WAAW;AAAA,MACvC,SAAS,KAAU;AACjB,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO,2BAA2B,IAAI,OAAO;AAAA,UAC7C,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,OAAO;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,QAAI;AACF,aAAO,KAAK,KAAK,SAAS,MAAM,QAAQ,WAAW,UAAU,MAAM,CAAC;AAAA,IACtE,SAAS,SAAc;AACrB,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,OAAO,0BAA0B,QAAQ,OAAO;AAAA,QAChD,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF;AAIA,iBAAe,WACb,UACA,UACA,SACyB;AACzB,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,MAAM,YAAY,EAAE;AAAA,IAClC,QAAQ;AACN,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,OAAO;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,UAAM,EAAE,QAAQ,KAAK,OAAO,QAAQ,WAAW,SAAS,WAAW,MAAM,IAAI,QAAQ,CAAC;AAEtF,QAAI,WAAW,YAAY,WAAW,WAAW;AAC/C,UAAI,CAAC,KAAK;AACR,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,UAAI,WAAW,UAAU;AACvB,cAAM,OAAO,OAAO,YAAY,WAAW,QAAQ,KAAK,IAAI;AAC5D,YAAI,CAAC,QAAQ,KAAK,SAAS,2BAA2B;AACpD,iBAAO,KAAK,KAAK,SAAS;AAAA,YACxB,OAAO,4BAA4B,yBAAyB;AAAA,YAC5D,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,YAAI;AACF,gBAAM,SAAS,MAAM,IAAI,OAAO,UAAU,IAAI;AAC9C,iBAAO,KAAK,KAAK,SAAS,EAAE,SAAS,MAAM,MAAM,OAAO,KAAK,CAAC;AAAA,QAChE,SAAS,KAAU;AACjB,iBAAO,KAAK,KAAK,SAAS,EAAE,OAAO,IAAI,SAAS,MAAM,oBAAoB,CAAC;AAAA,QAC7E;AAAA,MACF;AACA,UAAI;AACF,cAAM,IAAI,QAAQ,QAAQ;AAC1B,eAAO,KAAK,KAAK,SAAS,EAAE,SAAS,KAAK,CAAC;AAAA,MAC7C,SAAS,KAAU;AACjB,eAAO,KAAK,KAAK,SAAS,EAAE,OAAO,IAAI,SAAS,MAAM,qBAAqB,CAAC;AAAA,MAC9E;AAAA,IACF;AAEA,QAAI,WAAW,aAAa;AAC1B,UAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,YAAM,QAAQ,UAAU,UAAU,MAAM;AACxC,aAAO,KAAK,KAAK,SAAS,EAAE,SAAS,MAAM,OAAO,CAAC;AAAA,IACrD;AAEA,QAAI,WAAW,gBAAgB;AAC7B,UAAI,CAAC,oBAAoB,MAAM,KAAK,CAAC,oBAAoB,SAAS,GAAG;AACnE,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,YAAM,QAAQ,aAAa,UAAU,QAAQ,SAAS;AACtD,aAAO,KAAK,KAAK,SAAS,EAAE,SAAS,MAAM,QAAQ,UAAU,CAAC;AAAA,IAChE;AAEA,QAAI,WAAW,gBAAgB;AAC7B,UAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,YAAM,QAAQ,aAAa,UAAU,MAAM;AAC3C,aAAO,KAAK,KAAK,SAAS,EAAE,SAAS,MAAM,OAAO,CAAC;AAAA,IACrD;AAEA,QAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,OAAO;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,iBAAiB,GAAG,GAAG;AAC1B,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,OACE;AAAA,QACF,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,cAAc,GAAG,GAAG;AACvB,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,OACE;AAAA,QACF,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,mBAAmB,KAAK,GAAG;AAC9B,aAAO,KAAK,KAAK,SAAS;AAAA,QACxB,OAAO;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAIA,QAAI,UAAU,MAAM;AAClB,YAAM,cAAc,sBAAsB,KAAK;AAC/C,UAAI,aAAa;AACf,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO,mBAAmB,WAAW;AAAA,UACrC,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,UAAI,OAAO,cAAc,UAAU;AACjC,cAAM,aAAa,oBAAoB,WAAW,KAAK;AACvD,YAAI,CAAC,WAAW,IAAI;AAClB,gBAAM,QAAkB,CAAC;AACzB,cAAI,WAAW,QAAQ,OAAQ,OAAM,KAAK,WAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,EAAE;AACpF,cAAI,WAAW,MAAM,OAAQ,OAAM,KAAK,cAAc,WAAW,MAAM,KAAK,IAAI,CAAC,EAAE;AACnF,iBAAO,KAAK,KAAK,SAAS;AAAA,YACxB,OAAO,2CAA2C,MAAM,KAAK,IAAI,CAAC;AAAA,YAClE,MAAM;AAAA,YACN,SAAS,EAAE,SAAS,WAAW,SAAS,OAAO,WAAW,MAAM;AAAA,UAClE,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,gBAAgB,UAAU,QAAQ,KAAK,KAAK;AAE1D,WAAO,KAAK,KAAK,SAAS,EAAE,SAAS,MAAM,KAAK,OAAO,CAAC;AAAA,EAC1D;AAEA,SAAO;AAAA,IACL,MAAM,OAAO,SAAiD;AAC5D,YAAM,UAAU,YAAY,QAAQ,MAAM;AAG1C,YAAM,iBAAiB,kBAAkB;AACzC,UAAI,mBAAmB,MAAM;AAC3B,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO,4CAAuC,cAAc;AAAA,UAC5D,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAIA,UAAI,QAAQ,WAAW,WAAW;AAChC,eAAO,EAAE,QAAQ,KAAK,SAAS,MAAM,KAAK;AAAA,MAC5C;AAIA,UAAI,kBAAkB,MAAM,aAAa,CAAC,WAAW;AACnD,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OACE;AAAA,UACF,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAGA,UAAI,WAAW;AACb,cAAM,YAAY,YAAY,QAAQ,aAAa;AACnD,YAAI,CAAC,aAAa,CAAC,UAAU,WAAW,SAAS,GAAG;AAClD,iBAAO,KAAK,KAAK,SAAS;AAAA,YACxB,OAAO;AAAA,YACP,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAAA,MACF;AAIA,UAAI,QAAQ,WAAW,UAAU,QAAQ,UAAU,CAAC,gBAAgB,QAAQ,MAAM,GAAG;AACnF,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAIA,UACE,QAAQ,WAAW,UACnB,QAAQ,eACR,CAAC,QAAQ,YAAY,SAAS,kBAAkB,GAChD;AACA,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAEA,UAAI;AACF,YAAI,QAAQ,WAAW,OAAO;AAC5B,gBAAM,SAAS,IAAI,IAAI,QAAQ,KAAK,kBAAkB;AACtD,iBAAO,MAAM,UAAU,QAAQ,aAAa,OAAO;AAAA,QACrD;AAEA,YAAI,QAAQ,WAAW,QAAQ;AAC7B,iBAAO,MAAM,WAAW,QAAQ,UAAU,aAAa,OAAO;AAAA,QAChE;AAGA,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH,SAAS,KAAU;AACjB,eAAO,KAAK,KAAK,SAAS;AAAA,UACxB,OAAO,IAAI,WAAW;AAAA,UACtB,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACldO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAClC,OAAO;AAAA,EAChB,cAAc;AACZ,UAAM,yBAAyB;AAC/B,SAAK,OAAO;AAAA,EACd;AACF;AAMO,SAAS,aAAa,KAAsB,UAAmC;AACpF,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,QAAI,WAAW;AACf,QAAI,UAAU;AACd,QAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,UAAI,QAAS;AACb,kBAAY,MAAM,SAAS;AAC3B,UAAI,OAAO,WAAW,UAAU,OAAO,IAAI,UAAU;AACnD,kBAAU;AACV,eAAO,IAAI,kBAAkB,CAAC;AAC9B,YAAI,UAAU;AAAA,MAChB;AAAA,IACF,CAAC;AACD,QAAI,GAAG,OAAO,MAAM;AAClB,UAAI,QAAS;AACb,gBAAU;AACV,MAAAA,SAAQ,QAAQ;AAAA,IAClB,CAAC;AACD,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,UAAI,QAAS;AACb,gBAAU;AACV,aAAO,GAAG;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AACH;;;ACpDO,SAAS,UAAU,QAAa,QAAkB;AACvD,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAE1D,QAAM,SAAS,EAAE,GAAG,OAAO;AAC3B,aAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,QAAI,OAAO,QAAQ;AACjB,aAAO,GAAG,IAAI,UAAU,OAAO,GAAG,GAAG,OAAO,GAAG,CAAC;AAAA,IAClD,OAAO;AACL,aAAO,GAAG,IAAI,OAAO,GAAG;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;;;AZDA,IAAM,QAAQ;AAcP,SAAS,eAAe,QAA0B;AACvD,QAAM,UAAU,IAAI,YAAY;AAChC,QAAM,SAAS,qBAAqB,SAAS;AAAA,IAC3C,aAAa,OAAO;AAAA,IACpB,KAAK,OAAO,QAAQ,QAAQ,SAAY,IAAI,UAAU;AAAA,IACtD,WAAW,OAAO,aAAa,QAAQ,IAAI,kBAAkB;AAAA,EAC/D,CAAC;AAED,SAAO;AAAA,IACL,MAAM;AAAA;AAAA,IAEN,OAAO;AAAA,IAEP,gBAAgB,QAAa;AAC3B,aAAO,YAAY,IAAI,OAAO,KAAU,KAAU,SAAc;AAC9D,cAAM,MAAc,IAAI,OAAO;AAE/B,YAAI,QAAQ,SAAS,CAAC,IAAI,WAAW,GAAG,KAAK,GAAG,KAAK,CAAC,IAAI,WAAW,GAAG,KAAK,GAAG,GAAG;AACjF,iBAAO,KAAK;AAAA,QACd;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,OAAO,OAAO;AAAA,YACjC,QAAQ,IAAI;AAAA,YACZ,KAAK,IAAI;AAAA,YACT,QAAQ,IAAI,QAAQ,QAAQ,KAAK;AAAA,YACjC,aAAa,IAAI,QAAQ,cAAc,KAAK;AAAA,YAC5C,eAAe,IAAI,QAAQ,eAAe,KAAK;AAAA,YAC/C,UAAU,IAAI,WAAW,SAAS,MAAM,aAAa,KAAK,cAAc,IAAI;AAAA,UAC9E,CAAC;AAED,iBAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,UAAU,GAAG,CAAC,CAAC;AACtE,cAAI,aAAa,OAAO;AACxB,cAAI,IAAI,OAAO,QAAQ,EAAE;AAAA,QAC3B,SAAS,KAAU;AACjB,cAAI,KAAK,SAAS,kBAAkB;AAClC,gBAAI,aAAa;AACjB,gBAAI,UAAU,gBAAgB,kBAAkB;AAChD,gBAAI,IAAI,KAAK,UAAU,EAAE,OAAO,IAAI,SAAS,MAAM,iBAAiB,CAAC,CAAC;AACtE;AAAA,UACF;AACA,cAAI,aAAa;AACjB,cAAI,UAAU,gBAAgB,kBAAkB;AAChD,cAAI;AAAA,YACF,KAAK,UAAU;AAAA,cACb,OAAO,IAAI,WAAW;AAAA,cACtB,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,mBAAmB,MAAc;AAG/B,YAAM,OAAO,QAAQ,IAAI,YAAY;AACrC,YAAM,aAAa,OACf,kBAAkB,mBAAmB,MAAM,KAAK,UAAU,IAAI,CAAC;AAAA,IAC/D;AACJ,UAAI,CAAC,oBAAoB,GAAG;AAC1B,eAAO,aAAa,KAAK,QAAQ,WAAW,GAAG,UAAU,SAAS,IAAI;AAAA,MACxE;AACA,UAAI;AACF,cAAM,sBAA2B,cAAQ,OAAO,WAAW;AAC3D,cAAM,UAAa,gBAAY,mBAAmB;AAClD,YAAI,eAAoC,CAAC;AACzC,mBAAW,SAAS,SAAS;AAC3B,cAAI,MAAM,WAAW,GAAG,EAAG;AAC3B,gBAAM,YAAiB,WAAK,qBAAqB,KAAK;AACtD,cAAI,aAAa;AACjB,cAAI;AACF,yBAAgB,aAAS,SAAS,EAAE,YAAY;AAAA,UAClD,QAAQ;AAAA,UAAC;AAET,cAAI,YAAY;AACd,gBAAI;AACF,oBAAM,QAAW,gBAAY,SAAS;AACtC,yBAAW,QAAQ,OAAO;AACxB,oBAAI,KAAK,SAAS,OAAO,GAAG;AAC1B,wBAAM,KAAK,KAAK,MAAM,GAAG,EAAE;AAC3B,wBAAM,MAAS,iBAAkB,WAAK,WAAW,IAAI,GAAG,OAAO;AAC/D,sBAAI;AACF,0BAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,iCAAa,EAAE,IAAI,UAAU,aAAa,EAAE,KAAK,CAAC,GAAG,MAAM;AAAA,kBAC7D,QAAQ;AAAA,kBAAC;AAAA,gBACX;AAAA,cACF;AAAA,YACF,QAAQ;AAAA,YAAC;AAAA,UACX,WAAW,MAAM,SAAS,OAAO,GAAG;AAClC,kBAAM,MAAS,iBAAa,WAAW,OAAO;AAC9C,gBAAI;AACF,oBAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,6BAAe,UAAU,cAAc,MAAM;AAAA,YAC/C,QAAQ;AAAA,YAAC;AAAA,UACX;AAAA,QACF;AAGA,cAAM,WAAW,KAAK,UAAU,YAAY,EACzC,QAAQ,MAAM,SAAS,EACvB,QAAQ,WAAW,SAAS,EAC5B,QAAQ,WAAW,SAAS;AAC/B,cAAM,SAAS,4CAA4C,QAAQ;AACnE,eAAO,KAAK,QAAQ,WAAW,GAAG,UAAU,GAAG,MAAM;AAAA,QAAW;AAAA,MAClE,SAAS,KAAK;AACZ,gBAAQ,KAAK,gDAAgD,GAAG;AAChE,eAAO,aAAa,KAAK,QAAQ,WAAW,GAAG,UAAU,SAAS,IAAI;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACF;","names":["path","fs","fs","path","path","fs","fs","path","resolve"]}
1
+ {"version":3,"sources":["../../src/server/vite-plugin.ts","../../src/server/file-mutator.ts","../../src/core/validate.ts","../../src/core/safe-path.ts","../../src/core/json-path.ts","../../src/server/key-scanner.ts","../../src/core/env.ts","../../src/core/gitignore.ts","../../src/server/git-client.ts","../../src/core/placeholders.ts","../../src/core/save-rules.ts","../../src/server/router.ts","../../src/server/node-body.ts","../../src/core/deep-merge.ts","../../src/server/pack.ts","../../src/core/flag-script.ts","../../src/core/change-link.ts","../../src/core/share-manifest.ts","../../src/server/build-info.ts","../../src/server/build-target.ts","../../src/server/module-dir.ts","../../src/server/pack-error.ts","../../src/server/share-html.ts"],"sourcesContent":["import * as path from 'path';\nimport * as fs from 'fs';\nimport { FileMutator } from './file-mutator.js';\nimport { GitClient } from './git-client.js';\nimport { createMutationRouter, MAX_BODY_BYTES } from './router.js';\nimport { readNodeBody } from './node-body.js';\nimport { deepMerge } from '../core/deep-merge.js';\nimport { isLiveLocaleEnabled, BROWSER_FLAG_GLOBAL } from '../core/env.js';\nimport { packOutput } from './pack.js';\nimport { flagSrcFor, injectFlagScript, normalizeBase, type AppBase } from './share-html.js';\n\nexport interface VitePluginConfig {\n /** Absolute or relative path to the directory containing your locale JSON files. */\n localesPath: string;\n /** Expose git status, commit, and discard to the Studio. Defaults to `true`. */\n git?: boolean;\n /** Bearer token required on every request. Defaults to `LIVELOCALE_TOKEN`. */\n authToken?: string;\n /** Write the share Studio into <outDir>/livelocale/ on `vite build`. Off by default. */\n share?: boolean;\n /** Put the GitHub owner/repo into the published manifest. Defaults to false. */\n exposeRepo?: boolean;\n /** Source locale for the manifest. Defaults to en when present, else the first locale. */\n sourceLocale?: string;\n /** Site path the HTML is served from, when Vite `base` is a full URL or differs. */\n shareBase?: string;\n}\n\n/** What `apply` receives from Vite: the command, and whether this is an SSR build. */\ninterface ViteConfigEnv {\n command: 'serve' | 'build';\n isSsrBuild?: boolean;\n /** The Vite 4 name of `isSsrBuild`. */\n ssrBuild?: boolean;\n}\n\n/** The parts of Vite's resolved config the share build reads. */\ninterface ShareBuildState {\n root: string;\n outDir: string;\n /** The raw base handed to pack (`shareBase` when set, else Vite `base`). */\n base: string;\n appBase: AppBase;\n logger: { warn(message: string): void };\n}\n\nconst ROUTE = '/api/livelocale';\n\n/**\n * A Vite plugin that serves the livelocale mutation API for Vite-based SPAs\n * (React, Vue, Svelte, etc.) that do not use Next.js.\n *\n * @example\n * // vite.config.ts\n * import { liveLocaleVite } from 'livelocale/vite';\n *\n * export default defineConfig({\n * plugins: [react(), liveLocaleVite({ localesPath: './src/locales' })],\n * });\n */\nexport function liveLocaleVite(config: VitePluginConfig) {\n const mutator = new FileMutator();\n const router = createMutationRouter(mutator, {\n localesPath: config.localesPath,\n git: config.git === false ? undefined : new GitClient(),\n authToken: config.authToken ?? process.env['LIVELOCALE_TOKEN'],\n });\n // Set by configResolved only for a client `vite build` with share on; null means dev behaviour.\n let shareBuild: ShareBuildState | null = null;\n let packed = false;\n\n return {\n name: 'livelocale',\n // Always active in dev; in builds only with `share: true`, and never for the SSR build.\n apply(_userConfig: unknown, env: ViteConfigEnv): boolean {\n if (env.command === 'serve') return true;\n return config.share === true && !env.isSsrBuild && !env.ssrBuild;\n },\n\n configResolved(resolved: any) {\n shareBuild = null;\n if (resolved.command !== 'build' || config.share !== true) return;\n // SSR and library builds have no app HTML to share.\n if (resolved.build?.ssr || resolved.build?.lib) return;\n const base: string = config.shareBase ?? resolved.base;\n shareBuild = {\n root: resolved.root,\n outDir: path.resolve(resolved.root, resolved.build.outDir),\n base,\n appBase: normalizeBase(base),\n logger: resolved.logger,\n };\n },\n\n buildStart() {\n // Watch mode rebuilds call buildStart again, so each rebuild packs once more.\n packed = false;\n },\n\n async writeBundle() {\n if (shareBuild === null || packed) return;\n packed = true;\n const result = await packOutput({\n outDir: shareBuild.outDir,\n projectDir: shareBuild.root,\n // Resolved against the working directory, as the serve half does.\n localesPath: path.resolve(config.localesPath),\n sourceLocale: config.sourceLocale,\n exposeRepo: config.exposeRepo ?? false,\n base: shareBuild.base,\n // transformIndexHtml already linked flag.js in every HTML file Vite wrote.\n injectHtml: false,\n });\n for (const warning of result.warnings) shareBuild.logger.warn(warning);\n },\n\n configureServer(server: any) {\n server.middlewares.use(async (req: any, res: any, next: any) => {\n const url: string = req.url ?? '';\n // Exact path only, so app routes such as /api/livelocale-stats stay with the app.\n if (url !== ROUTE && !url.startsWith(`${ROUTE}?`) && !url.startsWith(`${ROUTE}/`)) {\n return next();\n }\n\n try {\n const result = await router.handle({\n method: req.method,\n url: req.url,\n origin: req.headers['origin'] || '',\n contentType: req.headers['content-type'] || '',\n authorization: req.headers['authorization'] || undefined,\n bodyText: req.method === 'POST' ? await readNodeBody(req, MAX_BODY_BYTES) : undefined,\n });\n\n Object.entries(result.headers).forEach(([k, v]) => res.setHeader(k, v));\n res.statusCode = result.status;\n res.end(result.body ?? '');\n } catch (err: any) {\n if (err?.code === 'BODY_TOO_LARGE') {\n res.statusCode = 413;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: err.message, code: 'BODY_TOO_LARGE' }));\n return;\n }\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(\n JSON.stringify({\n error: err.message || 'Internal Server Error',\n code: 'INTERNAL_ERROR',\n })\n );\n }\n });\n },\n\n transformIndexHtml(html: string, ctx?: { path?: string }) {\n if (shareBuild !== null) {\n const htmlRelPath = (ctx?.path ?? '/index.html').replace(/^\\/+/, '');\n return injectFlagScript(html, flagSrcFor(shareBuild.appBase, htmlRelPath));\n }\n // Mirror the server-side LIVELOCALE flag (preview/off) into the page so the client\n // bundle resolves the same mode without bundler-specific env inlining.\n const flag = process.env['LIVELOCALE'];\n const flagScript = flag\n ? `<script>window.${BROWSER_FLAG_GLOBAL} = ${JSON.stringify(flag)};</script>\\n`\n : '';\n if (!isLiveLocaleEnabled()) {\n return flagScript ? html.replace('</head>', `${flagScript}</head>`) : html;\n }\n try {\n const resolvedLocalesPath = path.resolve(config.localesPath);\n const entries = fs.readdirSync(resolvedLocalesPath);\n let fallbackData: Record<string, any> = {};\n for (const entry of entries) {\n if (entry.startsWith('.')) continue;\n const entryPath = path.join(resolvedLocalesPath, entry);\n let entryIsDir = false;\n try {\n entryIsDir = fs.statSync(entryPath).isDirectory();\n } catch {}\n\n if (entryIsDir) {\n try {\n const files = fs.readdirSync(entryPath);\n for (const file of files) {\n if (file.endsWith('.json')) {\n const ns = file.slice(0, -5);\n const raw = fs.readFileSync(path.join(entryPath, file), 'utf-8');\n try {\n const parsed = JSON.parse(raw);\n fallbackData[ns] = deepMerge(fallbackData[ns] || {}, parsed);\n } catch {}\n }\n }\n } catch {}\n } else if (entry.endsWith('.json')) {\n const raw = fs.readFileSync(entryPath, 'utf-8');\n try {\n const parsed = JSON.parse(raw);\n fallbackData = deepMerge(fallbackData, parsed);\n } catch {}\n }\n }\n // RULE SEC-008: XSS-SAFE INJECTION — escape U+2028/U+2029 and \"<\" so\n // translation values containing \"</script>\" cannot break out of the tag.\n const safeJson = JSON.stringify(fallbackData)\n .replace(/</g, '\\\\u003c')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n const script = `<script>window.__livelocale_fallback__ = ${safeJson};</script>`;\n return html.replace('</head>', `${flagScript}${script}\\n</head>`);\n } catch (err) {\n console.warn('[livelocale] Failed to inject fallback data:', err);\n return flagScript ? html.replace('</head>', `${flagScript}</head>`) : html;\n }\n },\n };\n}\n","import { promises as fs } from 'fs';\nimport * as path from 'path';\nimport { isSafeLocaleSegment } from '../core/validate.js';\nimport { LOCALE_RE } from '../core/validate.js';\nimport { DANGEROUS_KEY_SEGMENTS } from '../core/validate.js';\nimport { resolveSafeLocalePath } from '../core/safe-path.js';\nimport { setNestedProperty, detectJsonIndent } from '../core/json-path.js';\nimport { KeyScanner } from './key-scanner.js';\nimport { getDisabledReason } from '../core/env.js';\n\nexport { detectJsonIndent };\n\nclass FileMutex {\n private queue: Promise<any> = Promise.resolve();\n\n async acquire<T>(fn: () => Promise<T>): Promise<T> {\n const next = this.queue.then(fn);\n // Suppress errors on the main queue chain to prevent locking up the mutex\n this.queue = next.then(\n () => {},\n () => {}\n );\n return next;\n }\n}\n\nexport interface FileMutatorOptions {\n /** Refuse to run unless LiveLocale is enabled. Default true; only CLI tooling passes false. */\n requireEnabled?: boolean;\n /** Skip the console line each `updateLocaleKey` write prints. Default false. */\n quiet?: boolean;\n}\n\nexport class FileMutator {\n private fileLocks = new Map<string, FileMutex>();\n private readonly requireEnabled: boolean;\n private readonly quiet: boolean;\n private readonly scanner: KeyScanner;\n\n constructor(options: FileMutatorOptions = {}) {\n this.requireEnabled = options.requireEnabled ?? true;\n this.quiet = options.quiet ?? false;\n this.scanner = new KeyScanner({ requireEnabled: this.requireEnabled });\n }\n\n private assertEnabled(): void {\n if (!this.requireEnabled) return;\n const reason = getDisabledReason();\n if (reason !== null) {\n throw new Error(\n `[livelocale] FileMutator can only be executed in development mode (${reason}).`\n );\n }\n }\n\n /**\n * Helper to serialize execution per file path\n */\n private async withLock<T>(filePath: string, fn: () => Promise<T>): Promise<T> {\n let lock = this.fileLocks.get(filePath);\n if (!lock) {\n lock = new FileMutex();\n this.fileLocks.set(filePath, lock);\n }\n return lock.acquire(fn);\n }\n\n /**\n * Updates a locale JSON file on disk atomically and safely.\n * Enforces development mode checks, path traversal mitigation, key depth restrictions,\n * structural JSON validation, and concurrency serialization.\n */\n async updateLocaleKey(\n basePath: string,\n locale: string,\n keyPath: string,\n newValue: string\n ): Promise<void> {\n // RULE GEN-001: DEVELOPMENT-ONLY ENFORCEMENT\n this.assertEnabled();\n\n // RULE SRV-002: PATH TRAVERSAL PREVENTION\n // Reject any locale string containing invalid characters\n if (!isSafeLocaleSegment(locale)) {\n throw new Error(\n `[livelocale] Path traversal attempt detected in locale identifier: \"${locale}\"`\n );\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const localeDir = path.resolve(resolvedBasePath, locale);\n\n let filePath = '';\n let mutationKeyPath = keyPath;\n let isNamespaceDir = false;\n\n try {\n const stats = await fs.stat(localeDir);\n if (stats.isDirectory()) {\n isNamespaceDir = true;\n }\n } catch {}\n\n if (isNamespaceDir) {\n const firstDot = keyPath.indexOf('.');\n if (firstDot !== -1) {\n const namespace = keyPath.substring(0, firstDot);\n if (DANGEROUS_KEY_SEGMENTS.has(namespace)) {\n throw new Error(\n `[livelocale] Forbidden namespace segment in path: \"${namespace}\". Segment names like __proto__, constructor, and prototype are not allowed.`\n );\n }\n mutationKeyPath = keyPath.substring(firstDot + 1);\n filePath = path.resolve(localeDir, `${namespace}.json`);\n } else {\n filePath = path.resolve(localeDir, `${keyPath}.json`);\n mutationKeyPath = '';\n }\n } else {\n filePath = path.resolve(resolvedBasePath, `${locale}.json`);\n }\n\n if (!filePath.startsWith(resolvedBasePath)) {\n throw new Error(\n `[livelocale] Path traversal attempt: Resolved file path must start with basePath.`\n );\n }\n\n // Perform atomic operation within a file lock to guarantee concurrency safety (RULE SRV-006)\n await this.withLock(filePath, async () => {\n let currentData: Record<string, any> = {};\n // Formatting of the existing file, preserved on rewrite (git-friendly diffs).\n let indent = ' ';\n let trailingNewline = '\\n';\n\n try {\n const fileContent = await fs.readFile(filePath, 'utf-8');\n indent = detectJsonIndent(fileContent);\n trailingNewline = fileContent.endsWith('\\n') ? '\\n' : '';\n try {\n const parsed = JSON.parse(fileContent);\n\n // RULE SRV-004: JSON PARSE VALIDATION\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('Root must be an object');\n }\n currentData = parsed;\n } catch (parseError: any) {\n throw new Error(\n `[livelocale] Invalid locale file: root must be an object. Code: INVALID_LOCALE. Original error: ${parseError.message}`\n );\n }\n } catch (err: any) {\n // If file does not exist, initialize it as empty object\n if (err.code !== 'ENOENT') {\n throw new Error(\n `[livelocale] Failed to read locale file at ${filePath}. Operation: read. Original error: ${err.message}`\n );\n }\n }\n\n // Update nested property using safe array traversal (RULE SEC-003 & RULE SRV-003)\n if (mutationKeyPath) {\n setNestedProperty(currentData, mutationKeyPath, newValue);\n } else {\n // Root value mutation inside split JSON files (rare but possible: e.g. key has no dot)\n throw new Error(\n '[livelocale] Invalid namespace key path: must contain at least one nested key.'\n );\n }\n\n // RULE SEC-004: VALIDATE JSON BEFORE WRITE\n let stringified: string;\n try {\n stringified = JSON.stringify(currentData, null, indent);\n JSON.parse(stringified); // Paranoid check\n stringified += trailingNewline;\n } catch (err: any) {\n throw new Error(\n `[livelocale] Produced JSON is invalid or has circular references. Original error: ${err.message}`\n );\n }\n\n // RULE SRV-001: ATOMIC WRITES ONLY\n const tempPath = `${filePath}.tmp`;\n try {\n await fs.writeFile(tempPath, stringified, 'utf-8');\n } catch (err: any) {\n throw new Error(\n `[livelocale] Failed to write temp file at ${tempPath}. Operation: write. Original error: ${err.message}`\n );\n }\n\n try {\n await fs.rename(tempPath, filePath);\n } catch (err: any) {\n // Attempt clean up of temp file\n try {\n await fs.unlink(tempPath);\n } catch {\n // Ignore unlink error to bubble original rename error\n }\n throw new Error(\n `[livelocale] Failed to rename temp file to target at ${filePath}. Operation: rename. Original error: ${err.message}`\n );\n }\n\n // RULE SEC-001: NEVER LOG TRANSLATION VALUES\n if (!this.quiet) {\n console.log(`[livelocale] Updated key '${keyPath}' in locale '${locale}'`);\n }\n });\n }\n\n /**\n * Returns the full dictionary for a locale: a flat `{locale}.json`, or every\n * `*.json` inside the `{locale}/` folder merged under its namespace.\n */\n async readLocale(basePath: string, locale: string): Promise<Record<string, any>> {\n this.assertEnabled();\n const localePath = path.resolve(basePath, locale);\n let isDir = false;\n try {\n isDir = (await fs.stat(localePath)).isDirectory();\n } catch {}\n\n if (isDir) {\n const merged: Record<string, any> = {};\n for (const file of await fs.readdir(localePath)) {\n if (!file.endsWith('.json')) continue;\n try {\n merged[path.basename(file, '.json')] = JSON.parse(\n await fs.readFile(path.join(localePath, file), 'utf-8')\n );\n } catch {}\n }\n return merged;\n }\n\n const filePath = resolveSafeLocalePath(basePath, locale);\n return JSON.parse(await fs.readFile(filePath, 'utf-8'));\n }\n\n /**\n * Lists all locale codes dynamically scanned from the locales directory.\n */\n async listLocales(basePath: string): Promise<string[]> {\n this.assertEnabled();\n\n const resolvedBasePath = path.resolve(basePath);\n const items = await fs.readdir(resolvedBasePath, { withFileTypes: true });\n const localesSet = new Set<string>();\n\n for (const item of items) {\n if (item.isDirectory()) {\n if (LOCALE_RE.test(item.name)) {\n localesSet.add(item.name);\n }\n } else if (item.isFile() && item.name.endsWith('.json')) {\n const name = path.basename(item.name, '.json');\n if (LOCALE_RE.test(name)) {\n localesSet.add(name);\n }\n }\n }\n\n return Array.from(localesSet).sort();\n }\n\n /**\n * Adds a new locale directory/file based on current repository structure.\n */\n async addLocale(basePath: string, locale: string): Promise<void> {\n this.assertEnabled();\n\n if (!isSafeLocaleSegment(locale)) {\n throw new Error(\n `[livelocale] Path traversal attempt detected in locale identifier: \"${locale}\"`\n );\n }\n\n if (!LOCALE_RE.test(locale)) {\n throw new Error(`[livelocale] Invalid locale format: \"${locale}\"`);\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const localePath = path.resolve(resolvedBasePath, locale);\n\n if (!localePath.startsWith(resolvedBasePath)) {\n throw new Error(\n `[livelocale] Path traversal attempt: Resolved file path must start with basePath.`\n );\n }\n\n // Check if directory or file already exists\n let exists = false;\n try {\n await fs.stat(localePath);\n exists = true;\n } catch {}\n try {\n await fs.stat(`${localePath}.json`);\n exists = true;\n } catch {}\n\n if (exists) {\n throw new Error(`[livelocale] Locale \"${locale}\" already exists.`);\n }\n\n // Scan basePath for layout detection: if we find any directories matching locale regex,\n // we use folder-based namespaces.\n const items = await fs.readdir(resolvedBasePath, { withFileTypes: true });\n let referenceDir: string | null = null;\n for (const item of items) {\n if (item.isDirectory() && LOCALE_RE.test(item.name)) {\n referenceDir = item.name;\n break;\n }\n }\n\n if (referenceDir) {\n // Create folder-based layout: create directory and write empty namespace files corresponding\n // to those in referenceDir\n await fs.mkdir(localePath, { recursive: true });\n const refPath = path.resolve(resolvedBasePath, referenceDir);\n const files = await fs.readdir(refPath);\n for (const file of files) {\n if (file.endsWith('.json')) {\n await fs.writeFile(path.join(localePath, file), '{}', 'utf-8');\n }\n }\n } else {\n // Flat layout: write empty json file\n await fs.writeFile(`${localePath}.json`, '{}', 'utf-8');\n }\n\n console.log(`[livelocale] Added locale '${locale}'`);\n }\n\n /**\n * Renames an existing locale (directory or file) to a new name.\n */\n async renameLocale(basePath: string, oldLocale: string, newLocale: string): Promise<void> {\n this.assertEnabled();\n\n if (!isSafeLocaleSegment(oldLocale) || !isSafeLocaleSegment(newLocale)) {\n throw new Error('[livelocale] Path traversal attempt detected');\n }\n\n if (!LOCALE_RE.test(oldLocale) || !LOCALE_RE.test(newLocale)) {\n throw new Error(`[livelocale] Invalid locale format`);\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const oldPath = path.resolve(resolvedBasePath, oldLocale);\n const newPath = path.resolve(resolvedBasePath, newLocale);\n\n if (!oldPath.startsWith(resolvedBasePath) || !newPath.startsWith(resolvedBasePath)) {\n throw new Error('[livelocale] Path traversal attempt');\n }\n\n let isDir = false;\n let exists = false;\n try {\n const stats = await fs.stat(oldPath);\n isDir = stats.isDirectory();\n exists = true;\n } catch {}\n\n let isFile = false;\n if (!exists) {\n try {\n await fs.stat(`${oldPath}.json`);\n isFile = true;\n exists = true;\n } catch {}\n }\n\n if (!exists) {\n throw new Error(`[livelocale] Locale \"${oldLocale}\" does not exist.`);\n }\n\n // Check if new path already exists\n let newExists = false;\n try {\n await fs.stat(newPath);\n newExists = true;\n } catch {}\n try {\n await fs.stat(`${newPath}.json`);\n newExists = true;\n } catch {}\n\n if (newExists) {\n throw new Error(`[livelocale] Target locale \"${newLocale}\" already exists.`);\n }\n\n if (isDir) {\n await fs.rename(oldPath, newPath);\n } else if (isFile) {\n await fs.rename(`${oldPath}.json`, `${newPath}.json`);\n }\n\n console.log(`[livelocale] Renamed locale '${oldLocale}' to '${newLocale}'`);\n }\n\n /**\n * Deletes an existing locale (directory or file).\n */\n async deleteLocale(basePath: string, locale: string): Promise<void> {\n this.assertEnabled();\n\n if (!isSafeLocaleSegment(locale)) {\n throw new Error('[livelocale] Path traversal attempt detected');\n }\n\n if (!LOCALE_RE.test(locale)) {\n throw new Error(`[livelocale] Invalid locale format`);\n }\n\n const resolvedBasePath = path.resolve(basePath);\n const localePath = path.resolve(resolvedBasePath, locale);\n\n if (!localePath.startsWith(resolvedBasePath)) {\n throw new Error('[livelocale] Path traversal attempt');\n }\n\n let isDir = false;\n let exists = false;\n try {\n const stats = await fs.stat(localePath);\n isDir = stats.isDirectory();\n exists = true;\n } catch {}\n\n let isFile = false;\n if (!exists) {\n try {\n await fs.stat(`${localePath}.json`);\n isFile = true;\n exists = true;\n } catch {}\n }\n\n if (!exists) {\n throw new Error(`[livelocale] Locale \"${locale}\" does not exist.`);\n }\n\n if (isDir) {\n await fs.rm(localePath, { recursive: true, force: true });\n } else if (isFile) {\n await fs.unlink(`${localePath}.json`);\n }\n\n console.log(`[livelocale] Deleted locale '${locale}'`);\n }\n\n /**\n * Scans codebase directory recursively for references to translation keys.\n * Delegates to the composed KeyScanner (SRP).\n */\n async scanCodebaseKeys(basePath: string): Promise<string[]> {\n return this.scanner.scanCodebaseKeys(basePath);\n }\n\n /**\n * Scans all locale files/folders to compile the set of all keys currently in locales.\n */\n async scanLocalesKeys(basePath: string): Promise<string[]> {\n return this.scanner.scanLocalesKeys(basePath, this.listLocales.bind(this));\n }\n\n /**\n * Returns merged lists of keys from codebase and existing locale files.\n */\n async getKeysMetadata(basePath: string): Promise<{ allKeys: string[]; codeKeys: string[] }> {\n return this.scanner.getKeysMetadata(basePath, this.listLocales.bind(this));\n }\n}\n","/**\n * Shared validation rules for locale codes, translation keys, and values.\n *\n * Platform-agnostic: used by the server router, FileMutator, and the Studio\n * UI so that every layer enforces exactly the same contract.\n */\n\nexport const LOCALE_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,4})?$/;\n\nexport const KEY_RE = /^[a-zA-Z0-9._-]+$/;\n\nexport const MAX_LOCALE_LENGTH = 10;\nexport const MAX_KEY_LENGTH = 200;\nexport const MAX_VALUE_LENGTH = 10000;\n\n/**\n * Object-prototype member names that must never be used as translation key\n * segments. Assigning through these bypasses own-property checks and can\n * pollute `Object.prototype` process-wide (prototype pollution).\n */\nexport const DANGEROUS_KEY_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Returns true if no segment of the dot-separated `key` is a dangerous\n * object-prototype member name.\n */\nexport function isSafeKeyPath(key: unknown): boolean {\n if (typeof key !== 'string') return false;\n return !key.split('.').some((segment) => DANGEROUS_KEY_SEGMENTS.has(segment));\n}\n\n/**\n * Returns true if `locale` is a 2-10 character code matching the loose\n * standard pattern (supports es-419, zh-Hans, eng, etc.).\n */\nexport function isValidLocaleFormat(locale: unknown): boolean {\n return (\n typeof locale === 'string' &&\n locale.length >= 2 &&\n locale.length <= MAX_LOCALE_LENGTH &&\n LOCALE_RE.test(locale)\n );\n}\n\n/**\n * Returns true if `key` is a 1-200 character dot-separated path containing\n * only alphanumerics, dots, underscores, or hyphens.\n */\nexport function isValidKeyFormat(key: unknown): boolean {\n return (\n typeof key === 'string' && key.length >= 1 && key.length <= MAX_KEY_LENGTH && KEY_RE.test(key)\n );\n}\n\n/**\n * Returns true if `value` is a string no longer than MAX_VALUE_LENGTH.\n */\nexport function isValidValueFormat(value: unknown): boolean {\n return typeof value === 'string' && value.length <= MAX_VALUE_LENGTH;\n}\n\n/**\n * Rejects locale identifiers that could escape the locales directory.\n */\nexport function isSafeLocaleSegment(locale: string): boolean {\n return !(\n locale.includes('..') ||\n locale.includes('/') ||\n locale.includes('\\\\') ||\n locale.includes('%') ||\n locale.includes('\\0')\n );\n}\n","import * as path from 'path';\nimport { isSafeLocaleSegment } from './validate.js';\n\n/**\n * Resolves a locale code to a safe `{locale}.json` path inside `basePath`,\n * rejecting path traversal attempts.\n */\nexport function resolveSafeLocalePath(basePath: string, locale: string): string {\n if (!isSafeLocaleSegment(locale)) {\n throw new Error('Path traversal attempt');\n }\n const resolvedBasePath = path.resolve(basePath);\n const filePath = path.resolve(resolvedBasePath, `${locale}.json`);\n if (!filePath.startsWith(resolvedBasePath)) {\n throw new Error('Path traversal attempt');\n }\n return filePath;\n}\n","import { DANGEROUS_KEY_SEGMENTS } from './validate.js';\n\n/** RULE SRV-003: deeper key paths are rejected to keep recursion bounded. */\nexport const MAX_KEY_DEPTH = 30;\n\n/**\n * Sets `value` at the dot-separated `keyPath` inside `obj`, creating\n * intermediate objects as needed. Rejects paths deeper than `MAX_KEY_DEPTH`\n * and any segment that could pollute `Object.prototype` (RULE SEC-005).\n */\nexport function setNestedProperty(obj: Record<string, any>, keyPath: string, value: string): void {\n const keys = keyPath.split('.');\n\n if (keys.length > MAX_KEY_DEPTH) {\n throw new Error(\n `[livelocale] Key depth limit exceeded. Maximum depth is ${MAX_KEY_DEPTH} levels.`\n );\n }\n\n if (keys.some((k) => DANGEROUS_KEY_SEGMENTS.has(k))) {\n throw new Error(\n `[livelocale] Forbidden key segment in path: \"${keyPath}\". Segment names like __proto__, constructor, and prototype are not allowed.`\n );\n }\n\n let current = obj;\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i]!;\n\n if (i === keys.length - 1) {\n current[key] = value;\n return;\n }\n\n if (\n !(key in current) ||\n current[key] === null ||\n typeof current[key] !== 'object' ||\n Array.isArray(current[key])\n ) {\n current[key] = {};\n }\n\n current = current[key];\n }\n}\n\n/** The outcome of reading a dotted key without writing to it. */\nexport type KeyLookup =\n | { state: 'string'; value: string }\n | { state: 'missing' }\n | { state: 'occupied' };\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction hasOwn(obj: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(obj, key);\n}\n\n/**\n * Reads the string at the dot-separated `keyPath` inside `dict` through own\n * properties only, following the same nested walk `setNestedProperty` writes\n * through.\n *\n * - `string`: the path leads to a string value.\n * - `missing`: a segment is absent, or `dict` is not a plain object. Writing\n * here creates the key.\n * - `occupied`: a write would overwrite data or create a duplicate the app\n * never reads. That is an intermediate value that is not a plain object, a\n * final value that is not a string, or, at any level, an own property named\n * exactly like the rest of the dotted path (a literal key such as `\"a.b\"`).\n */\nexport function lookupKey(dict: unknown, keyPath: string): KeyLookup {\n if (!isPlainObject(dict)) return { state: 'missing' };\n\n const segments = keyPath.split('.');\n const leaf = segments.length - 1;\n let current = dict;\n\n for (let i = 0; i < leaf; i++) {\n if (hasOwn(current, segments.slice(i).join('.'))) return { state: 'occupied' };\n\n const segment = segments[i]!;\n if (!hasOwn(current, segment)) return { state: 'missing' };\n\n const next = current[segment];\n if (!isPlainObject(next)) return { state: 'occupied' };\n current = next;\n }\n\n const key = segments[leaf]!;\n if (!hasOwn(current, key)) return { state: 'missing' };\n\n const value = current[key];\n return typeof value === 'string' ? { state: 'string', value } : { state: 'occupied' };\n}\n\n/**\n * Detects the indentation unit of an existing JSON document so rewrites keep\n * the project's style (tabs, 2 or 4 spaces). Defaults to two spaces.\n */\nexport function detectJsonIndent(content: string): string {\n const match = /^([ \\t]+)\"/m.exec(content);\n if (!match) return ' ';\n const ws = match[1]!;\n return ws.includes('\\t') ? '\\t' : ws;\n}\n\n/**\n * Serializes `data` the way the original `content` was formatted: same\n * indentation unit and the same presence or absence of a trailing newline.\n * New documents get two spaces and a trailing newline.\n */\nexport function stringifyLike(data: unknown, content: string | null): string {\n const indent = content === null ? ' ' : detectJsonIndent(content);\n const trailing = content === null || content.endsWith('\\n') ? '\\n' : '';\n return JSON.stringify(data, null, indent) + trailing;\n}\n\n/** Flattens a nested dictionary into dot-separated leaf keys. */\nexport function flattenKeys(obj: unknown, prefix = '', out: Set<string> = new Set()): Set<string> {\n if (!obj || typeof obj !== 'object') return out;\n for (const key of Object.keys(obj as Record<string, unknown>)) {\n const value = (obj as Record<string, unknown>)[key];\n const full = prefix ? `${prefix}.${key}` : key;\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n flattenKeys(value, full, out);\n } else {\n out.add(full);\n }\n }\n return out;\n}\n","import { promises as fs } from 'fs';\nimport * as path from 'path';\nimport { isSafeLocaleSegment } from '../core/validate.js';\nimport { getDisabledReason } from '../core/env.js';\nimport { parseGitignore, type GitignoreMatcher } from '../core/gitignore.js';\n\nconst IGNORE_DIRS = new Set([\n 'node_modules',\n '.next',\n 'dist',\n 'build',\n '.git',\n 'coverage',\n 'test-results',\n 'out',\n '.idea',\n '.vscode',\n '.turbo',\n '.cache',\n]);\n\nconst SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);\n\n/** Files above this size are almost certainly bundles or fixtures, not components. */\nconst MAX_SOURCE_BYTES = 1_000_000;\n\nconst PROP_RE = /\\bt(?:\\??\\.[a-zA-Z0-9_]+)+/g;\nconst IGNORE_PROPS = new Set([\n 'toString',\n 'length',\n 'constructor',\n 'prototype',\n 'map',\n 'forEach',\n 'filter',\n 'reduce',\n 'call',\n 'apply',\n 'bind',\n 'name',\n 'displayName',\n 'raw',\n 'rich',\n 'markup',\n 'has',\n]);\n\nconst KEY_SHAPE = /^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*$/;\nconst DECL = String.raw`(?:const|let|var)\\s+`;\n/** A string literal in any quote style; read the value with `stringArg(match)`. */\nconst STR = String.raw`(?:'(?<s1>[^']*)'|\"(?<s2>[^\"]*)\"|\\x60(?<s3>[^\\x60]*)\\x60)`;\nconst stringArg = (m: RegExpMatchArray): string | undefined =>\n m.groups?.['s1'] ?? m.groups?.['s2'] ?? m.groups?.['s3'];\n\n/** Translator-method names whose first argument is a key. */\nconst METHODS = String.raw`(?:\\.(?:rich|raw|markup|has))?`;\n\n/**\n * Maps translator variable names to their key prefix (namespace) from the\n * declarations in a file:\n * const t = useTranslations('home') // next-intl (also getTranslations, await)\n * const { t } = useTranslation('auth') // react-i18next\n * const t = wrapTranslationEngine(x, { keyPrefix: 'auth' })\n * const t = wrapNextIntl(x, 'checkout')\n * Scope is not tracked; the last declaration of a name in the file wins.\n */\nfunction namespaceBindings(content: string): Map<string, string> {\n const map = new Map<string, string>();\n\n const hookRe = new RegExp(\n DECL +\n String.raw`(?<name>\\w+)\\s*=\\s*(?:await\\s+)?(?:useTranslations|getTranslations)\\(\\s*(?:` +\n STR +\n String.raw`)?\\s*\\)`,\n 'g'\n );\n for (const m of content.matchAll(hookRe)) map.set(m.groups!['name']!, stringArg(m) ?? '');\n\n const wrapNextIntlRe = new RegExp(\n DECL + String.raw`(?<name>\\w+)\\s*=\\s*wrapNextIntl\\([^;]*?,\\s*` + STR + String.raw`\\s*\\)`,\n 'g'\n );\n for (const m of content.matchAll(wrapNextIntlRe)) {\n map.set(m.groups!['name']!, stringArg(m) ?? '');\n }\n\n const wrapEngineRe = new RegExp(\n DECL + String.raw`(?<name>\\w+)\\s*=\\s*wrapTranslationEngine\\([^;]*?keyPrefix:\\s*` + STR,\n 'g'\n );\n for (const m of content.matchAll(wrapEngineRe)) {\n map.set(m.groups!['name']!, stringArg(m) ?? '');\n }\n\n const i18nextRe = new RegExp(\n DECL + String.raw`\\{(?<bindings>[^}]*)\\}\\s*=\\s*useTranslation\\(\\s*(?:` + STR + String.raw`)?`,\n 'g'\n );\n for (const m of content.matchAll(i18nextRe)) {\n const binding = /\\bt\\s*(?::\\s*(\\w+))?/.exec(m.groups!['bindings']!);\n if (binding) map.set(binding[1] ?? 't', stringArg(m) ?? '');\n }\n\n return map;\n}\n\n/** Extracts translation key references from one source file's text. */\nexport function extractKeys(content: string): string[] {\n const keys = new Set<string>();\n const bindings = namespaceBindings(content);\n const translators = new Set<string>(['t', ...bindings.keys()]);\n\n for (const name of translators) {\n const prefix = bindings.get(name) ?? '';\n const callRe = new RegExp(String.raw`\\b` + name + METHODS + String.raw`\\(\\s*` + STR, 'g');\n for (const m of content.matchAll(callRe)) {\n const key = stringArg(m);\n if (!key || key.includes('${') || !KEY_SHAPE.test(key)) continue;\n keys.add(prefix ? `${prefix}.${key}` : key);\n }\n }\n\n let match: RegExpExecArray | null;\n PROP_RE.lastIndex = 0;\n while ((match = PROP_RE.exec(content)) !== null) {\n const clean = match[0].replace(/\\bt/, '').replace(/\\?/g, '');\n const segments = clean\n .split('.')\n .map((seg) => seg.trim())\n .filter(Boolean);\n if (segments.length > 0 && !segments.some((seg) => IGNORE_PROPS.has(seg))) {\n keys.add(segments.join('.'));\n }\n }\n\n return Array.from(keys).sort();\n}\n\ninterface CachedFile<T> {\n mtimeMs: number;\n size: number;\n value: T;\n}\n\nconst NO_IGNORES: GitignoreMatcher = { ignores: () => false };\n\n/**\n * Read-only analysis of translation keys: scans the codebase for `t(...)`\n * references and compiles the set of keys present in locale files.\n *\n * Scans are incremental: each source file's extracted keys are cached by\n * mtime and size, so a rescan only re-reads files that changed. The project's\n * root `.gitignore` is honoured in addition to the built-in ignore list.\n */\nexport interface KeyScannerOptions {\n /**\n * Refuse to scan unless LiveLocale is enabled (development or preview mode).\n * Defaults to true; read-only tooling such as `check` turns it off so it can run in CI.\n */\n requireEnabled?: boolean;\n}\n\nexport class KeyScanner {\n private fileCache = new Map<string, CachedFile<string[]>>();\n private gitignoreCache: CachedFile<GitignoreMatcher> | null = null;\n private readonly requireEnabled: boolean;\n\n constructor(options: KeyScannerOptions = {}) {\n this.requireEnabled = options.requireEnabled ?? true;\n }\n\n private assertEnabled(): void {\n if (!this.requireEnabled) return;\n const reason = getDisabledReason();\n if (reason !== null) {\n throw new Error(\n `[livelocale] KeyScanner can only be executed in development mode (${reason}).`\n );\n }\n }\n\n private async loadGitignore(projectRoot: string): Promise<GitignoreMatcher> {\n const gitignorePath = path.join(projectRoot, '.gitignore');\n let stat;\n try {\n stat = await fs.stat(gitignorePath);\n } catch {\n this.gitignoreCache = null;\n return NO_IGNORES;\n }\n const hit = this.gitignoreCache;\n if (hit && hit.mtimeMs === stat.mtimeMs && hit.size === stat.size) {\n return hit.value;\n }\n try {\n const content = await fs.readFile(gitignorePath, 'utf-8');\n const matcher = parseGitignore(content);\n this.gitignoreCache = { mtimeMs: stat.mtimeMs, size: stat.size, value: matcher };\n return matcher;\n } catch {\n return NO_IGNORES;\n }\n }\n\n /**\n * Scans the project (the parent directory of `basePath`) for translation key references.\n */\n async scanCodebaseKeys(basePath: string): Promise<string[]> {\n return this.scanSourceKeys(path.dirname(path.resolve(basePath)));\n }\n\n /** Scans every source file under `projectRoot` for translation key references. */\n async scanSourceKeys(projectRoot: string): Promise<string[]> {\n this.assertEnabled();\n\n projectRoot = path.resolve(projectRoot);\n const gitignore = await this.loadGitignore(projectRoot);\n const keysSet = new Set<string>();\n const seen = new Set<string>();\n\n const toRel = (fullPath: string) =>\n path.relative(projectRoot, fullPath).split(path.sep).join('/');\n\n const scanDir = async (dir: string): Promise<void> => {\n let items;\n try {\n items = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const item of items) {\n const fullPath = path.join(dir, item.name);\n\n if (item.isDirectory()) {\n if (IGNORE_DIRS.has(item.name)) continue;\n if (gitignore.ignores(toRel(fullPath), true)) continue;\n await scanDir(fullPath);\n continue;\n }\n\n if (!item.isFile()) continue;\n if (!SOURCE_EXTENSIONS.has(path.extname(item.name))) continue;\n if (item.name.endsWith('.d.ts')) continue;\n if (gitignore.ignores(toRel(fullPath), false)) continue;\n\n let stat;\n try {\n stat = await fs.stat(fullPath);\n } catch {\n continue;\n }\n if (stat.size > MAX_SOURCE_BYTES) continue;\n\n seen.add(fullPath);\n const cached = this.fileCache.get(fullPath);\n if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {\n for (const k of cached.value) keysSet.add(k);\n continue;\n }\n\n try {\n const content = await fs.readFile(fullPath, 'utf-8');\n const keys = extractKeys(content);\n this.fileCache.set(fullPath, { mtimeMs: stat.mtimeMs, size: stat.size, value: keys });\n for (const k of keys) keysSet.add(k);\n } catch {}\n }\n };\n\n await scanDir(projectRoot);\n\n // Forget files that disappeared so the cache cannot grow without bound.\n for (const cachedPath of this.fileCache.keys()) {\n if (!seen.has(cachedPath)) this.fileCache.delete(cachedPath);\n }\n\n return Array.from(keysSet).sort();\n }\n\n /**\n * Scans all locale files/folders to compile the set of all keys currently in locales.\n */\n async scanLocalesKeys(\n basePath: string,\n listLocales: (base: string) => Promise<string[]>\n ): Promise<string[]> {\n this.assertEnabled();\n\n const locales = await listLocales(basePath);\n const keysSet = new Set<string>();\n\n for (const locale of locales) {\n try {\n const resolvedBasePath = path.resolve(basePath);\n const localePath = path.resolve(resolvedBasePath, locale);\n\n let isDir = false;\n try {\n const stats = await fs.stat(localePath);\n isDir = stats.isDirectory();\n } catch {}\n\n let data: Record<string, any> = {};\n\n if (isDir) {\n const files = await fs.readdir(localePath);\n for (const file of files) {\n if (file.endsWith('.json')) {\n const ns = path.basename(file, '.json');\n try {\n const content = await fs.readFile(path.join(localePath, file), 'utf-8');\n data[ns] = JSON.parse(content);\n } catch {}\n }\n }\n } else {\n try {\n if (!isSafeLocaleSegment(locale)) {\n continue;\n }\n const filePath = path.resolve(resolvedBasePath, `${locale}.json`);\n if (filePath.startsWith(resolvedBasePath)) {\n const content = await fs.readFile(filePath, 'utf-8');\n data = JSON.parse(content);\n }\n } catch {}\n }\n\n const flatten = (obj: any, prefix = '') => {\n if (!obj || typeof obj !== 'object') return;\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const newKey = prefix ? `${prefix}.${key}` : key;\n if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {\n flatten(obj[key], newKey);\n } else {\n keysSet.add(newKey);\n }\n }\n }\n };\n\n flatten(data);\n } catch {}\n }\n\n return Array.from(keysSet).sort();\n }\n\n /**\n * Returns merged lists of keys from codebase and existing locale files.\n */\n async getKeysMetadata(\n basePath: string,\n listLocales: (base: string) => Promise<string[]>\n ): Promise<{ allKeys: string[]; codeKeys: string[] }> {\n const codeKeys = await this.scanCodebaseKeys(basePath);\n const localeKeys = await this.scanLocalesKeys(basePath, listLocales);\n const allKeys = Array.from(new Set([...codeKeys, ...localeKeys])).sort();\n return {\n allKeys,\n codeKeys,\n };\n }\n}\n","/**\n * Single source of truth for \"is LiveLocale active right now, and in which mode?\".\n *\n * Modes:\n * - `development`: NODE_ENV is `development` (the default, zero-config mode).\n * - `preview`: explicitly opted in with `LIVELOCALE=preview` (server) or\n * `NEXT_PUBLIC_LIVELOCALE=preview` (client bundles). Works in production builds so a\n * preview deployment can be edited through a hosted Studio. The mutation API then\n * requires a bearer token (see the router).\n * - `off`: everything else.\n *\n * Rules, in order:\n * 1. `LIVELOCALE=off|0|false` (or `NEXT_PUBLIC_LIVELOCALE=off`) always wins → off.\n * 2. `LIVELOCALE=preview` / `NEXT_PUBLIC_LIVELOCALE=preview` → preview.\n * 3. `NODE_ENV` must be `development`, otherwise off.\n * 4. `LIVELOCALE=force` skips rule 5.\n * 5. On the server, a known hosting platform (Vercel preview/production, Netlify,\n * Kubernetes, Render, Fly.io, Railway, Heroku) → off, so a mis-configured staging\n * deploy cannot leak watermarks. A one-time warning is logged.\n *\n * `process.env.NODE_ENV` and `process.env.NEXT_PUBLIC_LIVELOCALE` are referenced as\n * literal member expressions so Next.js and Vite can inline them in browser bundles.\n */\n\nexport type LiveLocaleMode = 'off' | 'development' | 'preview';\n\nconst OFF_VALUES = new Set(['off', '0', 'false']);\n\nlet warnedDeployed = false;\n\n/** Test hook: forget that the deployed-platform warning was already printed. */\nexport function resetEnvWarnings(): void {\n warnedDeployed = false;\n}\n\nfunction readNodeEnv(): string | undefined {\n try {\n return process.env.NODE_ENV;\n } catch {\n return undefined;\n }\n}\n\n/** Set by the Vite plugin (and usable by any host) to mirror the server flag into the page. */\nexport const BROWSER_FLAG_GLOBAL = '__LIVELOCALE_FLAG__';\n\nfunction readPublicFlag(): string | undefined {\n let value: string | undefined;\n try {\n value = process.env.NEXT_PUBLIC_LIVELOCALE;\n } catch {\n value = undefined;\n }\n if (value) return value;\n try {\n const g = globalThis as any;\n const fromGlobal = g.window?.[BROWSER_FLAG_GLOBAL] ?? g[BROWSER_FLAG_GLOBAL];\n return typeof fromGlobal === 'string' ? fromGlobal : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readEnv(name: string): string | undefined {\n if (typeof process === 'undefined' || !process.env) return undefined;\n const value = process.env[name];\n return value === '' ? undefined : value;\n}\n\n/** Returns a human-readable platform name when running on a known hosting platform. */\nexport function detectDeployedPlatform(): string | null {\n if (typeof window !== 'undefined') return null; // browser: nothing to detect\n const vercelEnv = readEnv('VERCEL_ENV');\n if (vercelEnv === 'production' || vercelEnv === 'preview')\n return `Vercel (VERCEL_ENV=${vercelEnv})`;\n const netlifyContext = readEnv('CONTEXT');\n if (readEnv('NETLIFY') === 'true' && netlifyContext && netlifyContext !== 'dev') {\n return `Netlify (CONTEXT=${netlifyContext})`;\n }\n if (readEnv('KUBERNETES_SERVICE_HOST')) return 'Kubernetes';\n if (readEnv('RENDER') === 'true') return 'Render';\n if (readEnv('FLY_APP_NAME')) return 'Fly.io';\n if (readEnv('RAILWAY_ENVIRONMENT')) return 'Railway';\n if (readEnv('DYNO')) return 'Heroku';\n return null;\n}\n\nfunction evaluate(): { mode: LiveLocaleMode; reason: string | null } {\n const flag = (readEnv('LIVELOCALE') ?? '').toLowerCase();\n const publicFlag = (readPublicFlag() ?? '').toLowerCase();\n\n if (OFF_VALUES.has(flag)) return { mode: 'off', reason: 'LIVELOCALE kill switch is set' };\n if (OFF_VALUES.has(publicFlag)) {\n return { mode: 'off', reason: 'NEXT_PUBLIC_LIVELOCALE kill switch is set' };\n }\n\n if (flag === 'preview' || publicFlag === 'preview') return { mode: 'preview', reason: null };\n\n const nodeEnv = readNodeEnv();\n if (nodeEnv !== 'development') {\n return {\n mode: 'off',\n reason: `NODE_ENV is \"${nodeEnv ?? 'undefined'}\" (must be \"development\", or set LIVELOCALE=preview)`,\n };\n }\n\n if (flag === 'force') return { mode: 'development', reason: null };\n\n const platform = detectDeployedPlatform();\n if (platform) {\n if (!warnedDeployed) {\n warnedDeployed = true;\n console.warn(\n `[livelocale] Disabled: deployed environment detected (${platform}) while NODE_ENV=development. ` +\n `Set LIVELOCALE=force to override, LIVELOCALE=preview for a hosted preview, or LIVELOCALE=off to silence this warning.`\n );\n }\n return { mode: 'off', reason: `deployed environment detected (${platform})` };\n }\n\n return { mode: 'development', reason: null };\n}\n\n/** The active mode: `off`, `development`, or `preview`. */\nexport function getLiveLocaleMode(): LiveLocaleMode {\n return evaluate().mode;\n}\n\n/**\n * Returns `null` when LiveLocale should be active, otherwise a short reason\n * suitable for error messages and logs.\n */\nexport function getDisabledReason(): string | null {\n return evaluate().reason;\n}\n\n/** True when watermarking, the mutation API, and the client scanner should run. */\nexport function isLiveLocaleEnabled(): boolean {\n return evaluate().mode !== 'off';\n}\n","/**\n * A small, dependency-free `.gitignore` matcher covering the patterns that\n * matter for skipping generated code during key scanning: directory rules\n * (`build/`), anchored rules (`/dist`), globs (`*.log`, `**\\/tmp`,\n * `docs/**\\/*.snap`) and negation (`!keep.log`). Last matching rule wins, and a\n * path whose ancestor directory is ignored is ignored too.\n */\n\nexport interface GitignoreMatcher {\n /** `relPath` is relative to the directory holding the .gitignore, using `/` separators. */\n ignores(relPath: string, isDir: boolean): boolean;\n}\n\ninterface Rule {\n regex: RegExp;\n negated: boolean;\n dirOnly: boolean;\n}\n\nfunction globToRegexSource(pattern: string): string {\n let out = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i]!;\n if (ch === '*') {\n if (pattern[i + 1] === '*') {\n // `**/` → any number of directories (including none); `**` elsewhere → anything\n if (pattern[i + 2] === '/') {\n out += '(?:.*/)?';\n i += 3;\n } else {\n out += '.*';\n i += 2;\n }\n } else {\n out += '[^/]*';\n i += 1;\n }\n } else if (ch === '?') {\n out += '[^/]';\n i += 1;\n } else {\n out += /[.+^${}()|[\\]\\\\]/.test(ch) ? `\\\\${ch}` : ch;\n i += 1;\n }\n }\n return out;\n}\n\nfunction compileRule(rawLine: string): Rule | null {\n let line = rawLine.replace(/\\r$/, '');\n if (!line.trim() || line.trimStart().startsWith('#')) return null;\n // Trailing spaces are ignored unless escaped\n line = line.replace(/(?<!\\\\)\\s+$/, '');\n\n let negated = false;\n if (line.startsWith('!')) {\n negated = true;\n line = line.slice(1);\n }\n\n let dirOnly = false;\n if (line.endsWith('/')) {\n dirOnly = true;\n line = line.slice(0, -1);\n }\n\n let anchored = false;\n if (line.startsWith('/')) {\n anchored = true;\n line = line.slice(1);\n } else if (line.includes('/')) {\n anchored = true;\n }\n\n if (!line) return null;\n\n const body = globToRegexSource(line);\n const regex = anchored ? new RegExp(`^${body}$`) : new RegExp(`(?:^|/)${body}$`);\n return { regex, negated, dirOnly };\n}\n\nexport function parseGitignore(content: string): GitignoreMatcher {\n const rules: Rule[] = [];\n for (const line of content.split('\\n')) {\n const rule = compileRule(line);\n if (rule) rules.push(rule);\n }\n\n const matchOne = (relPath: string, isDir: boolean): boolean => {\n let ignored = false;\n for (const rule of rules) {\n if (rule.dirOnly && !isDir) continue;\n if (rule.regex.test(relPath)) ignored = !rule.negated;\n }\n return ignored;\n };\n\n return {\n ignores(relPath: string, isDir: boolean): boolean {\n if (rules.length === 0) return false;\n const normalized = relPath.replace(/\\\\/g, '/').replace(/^\\.\\//, '').replace(/^\\/+/, '');\n if (!normalized) return false;\n if (matchOne(normalized, isDir)) return true;\n\n // An ignored ancestor directory ignores everything beneath it.\n const segments = normalized.split('/');\n for (let i = 1; i < segments.length; i++) {\n if (matchOne(segments.slice(0, i).join('/'), true)) return true;\n }\n return false;\n },\n };\n}\n","import { execFile } from 'child_process';\nimport { promisify } from 'util';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { GitGateway, GitStatus, GitChange, GitChangeStatus } from './router.js';\n\nconst execFileAsync = promisify(execFile);\n\n/** Diffs larger than this are truncated so the Studio never receives megabytes of JSON. */\nconst MAX_DIFF_BYTES = 200_000;\n\nfunction mapStatus(xy: string): GitChangeStatus {\n if (xy === '??') return 'added';\n const code = xy.trim()[0] ?? '';\n switch (code) {\n case 'M':\n return 'modified';\n case 'A':\n return 'added';\n case 'D':\n return 'deleted';\n case 'R':\n return 'renamed';\n default:\n return 'modified';\n }\n}\n\n/**\n * Thin wrapper over the `git` CLI, scoped to the locales directory. Every\n * command receives arguments as an array (no shell), and every path argument\n * is the locales directory relative to the repository root, so the Studio can\n * only ever stage, commit, or discard translation files.\n */\nexport class GitClient implements GitGateway {\n private async git(cwd: string, args: string[]): Promise<string> {\n const { stdout } = await execFileAsync('git', args, {\n cwd,\n maxBuffer: 16 * 1024 * 1024,\n windowsHide: true,\n env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },\n });\n return stdout;\n }\n\n /** Resolves the repository root and the locales path relative to it (forward slashes). */\n private async locate(\n localesPath: string\n ): Promise<{ root: string; rel: string; absolute: string } | null> {\n const absolute = path.resolve(localesPath);\n const cwd = fs.existsSync(absolute) ? absolute : path.dirname(absolute);\n if (!fs.existsSync(cwd)) return null;\n try {\n const root = (await this.git(cwd, ['rev-parse', '--show-toplevel'])).trim();\n if (!root) return null;\n const rel = path.relative(root, absolute).split(path.sep).join('/');\n if (rel.startsWith('..')) return null; // locales directory lives outside the repo\n return { root: path.resolve(root), rel: rel || '.', absolute };\n } catch {\n return null;\n }\n }\n\n async status(localesPath: string): Promise<GitStatus> {\n const loc = await this.locate(localesPath);\n if (!loc) return { isRepo: false, branch: null, changes: [] };\n\n let branch: string | null = null;\n try {\n branch = (await this.git(loc.root, ['rev-parse', '--abbrev-ref', 'HEAD'])).trim() || null;\n } catch {\n branch = null; // fresh repository without commits\n }\n\n const changes: GitChange[] = [];\n try {\n const out = await this.git(loc.root, [\n 'status',\n '--porcelain=v1',\n '--untracked-files=all',\n '--',\n loc.rel,\n ]);\n for (const line of out.split('\\n')) {\n if (line.length < 4) continue;\n const xy = line.slice(0, 2);\n let file = line.slice(3).trim();\n const arrow = file.indexOf(' -> ');\n if (arrow !== -1) file = file.slice(arrow + 4);\n if (file.startsWith('\"') && file.endsWith('\"')) file = file.slice(1, -1);\n changes.push({ path: file, status: mapStatus(xy) });\n }\n } catch {}\n\n return { isRepo: true, branch, changes, mode: 'local', pullRequestUrl: null };\n }\n\n async diff(localesPath: string): Promise<string> {\n const loc = await this.locate(localesPath);\n if (!loc) return '';\n\n let out = '';\n try {\n out = await this.git(loc.root, ['diff', '--no-color', '--', loc.rel]);\n } catch {}\n\n // Untracked files are not part of `git diff`; synthesize an all-additions hunk.\n try {\n const untracked = await this.git(loc.root, [\n 'ls-files',\n '--others',\n '--exclude-standard',\n '--',\n loc.rel,\n ]);\n for (const file of untracked\n .split('\\n')\n .map((l) => l.trim())\n .filter(Boolean)) {\n const full = path.join(loc.root, file);\n let content = '';\n try {\n content = fs.readFileSync(full, 'utf-8');\n } catch {\n continue;\n }\n const lines = content.split('\\n');\n if (lines[lines.length - 1] === '') lines.pop();\n out +=\n `diff --git a/${file} b/${file}\\nnew file mode 100644\\n--- /dev/null\\n+++ b/${file}\\n` +\n `@@ -0,0 +1,${lines.length} @@\\n` +\n lines.map((l) => `+${l}`).join('\\n') +\n '\\n';\n }\n } catch {}\n\n if (Buffer.byteLength(out, 'utf-8') > MAX_DIFF_BYTES) {\n out = out.slice(0, MAX_DIFF_BYTES) + '\\n… diff truncated …\\n';\n }\n return out;\n }\n\n async commit(localesPath: string, message: string): Promise<{ hash: string }> {\n const loc = await this.locate(localesPath);\n if (!loc) throw new Error('Not a git repository.');\n\n const status = await this.status(localesPath);\n if (status.changes.length === 0) {\n throw new Error(`No changes to commit under ${loc.rel}.`);\n }\n\n await this.git(loc.root, ['add', '-A', '--', loc.rel]);\n await this.git(loc.root, ['commit', '-q', '-m', message, '--', loc.rel]);\n const hash = (await this.git(loc.root, ['rev-parse', '--short', 'HEAD'])).trim();\n return { hash };\n }\n\n async discard(localesPath: string): Promise<void> {\n const loc = await this.locate(localesPath);\n if (!loc) throw new Error('Not a git repository.');\n\n try {\n await this.git(loc.root, ['checkout', '--', loc.rel]);\n } catch {\n // No tracked files under the locales directory yet; nothing to restore.\n }\n await this.git(loc.root, ['clean', '-fd', '--', loc.rel]);\n }\n}\n","/**\n * Placeholder analysis for translation messages.\n *\n * Understands the formats the supported libraries use:\n * - ICU MessageFormat (next-intl, react-intl, FormatJS): `{name}`, `{count, number}`,\n * `{count, plural, one {…} other {…}}`, apostrophe quoting.\n * - i18next: `{{name}}`, `{{count, number}}`, nesting `$t(key)`.\n * - printf-style: `%s`, `%d`, `%1$s`.\n * - rich-text tags (react-intl, next-intl `t.rich`, i18next `<Trans>`): `<b>…</b>`, `<0>…</0>`.\n *\n * A placeholder token is a canonical string such as `{name}`, `{{name}}`, `%s` or `<b>`,\n * so two messages can be compared as sets.\n */\n\nconst PLURAL_TYPES = new Set(['plural', 'select', 'selectordinal']);\n\ninterface ParseResult {\n args: Set<string>;\n error: string | null;\n}\n\n/**\n * Removes ICU apostrophe-quoted spans (`'{'`, `'{literal}'`) and turns `''`\n * into a plain apostrophe so the structural scan only sees real syntax.\n */\nfunction unquoteIcu(message: string): string {\n let out = '';\n let i = 0;\n while (i < message.length) {\n const ch = message[i]!;\n if (ch !== \"'\") {\n out += ch;\n i += 1;\n continue;\n }\n if (message[i + 1] === \"'\") {\n out += \"'\";\n i += 2;\n continue;\n }\n const next = message[i + 1];\n if (next === '{' || next === '}' || next === '#' || next === '|') {\n // Quoted span: skip to the closing single apostrophe (doubled ones are literal).\n let j = i + 1;\n while (j < message.length) {\n if (message[j] === \"'\") {\n if (message[j + 1] === \"'\") {\n j += 2;\n continue;\n }\n break;\n }\n j += 1;\n }\n i = j + 1;\n continue;\n }\n out += ch;\n i += 1;\n }\n return out;\n}\n\n/** Recursive-descent walk over ICU structure collecting argument names and the first error. */\nfunction parseIcu(message: string): ParseResult {\n const args = new Set<string>();\n let pos = 0;\n let error: string | null = null;\n\n const fail = (msg: string) => {\n if (!error) error = msg;\n };\n\n const parseMessage = (depth: number): void => {\n while (pos < message.length) {\n const ch = message[pos]!;\n if (ch === '{') {\n pos += 1;\n parseArgument(depth + 1);\n } else if (ch === '}') {\n if (depth === 0) {\n fail('Unbalanced braces: unexpected \"}\"');\n pos += 1;\n } else {\n return; // caller consumes the brace\n }\n } else {\n pos += 1;\n }\n }\n if (depth > 0) fail('Unbalanced braces: missing \"}\"');\n };\n\n const readUntil = (stops: string[]): string => {\n let out = '';\n while (pos < message.length && !stops.includes(message[pos]!)) {\n out += message[pos]!;\n pos += 1;\n }\n return out;\n };\n\n const parseArgument = (depth: number): void => {\n const name = readUntil([',', '}']).trim();\n if (name && !name.includes('{')) args.add(name);\n if (pos >= message.length) {\n fail('Unbalanced braces: missing \"}\"');\n return;\n }\n if (message[pos] === '}') {\n pos += 1;\n return;\n }\n pos += 1; // ','\n const type = readUntil([',', '}']).trim();\n if (pos >= message.length) {\n fail('Unbalanced braces: missing \"}\"');\n return;\n }\n if (message[pos] === '}') {\n pos += 1;\n return;\n }\n pos += 1; // ','\n\n if (PLURAL_TYPES.has(type)) {\n let hasOther = false;\n while (pos < message.length) {\n // skip whitespace\n while (pos < message.length && /\\s/.test(message[pos]!)) pos += 1;\n if (message[pos] === '}') {\n pos += 1;\n if (!hasOther) fail(`\"${name}\" (${type}) has no \"other\" branch`);\n return;\n }\n const selector = readUntil(['{', '}', ' ', '\\t', '\\n']).trim();\n while (pos < message.length && /\\s/.test(message[pos]!)) pos += 1;\n if (message[pos] !== '{') {\n fail(`\"${name}\" (${type}): expected \"{\" after \"${selector}\"`);\n return;\n }\n if (selector === 'other') hasOther = true;\n pos += 1;\n parseMessage(depth + 1);\n if (message[pos] === '}') pos += 1;\n }\n fail('Unbalanced braces: missing \"}\"');\n return;\n }\n\n // number/date/time style, or unknown: skip to the matching brace\n let level = 1;\n while (pos < message.length && level > 0) {\n if (message[pos] === '{') level += 1;\n else if (message[pos] === '}') level -= 1;\n pos += 1;\n }\n if (level > 0) fail('Unbalanced braces: missing \"}\"');\n };\n\n parseMessage(0);\n return { args, error };\n}\n\nconst I18NEXT_RE = /\\{\\{\\s*([^{}]*?)\\s*\\}\\}/g;\nconst NESTING_RE = /\\$t\\([^)]*\\)/g;\nconst PRINTF_RE = /%(?:\\d+\\$)?[-+ 0#]*\\d*(?:\\.\\d+)?[sdifgxXoc]/g;\nconst TAG_RE = /<\\/?([A-Za-z][\\w-]*|\\d+)\\b[^<>]*>/g;\n\n/** Returns the sorted, de-duplicated placeholder tokens found in `message`. */\nexport function extractPlaceholders(message: string): string[] {\n if (typeof message !== 'string' || message === '') return [];\n const tokens = new Set<string>();\n\n // i18next interpolation first, then blank it out so the ICU scan does not see `{{`.\n const withoutI18next = message.replace(I18NEXT_RE, (_m, inner: string) => {\n const name = inner.split(',')[0]!.trim();\n if (name) tokens.add(`{{${name}}}`);\n return ' ';\n });\n\n for (const m of withoutI18next.match(NESTING_RE) ?? []) tokens.add(m);\n for (const m of withoutI18next.replace(/%%/g, '').match(PRINTF_RE) ?? []) tokens.add(m);\n for (const m of withoutI18next.matchAll(TAG_RE)) tokens.add(`<${m[1]}>`);\n\n const icu = parseIcu(unquoteIcu(withoutI18next.replace(TAG_RE, ' ')));\n for (const arg of icu.args) tokens.add(`{${arg}}`);\n\n return Array.from(tokens).sort();\n}\n\nexport interface PlaceholderComparison {\n ok: boolean;\n /** Present in the reference but not in the value. */\n missing: string[];\n /** Present in the value but not in the reference. */\n extra: string[];\n}\n\n/** Compares placeholder sets; an empty reference is compatible with anything. */\nexport function comparePlaceholders(\n reference: string | undefined | null,\n value: string\n): PlaceholderComparison {\n if (!reference) return { ok: true, missing: [], extra: [] };\n const ref = new Set(extractPlaceholders(reference));\n const val = new Set(extractPlaceholders(value));\n const missing = Array.from(ref).filter((t) => !val.has(t));\n const extra = Array.from(val).filter((t) => !ref.has(t));\n return { ok: missing.length === 0 && extra.length === 0, missing, extra };\n}\n\n/** Returns a description of the first ICU structural problem, or null when the message is sound. */\nexport function validateMessageSyntax(message: string): string | null {\n if (typeof message !== 'string') return null;\n const prepared = unquoteIcu(message.replace(I18NEXT_RE, ' ').replace(TAG_RE, ' '));\n return parseIcu(prepared).error;\n}\n","/**\n * The write rules every translation save must pass, in one browser-safe place.\n *\n * The mutation router, the Studio's static transport and `livelocale apply`\n * all call `validateWrite`, so a value accepted by one is accepted by all.\n * `validateEdit` adds what a write needs when its caller holds the locale\n * files: the reference from the source locale and the structure guard.\n */\n\nimport { lookupKey } from './json-path.js';\nimport { comparePlaceholders, validateMessageSyntax } from './placeholders.js';\nimport {\n isSafeKeyPath,\n isValidKeyFormat,\n isValidLocaleFormat,\n isValidValueFormat,\n} from './validate.js';\n\nexport type WriteRuleCode =\n | 'INVALID_LOCALE_FORMAT'\n | 'INVALID_KEY_FORMAT'\n | 'INVALID_VALUE_FORMAT'\n | 'MESSAGE_SYNTAX'\n | 'PLACEHOLDER_MISMATCH'\n | 'KEY_NOT_A_STRING';\n\nexport interface WriteAccepted {\n ok: true;\n locale: string;\n key: string;\n value: string;\n}\n\nexport interface WriteRejected {\n ok: false;\n /** 422 means that `force` would let the write through. */\n status: 400 | 409 | 422;\n code: WriteRuleCode;\n error: string;\n /** Only for PLACEHOLDER_MISMATCH. */\n details?: { missing: string[]; extra: string[] };\n}\n\nexport type WriteVerdict = WriteAccepted | WriteRejected;\n\nexport interface WriteRuleOptions {\n /** Text whose placeholders the value must keep. null, undefined or '' skips the parity check. */\n reference?: string | null;\n /** Skips only the 422 rules (syntax and parity). The format rules always apply. */\n force?: boolean;\n}\n\nconst reject = (\n status: WriteRejected['status'],\n code: WriteRuleCode,\n error: string\n): WriteRejected => ({\n ok: false,\n status,\n code,\n error,\n});\n\n/**\n * Checks one write in a fixed order: locale, key format, key safety, value,\n * then (unless forced) message syntax and placeholder parity. Returns the\n * first rule that fails, or the narrowed write.\n */\nexport function validateWrite(\n input: { locale: unknown; key: unknown; value: unknown },\n options: WriteRuleOptions = {}\n): WriteVerdict {\n const { locale, key, value } = input;\n\n if (!isValidLocaleFormat(locale)) {\n return reject(400, 'INVALID_LOCALE_FORMAT', 'Invalid locale string parameter.');\n }\n\n if (!isValidKeyFormat(key)) {\n return reject(\n 400,\n 'INVALID_KEY_FORMAT',\n 'Invalid key path. Must be 1-200 characters containing alphanumeric, dots, underscores, or hyphens.'\n );\n }\n\n if (!isSafeKeyPath(key)) {\n return reject(\n 400,\n 'INVALID_KEY_FORMAT',\n 'Forbidden key path: segments like __proto__, constructor, or prototype are not allowed.'\n );\n }\n\n if (!isValidValueFormat(value)) {\n return reject(\n 400,\n 'INVALID_VALUE_FORMAT',\n 'Invalid value. Must be a string less than 10000 characters.'\n );\n }\n\n const accepted: WriteAccepted = {\n ok: true,\n locale: locale as string,\n key: key as string,\n value: value as string,\n };\n\n // RULE SRV-007: PLACEHOLDER SAFETY — a translation must keep the placeholders\n // of its reference and stay structurally valid, unless the author forces it.\n if (options.force === true) return accepted;\n\n const syntaxError = validateMessageSyntax(accepted.value);\n if (syntaxError) {\n return reject(422, 'MESSAGE_SYNTAX', `Message syntax: ${syntaxError}.`);\n }\n\n // An empty, null or undefined reference is compatible with any value.\n const comparison = comparePlaceholders(options.reference, accepted.value);\n if (!comparison.ok) {\n const parts: string[] = [];\n if (comparison.missing.length) parts.push(`missing ${comparison.missing.join(', ')}`);\n if (comparison.extra.length) parts.push(`unexpected ${comparison.extra.join(', ')}`);\n return {\n ...reject(\n 422,\n 'PLACEHOLDER_MISMATCH',\n `Placeholders differ from the reference: ${parts.join('; ')}.`\n ),\n details: { missing: comparison.missing, extra: comparison.extra },\n };\n }\n\n return accepted;\n}\n\n/** The locale files an edit is checked against. */\nexport interface SourceContext {\n /** apply: its --source argument; Studio: manifest.sourceLocale. */\n sourceLocale: string;\n /** The source-locale dictionary as stored (namespaces merged under their names). */\n source: Record<string, unknown>;\n /** The edit locale's dictionary; omitted when that locale has no file. */\n target?: Record<string, unknown>;\n}\n\n/** The string at `key` in ctx.source, else null. For a source-locale edit this is its current text. */\nexport function sourceReference(key: string, ctx: SourceContext): string | null {\n const found = lookupKey(ctx.source, key);\n return found.state === 'string' ? found.value : null;\n}\n\n/**\n * Checks one edit against the locale files: the format rules, then the\n * structure guard (409, which `force` cannot override), then message syntax\n * and placeholder parity against `sourceReference` unless `options.force`.\n * The reference is always recomputed here; any `reference` or `force`\n * carried on `edit` is ignored.\n */\nexport function validateEdit(\n edit: { locale: string; key: string; value: string },\n ctx: SourceContext,\n options?: { force?: boolean }\n): WriteVerdict {\n const { locale, key, value } = edit;\n const input = { locale, key, value };\n\n // `force` skips only the 422 rules, so this runs the format rules alone.\n const format = validateWrite(input, { force: true });\n if (!format.ok) return format;\n\n const dict = locale === ctx.sourceLocale ? ctx.source : (ctx.target ?? {});\n if (lookupKey(dict, key).state === 'occupied') {\n return reject(\n 409,\n 'KEY_NOT_A_STRING',\n `Cannot save \"${key}\" in ${locale}: it would replace translations that are not a single text.`\n );\n }\n\n return validateWrite(input, {\n reference: sourceReference(key, ctx),\n force: options?.force === true,\n });\n}\n\nexport type ContentRiskFlag = 'markup' | 'javascript-url' | 'new-url' | 'bidi-control';\n\nexport const RISK_FLAG_MESSAGES: Record<ContentRiskFlag, string> = {\n markup: 'Adds markup that the source text does not have.',\n 'javascript-url': 'Adds a javascript:, vbscript: or data: link.',\n 'new-url': 'Adds a web address that the source text does not have.',\n 'bidi-control': 'Contains invisible text-direction control characters.',\n};\n\n// Global patterns are only ever read through String.match and String.matchAll,\n// which never leave state behind on a shared regex. No lookbehind: the core\n// must run in every browser the Studio supports.\nconst TAG_PATTERN = /<\\/?([A-Za-z][\\w-]*)\\b[^<>]*>/g;\nconst SCRIPT_LINK_PATTERNS = [/\\b(?:javascript|vbscript)\\s*:/i, /\\bdata:[a-z]+\\/[\\w.+-]+/i];\nconst URL_PATTERN = /\\b(?:https?:\\/\\/|www\\.)[^\\s<>\"')]+/gi;\n// U+202A-U+202E (embeddings and overrides) and U+2066-U+2069 (isolates).\n// LRM and RLM are ordinary Arabic and Hebrew typography, so they are left out.\nconst BIDI_CONTROL_PATTERN = /[\\u202A-\\u202E\\u2066-\\u2069]/g;\n\n/** Tag names are case-insensitive in HTML, so `<B>` and `<b>` are one name. */\nconst tagNames = (text: string): string[] =>\n Array.from(text.matchAll(TAG_PATTERN), (m) => m[1]!.toLowerCase());\n\nconst allMatches = (pattern: RegExp, text: string): string[] => text.match(pattern) ?? [];\n\n/** True when `found` holds an item that `known` lacks. */\nconst hasNew = (known: string[], found: string[]): boolean => {\n const set = new Set(known);\n return found.some((item) => !set.has(item));\n};\n\n/**\n * Flags only what `value` adds compared with `reference`, in the order of\n * the union above, each at most once. A missing reference (a key the source\n * locale lacks) counts as empty, so everything in `value` is new. This only\n * reports; what a flag blocks, if anything, is the caller's decision.\n */\nexport function detectRiskFlags(reference: string | undefined, value: string): ContentRiskFlag[] {\n const ref = reference ?? '';\n const flags: ContentRiskFlag[] = [];\n\n if (hasNew(tagNames(ref), tagNames(value))) flags.push('markup');\n\n // Each pattern is checked on its own, so a reference holding a data: link\n // does not excuse a new javascript: link.\n if (SCRIPT_LINK_PATTERNS.some((pattern) => pattern.test(value) && !pattern.test(ref))) {\n flags.push('javascript-url');\n }\n\n if (hasNew(allMatches(URL_PATTERN, ref), allMatches(URL_PATTERN, value))) flags.push('new-url');\n\n if (hasNew(allMatches(BIDI_CONTROL_PATTERN, ref), allMatches(BIDI_CONTROL_PATTERN, value))) {\n flags.push('bidi-control');\n }\n\n return flags;\n}\n","import { getDisabledReason, getLiveLocaleMode } from '../core/env.js';\nimport { validateWrite } from '../core/save-rules.js';\nimport { isValidLocaleFormat } from '../core/validate.js';\n\n/**\n * Platform-agnostic port for translation file operations. The Next.js and\n * Vite adapters both depend on this narrow contract (DIP) instead of the\n * concrete FileMutator implementation.\n */\nexport interface MutationGateway {\n updateLocaleKey(basePath: string, locale: string, key: string, value: string): Promise<void>;\n listLocales(basePath: string): Promise<string[]>;\n /** Full dictionary for one locale (namespace folders merged). Rejects when missing. */\n readLocale(basePath: string, locale: string): Promise<Record<string, any>>;\n addLocale(basePath: string, locale: string): Promise<void>;\n renameLocale(basePath: string, locale: string, newLocale: string): Promise<void>;\n deleteLocale(basePath: string, locale: string): Promise<void>;\n getKeysMetadata(basePath: string): Promise<{ allKeys: string[]; codeKeys: string[] }>;\n}\n\nexport type GitChangeStatus = 'modified' | 'added' | 'deleted' | 'renamed';\n\nexport interface GitChange {\n /** Path relative to the repository root, forward slashes. */\n path: string;\n status: GitChangeStatus;\n}\n\nexport interface GitStatus {\n isRepo: boolean;\n branch: string | null;\n changes: GitChange[];\n /** `local` = git CLI on the developer machine, `github` = branch + pull request via API. */\n mode?: 'local' | 'github';\n /** Open pull request for the branch, when the gateway knows about one. */\n pullRequestUrl?: string | null;\n}\n\n/**\n * Optional port for version-control operations scoped to the locales\n * directory. Implemented by `GitClient`; absent when git integration is off.\n */\nexport interface GitGateway {\n status(localesPath: string): Promise<GitStatus>;\n diff(localesPath: string): Promise<string>;\n commit(localesPath: string, message: string): Promise<{ hash: string }>;\n discard(localesPath: string): Promise<void>;\n /**\n * Optional self-test of credentials and permissions. Returns human-readable\n * problems (empty when everything needed is in place). Reported by `?action=info`.\n */\n checkAccess?(): Promise<string[]>;\n}\n\nexport interface RouterRequest {\n method: string;\n url: string;\n origin: string;\n /** Raw request body for body-carrying methods; parsed by the router. */\n bodyText?: string;\n /** Value of the Authorization header, if any. */\n authorization?: string;\n /** Value of the Content-Type header (lowercased), if any. */\n contentType?: string;\n}\n\nexport interface RouterResponse {\n status: number;\n headers: Record<string, string>;\n body: string | null;\n}\n\nexport interface MutationRouterOptions {\n localesPath: string;\n allowedOrigins?: string[];\n /** Enables the `git` read action and the `commit` / `discard` write actions. */\n git?: GitGateway;\n /**\n * When set, every request except CORS preflight must carry\n * `Authorization: Bearer <authToken>`. Mandatory in preview mode.\n */\n authToken?: string;\n}\n\nexport interface MutationRouter {\n handle(request: RouterRequest): Promise<RouterResponse>;\n}\n\nconst DEFAULT_ALLOWED_ORIGINS = ['http://localhost:3010', 'http://127.0.0.1:3010'];\n\n/** Maximum accepted request body size (1 MB) — far above any realistic payload. */\nexport const MAX_BODY_BYTES = 1_000_000;\n\n/** Commit messages longer than this are rejected. */\nexport const MAX_COMMIT_MESSAGE_LENGTH = 500;\n\n/** Any localhost origin (any port) is trusted — remote websites are not. */\nconst LOCALHOST_ORIGIN_RE = /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/;\n\n/** Constant-time string comparison so token checks do not leak length or prefix. */\nfunction safeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\nfunction bearerToken(authorization: string | undefined): string | null {\n if (!authorization) return null;\n const match = /^Bearer\\s+(.+)$/i.exec(authorization.trim());\n return match ? match[1]!.trim() : null;\n}\n\nconst json = (\n status: number,\n headers: Record<string, string>,\n payload: unknown\n): RouterResponse => ({\n status,\n headers,\n body: JSON.stringify(payload),\n});\n\n/**\n * The single implementation of the livelocale mutation API.\n *\n * Both the Next.js route handler and the Vite dev-server middleware are thin\n * adapters over this router, so validation, CORS, and error handling live in\n * exactly one place (OCP).\n */\nexport function createMutationRouter(\n mutator: MutationGateway,\n options: MutationRouterOptions\n): MutationRouter {\n // Extra origins may be supplied at run time (GitHub Codespaces, Gitpod,\n // custom HTTPS dev hostnames) without touching code:\n // LIVELOCALE_ALLOWED_ORIGINS=\"https://abc-3010.app.github.dev,https://studio.dev.local\"\n const envOrigins = (process.env['LIVELOCALE_ALLOWED_ORIGINS'] || '')\n .split(',')\n .map((o) => o.trim())\n .filter(Boolean);\n const allowedOrigins = [...(options.allowedOrigins || DEFAULT_ALLOWED_ORIGINS), ...envOrigins];\n const localesPath = options.localesPath;\n const git = options.git;\n const authToken = options.authToken && options.authToken.length > 0 ? options.authToken : null;\n\n const isOriginAllowed = (origin: string): boolean =>\n allowedOrigins.includes(origin) || LOCALHOST_ORIGIN_RE.test(origin);\n\n const resolveAllowedOrigin = (origin: string): string => {\n return isOriginAllowed(origin) ? origin : allowedOrigins[0] || '';\n };\n\n const corsHeaders = (origin: string): Record<string, string> => ({\n 'Access-Control-Allow-Origin': resolveAllowedOrigin(origin),\n 'Access-Control-Allow-Methods': 'POST, OPTIONS, GET',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n 'Content-Type': 'application/json',\n });\n\n // A passing access check is remembered; a failing one is re-run on every\n // `info` call so fixing the token takes effect without a restart.\n let accessOk = false;\n async function accessProblems(): Promise<string[]> {\n if (!git?.checkAccess || accessOk) return [];\n try {\n const problems = await git.checkAccess();\n accessOk = problems.length === 0;\n return problems;\n } catch (err: any) {\n return [`Could not verify repository access: ${err?.message || err}`];\n }\n }\n\n // ─── GET: Fetch locale dictionary, list locales, or fetch keys metadata ───\n\n async function handleGet(\n urlObj: URL,\n basePath: string,\n headers: Record<string, string>\n ): Promise<RouterResponse> {\n const locale = urlObj.searchParams.get('locale');\n const action = urlObj.searchParams.get('action');\n\n if (action === 'info') {\n return json(200, headers, {\n mode: getLiveLocaleMode(),\n authRequired: authToken !== null,\n git: Boolean(git),\n problems: await accessProblems(),\n });\n }\n\n if (action === 'git') {\n if (!git) {\n return json(200, headers, {\n available: false,\n isRepo: false,\n branch: null,\n changes: [],\n diff: '',\n });\n }\n try {\n const status = await git.status(basePath);\n const diff = status.isRepo && status.changes.length > 0 ? await git.diff(basePath) : '';\n return json(200, headers, { available: true, ...status, diff });\n } catch (err: any) {\n return json(500, headers, {\n error: `Failed to read git status: ${err.message}`,\n code: 'GIT_STATUS_FAILED',\n });\n }\n }\n\n if (action === 'keys') {\n try {\n const metadata = await mutator.getKeysMetadata(basePath);\n return json(200, headers, metadata);\n } catch (err: any) {\n return json(500, headers, {\n error: `Failed to fetch keys metadata: ${err.message}`,\n code: 'KEYS_METADATA_FAILED',\n });\n }\n }\n\n if (!locale) {\n try {\n const localesList = await mutator.listLocales(basePath);\n return json(200, headers, localesList);\n } catch (err: any) {\n return json(500, headers, {\n error: `Failed to list locales: ${err.message}`,\n code: 'LIST_LOCALES_FAILED',\n });\n }\n }\n\n if (!isValidLocaleFormat(locale)) {\n return json(400, headers, {\n error: 'Invalid locale string parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n\n try {\n return json(200, headers, await mutator.readLocale(basePath, locale));\n } catch (readErr: any) {\n return json(404, headers, {\n error: `Failed to load locale: ${readErr.message}`,\n code: 'LOCALE_LOAD_FAILED',\n });\n }\n }\n\n // ─── POST: Mutate a translation key or manage locales ────────────────────\n\n async function handlePost(\n bodyText: string | undefined,\n basePath: string,\n headers: Record<string, string>\n ): Promise<RouterResponse> {\n let body: any;\n try {\n body = JSON.parse(bodyText ?? '');\n } catch {\n return json(400, headers, {\n error: 'Invalid JSON request body',\n code: 'BAD_REQUEST',\n });\n }\n\n const { locale, key, value, action, newLocale, message, reference, force } = body || {};\n\n if (action === 'commit' || action === 'discard') {\n if (!git) {\n return json(501, headers, {\n error: 'Git integration is not enabled for this handler.',\n code: 'GIT_UNAVAILABLE',\n });\n }\n if (action === 'commit') {\n const text = typeof message === 'string' ? message.trim() : '';\n if (!text || text.length > MAX_COMMIT_MESSAGE_LENGTH) {\n return json(400, headers, {\n error: `Commit message must be 1-${MAX_COMMIT_MESSAGE_LENGTH} characters.`,\n code: 'INVALID_COMMIT_MESSAGE',\n });\n }\n try {\n const result = await git.commit(basePath, text);\n return json(200, headers, { success: true, hash: result.hash });\n } catch (err: any) {\n return json(409, headers, { error: err.message, code: 'GIT_COMMIT_FAILED' });\n }\n }\n try {\n await git.discard(basePath);\n return json(200, headers, { success: true });\n } catch (err: any) {\n return json(500, headers, { error: err.message, code: 'GIT_DISCARD_FAILED' });\n }\n }\n\n if (action === 'addLocale') {\n if (!isValidLocaleFormat(locale)) {\n return json(400, headers, {\n error: 'Invalid locale string parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n await mutator.addLocale(basePath, locale);\n return json(200, headers, { success: true, locale });\n }\n\n if (action === 'renameLocale') {\n if (!isValidLocaleFormat(locale) || !isValidLocaleFormat(newLocale)) {\n return json(400, headers, {\n error: 'Invalid oldLocale or newLocale parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n await mutator.renameLocale(basePath, locale, newLocale);\n return json(200, headers, { success: true, locale, newLocale });\n }\n\n if (action === 'deleteLocale') {\n if (!isValidLocaleFormat(locale)) {\n return json(400, headers, {\n error: 'Invalid locale string parameter.',\n code: 'INVALID_LOCALE_FORMAT',\n });\n }\n await mutator.deleteLocale(basePath, locale);\n return json(200, headers, { success: true, locale });\n }\n\n const verdict = validateWrite(\n { locale, key, value },\n { reference: typeof reference === 'string' ? reference : null, force: force === true }\n );\n if (!verdict.ok) {\n return json(verdict.status, headers, {\n error: verdict.error,\n code: verdict.code,\n details: verdict.details,\n });\n }\n await mutator.updateLocaleKey(basePath, verdict.locale, verdict.key, verdict.value);\n\n return json(200, headers, { success: true, key, locale });\n }\n\n return {\n async handle(request: RouterRequest): Promise<RouterResponse> {\n const headers = corsHeaders(request.origin);\n\n // RULE GEN-001: DEVELOPMENT-ONLY ENFORCEMENT\n const disabledReason = getDisabledReason();\n if (disabledReason !== null) {\n return json(403, headers, {\n error: `Forbidden: LiveLocale is disabled — ${disabledReason}. It only runs in development mode.`,\n code: 'FORBIDDEN',\n });\n }\n\n // Preflight never carries credentials; let it through so the browser can\n // learn that Authorization is an allowed header.\n if (request.method === 'OPTIONS') {\n return { status: 204, headers, body: null };\n }\n\n // RULE SEC-011: PREVIEW MODE REQUIRES A TOKEN — the API is reachable from the\n // internet in this mode, so refuse to serve anything without one configured.\n if (getLiveLocaleMode() === 'preview' && !authToken) {\n return json(403, headers, {\n error:\n 'Preview mode requires an access token. Set LIVELOCALE_TOKEN (or pass authToken) on the server.',\n code: 'PREVIEW_TOKEN_REQUIRED',\n });\n }\n\n // RULE SEC-012: BEARER AUTH — when a token is configured, every request needs it.\n if (authToken) {\n const presented = bearerToken(request.authorization);\n if (!presented || !safeEqual(presented, authToken)) {\n return json(401, headers, {\n error: 'Unauthorized: a valid Authorization: Bearer token is required.',\n code: 'UNAUTHORIZED',\n });\n }\n }\n\n // RULE SEC-006: CSRF PREVENTION — hard-reject writes from disallowed origins\n // (browsers always send Origin on cross-origin POSTs, including no-cors ones).\n if (request.method === 'POST' && request.origin && !isOriginAllowed(request.origin)) {\n return json(403, headers, {\n error: 'Forbidden: request origin is not allowed.',\n code: 'FORBIDDEN_ORIGIN',\n });\n }\n\n // RULE SEC-006: require a JSON content type on writes so the no-cors\n // \"text/plain\" CSRF vector cannot bypass validation via simple requests.\n if (\n request.method === 'POST' &&\n request.contentType &&\n !request.contentType.includes('application/json')\n ) {\n return json(415, headers, {\n error: 'Content-Type must be application/json.',\n code: 'UNSUPPORTED_MEDIA_TYPE',\n });\n }\n\n try {\n if (request.method === 'GET') {\n const urlObj = new URL(request.url, 'http://localhost');\n return await handleGet(urlObj, localesPath, headers);\n }\n\n if (request.method === 'POST') {\n return await handlePost(request.bodyText, localesPath, headers);\n }\n\n // Unsupported method\n return json(405, headers, {\n error: 'Method Not Allowed',\n code: 'METHOD_NOT_ALLOWED',\n });\n } catch (err: any) {\n return json(500, headers, {\n error: err.message || 'Internal Server Error',\n code: 'INTERNAL_ERROR',\n });\n }\n },\n };\n}\n","/**\n * Minimal structural type for a Node `http.IncomingMessage`-like request.\n * Used by the Pages Router adapter and the Vite middleware so neither needs\n * to import `next` or `vite` types.\n */\nexport interface NodeRequestLike {\n method?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n on(event: 'data', cb: (chunk: Buffer | string) => void): unknown;\n on(event: 'end', cb: () => void): unknown;\n on(event: 'error', cb: (err: Error) => void): unknown;\n destroy?(): void;\n}\n\n/** Minimal structural type for a Node `http.ServerResponse`-like response. */\nexport interface NodeResponseLike {\n statusCode: number;\n setHeader(name: string, value: string): unknown;\n end(chunk?: string): unknown;\n}\n\nexport class BodyTooLargeError extends Error {\n readonly code = 'BODY_TOO_LARGE';\n constructor() {\n super('Request body too large.');\n this.name = 'BodyTooLargeError';\n }\n}\n\n/**\n * Buffers a Node request body as UTF-8 text, rejecting with\n * `BodyTooLargeError` as soon as `maxBytes` is exceeded (RULE SEC-007).\n */\nexport function readNodeBody(req: NodeRequestLike, maxBytes: number): Promise<string> {\n return new Promise((resolve, reject) => {\n let bodyText = '';\n let settled = false;\n req.on('data', (chunk) => {\n if (settled) return;\n bodyText += chunk.toString();\n if (Buffer.byteLength(bodyText, 'utf-8') > maxBytes) {\n settled = true;\n reject(new BodyTooLargeError());\n req.destroy?.();\n }\n });\n req.on('end', () => {\n if (settled) return;\n settled = true;\n resolve(bodyText);\n });\n req.on('error', (err) => {\n if (settled) return;\n settled = true;\n reject(err);\n });\n });\n}\n\n/** Returns the first value of a possibly multi-valued Node header, lowercased name expected. */\nexport function headerValue(\n headers: Record<string, string | string[] | undefined>,\n name: string\n): string {\n const raw = headers[name];\n if (Array.isArray(raw)) return raw[0] ?? '';\n return raw ?? '';\n}\n","/**\n * Recursively merges `source` into `target`.\n *\n * Arrays and primitives in `source` replace `target` values; plain objects\n * are merged depth-first.\n */\nexport function deepMerge(target: any, source: any): any {\n if (typeof target !== 'object' || target === null) return source;\n if (typeof source !== 'object' || source === null) return target;\n\n const result = { ...target };\n for (const key of Object.keys(source)) {\n if (key in target) {\n result[key] = deepMerge(target[key], source[key]);\n } else {\n result[key] = source[key];\n }\n }\n return result;\n}\n","import { spawn } from 'child_process';\nimport { existsSync, readFileSync, statSync, promises as fs } from 'fs';\nimport * as path from 'path';\nimport { FLAG_SCRIPT } from '../core/flag-script.js';\nimport { LOCALE_RE } from '../core/validate.js';\nimport {\n SHARE_DIR,\n SHARE_FLAG_FILE,\n SHARE_LOCALES_DIR,\n SHARE_MANIFEST_FILE,\n SHARE_MANIFEST_FORMAT,\n type LocalesLayout,\n type ShareManifest,\n} from '../core/share-manifest.js';\nimport { resolveBuildInfo, type GitRunner } from './build-info.js';\nimport { parseGitignore } from '../core/gitignore.js';\nimport { detectBuildTarget, findExportBlockingRoute, outputCandidates } from './build-target.js';\nimport { KeyScanner } from './key-scanner.js';\nimport { moduleDir } from './module-dir.js';\nimport { PackError } from './pack-error.js';\nimport {\n basePathOf,\n flagSrcFor,\n injectFlagScript,\n normalizeBase,\n shareUrlPathFor,\n} from './share-html.js';\n\nexport interface ShareDataOptions {\n projectDir: string;\n /** The locales folder, relative to `projectDir`. Defaults to `locales`. */\n localesPath?: string;\n sourceLocale?: string;\n exposeRepo?: boolean;\n basePath: string | null;\n env?: NodeJS.ProcessEnv;\n git?: GitRunner;\n}\n\nexport interface ShareData {\n manifest: ShareManifest;\n /** Each locale's merged nested dictionary; namespaces are top-level keys. */\n dictionaries: Record<string, Record<string, unknown>>;\n warnings: string[];\n}\n\nconst NO_REPO_WARNING =\n 'exposeRepo is on but no GitHub repository was found; the manifest has no repo.';\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction toPosix(relative: string): string {\n return relative.split(path.sep).join('/') || '.';\n}\n\n/** True when `child` is `parent` or inside it. */\nfunction isInside(parent: string, child: string): boolean {\n const rel = path.relative(parent, child);\n return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n}\n\n/** Sets an own property, so a `__proto__` name never reaches the prototype. */\nfunction defineOwn(target: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(target, key, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n}\n\n/** Reads one JSON file whose root must be a plain object. Never goes through FileMutator. */\nasync function readJsonObject(file: string, projectDir: string): Promise<Record<string, unknown>> {\n const shown = toPosix(path.relative(projectDir, file));\n let parsed: unknown;\n try {\n parsed = JSON.parse(await fs.readFile(file, 'utf-8'));\n } catch (error) {\n throw new PackError(\n 'INVALID_LOCALE_FILE',\n `${shown} is not a JSON object: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n if (!isPlainObject(parsed)) {\n const kind = Array.isArray(parsed) ? 'an array' : parsed === null ? 'null' : typeof parsed;\n throw new PackError(\n 'INVALID_LOCALE_FILE',\n `${shown} is not a JSON object: the root is ${kind}`\n );\n }\n return parsed;\n}\n\n/**\n * Collects what the share folder needs: the manifest (build info, locales,\n * layout, code keys) and every locale's dictionary, read straight from disk.\n */\nexport async function collectShareData(options: ShareDataOptions): Promise<ShareData> {\n const projectDir = path.resolve(options.projectDir);\n const localesDir = path.resolve(projectDir, options.localesPath ?? 'locales');\n const warnings: string[] = [];\n\n let entries;\n try {\n if (!(await fs.stat(localesDir)).isDirectory()) throw new Error('not a directory');\n entries = await fs.readdir(localesDir, { withFileTypes: true });\n } catch {\n throw new PackError(\n 'LOCALES_NOT_FOUND',\n `Locales directory not found: ${localesDir}. Pass --locales <path>.`\n );\n }\n\n const dirLocales = new Set<string>();\n const fileLocales = new Set<string>();\n for (const entry of entries) {\n if (entry.isDirectory() && LOCALE_RE.test(entry.name)) {\n dirLocales.add(entry.name);\n } else if (entry.isFile() && entry.name.endsWith('.json')) {\n const code = entry.name.slice(0, -'.json'.length);\n if (LOCALE_RE.test(code)) fileLocales.add(code);\n }\n }\n const locales = [...new Set([...dirLocales, ...fileLocales])].sort();\n if (locales.length === 0) {\n throw new PackError(\n 'NO_LOCALES',\n `No locale files in ${localesDir} (expected en.json or en/*.json).`\n );\n }\n const layout: LocalesLayout = dirLocales.size > 0 ? 'namespaced' : 'flat';\n\n const sourceLocale = options.sourceLocale ?? (locales.includes('en') ? 'en' : locales[0]!);\n if (!locales.includes(sourceLocale)) {\n throw new PackError(\n 'SOURCE_LOCALE_NOT_FOUND',\n `Source locale \"${sourceLocale}\" not found in ${localesDir} (found: ${locales.join(', ')}).`\n );\n }\n\n const dictionaries: Record<string, Record<string, unknown>> = {};\n for (const locale of locales) {\n let dictionary: Record<string, unknown>;\n if (dirLocales.has(locale)) {\n dictionary = {};\n const folder = path.join(localesDir, locale);\n const files = (await fs.readdir(folder, { withFileTypes: true }))\n .filter((file) => file.isFile() && file.name.endsWith('.json'))\n .map((file) => file.name)\n .sort();\n for (const file of files) {\n const namespace = file.slice(0, -'.json'.length);\n defineOwn(dictionary, namespace, await readJsonObject(path.join(folder, file), projectDir));\n }\n } else {\n dictionary = await readJsonObject(path.join(localesDir, `${locale}.json`), projectDir);\n }\n defineOwn(dictionaries, locale, dictionary);\n }\n\n const info = await resolveBuildInfo({\n cwd: projectDir,\n env: options.env,\n git: options.git,\n includeRepo: options.exposeRepo === true,\n });\n const root =\n info.gitRoot !== null && isInside(info.gitRoot, localesDir) ? info.gitRoot : projectDir;\n const codeKeys = await new KeyScanner({ requireEnabled: false }).scanSourceKeys(projectDir);\n\n if (options.exposeRepo === true && info.repo === null) warnings.push(NO_REPO_WARNING);\n\n const manifest: ShareManifest = {\n format: SHARE_MANIFEST_FORMAT,\n ...(options.exposeRepo === true && info.repo !== null ? { repo: info.repo } : {}),\n defaultBranch: info.defaultBranch,\n branch: info.branch,\n base: info.base,\n localesPath: toPosix(path.relative(root, localesDir)),\n layout,\n sourceLocale,\n locales,\n basePath: options.basePath,\n codeKeys: [...codeKeys].sort(),\n };\n\n return { manifest, dictionaries, warnings };\n}\n\nexport const RELATIVE_BASE_WARNING =\n 'The app base is relative (./), so flag.js is linked per HTML file. Pages served by a client-side route fallback cannot load it; build with an absolute base (--base /path/) if the app has such routes.';\n\nexport interface PackOptions extends Omit<ShareDataOptions, 'basePath'> {\n /** The build output folder: absolute, or relative to `projectDir`. */\n outDir: string;\n /** The raw app base (`/`, `/app/`, `./`). Defaults to the detected stack's base, else `/`. */\n base?: string;\n /** `'auto'` (default) skips injection for Next projects or when `<outDir>/_next` exists. */\n injectHtml?: boolean | 'auto';\n /** The built Studio. Defaults to `resolveStudioDir()`. */\n studioDir?: string;\n}\n\nexport interface PackResult {\n shareDir: string;\n manifest: ShareManifest;\n /** POSIX paths relative to the output folder, sorted. */\n htmlInjected: string[];\n shareUrlPath: string;\n warnings: string[];\n}\n\n/** Where the built Studio may sit: beside the bundle in `dist/`, or `dist/studio` from source. */\nexport function defaultStudioCandidates(): string[] {\n const here = moduleDir();\n return [path.resolve(here, '../studio'), path.resolve(here, '../../dist/studio')];\n}\n\nfunction isDirectory(target: string): boolean {\n try {\n return statSync(target).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** The first candidate that holds a built Studio (`index.html` and `assets/`), or null. */\nexport function resolveStudioDir(candidates: string[] = defaultStudioCandidates()): string | null {\n for (const dir of candidates) {\n if (existsSync(path.join(dir, 'index.html')) && isDirectory(path.join(dir, 'assets'))) {\n return dir;\n }\n }\n return null;\n}\n\n/** True when `shareDir` is absent, or holds a `manifest.json` with a numeric `format`. */\nasync function ownsShareDir(shareDir: string): Promise<boolean> {\n try {\n await fs.lstat(shareDir);\n } catch {\n return true;\n }\n try {\n const manifest: unknown = JSON.parse(\n await fs.readFile(path.join(shareDir, SHARE_MANIFEST_FILE), 'utf-8')\n );\n return isPlainObject(manifest) && typeof manifest['format'] === 'number';\n } catch {\n return false;\n }\n}\n\n/** Every `*.html` file under `outDir` outside the share folder, skipping symlinks. POSIX, sorted. */\nasync function findHtmlFiles(outDir: string, shareDir: string): Promise<string[]> {\n const found: string[] = [];\n const walk = async (dir: string): Promise<void> => {\n for (const entry of await fs.readdir(dir, { withFileTypes: true })) {\n if (entry.isSymbolicLink()) continue;\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (full !== shareDir) await walk(full);\n } else if (entry.isFile() && entry.name.toLowerCase().endsWith('.html')) {\n found.push(toPosix(path.relative(outDir, full)));\n }\n }\n };\n await walk(outDir);\n return found.sort();\n}\n\n/**\n * Writes the share folder (`<outDir>/livelocale/`): the Studio, `manifest.json`, the locale\n * dictionaries and `flag.js`, then links `flag.js` from every app HTML file. Everything is\n * validated before the first write, and nothing outside the share folder is written except\n * the flag tag in HTML.\n */\nexport async function packOutput(options: PackOptions): Promise<PackResult> {\n const {\n outDir: outOption,\n base: baseOption,\n injectHtml = 'auto',\n studioDir: studioOption,\n ...shareOptions\n } = options;\n const projectDir = path.resolve(options.projectDir);\n const outDir = path.resolve(projectDir, outOption);\n if (!isDirectory(outDir)) {\n throw new PackError(\n 'OUT_DIR_NOT_FOUND',\n `Build output not found: ${outDir}. Run your build first.`\n );\n }\n\n const target = detectBuildTarget(projectDir);\n const base = normalizeBase(baseOption ?? target.base ?? '/');\n\n const candidates =\n studioOption !== undefined\n ? [path.resolve(projectDir, studioOption)]\n : defaultStudioCandidates();\n const studioDir = resolveStudioDir(candidates);\n if (studioDir === null) {\n throw new PackError(\n 'STUDIO_NOT_FOUND',\n `LiveLocale Studio files are missing from the installed package (looked in ${candidates.join(', ')}). Reinstall livelocale.`\n );\n }\n\n const data = await collectShareData({ ...shareOptions, projectDir, basePath: basePathOf(base) });\n\n const shareDir = path.join(outDir, SHARE_DIR);\n if (!(await ownsShareDir(shareDir))) {\n throw new PackError(\n 'SHARE_DIR_TAKEN',\n `${toPosix(path.relative(projectDir, outDir))}/${SHARE_DIR}/ already exists and was not written by LiveLocale. Move it away and run again.`\n );\n }\n\n await fs.rm(shareDir, { recursive: true, force: true });\n await fs.cp(studioDir, shareDir, { recursive: true });\n await fs.writeFile(\n path.join(shareDir, SHARE_MANIFEST_FILE),\n JSON.stringify(data.manifest, null, 2) + '\\n',\n 'utf-8'\n );\n const localesDir = path.join(shareDir, SHARE_LOCALES_DIR);\n await fs.mkdir(localesDir, { recursive: true });\n for (const locale of data.manifest.locales) {\n await fs.writeFile(\n path.join(localesDir, `${locale}.json`),\n JSON.stringify(data.dictionaries[locale]),\n 'utf-8'\n );\n }\n await fs.writeFile(path.join(shareDir, SHARE_FLAG_FILE), FLAG_SCRIPT, 'utf-8');\n\n const inject =\n injectHtml === 'auto'\n ? target.kind !== 'next' && !existsSync(path.join(outDir, '_next'))\n : injectHtml;\n const htmlInjected = inject ? await findHtmlFiles(outDir, shareDir) : [];\n for (const rel of htmlInjected) {\n const file = path.join(outDir, ...rel.split('/'));\n const html = await fs.readFile(file, 'utf-8');\n const injected = injectFlagScript(html, flagSrcFor(base, rel));\n if (injected !== html) await fs.writeFile(file, injected, 'utf-8');\n }\n\n const warnings = [...data.warnings];\n if (base.kind === 'relative') warnings.push(RELATIVE_BASE_WARNING);\n\n return {\n shareDir,\n manifest: data.manifest,\n htmlInjected,\n shareUrlPath: shareUrlPathFor(base),\n warnings,\n };\n}\n\n/** Command-line options shared by `livelocale pack` and `livelocale build`. */\nexport interface ShareCliArgs {\n positional: string[];\n dir: string;\n locales?: string;\n source?: string;\n base?: string;\n out?: string;\n exposeRepo: boolean;\n inject: boolean;\n preview: boolean;\n command: string[];\n}\n\nconst VALUE_OPTIONS = {\n '--dir': 'dir',\n '--locales': 'locales',\n '--source': 'source',\n '--base': 'base',\n '--out': 'out',\n} as const;\n\n/**\n * Parses `pack`/`build` arguments (`argv` excludes the command name). Tokens after the first\n * `--` go to `command` verbatim. Throws `PackError('USAGE')` for an unknown option or a\n * missing value.\n */\nexport function parseShareArgs(argv: string[]): ShareCliArgs {\n const args: ShareCliArgs = {\n positional: [],\n dir: '.',\n locales: undefined,\n source: undefined,\n base: undefined,\n out: undefined,\n exposeRepo: false,\n inject: true,\n preview: false,\n command: [],\n };\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (arg === '--') {\n args.command = argv.slice(i + 1);\n break;\n }\n if (Object.prototype.hasOwnProperty.call(VALUE_OPTIONS, arg)) {\n const value = argv[i + 1];\n if (value === undefined || value.startsWith('--')) {\n throw new PackError('USAGE', `${arg} needs a value.`);\n }\n args[VALUE_OPTIONS[arg as keyof typeof VALUE_OPTIONS]] = value;\n i++;\n } else if (arg === '--expose-repo') {\n args.exposeRepo = true;\n } else if (arg === '--no-inject') {\n args.inject = false;\n } else if (arg === '--preview') {\n args.preview = true;\n } else if (arg.startsWith('-') && arg !== '-') {\n throw new PackError('USAGE', `Unknown option \"${arg}\".`);\n } else {\n args.positional.push(arg);\n }\n }\n return args;\n}\n\n/** Runs one command and resolves with its exit code. */\nexport type RunCommand = (\n command: string,\n args: string[],\n options: { cwd: string; env: NodeJS.ProcessEnv }\n) => Promise<number>;\n\nconst WINDOWS_NEEDS_QUOTES = /[\\s\"&|<>^()]/;\n\n/**\n * Quotes one argument for a Windows command line: wrapped in double quotes when it is empty or\n * holds whitespace, a quote or a cmd.exe operator; quotes are escaped with `\\`, and backslashes\n * before a quote (or the closing quote) are doubled.\n */\nexport function quoteWindowsArg(arg: string): string {\n if (arg !== '' && !WINDOWS_NEEDS_QUOTES.test(arg)) return arg;\n let out = '\"';\n let backslashes = 0;\n for (const ch of arg) {\n if (ch === '\\\\') {\n backslashes++;\n continue;\n }\n if (ch === '\"') {\n out += '\\\\'.repeat(backslashes * 2 + 1) + '\"';\n } else {\n out += '\\\\'.repeat(backslashes) + ch;\n }\n backslashes = 0;\n }\n return out + '\\\\'.repeat(backslashes * 2) + '\"';\n}\n\n/**\n * Runs the command with inherited stdio. On Windows it runs one quoted command line through the\n * shell, so `.cmd` shims such as `next` and `vite` resolve. A missing program resolves with 127.\n */\nexport const spawnInherit: RunCommand = (command, args, { cwd, env }) =>\n new Promise((resolve, reject) => {\n const child =\n process.platform === 'win32'\n ? spawn([command, ...args].map(quoteWindowsArg).join(' '), {\n cwd,\n env,\n stdio: 'inherit',\n shell: true,\n })\n : spawn(command, args, { cwd, env, stdio: 'inherit' });\n child.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'ENOENT') resolve(127);\n else reject(error);\n });\n child.on('close', (code) => resolve(code ?? 1));\n });\n\nexport interface BuildCommandOptions extends Omit<PackOptions, 'outDir' | 'injectHtml'> {\n /** The build command and its arguments. */\n command: string[];\n /** Run the command with `LIVELOCALE=preview` and `NEXT_PUBLIC_LIVELOCALE=preview`. */\n preview?: boolean;\n /** The static build output folder. Defaults to the first fresh `outputCandidates` entry. */\n outDir?: string;\n /** Defaults to `spawnInherit`. */\n run?: RunCommand;\n /** Defaults to `Date.now`. */\n now?: () => number;\n}\n\nexport interface BuildResult {\n exitCode: number;\n pack: PackResult | null;\n /** This command's own warnings, then every `pack.warnings` entry. */\n warnings: string[];\n}\n\nexport const BUILD_USAGE = 'Usage: livelocale build [--preview] -- <your build command>';\nconst NEXT_PREVIEW_WARNING =\n 'Next.js pages have no watermarks without --preview; the Studio will open but nothing is editable.';\nconst NEXT_GITIGNORE_WARNING =\n 'Add public/livelocale/ to .gitignore; it is generated on every build.';\n/** How much older than the build start a fresh `index.html` may be (coarse file-system clocks). */\nconst FRESH_SLACK_MS = 2000;\n\nfunction gitignoresShareDir(projectDir: string): boolean {\n let content: string;\n try {\n content = readFileSync(path.join(projectDir, '.gitignore'), 'utf-8');\n } catch {\n return false;\n }\n return parseGitignore(content).ignores(`public/${SHARE_DIR}`, true);\n}\n\nfunction freshOutput(projectDir: string, candidates: string[], startMs: number): string | null {\n for (const candidate of candidates) {\n try {\n const stat = statSync(path.join(projectDir, candidate, 'index.html'));\n if (stat.isFile() && stat.mtimeMs >= startMs - FRESH_SLACK_MS) return candidate;\n } catch {\n // not built here\n }\n }\n return null;\n}\n\n/**\n * `livelocale build`: runs the build command and packs its output. Next.js projects are packed\n * into `public/` first (Next copies it into the build); static stacks are packed after a\n * successful build, into `outDir` or the freshly written output folder.\n */\nexport async function runBuildCommand(options: BuildCommandOptions): Promise<BuildResult> {\n const {\n command,\n preview = false,\n outDir,\n run = spawnInherit,\n now = Date.now,\n ...packOptions\n } = options;\n if (command.length === 0) throw new PackError('USAGE', BUILD_USAGE);\n const projectDir = path.resolve(options.projectDir);\n const target = detectBuildTarget(projectDir);\n const env = options.env ?? process.env;\n const childEnv = preview\n ? { ...env, LIVELOCALE: 'preview', NEXT_PUBLIC_LIVELOCALE: 'preview' }\n : env;\n const runBuild = () => run(command[0]!, command.slice(1), { cwd: projectDir, env: childEnv });\n\n if (target.kind === 'next') {\n const blocking = target.staticExport ? findExportBlockingRoute(projectDir) : null;\n if (blocking !== null) {\n throw new PackError(\n 'NEXT_EXPORT_POST_ROUTE',\n `Next.js static export (output: 'export') cannot build ${blocking}: it exports POST, and export mode only allows GET route handlers. The share link does not need this route. Delete ${blocking} (or move it out of this build) and run again.`\n );\n }\n await fs.mkdir(path.join(projectDir, 'public'), { recursive: true });\n const pack = await packOutput({\n ...packOptions,\n projectDir,\n outDir: 'public',\n injectHtml: false,\n });\n const warnings: string[] = [];\n if (!preview) warnings.push(NEXT_PREVIEW_WARNING);\n if (!gitignoresShareDir(projectDir)) warnings.push(NEXT_GITIGNORE_WARNING);\n const exitCode = await runBuild();\n return { exitCode, pack, warnings: [...warnings, ...pack.warnings] };\n }\n\n const startMs = now();\n const exitCode = await runBuild();\n if (exitCode !== 0) return { exitCode, pack: null, warnings: [] };\n\n const candidates = outputCandidates(target);\n const output = outDir ?? freshOutput(projectDir, candidates, startMs);\n if (output === null) {\n throw new PackError(\n 'OUTPUT_NOT_FOUND',\n `Could not find the build output (looked for a fresh index.html in ${candidates.map((c) => `${c}/`).join(', ')}). Pass --out <dir>.`\n );\n }\n const pack = await packOutput({ ...packOptions, projectDir, outDir: output, injectHtml: 'auto' });\n return { exitCode, pack, warnings: [...pack.warnings] };\n}\n","/**\n * The share folder's `flag.js`: a classic ES5 script that static share builds load\n * first in `<head>` of every app page.\n *\n * It sets `window.__LIVELOCALE_FLAG__ = 'preview'` (the global `env.ts` reads) only\n * when the page is framed by a same-origin parent whose path is under the folder\n * `flag.js` itself is served from, which is the Studio folder whatever the app base\n * is. Visitors who open the site directly, or frame it from anywhere else, stay\n * `off`. It never overwrites a flag the page already set, never throws, and never\n * logs.\n *\n * Browser-safe, as tests/unit/core-browser-safe.test.ts checks.\n */\n\n/** The first line of `FLAG_SCRIPT`, which `doctor` uses to recognise a deployed `flag.js`. */\nexport const FLAG_SCRIPT_SIGNATURE = '/*! livelocale flag v1 */';\n\n/** The exact contents `pack` writes to `<base>livelocale/flag.js`. */\nexport const FLAG_SCRIPT = `${FLAG_SCRIPT_SIGNATURE}\n(function () {\n try {\n var me = document.currentScript;\n if (!me || !me.src || window.parent === window || window.__LIVELOCALE_FLAG__ !== undefined)\n return;\n var studio = new URL('.', me.src);\n var host = window.parent.location;\n if (\n host.origin === 'null' ||\n host.origin !== window.location.origin ||\n host.origin !== studio.origin\n )\n return;\n if (host.pathname.indexOf(studio.pathname) !== 0) return;\n window.__LIVELOCALE_FLAG__ = 'preview';\n } catch (e) {}\n})();\n`;\n","/**\n * The `lc1.` change link: the error type every codec step throws, base64url,\n * finding a token in pasted text, the fenced block GitHub issues carry, the\n * repository slug and branch name rules, the change-set schema with its caps,\n * and encoding and decoding through Web Streams deflate.\n *\n * Browser-safe, as tests/unit/core-browser-safe.test.ts checks.\n */\n\nimport {\n isSafeKeyPath,\n isValidKeyFormat,\n isValidLocaleFormat,\n isValidValueFormat,\n MAX_VALUE_LENGTH,\n} from './validate.js';\n\nexport const CHANGE_LINK_PREFIX = 'lc1.';\n\nexport const CHANGE_BLOCK_OPEN = '```livelocale';\n\nconst CHANGE_BLOCK_CLOSE = '```';\n\nexport type ChangeLinkErrorCode =\n | 'NOT_FOUND'\n | 'AMBIGUOUS'\n | 'UNSUPPORTED_VERSION'\n | 'CORRUPT'\n | 'TOO_LARGE'\n | 'TOO_MANY_EDITS'\n | 'INVALID'\n | 'UNSUPPORTED_ENVIRONMENT';\n\n/** A change link that cannot be found, read or written. Messages never echo payload text. */\nexport class ChangeLinkError extends Error {\n readonly code: ChangeLinkErrorCode;\n\n constructor(code: ChangeLinkErrorCode, message: string) {\n super(message);\n this.name = 'ChangeLinkError';\n this.code = code;\n }\n}\n\nconst CORRUPT_MESSAGE =\n 'This change link is damaged or incomplete. Ask the sender to send the file instead.';\n\nconst UNSUPPORTED_VERSION_MESSAGE =\n 'This change link was made by a newer LiveLocale. Update LiveLocale and try again.';\n\nconst BASE64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n\n/** `String.fromCharCode` takes one argument per byte, so bytes are spread a chunk at a time. */\nconst CHUNK_BYTES = 0x8000;\n\n/** RFC 4648 §5 base64url without padding. */\nexport function toBase64Url(bytes: Uint8Array): string {\n // One binary string, one btoa call: 0x8000 is not a multiple of 3, so\n // encoding chunk by chunk would put padding in the middle of the output.\n let binary = '';\n for (let i = 0; i < bytes.length; i += CHUNK_BYTES) {\n binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_BYTES));\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Decodes canonical RFC 4648 §5 base64url without padding. Anything else\n * (another alphabet, `=`, an impossible length, or non-zero trailing bits)\n * throws `CORRUPT`, so a damaged link never decodes to different bytes.\n */\nexport function fromBase64Url(text: string): Uint8Array {\n if (!/^[A-Za-z0-9_-]*$/.test(text) || text.length % 4 === 1) {\n throw new ChangeLinkError('CORRUPT', CORRUPT_MESSAGE);\n }\n const tail = text.length % 4;\n if (tail !== 0) {\n // The last character of a 2- or 3-character group carries 4 or 2 unused bits.\n const unusedBits = tail === 2 ? 0x0f : 0x03;\n if ((BASE64URL_ALPHABET.indexOf(text[text.length - 1]!) & unusedBits) !== 0) {\n throw new ChangeLinkError('CORRUPT', CORRUPT_MESSAGE);\n }\n }\n const binary = atob(\n text.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat(tail === 0 ? 0 : 4 - tail)\n );\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n/** The fenced block a GitHub issue carries: `CHANGE_BLOCK_OPEN`, the token, the closing fence. */\nexport function fencedChangeBlock(token: string): string {\n return `${CHANGE_BLOCK_OPEN}\\n${token}\\n${CHANGE_BLOCK_CLOSE}\\n`;\n}\n\n/**\n * The content of the first fenced `livelocale` block, up to the next line\n * that is exactly the closing fence (or the end of the text), with all\n * whitespace removed so lines a mail or chat app wrapped join up again.\n * Returns null when the text has no such block.\n */\nfunction fencedBlockContent(text: string): string | null {\n const open = new RegExp(`${CHANGE_BLOCK_OPEN}(?:\\\\r\\\\n|\\\\r|\\\\n)`).exec(text);\n if (!open) return null;\n const lines = text.slice(open.index + open[0].length).split(/\\r\\n|\\r|\\n/);\n const close = lines.indexOf(CHANGE_BLOCK_CLOSE);\n return (close < 0 ? lines : lines.slice(0, close)).join('').replace(/\\s+/g, '');\n}\n\n/**\n * Finds the one `lc1.` token in pasted text: a bare token, a Studio link, a\n * link rewritten by a mail scanner, chat markup, prose, or a GitHub issue's\n * fenced block (which wins over any other text). Returns `lc1.<data>`.\n *\n * Throws `AMBIGUOUS` for two different tokens, `UNSUPPORTED_VERSION` for a\n * newer `lcN.` token only, and `NOT_FOUND` otherwise.\n */\nexport function findChangeToken(text: string): string {\n const scope = (fencedBlockContent(text) ?? text).replace(/%23/gi, '#');\n\n const tokens = new Set<string>();\n for (const match of scope.matchAll(/(?:^|[^A-Za-z0-9_-])lc1\\.([A-Za-z0-9_-]+)/g)) {\n tokens.add(match[1]!);\n }\n\n if (tokens.size === 1) return CHANGE_LINK_PREFIX + [...tokens][0]!;\n if (tokens.size > 1) {\n throw new ChangeLinkError(\n 'AMBIGUOUS',\n 'This text holds more than one change link. Paste one link at a time.'\n );\n }\n if (/(?:^|[^A-Za-z0-9_-])lc(?:[2-9]|[1-9]\\d+)\\.[A-Za-z0-9_-]/.test(scope)) {\n throw new ChangeLinkError('UNSUPPORTED_VERSION', UNSUPPORTED_VERSION_MESSAGE);\n }\n throw new ChangeLinkError(\n 'NOT_FOUND',\n 'This is not a LiveLocale change link. A change link contains \"lc1.\".'\n );\n}\n\n/** A GitHub `owner/repo` slug. The repository part may not be `.` or `..`. */\nexport function isValidRepoSlug(slug: string): boolean {\n if (!/^[A-Za-z0-9][A-Za-z0-9-]{0,38}\\/[A-Za-z0-9._-]{1,100}$/.test(slug)) return false;\n const repo = slug.slice(slug.indexOf('/') + 1);\n return repo !== '.' && repo !== '..';\n}\n\nconst BRANCH_NAME_RE = /^(?!-)(?!.*\\.\\.)[^\\s\\x00-\\x1f\\x7f]{1,255}$/;\n\n/**\n * The one branch-name rule. The share manifest, build info and the change-set\n * schema all use it. `''` is not valid. A name never starts with `-` (so git\n * never reads it as an option) and never holds `..`, whitespace, control\n * characters or a lone UTF-16 surrogate. The schema rejects a lone surrogate\n * anywhere as broken text, so a name that passes here never fails a link.\n */\nexport function isValidBranchName(name: string): boolean {\n return BRANCH_NAME_RE.test(name) && !hasLoneSurrogate(name);\n}\n\nexport const CHANGE_LINK_VERSION = 1;\nexport const MAX_CHANGE_EDITS = 500;\nexport const MAX_BY_CHARS = 100;\nexport const MAX_NOTE_CHARS = 2_000;\n\n/** One proposed edit. `from` is null when the key is absent in that locale in the build. */\nexport interface ChangeEdit {\n locale: string;\n key: string;\n from: string | null;\n to: string;\n force: boolean;\n}\n\n/** The edits a translator sends, with the build they were made against. */\nexport interface ChangeSet {\n repo?: string;\n branch: string;\n base: string;\n src: string;\n by: string;\n note: string;\n edits: ChangeEdit[];\n}\n\n/** An edit on the wire. A forced edit carries a trailing `1`. */\nexport type ChangeEditTuple =\n | [locale: string, key: string, from: string | null, to: string]\n | [locale: string, key: string, from: string | null, to: string, force: 1];\n\n/** The JSON a change link carries. */\nexport interface ChangeWire {\n v: 1;\n repo?: string;\n branch: string;\n base: string;\n src: string;\n by: string;\n note: string;\n e: ChangeEditTuple[];\n}\n\n/** The wire form of a change set: no `repo` key when it is unset, and `1` appended only to forced edits. */\nexport function toWire(set: ChangeSet): ChangeWire {\n return {\n v: CHANGE_LINK_VERSION,\n ...(set.repo === undefined ? {} : { repo: set.repo }),\n branch: set.branch,\n base: set.base,\n src: set.src,\n by: set.by,\n note: set.note,\n e: set.edits.map(\n ({ locale, key, from, to, force }): ChangeEditTuple =>\n force ? [locale, key, from, to, 1] : [locale, key, from, to]\n ),\n };\n}\n\n/** Empty, or a full SHA-1 or SHA-256 commit id in lowercase hex. */\nconst BASE_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})?$/;\n\nconst BY_FORBIDDEN_RE = /[\\x00-\\x1f\\x7f]/;\n\n/** Control characters other than tab, line feed and carriage return. */\nconst NOTE_FORBIDDEN_RE = /[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/;\n\n/** A high surrogate with no low one after it, or a low surrogate with no high one before it. */\nconst LONE_SURROGATE_RE =\n /[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:^|[^\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Reads an own property only, so no field ever comes from a prototype. */\nfunction own(obj: Record<string, unknown>, key: string): unknown {\n return Object.prototype.hasOwnProperty.call(obj, key) ? obj[key] : undefined;\n}\n\nfunction invalid(message: string): ChangeLinkError {\n return new ChangeLinkError('INVALID', message);\n}\n\nfunction hasLoneSurrogate(text: string | null | undefined): boolean {\n return typeof text === 'string' && LONE_SURROGATE_RE.test(text);\n}\n\nfunction parseEdit(entry: unknown, n: number): ChangeEdit {\n const fail = (problem: string) => invalid(`edit ${n}: ${problem}`);\n if (!Array.isArray(entry) || (entry.length !== 4 && entry.length !== 5)) {\n throw fail('is not a [locale, key, from, to] entry.');\n }\n const [locale, key, from, to, force] = entry as unknown[];\n if (typeof locale !== 'string' || !isValidLocaleFormat(locale)) {\n throw fail('the locale is not a locale code.');\n }\n if (typeof key !== 'string' || !isValidKeyFormat(key) || !isSafeKeyPath(key)) {\n throw fail('the key is not a valid translation key.');\n }\n if (from !== null && (typeof from !== 'string' || !isValidValueFormat(from))) {\n throw fail(`the current text is not text of at most ${MAX_VALUE_LENGTH} characters.`);\n }\n if (typeof to !== 'string' || !isValidValueFormat(to)) {\n throw fail(`the new text is not text of at most ${MAX_VALUE_LENGTH} characters.`);\n }\n if (entry.length === 5 && force !== 1) throw fail('the force flag must be 1.');\n return { locale, key, from, to, force: entry.length === 5 };\n}\n\n/**\n * Rejects two edits to one locale and key, and, within one locale, an edit\n * whose key is a dotted ancestor of another edit's key (`a.b` and `a.b.c`),\n * which would need both a text and a nested object at the same path.\n */\nfunction checkKeysDistinct(edits: ChangeEdit[]): void {\n const keysByLocale = new Map<string, Set<string>>();\n edits.forEach(({ locale, key }, i) => {\n let keys = keysByLocale.get(locale);\n if (!keys) keysByLocale.set(locale, (keys = new Set()));\n if (keys.has(key)) throw invalid(`edit ${i + 1}: an earlier edit changes the same key.`);\n keys.add(key);\n });\n edits.forEach(({ locale, key }, i) => {\n const keys = keysByLocale.get(locale)!;\n for (let dot = key.indexOf('.'); dot >= 0; dot = key.indexOf('.', dot + 1)) {\n if (keys.has(key.slice(0, dot))) {\n throw invalid(`edit ${i + 1}: another edit changes a key that contains this one.`);\n }\n }\n });\n}\n\n/**\n * Validates a decoded change link and returns it as a change set made of\n * fresh objects, ignoring unknown top-level fields. Throws\n * `UNSUPPORTED_VERSION` unless `v` is 1, `TOO_MANY_EDITS` above\n * `MAX_CHANGE_EDITS` edits, and `INVALID` for anything else outside the\n * schema; a bad entry's message starts with `edit <n>:` (1-based).\n */\nexport function parseChangeSet(value: unknown): ChangeSet {\n if (!isPlainObject(value)) throw invalid('This change link does not hold a change set.');\n if (own(value, 'v') !== CHANGE_LINK_VERSION) {\n throw new ChangeLinkError('UNSUPPORTED_VERSION', UNSUPPORTED_VERSION_MESSAGE);\n }\n\n const repo = own(value, 'repo');\n if (repo !== undefined && (typeof repo !== 'string' || !isValidRepoSlug(repo))) {\n throw invalid('The repository is not an owner/name slug.');\n }\n const branch = own(value, 'branch');\n if (typeof branch !== 'string' || (branch !== '' && !isValidBranchName(branch))) {\n throw invalid('The branch is not a valid branch name.');\n }\n const base = own(value, 'base');\n if (typeof base !== 'string' || !BASE_SHA_RE.test(base)) {\n throw invalid('The base is not a full commit id.');\n }\n const src = own(value, 'src');\n if (typeof src !== 'string' || !isValidLocaleFormat(src)) {\n throw invalid('The source locale is not a locale code.');\n }\n const by = own(value, 'by');\n if (typeof by !== 'string' || by.length > MAX_BY_CHARS || BY_FORBIDDEN_RE.test(by)) {\n throw invalid(`The name is not one line of at most ${MAX_BY_CHARS} characters.`);\n }\n const note = own(value, 'note');\n if (typeof note !== 'string' || note.length > MAX_NOTE_CHARS || NOTE_FORBIDDEN_RE.test(note)) {\n throw invalid(`The note is not plain text of at most ${MAX_NOTE_CHARS} characters.`);\n }\n\n const entries = own(value, 'e');\n if (!Array.isArray(entries)) throw invalid('This change link holds no list of edits.');\n if (entries.length > MAX_CHANGE_EDITS) {\n throw new ChangeLinkError(\n 'TOO_MANY_EDITS',\n `A change link holds at most ${MAX_CHANGE_EDITS} edits. Send the rest in another link.`\n );\n }\n if (entries.length === 0) throw invalid('This change link holds no edits.');\n // Array.from visits the holes of a sparse array, which map would skip.\n const edits = Array.from(entries, (entry: unknown, i) => parseEdit(entry, i + 1));\n checkKeysDistinct(edits);\n\n if ([repo, branch, base, src, by, note].some(hasLoneSurrogate)) {\n throw invalid('This change link holds broken text.');\n }\n edits.forEach(({ locale, key, from, to }, i) => {\n if ([locale, key, from, to].some(hasLoneSurrogate)) {\n throw invalid(`edit ${i + 1}: holds broken text.`);\n }\n });\n\n return { ...(repo === undefined ? {} : { repo }), branch, base, src, by, note, edits };\n}\n\n/** At most this many bytes of JSON after inflation, on encode and on decode. */\nexport const MAX_INFLATED_BYTES = 512 * 1024;\n\n/** A longer token is refused before any base64 or inflate work. */\nexport const MAX_TOKEN_CHARS = 1_000_000;\n\n/** Above this many link characters, Send offers a file instead of a link. */\nexport const CHANGE_LINK_FILE_THRESHOLD = 6_000;\n\n/** Compresses to the zlib format (RFC 1950). */\nexport type Deflater = (bytes: Uint8Array) => Promise<Uint8Array>;\n\n/**\n * Inflates the zlib format (RFC 1950). Throws `TOO_LARGE` once the output\n * passes `maxOutputLength`, and `CORRUPT` on bad or truncated data.\n */\nexport type Inflater = (bytes: Uint8Array, maxOutputLength: number) => Promise<Uint8Array>;\n\nconst TOO_LARGE_MESSAGE = `A change link holds at most ${MAX_INFLATED_BYTES / 1024} KB of text. Send the changes in smaller links.`;\n\nconst UNSUPPORTED_ENVIRONMENT_MESSAGE =\n 'This browser cannot create or read change links. Use a current Chrome, Edge or Firefox, or Safari 16.4 or later.';\n\n/**\n * Input is written to a stream this many bytes at a time. Browsers inflate a\n * written chunk whole before the reader sees any of it, so this bounds how far\n * past the cap a decompression bomb can grow (about 1,000 times this).\n */\nconst FEED_SLICE_BYTES = 4096;\n\n/**\n * Feeds `bytes` through a compression or decompression stream and collects\n * the output, throwing `TOO_LARGE` as soon as it passes `maxOutputLength`.\n * A failed read rejects with the stream's own error.\n */\nasync function pipeBytes(\n stream: CompressionStream | DecompressionStream,\n bytes: Uint8Array,\n maxOutputLength: number\n): Promise<Uint8Array> {\n // The copy keeps a caller's later change to `bytes` out of the stream.\n const input = new Uint8Array(bytes);\n const writer = stream.writable.getWriter();\n const reader = stream.readable.getReader();\n // Set once the reads below stop for any reason. The feeding loop checks it\n // before every write, so no slice is written after the reads stop, however\n // far ahead of the reads the runtime lets writes resolve.\n let stopped = false;\n // Runs alongside the reads below and is not awaited: a transform stream\n // takes more input only once its output is read. Each write waits for the\n // one before, so at most one slice is in flight. How many written slices\n // the stream buffers before inflating them is up to the runtime: a browser\n // takes the next one only once the reader asks for more output, Node's\n // zlib-backed stream up to its highWaterMark of input (16 KB or 64 KB). The\n // reads report the stream's error, so the loop's rejection is silenced.\n void (async () => {\n for (let at = 0; at < input.length; at += FEED_SLICE_BYTES) {\n if (stopped) return;\n await writer.write(input.subarray(at, at + FEED_SLICE_BYTES));\n }\n if (!stopped) await writer.close();\n })().catch(() => {});\n\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.length;\n if (total > maxOutputLength) throw new ChangeLinkError('TOO_LARGE', TOO_LARGE_MESSAGE);\n chunks.push(value);\n }\n } catch (err) {\n // Tear down both sides: the cancel discards inflated output not yet\n // read, the abort drops input written but not yet taken. Neither waits\n // on the other's pending work.\n stopped = true;\n void writer.abort(err).catch(() => {});\n await reader.cancel(err).catch(() => {});\n throw err;\n }\n\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n}\n\n/** Deflates with `CompressionStream('deflate')`. */\nexport const streamDeflate: Deflater = async (bytes) => {\n if (typeof globalThis.CompressionStream !== 'function') {\n throw new ChangeLinkError('UNSUPPORTED_ENVIRONMENT', UNSUPPORTED_ENVIRONMENT_MESSAGE);\n }\n return pipeBytes(new CompressionStream('deflate'), bytes, Infinity);\n};\n\n/**\n * Inflates with `DecompressionStream('deflate')`. Trailing data after the\n * zlib stream is rejected only where the runtime rejects it (browsers, Node\n * 24); `nodeInflate` checks it everywhere.\n */\nexport const streamInflate: Inflater = async (bytes, maxOutputLength) => {\n if (typeof globalThis.DecompressionStream !== 'function') {\n throw new ChangeLinkError('UNSUPPORTED_ENVIRONMENT', UNSUPPORTED_ENVIRONMENT_MESSAGE);\n }\n try {\n return await pipeBytes(new DecompressionStream('deflate'), bytes, maxOutputLength);\n } catch (err) {\n if (err instanceof ChangeLinkError) throw err;\n throw new ChangeLinkError('CORRUPT', CORRUPT_MESSAGE);\n }\n};\n\n/**\n * Encodes a change set as `lc1.<base64url>`. The set is validated against\n * the schema first, so every link this returns also decodes. Throws the\n * schema's codes, `TOO_LARGE` above `MAX_INFLATED_BYTES` of JSON, and\n * `UNSUPPORTED_ENVIRONMENT` without `CompressionStream`.\n */\nexport async function encodeChangeLink(\n set: ChangeSet,\n deflate: Deflater = streamDeflate\n): Promise<string> {\n // Serialize the validated copy, so the link holds exactly what was checked.\n const json = JSON.stringify(toWire(parseChangeSet(toWire(set))));\n const bytes = new TextEncoder().encode(json);\n if (bytes.length > MAX_INFLATED_BYTES) {\n throw new ChangeLinkError('TOO_LARGE', TOO_LARGE_MESSAGE);\n }\n return CHANGE_LINK_PREFIX + toBase64Url(await deflate(bytes));\n}\n\n/**\n * Decodes the one change link in `text`: a token, a Studio link, a change\n * file or a GitHub issue body. Throws `findChangeToken`'s and the schema's\n * codes, `TOO_LARGE` for a token over `MAX_TOKEN_CHARS` (before any other\n * work) or a payload over `MAX_INFLATED_BYTES`, and `CORRUPT` for anything\n * that does not decode to JSON.\n */\nexport async function decodeChangeLink(\n text: string,\n inflate: Inflater = streamInflate\n): Promise<ChangeSet> {\n const token = findChangeToken(text);\n if (token.length > MAX_TOKEN_CHARS) {\n throw new ChangeLinkError('TOO_LARGE', TOO_LARGE_MESSAGE);\n }\n try {\n const bytes = await inflate(\n fromBase64Url(token.slice(CHANGE_LINK_PREFIX.length)),\n MAX_INFLATED_BYTES\n );\n const json = new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n return parseChangeSet(JSON.parse(json));\n } catch (err) {\n if (err instanceof ChangeLinkError) throw err;\n throw new ChangeLinkError('CORRUPT', CORRUPT_MESSAGE);\n }\n}\n","/**\n * The share manifest contract: the names inside the share folder\n * (`<base>livelocale/`) and the shape of its `manifest.json`, which `pack`\n * writes and the static Studio and `doctor` read.\n *\n * Browser-safe, as tests/unit/core-browser-safe.test.ts checks.\n */\n\nimport { isValidBranchName, isValidRepoSlug } from './change-link.js';\nimport { LOCALE_RE } from './validate.js';\n\n/** The share folder under the app base. No underscore, because Jekyll drops such folders. */\nexport const SHARE_DIR = 'livelocale';\nexport const SHARE_MANIFEST_FILE = 'manifest.json';\nexport const SHARE_FLAG_FILE = 'flag.js';\nexport const SHARE_LOCALES_DIR = 'locales';\nexport const SHARE_MANIFEST_FORMAT = 1;\n\n/** `flat`: one `<code>.json` per locale; `namespaced`: one folder per locale, one file per namespace. */\nexport type LocalesLayout = 'flat' | 'namespaced';\n\nexport interface ShareManifest {\n format: 1;\n /** GitHub `owner/name`. The key is absent unless the build sets `exposeRepo`. */\n repo?: string;\n defaultBranch: string | null;\n /** The branch the site was built from. */\n branch: string | null;\n /** The commit the site was built from: lowercase hex, 40 or 64 characters. */\n base: string | null;\n /** Repo-relative POSIX path of the locales folder, `'.'` for the root. */\n localesPath: string;\n layout: LocalesLayout;\n /** Always one of `locales`. */\n sourceLocale: string;\n /** Sorted. Each code has one file at `./locales/<code>.json`. */\n locales: string[];\n /** The app base, such as `'/'` or `'/repo/'`; null when the app uses a relative base. */\n basePath: string | null;\n /** Sorted keys found in the code by KeyScanner. */\n codeKeys: string[];\n}\n\nconst BASE_SHA_RE = /^[0-9a-f]{40}([0-9a-f]{24})?$/;\n\n/** Reads an own property only, so no field ever comes from a prototype. */\nfunction own(obj: object, key: string): unknown {\n return Object.prototype.hasOwnProperty.call(obj, key)\n ? (obj as Record<string, unknown>)[key]\n : undefined;\n}\n\n/** True for an array whose every slot passes `test`. A hole fails, which `every` would skip. */\nfunction isArrayOf<T>(value: unknown, test: (item: unknown) => item is T): value is T[] {\n if (!Array.isArray(value)) return false;\n for (let i = 0; i < value.length; i++) {\n if (!test(value[i])) return false;\n }\n return true;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isLocaleCode(value: unknown): value is string {\n return typeof value === 'string' && LOCALE_RE.test(value);\n}\n\nfunction isNullOr(value: unknown, test: (text: string) => boolean): boolean {\n return value === null || (typeof value === 'string' && test(value));\n}\n\n/**\n * True when `value` is a format-1 share manifest. Unknown fields are ignored,\n * so a newer build can add fields. Both branches must pass `isValidBranchName`,\n * the rule the change-set schema applies to a link's branch, and an empty\n * locales list fails because the source locale must be one of them.\n */\nexport function isShareManifest(value: unknown): value is ShareManifest {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n const repo = own(value, 'repo');\n const layout = own(value, 'layout');\n const locales = own(value, 'locales');\n const sourceLocale = own(value, 'sourceLocale');\n return (\n own(value, 'format') === SHARE_MANIFEST_FORMAT &&\n (repo === undefined || (typeof repo === 'string' && isValidRepoSlug(repo))) &&\n isNullOr(own(value, 'defaultBranch'), isValidBranchName) &&\n isNullOr(own(value, 'branch'), isValidBranchName) &&\n isNullOr(own(value, 'base'), (base) => BASE_SHA_RE.test(base)) &&\n typeof own(value, 'localesPath') === 'string' &&\n (layout === 'flat' || layout === 'namespaced') &&\n isArrayOf(locales, isLocaleCode) &&\n typeof sourceLocale === 'string' &&\n locales.includes(sourceLocale) &&\n isNullOr(own(value, 'basePath'), (path) => path.startsWith('/') && path.endsWith('/')) &&\n isArrayOf(own(value, 'codeKeys'), isString)\n );\n}\n\n/** The value itself when it is a share manifest, otherwise null (an HTML fallback page, say). */\nexport function parseShareManifest(value: unknown): ShareManifest | null {\n return isShareManifest(value) ? value : null;\n}\n","import { execFile } from 'child_process';\nimport { readFileSync } from 'fs';\nimport * as path from 'path';\nimport { isValidBranchName, isValidRepoSlug } from '../core/change-link.js';\n\n/** Runs git with `args` in `cwd`. Resolves to trimmed stdout, or null on any failure. */\nexport type GitRunner = (args: string[], cwd: string) => Promise<string | null>;\n\n/** The default runner: no shell, no window, and git never prompts for credentials. */\nexport const runGit: GitRunner = (args, cwd) =>\n new Promise((resolve) => {\n try {\n execFile(\n 'git',\n args,\n { cwd, windowsHide: true, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } },\n (error, stdout) => resolve(error ? null : String(stdout).trim())\n );\n } catch {\n resolve(null);\n }\n });\n\nexport interface BuildInfoOptions {\n cwd: string;\n env?: NodeJS.ProcessEnv;\n git?: GitRunner;\n /** Only when true is the repository slug resolved (and `git remote` called). */\n includeRepo?: boolean;\n}\n\nexport interface BuildInfo {\n repo: string | null;\n defaultBranch: string | null;\n branch: string | null;\n base: string | null;\n gitRoot: string | null;\n}\n\nconst SHA_RE = /^[0-9a-f]{40}([0-9a-f]{24})?$/i;\n\ntype Candidate = string | null | undefined | (() => Promise<string | null | undefined>);\n\n/** The first candidate that `accept` maps to a non-null value wins; later ones are never evaluated. */\nasync function firstValid(\n candidates: Candidate[],\n accept: (value: string) => string | null\n): Promise<string | null> {\n for (const candidate of candidates) {\n const value = typeof candidate === 'function' ? await candidate() : candidate;\n if (typeof value !== 'string' || value === '') continue;\n const accepted = accept(value);\n if (accepted !== null) return accepted;\n }\n return null;\n}\n\nconst asBranch = (value: string): string | null => (isValidBranchName(value) ? value : null);\nconst asSha = (value: string): string | null => (SHA_RE.test(value) ? value.toLowerCase() : null);\nconst asSlug = (value: string): string | null => (isValidRepoSlug(value) ? value : null);\n\nfunction readEvent(eventPath: string | undefined): Record<string, unknown> {\n if (!eventPath) return {};\n try {\n const parsed: unknown = JSON.parse(readFileSync(eventPath, 'utf-8'));\n return isRecord(parsed) ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Reads a nested string from a parsed event payload, or undefined. */\nfunction pick(obj: unknown, ...keys: string[]): string | undefined {\n let current = obj;\n for (const key of keys) {\n if (!isRecord(current)) return undefined;\n current = current[key];\n }\n return typeof current === 'string' ? current : undefined;\n}\n\n/**\n * Resolves the repository, branch, commit and default branch of the build from\n * CI variables first and the local git checkout second. An empty or invalid\n * value from one source falls through to the next.\n */\nexport async function resolveBuildInfo(options: BuildInfoOptions): Promise<BuildInfo> {\n const { cwd, env = process.env, git = runGit, includeRepo = false } = options;\n const event = readEvent(env.GITHUB_EVENT_PATH);\n const isPullRequest =\n env.GITHUB_EVENT_NAME === 'pull_request' || env.GITHUB_EVENT_NAME === 'pull_request_target';\n const netlify = env.NETLIFY === 'true';\n const gitlab = env.GITLAB_CI === 'true';\n\n const branch = await firstValid(\n [\n env.GITHUB_HEAD_REF,\n env.GITHUB_REF_TYPE === 'branch' ? env.GITHUB_REF_NAME : null,\n env.VERCEL_GIT_COMMIT_REF,\n env.CF_PAGES_BRANCH,\n netlify ? env.BRANCH : null,\n gitlab ? env.CI_COMMIT_REF_NAME : null,\n async () => {\n const head = await git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);\n return head === 'HEAD' ? null : head;\n },\n ],\n asBranch\n );\n\n const base = await firstValid(\n [\n isPullRequest ? pick(event, 'pull_request', 'head', 'sha') : null,\n env.GITHUB_SHA,\n env.VERCEL_GIT_COMMIT_SHA,\n env.CF_PAGES_COMMIT_SHA,\n netlify ? env.COMMIT_REF : null,\n gitlab ? env.CI_COMMIT_SHA : null,\n () => git(['rev-parse', 'HEAD'], cwd),\n ],\n asSha\n );\n\n const defaultBranch = await firstValid(\n [\n pick(event, 'repository', 'default_branch'),\n async () => {\n const ref = await git(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'], cwd);\n return ref?.startsWith('origin/') ? ref.slice('origin/'.length) : null;\n },\n ],\n asBranch\n );\n\n const githubCom =\n env.GITHUB_SERVER_URL === undefined ||\n env.GITHUB_SERVER_URL === '' ||\n env.GITHUB_SERVER_URL === 'https://github.com';\n const repo = includeRepo\n ? await firstValid(\n [\n githubCom ? env.GITHUB_REPOSITORY : null,\n env.VERCEL_GIT_PROVIDER === 'github' &&\n env.VERCEL_GIT_REPO_OWNER &&\n env.VERCEL_GIT_REPO_SLUG\n ? `${env.VERCEL_GIT_REPO_OWNER}/${env.VERCEL_GIT_REPO_SLUG}`\n : null,\n netlify && env.REPOSITORY_URL ? parseGitHubSlug(env.REPOSITORY_URL) : null,\n async () => {\n const remote = await git(['remote', 'get-url', 'origin'], cwd);\n return remote ? parseGitHubSlug(remote) : null;\n },\n ],\n asSlug\n )\n : null;\n\n const top = await git(['rev-parse', '--show-toplevel'], cwd);\n const gitRoot = top ? path.resolve(top) : null;\n\n return { repo, defaultBranch, branch, base, gitRoot };\n}\n\n/**\n * The `owner/repo` slug of a github.com remote, or null. Accepts https, http,\n * ssh and git URLs and the scp-like `git@github.com:owner/repo` form, with or\n * without `.git` and a trailing slash. Credentials in the URL are discarded\n * here and never returned.\n */\nexport function parseGitHubSlug(remote: string): string | null {\n const text = remote.trim();\n let repoPath: string;\n const scp = /^[A-Za-z0-9._-]+@github\\.com:(.*)$/i.exec(text);\n if (scp) {\n repoPath = scp[1] ?? '';\n } else {\n let url: URL;\n try {\n url = new URL(text);\n } catch {\n return null;\n }\n if (!['https:', 'http:', 'ssh:', 'git:'].includes(url.protocol)) return null;\n if (url.hostname.toLowerCase() !== 'github.com') return null;\n repoPath = url.pathname;\n }\n const trimmed = repoPath\n .replace(/^\\/+/, '')\n .replace(/\\/+$/, '')\n .replace(/\\.git$/i, '');\n return isValidRepoSlug(trimmed) ? trimmed : null;\n}\n","import { existsSync, readFileSync } from 'fs';\nimport * as path from 'path';\n\n/** Next.js config file names, in the order they are read. */\nexport const NEXT_CONFIG_FILES = [\n 'next.config.js',\n 'next.config.mjs',\n 'next.config.cjs',\n 'next.config.ts',\n 'next.config.mts',\n];\n\nconst VITE_CONFIG_FILES = [\n 'vite.config.js',\n 'vite.config.mjs',\n 'vite.config.cjs',\n 'vite.config.ts',\n 'vite.config.mts',\n];\n\nexport type BuildTarget =\n | { kind: 'next'; staticExport: boolean; base: string | null } // base = Next basePath\n | { kind: 'cra'; base: string | null } // from package.json homepage ('./' for '.')\n | { kind: 'vite'; base: string | null } // from vite.config base (site path or './')\n | { kind: 'other'; base: null };\n\nconst OUTPUT_EXPORT = /\\boutput\\s*:\\s*['\"`]export['\"`]/;\nconst BASE_PATH = /\\bbasePath\\s*:\\s*['\"`](\\/[^'\"`]*)['\"`]/;\n/** Vite `base`: a site path ('/repo/') or a relative base ('./', ''). Full URLs are not a site path. */\nconst VITE_BASE = /\\bbase\\s*:\\s*['\"`](\\/[^'\"`]*|\\.\\/?|)['\"`]/;\nconst EXPORTS_POST =\n /export\\s+(?:const|let|var|async\\s+function|function)\\s+POST\\b|export\\s*\\{[^}]*\\bPOST\\b[^}]*\\}/;\n\ninterface PackageJson {\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n homepage?: unknown;\n}\n\nfunction readText(file: string): string | null {\n try {\n return readFileSync(file, 'utf-8');\n } catch {\n return null;\n }\n}\n\nfunction readPackageJson(projectDir: string): PackageJson {\n const text = readText(path.join(projectDir, 'package.json'));\n if (text === null) return {};\n try {\n const parsed: unknown = JSON.parse(text);\n return parsed && typeof parsed === 'object' ? (parsed as PackageJson) : {};\n } catch {\n return {};\n }\n}\n\nfunction hasDependency(pkg: PackageJson, name: string): boolean {\n return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);\n}\n\nfunction firstExisting(projectDir: string, names: string[]): string | null {\n for (const name of names) {\n const file = path.join(projectDir, name);\n if (existsSync(file)) return file;\n }\n return null;\n}\n\n/** Removes block comments and whole-line `//` comments so commented-out options never match. */\nfunction stripComments(source: string): string {\n return source\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .split('\\n')\n .filter((line) => !line.trimStart().startsWith('//'))\n .join('\\n');\n}\n\nfunction craBase(homepage: unknown): string | null {\n if (typeof homepage !== 'string') return null;\n const value = homepage.trim();\n if (value === '.' || value === './') return './';\n if (value.startsWith('/')) return value;\n try {\n return new URL(value).pathname;\n } catch {\n return null;\n }\n}\n\n/** Detects the project's build stack. The first match wins: Next, CRA, Vite, then other. */\nexport function detectBuildTarget(projectDir: string): BuildTarget {\n const pkg = readPackageJson(projectDir);\n\n const nextConfig = firstExisting(projectDir, NEXT_CONFIG_FILES);\n if (nextConfig || hasDependency(pkg, 'next')) {\n const source = stripComments((nextConfig && readText(nextConfig)) ?? '');\n return {\n kind: 'next',\n staticExport: OUTPUT_EXPORT.test(source),\n base: BASE_PATH.exec(source)?.[1] ?? null,\n };\n }\n\n if (hasDependency(pkg, 'react-scripts')) return { kind: 'cra', base: craBase(pkg.homepage) };\n\n if (hasDependency(pkg, 'vite') || firstExisting(projectDir, VITE_CONFIG_FILES)) {\n const viteConfig = firstExisting(projectDir, VITE_CONFIG_FILES);\n const source = stripComments((viteConfig && readText(viteConfig)) ?? '');\n return { kind: 'vite', base: VITE_BASE.exec(source)?.[1] ?? null };\n }\n\n return { kind: 'other', base: null };\n}\n\nconst ROUTE_DIRS = ['app', 'src/app'];\nconst ROUTE_EXTENSIONS = ['ts', 'js', 'mjs', 'tsx', 'jsx'];\n\n/**\n * Finds a LiveLocale Next route that exports POST, which `output: 'export'` cannot build.\n * Returns its POSIX path relative to the project, or null.\n */\nexport function findExportBlockingRoute(projectDir: string): string | null {\n for (const dir of ROUTE_DIRS) {\n for (const ext of ROUTE_EXTENSIONS) {\n const rel = `${dir}/api/livelocale/route.${ext}`;\n const source = readText(path.join(projectDir, rel));\n if (source !== null && EXPORTS_POST.test(source)) return rel;\n }\n }\n return null;\n}\n\n/** Output folders to look for, most likely first. */\nexport function outputCandidates(target: BuildTarget): string[] {\n return target.kind === 'cra' ? ['build', 'dist', 'out'] : ['dist', 'build', 'out'];\n}\n","import * as path from 'path';\nimport { fileURLToPath } from 'url';\n\n/** The folder of the running module: `__dirname` in CommonJS, else derived from `import.meta.url`. */\nexport function moduleDir(): string {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n return path.dirname(fileURLToPath((import.meta as any).url));\n}\n","/** Every reason `livelocale pack`, `livelocale build` or the Vite share step can stop. */\nexport type PackErrorCode =\n | 'USAGE'\n | 'BAD_BASE'\n | 'LOCALES_NOT_FOUND'\n | 'NO_LOCALES'\n | 'SOURCE_LOCALE_NOT_FOUND'\n | 'INVALID_LOCALE_FILE'\n | 'OUT_DIR_NOT_FOUND'\n | 'STUDIO_NOT_FOUND'\n | 'SHARE_DIR_TAKEN'\n | 'NEXT_EXPORT_POST_ROUTE'\n | 'OUTPUT_NOT_FOUND';\n\n/** A pack failure with a stable code and a message that names the fix. */\nexport class PackError extends Error {\n readonly code: PackErrorCode;\n\n constructor(code: PackErrorCode, message: string) {\n super(message);\n this.name = 'PackError';\n this.code = code;\n }\n}\n","import { SHARE_DIR, SHARE_FLAG_FILE } from '../core/share-manifest.js';\nimport { PackError } from './pack-error.js';\n\n/** Where the app's HTML is served from: a site path such as `/app/`, or relative (`./`). */\nexport type AppBase = { kind: 'absolute'; path: string } | { kind: 'relative' };\n\n/** A URL scheme (`https:`) or a protocol-relative start (`//`). */\nconst FULL_URL = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i;\n/** A Windows drive path (`C:/...`), which Git Bash/MSYS makes from a `/repo/` argument. */\nconst WINDOWS_PATH = /^[A-Za-z]:[\\\\/]/;\n/** One path segment: unreserved characters, a few safe sub-delimiters and valid percent escapes. */\nconst SEGMENT = /^(?:[A-Za-z0-9._~@+-]|%[0-9A-Fa-f]{2})+$/;\n\n/**\n * Turns `--base` / `shareBase` into an `AppBase`. `''`, `.` and `./` are relative; a path\n * starting with `/` is absolute and always ends in `/`. Anything else throws `BAD_BASE`.\n */\nexport function normalizeBase(value: string): AppBase {\n if (value === '' || value === '.' || value === './') return { kind: 'relative' };\n if (WINDOWS_PATH.test(value)) {\n throw new PackError(\n 'BAD_BASE',\n `The app base \"${value}\" is a Windows path. Your shell (Git Bash/MSYS) probably converted a site path such as /repo/ into it. Run the command with MSYS_NO_PATHCONV=1, or from an npm script, and pass the site path, for example --base /app/.`\n );\n }\n if (FULL_URL.test(value)) {\n throw new PackError(\n 'BAD_BASE',\n `The app base \"${value}\" is a full URL. Pass the path your HTML is served from, for example --base /app/ (shareBase in the Vite plugin).`\n );\n }\n const notASitePath = () =>\n new PackError(\n 'BAD_BASE',\n `The app base \"${value}\" is not a site path. Use \"/\", \"/app/\" or \"./\".`\n );\n if (!value.startsWith('/')) throw notASitePath();\n const segments = value.slice(1).split('/');\n if (segments[segments.length - 1] === '') segments.pop();\n for (const segment of segments) {\n if (segment === '.' || segment === '..' || !SEGMENT.test(segment)) throw notASitePath();\n }\n return { kind: 'absolute', path: segments.length === 0 ? '/' : `/${segments.join('/')}/` };\n}\n\n/** The manifest's `basePath`: the absolute path, or null for a relative base. */\nexport function basePathOf(base: AppBase): string | null {\n return base.kind === 'absolute' ? base.path : null;\n}\n\n/** The `src` of the flag tag for the HTML file at `htmlRelPath` (POSIX, relative to the output root). */\nexport function flagSrcFor(base: AppBase, htmlRelPath: string): string {\n const file = `${SHARE_DIR}/${SHARE_FLAG_FILE}`;\n if (base.kind === 'absolute') return `${base.path}${file}`;\n const depth = htmlRelPath.split('/').length - 1;\n return depth === 0 ? `./${file}` : `${'../'.repeat(depth)}${file}`;\n}\n\n/** The share link's path: `<base>livelocale/index.html`, relative when the base is. */\nexport function shareUrlPathFor(base: AppBase): string {\n const page = `${SHARE_DIR}/index.html`;\n return base.kind === 'absolute' ? `${base.path}${page}` : page;\n}\n\nconst EXISTING_FLAG_TAG = /<script\\b[^>]*\\bsrc=\"[^\"]*livelocale\\/flag\\.js\"[^>]*>\\s*<\\/script>/gi;\nconst HEAD_OPEN = /<head(?=[\\s>])[^>]*>/i;\nconst HTML_OPEN = /<html(?=[\\s>])[^>]*>/i;\nconst DOCTYPE = /<!doctype\\b[^>]*>/i;\n\nfunction escapeAttribute(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n\n/**\n * Puts `<script src=\"…\"></script>` first in `<head>`, replacing any earlier flag tag, so running\n * it twice changes nothing. Without a `<head>` it goes after `<html…>`, then after the doctype,\n * then at the start.\n */\nexport function injectFlagScript(html: string, src: string): string {\n const tag = `<script src=\"${escapeAttribute(src)}\"></script>`;\n const cleaned = html.replace(EXISTING_FLAG_TAG, '');\n for (const pattern of [HEAD_OPEN, HTML_OPEN, DOCTYPE]) {\n const match = pattern.exec(cleaned);\n if (match) {\n const end = match.index + match[0].length;\n return cleaned.slice(0, end) + tag + cleaned.slice(end);\n }\n }\n return tag + cleaned;\n}\n"],"mappings":"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","names":["path","fs","fs","path","path","fs","fs","path","resolve","reject","existsSync","readFileSync","fs","path","execFile","readFileSync","path","resolve","execFile","readFileSync","existsSync","readFileSync","path","readFileSync","existsSync","path","relative","fs","existsSync"]}