@walkeros/cli 4.9.0 → 4.10.0-next-1794522704623

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/validate/index.ts","../src/core/cli-logger.ts","../src/core/timer.ts","../src/core/output.ts","../src/core/tmp.ts","../src/config/utils.ts","../src/core/fetch-failure.ts","../src/core/utils.ts","../src/core/local-packages.ts","../src/core/input-detector.ts","../src/core/stdin.ts","../src/core/event-validation.ts","../src/config/validators.ts","../src/config/loader.ts","../src/core/content-digest.ts","../src/config/window-collector.ts","../src/commands/validate/validators/contract.ts","../src/commands/validate/validators/event.ts","../src/commands/validate/validators/flow.ts","../src/commands/validate/scope.ts","../src/commands/validate/validators/resolve.ts","../src/commands/validate/validators/settings.ts","../src/core/resolve-export-name.ts","../src/core/step-packages.ts","../src/core/package-info.ts","../src/commands/validate/validators/mapping.ts","../src/commands/validate/validators/entry.ts","../src/commands/validate/types.ts"],"sourcesContent":["// walkerOS/packages/cli/src/commands/validate/index.ts\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport chalk from 'chalk';\nimport { isObject } from '@walkeros/core';\nimport { createCLILogger } from '../../core/cli-logger.js';\nimport {\n getErrorMessage,\n isStdinPiped,\n readStdin,\n writeResult,\n} from '../../core/index.js';\nimport { loadJsonConfig } from '../../config/index.js';\nimport {\n validateContract,\n validateEvent,\n validateMapping,\n} from './validators/index.js';\nimport { validateEntry } from './validators/entry.js';\nimport { isFlowJson } from './scope.js';\nimport { validateFlowWithPackages } from './validators/flow.js';\nimport type { ValidateOptions } from '../../schemas/validate.js';\nimport {\n USAGE_CODES,\n type ValidateCheck,\n type ValidateCommandOptions,\n type ValidateResult,\n type ValidationError,\n type ValidationType,\n} from './types.js';\n\n/**\n * Options that do not apply to the validation type: `--path` and `--flow`\n * address flow files only, `--offline` removes the package settings check\n * of a whole-file flow run. Never ignored silently (OPTION_NOT_APPLICABLE).\n */\nfunction inapplicableOptions(\n type: ValidationType,\n options: ValidateOptions,\n): ValidationError[] {\n const errors: ValidationError[] = [];\n if (options.offline === true && (type !== 'flow' || options.path)) {\n errors.push({\n path: 'options.offline',\n message:\n type === 'flow'\n ? '--offline removes the package settings check, which is all --path checks; drop one of them'\n : `--offline applies to a whole flow file and does not apply to -t ${type}`,\n code: 'OPTION_NOT_APPLICABLE',\n });\n }\n if (type === 'flow') return errors;\n if (options.path !== undefined) {\n errors.push({\n path: 'options.path',\n message: `--path addresses an entry in a flow file and does not apply to -t ${type}`,\n code: 'OPTION_NOT_APPLICABLE',\n });\n }\n if (options.flow !== undefined) {\n errors.push({\n path: 'options.flow',\n message: `--flow names a flow in a flow file and does not apply to -t ${type}`,\n code: 'OPTION_NOT_APPLICABLE',\n });\n }\n return errors;\n}\n\n/**\n * `-t contract` on a flow file validates its `contract` section, exactly as\n * on the section alone (M-dispatch).\n */\nfunction validateContractInput(input: unknown): ValidateResult {\n if (!isFlowJson(input)) return validateContract(input);\n const contract = isObject(input) ? input.contract : undefined;\n if (contract === undefined) {\n return {\n valid: false,\n type: 'contract',\n errors: [\n {\n path: 'contract',\n message: 'The flow file has no contract section to validate',\n code: 'NO_CONTRACT_SECTION',\n },\n ],\n warnings: [],\n details: {},\n };\n }\n return validateContract(contract);\n}\n\n/**\n * The directory a local `path` package resolves against: the input file's,\n * when the input is a file on disk. Otherwise none, and no disk reads.\n */\nfunction configDirOf(input: unknown): string | undefined {\n if (typeof input !== 'string') return undefined;\n const candidate = path.resolve(input);\n return fs.existsSync(candidate) && fs.statSync(candidate).isFile()\n ? path.dirname(candidate)\n : undefined;\n}\n\n/** Whole-document validations state their one check as their scope. */\nfunction withDocumentScope(\n result: ValidateResult,\n check: ValidateCheck,\n): ValidateResult {\n return {\n ...result,\n details: {\n ...result.details,\n scope: result.details.scope ?? { flows: [], checks: [check] },\n skipped: result.details.skipped ?? [],\n },\n };\n}\n\n/**\n * Programmatic API for validation.\n * Can be called directly from code or MCP server.\n *\n * Accepts parsed objects, JSON strings, file paths, or URLs as input.\n */\nexport async function validate(\n type: ValidationType,\n input: unknown,\n options: ValidateOptions = {},\n): Promise<ValidateResult> {\n // Resolve string inputs (file paths, URLs, JSON strings) to parsed objects.\n // A path that cannot be read or parsed is an input error, never a document.\n let resolved = input;\n if (typeof input === 'string') {\n if (input.trim() === '') throw new Error(`${type} is required`);\n resolved = await loadJsonConfig(input);\n }\n\n const misuse = inapplicableOptions(type, options);\n if (misuse.length > 0) {\n return {\n valid: false,\n type,\n errors: misuse,\n warnings: [],\n details: { scope: { flows: [], checks: [] }, skipped: [] },\n };\n }\n\n const result = await validateByType(type, input, resolved, options);\n return options.strict === true ? escalateStrict(result) : result;\n}\n\n/**\n * `strict` turns warnings and skipped checks into failures (C4), in every\n * entry point (C9): the result is invalid, with the findings unchanged.\n */\nfunction escalateStrict(result: ValidateResult): ValidateResult {\n const skipped = result.details.skipped ?? [];\n if (result.warnings.length === 0 && skipped.length === 0) return result;\n return { ...result, valid: false };\n}\n\nasync function validateByType(\n type: ValidationType,\n input: unknown,\n resolved: unknown,\n options: ValidateOptions,\n): Promise<ValidateResult> {\n switch (type) {\n case 'contract':\n return withDocumentScope(validateContractInput(resolved), 'contract');\n case 'event':\n return withDocumentScope(validateEvent(resolved), 'event');\n case 'flow':\n if (options.path !== undefined) {\n return validateEntry(options.path, resolved, {\n flow: options.flow,\n configDir: options.configDir ?? configDirOf(input),\n });\n }\n return validateFlowWithPackages(resolved, {\n flow: options.flow,\n strict: options.strict,\n offline: options.offline,\n configDir: options.configDir ?? configDirOf(input),\n });\n case 'mapping':\n return withDocumentScope(validateMapping(resolved), 'mapping');\n default:\n throw new Error(`Unknown validation type: ${type}`);\n }\n}\n\n/** One line naming what the run covered (C6). */\nfunction formatScope(result: ValidateResult): string | undefined {\n const scope = result.details.scope;\n if (!scope) return undefined;\n const parts: string[] = [];\n if (scope.flows.length > 0) parts.push(`flows ${scope.flows.join(', ')}`);\n if (scope.entry) {\n const { section, key, flows, searchedFlows } = scope.entry;\n const name = section ? `${section}.${key}` : key;\n parts.push(\n `entry ${name} in ${flows.join(', ') || 'no flow'} (searched ${searchedFlows.join(', ') || 'none'})`,\n );\n }\n parts.push(`${scope.checks.length} check(s): ${scope.checks.join(', ')}`);\n if (scope.offline) parts.push('offline: package settings not checked');\n return `Scope: ${parts.join('; ')}`;\n}\n\n/**\n * Format validation result for CLI output.\n */\nfunction formatResult(\n result: ValidateResult,\n options: { json?: boolean; verbose?: boolean; exitCode: number },\n): string {\n if (options.json) {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push('');\n lines.push(`Validating ${result.type}...`);\n lines.push('');\n\n const scopeLine = formatScope(result);\n if (scopeLine) {\n lines.push(scopeLine);\n lines.push('');\n }\n\n // Show details if verbose\n if (options.verbose && Object.keys(result.details).length > 0) {\n lines.push('Details:');\n for (const [key, value] of Object.entries(result.details)) {\n lines.push(` ${key}: ${JSON.stringify(value)}`);\n }\n lines.push('');\n }\n\n lines.push('Validation Results:');\n\n // Errors\n for (const error of result.errors) {\n lines.push(chalk.red(` ✗ ${error.path}: ${error.message}`));\n }\n\n // Warnings\n for (const warning of result.warnings) {\n lines.push(chalk.yellow(` ⚠ ${warning.path}: ${warning.message}`));\n if (warning.suggestion) {\n lines.push(chalk.gray(` → ${warning.suggestion}`));\n }\n }\n\n // Skips are listed whatever the verbosity (C5)\n const skipped = result.details.skipped ?? [];\n for (const skip of skipped) {\n lines.push(\n chalk.gray(` - skipped ${skip.check} at ${skip.path}: ${skip.reason}`),\n );\n }\n\n // The verdict line follows the exit code: a run that fails under\n // --strict (exit 2) never reads as passed.\n if (options.exitCode === 2) {\n lines.push(\n chalk.red(\n ` ✗ Failed under --strict: ${result.warnings.length} warning(s) and ${skipped.length} skipped check(s) count as failures`,\n ),\n );\n } else if (result.valid && skipped.length === 0) {\n lines.push(chalk.green(` ✓ All checks passed`));\n } else if (result.valid) {\n lines.push(\n chalk.green(\n ` ✓ No errors in checked scope; ${skipped.length} check(s) skipped`,\n ),\n );\n }\n\n lines.push('');\n lines.push(\n `Summary: ${result.errors.length} error(s), ${result.warnings.length} warning(s), ${skipped.length} skipped`,\n );\n\n return lines.join('\\n');\n}\n\n/**\n * CLI command handler for validate command.\n */\nexport async function validateCommand(\n options: ValidateCommandOptions,\n): Promise<void> {\n // Result always goes to stdout; logs to stderr\n const logger = createCLILogger({ ...options, stderr: true });\n\n let exitCode: number;\n try {\n // Load input: stdin > argument > error\n let input: unknown;\n if (isStdinPiped() && !options.input) {\n const stdinContent = await readStdin();\n try {\n input = JSON.parse(stdinContent);\n } catch {\n throw new Error('Invalid JSON received on stdin');\n }\n } else {\n input = options.input;\n }\n\n // Run validation\n const result = await validate(options.type, input, {\n flow: options.flow,\n path: options.path,\n strict: options.strict,\n offline: options.offline,\n });\n\n exitCode = exitCodeOf(result, options.strict === true);\n\n // Format and write result; --silent suppresses stdout, never a file\n if (!options.silent || options.output) {\n const formatted = formatResult(result, {\n json: options.json,\n verbose: options.verbose,\n exitCode,\n });\n await writeResult(formatted + '\\n', { output: options.output });\n }\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n\n if (options.json) {\n const errorOutput = JSON.stringify(\n {\n valid: false,\n type: options.type,\n errors: [\n { path: 'input', message: errorMessage, code: 'INPUT_ERROR' },\n ],\n warnings: [],\n details: { scope: { flows: [], checks: [] }, skipped: [] },\n },\n null,\n 2,\n );\n await writeResult(errorOutput + '\\n', { output: options.output });\n } else {\n logger.error(`Error: ${errorMessage}`);\n }\n\n exitCode = 3;\n }\n process.exit(exitCode);\n}\n\n/**\n * Exit code (C7): 3 could not run, 1 errors, 2 warnings or skips under\n * --strict, 0 valid.\n */\nfunction exitCodeOf(result: ValidateResult, strict: boolean): number {\n if (\n result.errors.some((e) =>\n USAGE_CODES.some((usageCode) => usageCode === e.code),\n )\n )\n return 3;\n if (result.errors.length > 0) return 1;\n const skipped = result.details.skipped ?? [];\n if (strict && (result.warnings.length > 0 || skipped.length > 0)) return 2;\n return result.valid ? 0 : 1;\n}\n\n// Re-export types\nexport * from './types.js';\nexport {\n validateContract,\n validateEvent,\n validateFlow,\n validateMapping,\n} from './validators/index.js';\nexport { validateEntry } from './validators/entry.js';\n","import chalk from 'chalk';\nimport {\n createCLILogger as createCoreCLILogger,\n createCLILoggerConfig as createCoreCLILoggerConfig,\n type CLILoggerColors,\n type CLILoggerOptions,\n} from '@walkeros/core/node';\nimport type { Logger } from '@walkeros/core';\n\nexport type { CLILoggerOptions };\n\n/**\n * The CLI's terminal colours. Binding them here matters: the shared factory in\n * `@walkeros/core/node` defaults every level to identity, so a plain re-export\n * would silently drop red ERROR output at every CLI call site.\n */\nconst CLI_COLORS: CLILoggerColors = { error: chalk.red };\n\n/**\n * Build the `Logger.Config` backing the CLI logger, with the CLI's colours.\n * See `createCLILoggerConfig` in `@walkeros/core/node` for the handler contract.\n */\nexport function createCLILoggerConfig(\n options: CLILoggerOptions = {},\n): Logger.Config {\n return createCoreCLILoggerConfig(options, CLI_COLORS);\n}\n\n/**\n * Create a core Logger.Instance with CLI-appropriate behavior.\n *\n * Behavior:\n * - ERROR: always shown (chalk red, via console.error) unless --json\n * - WARN: shown unless --silent or --json\n * - INFO: shown unless --silent or --json\n * - DEBUG: shown only with --verbose (and not --silent/--json)\n * - json(): shown unless --silent\n */\nexport function createCLILogger(\n options: CLILoggerOptions = {},\n): Logger.Instance {\n return createCoreCLILogger(options, CLI_COLORS);\n}\n","export { createTimer, type Timer } from '@walkeros/core';\n","/**\n * Output formatting utilities for CLI commands\n */\n\nimport fs from 'fs-extra';\nimport path from 'path';\n\n/**\n * Write command result to stdout or file.\n * Used by all commands to implement -o/--output flag.\n */\nexport async function writeResult(\n content: string | Buffer,\n options: { output?: string },\n): Promise<void> {\n if (options.output) {\n const outputPath = path.resolve(options.output);\n await fs.ensureDir(path.dirname(outputPath));\n await fs.writeFile(outputPath, content);\n } else {\n process.stdout.write(content);\n process.stdout.write('\\n');\n }\n}\n\nexport interface JsonOutput<T = Record<string, unknown>> {\n success: boolean;\n data?: T;\n error?: string;\n duration?: number;\n}\n\n/**\n * Create standardized JSON output for CLI commands\n */\nexport function createJsonOutput<T = Record<string, unknown>>(\n success: boolean,\n data?: T,\n error?: string,\n duration?: number,\n): JsonOutput<T> {\n return {\n success,\n ...(data && { data }),\n ...(error && { error }),\n ...(duration && { duration }),\n };\n}\n\n/**\n * Create success JSON output\n */\nexport function createSuccessOutput<T = Record<string, unknown>>(\n data?: T,\n duration?: number,\n): JsonOutput<T> {\n return createJsonOutput(true, data, undefined, duration);\n}\n\n/**\n * Create error JSON output\n */\nexport function createErrorOutput(\n error: string,\n duration?: number,\n): JsonOutput<never> {\n return createJsonOutput<never>(false, undefined, error, duration);\n}\n\n/**\n * Format bytes to KB with 2 decimal places\n */\nexport function formatBytes(bytes: number): string {\n return (bytes / 1024).toFixed(2);\n}\n\n/**\n * Format file size with appropriate unit\n */\nexport function formatFileSize(bytes: number): string {\n const units = ['B', 'KB', 'MB', 'GB'];\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(2)} ${units[unitIndex]}`;\n}\n","/**\n * Temp path helpers live in `@walkeros/core/node`, shared with the runtime.\n * All temp files go to os.tmpdir() by default.\n */\nexport {\n getTmpPath,\n createTmpResolver,\n getDefaultTmpRoot,\n type TmpResolver,\n} from '@walkeros/core/node';\n","/**\n * Configuration Utility Functions\n */\n\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { fetchFailureReason } from '../core/fetch-failure.js';\n\n/**\n * Check if a string is a valid URL\n *\n * @param str - String to check\n * @returns True if string is a valid HTTP/HTTPS URL\n */\nexport function isUrl(str: string): boolean {\n try {\n const url = new URL(str);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/** A config input named an http(s) URL that could not be read. */\nexport class ConfigFetchError extends Error {\n readonly url: string;\n /** The HTTP status, when the host answered. */\n readonly status?: number;\n\n constructor(message: string, url: string, status?: number) {\n super(message);\n this.name = 'ConfigFetchError';\n this.url = url;\n this.status = status;\n }\n}\n\n/**\n * Fetch content from a URL as a string.\n * Shared helper for all URL-loading paths.\n *\n * A plain GET with no credential for every host: a config input is any URL\n * the person names, and a link to a saved flow carries its own signature.\n *\n * @param url - HTTP/HTTPS URL to fetch\n * @returns Response body as a string\n * @throws ConfigFetchError if the host cannot be reached or answers non-OK\n */\nexport async function fetchContentString(url: string): Promise<string> {\n let response: Response;\n try {\n response = await fetch(url);\n } catch (error) {\n throw new ConfigFetchError(\n `Could not reach ${url}: ${fetchFailureReason(error)}`,\n url,\n );\n }\n if (!response.ok) {\n throw new ConfigFetchError(\n `Failed to fetch ${url}: ${response.status} ${response.statusText}`.trimEnd(),\n url,\n response.status,\n );\n }\n return response.text();\n}\n\n/**\n * Substitute environment variables in a string.\n *\n * @param value - String with ${VAR} placeholders\n * @returns String with environment variables substituted\n * @throws Error if environment variable is not found\n *\n * @example\n * ```typescript\n * substituteEnvVariables('${HOME}/config') // \"/Users/name/config\"\n * ```\n */\nexport function substituteEnvVariables(value: string): string {\n return value.replace(/\\${([^}]+)}/g, (_, envVar) => {\n const envValue = process.env[envVar];\n if (!envValue) {\n throw new Error(`Environment variable ${envVar} not found`);\n }\n return envValue;\n });\n}\n\n/**\n * Resolve raw string content from a URL, file path, or inline string.\n *\n * Detection priority:\n * 1. URL (http://, https://) — download content\n * 2. Existing file path — read file content\n * 3. Inline string — return as-is\n *\n * @param input - URL, file path, or inline string\n * @returns Raw string content and the resolved absolute path (if file-based)\n * @throws Error if file not found or download fails\n */\nasync function resolveContent(input: string): Promise<string> {\n const trimmed = input.trim();\n\n // 1. Check if input is a URL\n if (isUrl(trimmed)) {\n return fetchContentString(trimmed);\n }\n\n // 2. Check if file path exists\n const absolutePath = path.resolve(trimmed);\n if (await fs.pathExists(absolutePath)) {\n return fs.readFile(absolutePath, 'utf-8');\n }\n\n // 3. Inline content — return as-is\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return trimmed;\n }\n\n // 4. Nothing matched — file not found\n throw new Error(`Configuration file not found: ${absolutePath}`);\n}\n\n/**\n * Load configuration from a file path, URL, or inline string.\n *\n * Supports two modes:\n * - `json: true` (default) — resolves content and parses as JSON\n * - `json: false` — resolves content and returns raw string\n *\n * Detection priority:\n * 1. URL (http://, https://) — download content\n * 2. Existing file path — read file content\n * 3. Inline string (starting with { or [) — use directly\n *\n * @param input - Path to file, HTTP/HTTPS URL, or inline string\n * @param options - Optional settings\n * @param options.json - Parse as JSON (default: true)\n * @returns Parsed object (json: true) or raw string (json: false)\n * @throws Error if file not found, download fails, or invalid JSON\n *\n * @example\n * ```typescript\n * // JSON mode (default) — same as loadJsonConfig\n * const config = await loadConfig('./config.json')\n * const config = await loadConfig('https://example.com/config.json')\n * const config = await loadConfig('{\"version\":3,\"flows\":{}}')\n *\n * // Raw string mode — returns file/URL content as string\n * const code = await loadConfig('./bundle.js', { json: false })\n * ```\n */\nexport async function loadConfig<T = unknown>(\n input: string,\n options?: { json?: boolean },\n): Promise<T | string> {\n const json = options?.json !== false; // default true\n\n if (!json) {\n return resolveContent(input);\n }\n\n // JSON mode — resolve content then parse\n const trimmed = input.trim();\n\n try {\n const content = await resolveContent(trimmed);\n\n // Parse the resolved content as JSON\n return JSON.parse(content) as T;\n } catch (error) {\n // Distinguish between JSON parse errors and resolution errors\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Re-throw resolution errors (a URL that could not be read, a file not\n // found) as-is\n if (error instanceof ConfigFetchError || errorMessage.includes('not found'))\n throw error;\n\n // JSON-looking inline content with parse errors\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n throw new Error(\n `Input appears to be JSON but contains errors: ${errorMessage}`,\n );\n }\n\n // File or URL with invalid JSON content\n throw new Error(`Invalid JSON in config file: ${input}. ${errorMessage}`);\n }\n}\n\n/**\n * Load and parse JSON configuration from a file path, URL, or inline JSON string.\n *\n * Thin wrapper around `loadConfig` with `json: true` (default).\n *\n * Detection priority:\n * 1. URL (http://, https://) — download and parse\n * 2. Existing file path — read and parse\n * 3. Inline JSON string (starting with { or [) — parse directly\n *\n * @param configPath - Path to JSON file, HTTP/HTTPS URL, or inline JSON string\n * @returns Parsed configuration object\n * @throws Error if file not found, download fails, or invalid JSON\n *\n * @example\n * ```typescript\n * // Local file\n * const config = await loadJsonConfig('./config.json')\n *\n * // Remote URL\n * const config = await loadJsonConfig('https://example.com/config.json')\n *\n * // Inline JSON\n * const config = await loadJsonConfig('{\"version\":3,\"flows\":{}}')\n * ```\n */\nexport async function loadJsonConfig<T>(configPath: string): Promise<T> {\n return loadConfig<T>(configPath, { json: true }) as Promise<T>;\n}\n\n/**\n * Load JSON from inline string, file path, or URL.\n *\n * Supports three input formats:\n * 1. Inline JSON string - parsed directly\n * 2. Local file path - read and parsed\n * 3. HTTP/HTTPS URL - downloaded and parsed\n *\n * Detection priority:\n * 1. URL (http://, https://) → download and parse\n * 2. Existing file path → read and parse\n * 3. Valid JSON string → parse directly\n * 4. Simple string → treat as {name: string} for backward compatibility\n *\n * @param source - JSON string, file path, or URL\n * @param options - Optional configuration\n * @param options.name - Parameter name for error messages (e.g., \"event\", \"config\")\n * @param options.required - Throw error if source is empty (default: false)\n * @param options.fallback - Default value if source is empty\n * @returns Parsed JSON object\n * @throws Error if source is required but empty, or if loading/parsing fails\n *\n * @example\n * ```typescript\n * // Inline JSON\n * await loadJsonFromSource('{\"name\":\"order complete\",\"data\":{}}')\n *\n * // File path\n * await loadJsonFromSource('./examples/event.json')\n *\n * // URL\n * await loadJsonFromSource('https://example.com/event.json')\n *\n * // With options\n * await loadJsonFromSource(input, {\n * name: 'event',\n * required: true,\n * fallback: { name: 'default' }\n * })\n * ```\n */\nexport async function loadJsonFromSource<T = unknown>(\n source: string | undefined,\n options?: {\n name?: string;\n required?: boolean;\n fallback?: T;\n },\n): Promise<T> {\n const paramName = options?.name || 'input';\n\n // 1. Handle empty/undefined input (pre-check)\n if (!source || source.trim() === '') {\n if (options?.required) throw new Error(`${paramName} is required`);\n if (options?.fallback !== undefined) return options.fallback;\n return {} as T;\n }\n\n // 2. Try the strict loader (handles URL, file, inline JSON)\n try {\n return await loadJsonConfig<T>(source);\n } catch (error) {\n const trimmed = source.trim();\n\n // 3. Not JSON-looking? Treat as event name (backward compat)\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return { name: trimmed } as T;\n }\n\n // 4. JSON-looking but invalid -- re-throw with context\n throw new Error(\n `Failed to parse ${paramName}. ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n","function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Why a request did not reach its host: `timeout after <ms> ms`, or the\n * network error with its code, such as `fetch failed (ECONNREFUSED)`. Read by\n * shape, not instanceof: an abort's DOMException can come from another realm.\n */\nexport function fetchFailureReason(error: unknown, timeoutMs?: number): string {\n if (!isRecord(error)) return String(error);\n if (error.name === 'TimeoutError')\n return timeoutMs === undefined\n ? 'timeout'\n : `timeout after ${timeoutMs} ms`;\n const message =\n typeof error.message === 'string' ? error.message : String(error);\n const cause = error.cause;\n const code =\n isRecord(cause) && typeof cause.code === 'string' ? cause.code : undefined;\n return code ? `${message} (${code})` : message;\n}\n","export { getErrorMessage } from '@walkeros/core';\n","import path from 'path';\nimport fs from 'fs-extra';\nimport type { Logger } from '@walkeros/core';\n\nexport type LocalPackageInfo =\n | { name: string; absolutePath: string; type: 'file' }\n | { name: string; absolutePath: string; type: 'directory' }\n | {\n name: string;\n absolutePath: string;\n type: 'package';\n distPath: string;\n hasDistFolder: boolean;\n };\n\n/**\n * Resolve and validate a local package path.\n *\n * Handles three cases:\n * 1. Single file (with or without extension) → type: 'file'\n * 2. Directory without package.json → type: 'directory'\n * 3. Directory with package.json → type: 'package' (existing behavior)\n */\nexport async function resolveLocalPackage(\n packageName: string,\n localPath: string,\n configDir: string,\n logger: Logger.Instance,\n): Promise<LocalPackageInfo> {\n // Resolve relative to config file directory\n const absolutePath = path.isAbsolute(localPath)\n ? localPath\n : path.resolve(configDir, localPath);\n\n const stat = await fs.stat(absolutePath).catch(() => null);\n\n // Case 1a: Direct file reference (e.g., ./src/decoder.ts)\n if (stat?.isFile()) {\n return { name: packageName, absolutePath, type: 'file' };\n }\n\n // Case 1b: Try with extensions (e.g., ./src/decoder → ./src/decoder.ts)\n if (!stat) {\n for (const ext of ['.ts', '.mjs', '.js', '.json']) {\n const withExt = absolutePath + ext;\n if (await fs.pathExists(withExt)) {\n return { name: packageName, absolutePath: withExt, type: 'file' };\n }\n }\n throw new Error(\n `Local package path not found: ${localPath} (resolved to ${absolutePath})`,\n );\n }\n\n // Path is a directory\n if (stat.isDirectory()) {\n const hasPkgJson = await fs.pathExists(\n path.join(absolutePath, 'package.json'),\n );\n\n if (hasPkgJson) {\n // Case 3: Full package (existing behavior)\n const distPath = path.join(absolutePath, 'dist');\n const hasDistFolder = await fs.pathExists(distPath);\n\n if (!hasDistFolder) {\n logger.warn(\n `⚠️ ${packageName}: No dist/ folder found. Using package root.`,\n );\n }\n\n return {\n name: packageName,\n absolutePath,\n type: 'package',\n distPath: hasDistFolder ? distPath : absolutePath,\n hasDistFolder,\n };\n }\n\n // Case 2: Directory without package.json\n return { name: packageName, absolutePath, type: 'directory' };\n }\n\n throw new Error(\n `Local package path not found: ${localPath} (resolved to ${absolutePath})`,\n );\n}\n\n/**\n * Copy local package to target node_modules directory.\n *\n * Handles three cases:\n * - file: Copy as index.{ext} with generated package.json\n * - directory: Copy contents with generated package.json\n * - package: Existing behavior (copy package.json + dist/)\n */\nexport async function copyLocalPackage(\n localPkg: LocalPackageInfo,\n targetDir: string,\n logger: Logger.Instance,\n): Promise<string> {\n const packageDir = path.join(targetDir, 'node_modules', localPkg.name);\n\n if (localPkg.type === 'file') {\n await fs.ensureDir(packageDir);\n\n // Copy the single file as index (esbuild will resolve it)\n const ext = path.extname(localPkg.absolutePath);\n await fs.copy(localPkg.absolutePath, path.join(packageDir, `index${ext}`));\n\n // Create minimal package.json for module resolution\n await fs.writeJson(path.join(packageDir, 'package.json'), {\n name: localPkg.name,\n main: `./index${ext}`,\n });\n\n logger.info(\n `📦 Using local file: ${localPkg.name} from ${localPkg.absolutePath}`,\n );\n return packageDir;\n }\n\n if (localPkg.type === 'directory') {\n await fs.ensureDir(path.dirname(packageDir));\n\n // Copy directory contents (excluding node_modules etc.)\n const entries = await fs.readdir(localPkg.absolutePath);\n for (const entry of entries) {\n if (!['node_modules', '.turbo', '.git'].includes(entry)) {\n await fs.copy(\n path.join(localPkg.absolutePath, entry),\n path.join(packageDir, entry),\n );\n }\n }\n\n // Generate minimal package.json for module resolution\n await fs.writeJson(path.join(packageDir, 'package.json'), {\n name: localPkg.name,\n main: './index.ts',\n });\n\n logger.info(\n `📦 Using local dir: ${localPkg.name} from ${localPkg.absolutePath}`,\n );\n return packageDir;\n }\n\n // type === 'package': Existing behavior\n await fs.ensureDir(path.dirname(packageDir));\n\n // Always copy package.json for module resolution\n await fs.copy(\n path.join(localPkg.absolutePath, 'package.json'),\n path.join(packageDir, 'package.json'),\n );\n\n // Copy dist folder AS dist folder (preserving structure for exports like ./dist/index.mjs)\n if (localPkg.hasDistFolder) {\n await fs.copy(localPkg.distPath, path.join(packageDir, 'dist'));\n } else {\n // No dist folder - copy package root contents (excluding node_modules, etc.)\n const entries = await fs.readdir(localPkg.absolutePath);\n for (const entry of entries) {\n if (!['node_modules', '.turbo', '.git'].includes(entry)) {\n await fs.copy(\n path.join(localPkg.absolutePath, entry),\n path.join(packageDir, entry),\n );\n }\n }\n }\n\n logger.info(`📦 Using local: ${localPkg.name} from ${localPkg.absolutePath}`);\n\n return packageDir;\n}\n","/**\n * Input Detector\n *\n * Detects whether CLI input is a config JSON or pre-built bundle.\n * Supports both local files and URLs.\n */\n\nimport fs from 'fs-extra';\nimport { fetchContentString, isUrl } from '../config/utils.js';\n\nexport type Platform = 'web' | 'server';\n\nexport type DetectedInput =\n | { type: 'config'; content: string }\n | { type: 'bundle'; content: string; platform: Platform };\n\n/**\n * Detect if input is config JSON or pre-built bundle.\n *\n * Detection: Try JSON.parse, if fails = bundle\n * Platform: .mjs = server, .js = web\n *\n * @param inputPath - Path to file or URL\n * @param platformOverride - Optional platform override\n * @returns Detected input type with content\n */\nexport async function detectInput(\n inputPath: string,\n platformOverride?: Platform,\n): Promise<DetectedInput> {\n // Load content (URL or local file)\n const content = await loadContent(inputPath);\n\n // Try parsing as JSON\n try {\n JSON.parse(content);\n return { type: 'config', content };\n } catch {\n // Not JSON, treat as bundle\n const platform = platformOverride ?? detectPlatformFromPath(inputPath);\n return { type: 'bundle', content, platform };\n }\n}\n\n/**\n * Detect platform from file extension.\n *\n * @param inputPath - Path to file or URL\n * @returns Platform based on extension (.mjs = server, .js = web)\n */\nexport function detectPlatformFromPath(inputPath: string): Platform {\n // Remove query params for URLs\n const cleanPath = inputPath.split('?')[0];\n return cleanPath.endsWith('.mjs') ? 'server' : 'web';\n}\n\n/**\n * Load content from URL or local file.\n *\n * @param inputPath - Path to file or URL\n * @returns File content as string\n */\nasync function loadContent(inputPath: string): Promise<string> {\n if (isUrl(inputPath)) {\n return fetchContentString(inputPath);\n }\n // Inline JSON passthrough: JS bundles never start with { or [\n const trimmed = inputPath.trim();\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return trimmed;\n }\n\n return fs.readFile(inputPath, 'utf8');\n}\n","import fs from 'fs-extra';\nimport path from 'path';\nimport { getTmpPath } from './tmp.js';\n\nexport function isStdinPiped(): boolean {\n return !process.stdin.isTTY;\n}\n\nexport async function readStdin(\n stream: NodeJS.ReadableStream = process.stdin,\n): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n // A stream may yield strings as well as buffers.\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n const content = Buffer.concat(chunks).toString('utf-8');\n if (!content.trim()) {\n throw new Error('No input received on stdin');\n }\n return content;\n}\n\n/**\n * Read stdin content and write to a labeled temp file.\n * Returns the temp file path for downstream functions that expect file paths.\n */\nexport async function readStdinToTempFile(label: string): Promise<string> {\n const content = await readStdin();\n const tmpPath = getTmpPath(undefined, `stdin-${label}.json`);\n await fs.ensureDir(path.dirname(tmpPath));\n await fs.writeFile(tmpPath, content, 'utf-8');\n return tmpPath;\n}\n","import { schemas } from '@walkeros/core/dev';\n\nconst { PartialEventSchema } = schemas;\n\nexport type ValidationLevel = 'strict' | 'standard' | 'minimal';\n\nexport type EventValidationCode =\n | 'NOT_AN_OBJECT'\n | 'MISSING_EVENT_NAME'\n | 'EMPTY_EVENT_NAME'\n | 'INVALID_EVENT_NAME'\n | 'SCHEMA_VALIDATION';\n\nexport interface EventValidationError {\n path: string;\n message: string;\n value?: unknown;\n code: EventValidationCode;\n}\n\nexport interface EventValidationWarning {\n path: string;\n message: string;\n suggestion?: string;\n}\n\nexport interface EventValidationDetails {\n entity?: string | null;\n action?: string | null;\n hasConsent?: boolean;\n hasData?: boolean;\n hasContext?: boolean;\n}\n\nexport interface EventValidationResult {\n valid: boolean;\n errors: EventValidationError[];\n warnings: EventValidationWarning[];\n details: EventValidationDetails;\n}\n\nexport function validateEvent(\n input: unknown,\n level: ValidationLevel = 'strict',\n): EventValidationResult {\n const errors: EventValidationError[] = [];\n const warnings: EventValidationWarning[] = [];\n const details: EventValidationDetails = {};\n\n // All levels: must be an object\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n errors.push({\n path: 'root',\n message: 'Event must be an object',\n code: 'NOT_AN_OBJECT',\n });\n return { valid: false, errors, warnings, details };\n }\n\n const event = input as Record<string, unknown>;\n\n // All levels: name field checks\n if (!('name' in event) || event.name === undefined) {\n errors.push({\n path: 'name',\n message: 'Event must have a name field',\n code: 'MISSING_EVENT_NAME',\n });\n } else if (typeof event.name !== 'string' || event.name.trim() === '') {\n errors.push({\n path: 'name',\n message: 'Event name cannot be empty',\n value: event.name,\n code: 'EMPTY_EVENT_NAME',\n });\n } else {\n // Entity-action format check (strict + standard only)\n const name = event.name;\n if (!name.includes(' ')) {\n if (level === 'strict') {\n errors.push({\n path: 'name',\n message:\n 'Event name must be \"entity action\" format with space (e.g., \"page view\")',\n value: name,\n code: 'INVALID_EVENT_NAME',\n });\n details.entity = null;\n details.action = null;\n } else if (level === 'standard') {\n warnings.push({\n path: 'name',\n message: `Event name \"${name}\" should follow \"ENTITY ACTION\" format (e.g., \"page view\")`,\n });\n details.entity = null;\n details.action = null;\n }\n // minimal: skip entirely\n } else {\n const parts = name.trim().split(/\\s+/);\n const action = parts.pop()!;\n const entity = parts.join(' ');\n details.entity = entity;\n details.action = action;\n }\n }\n\n // Minimal stops here\n if (level === 'minimal') {\n return { valid: errors.length === 0, errors, warnings, details };\n }\n\n // Standard + Strict: Zod schema validation\n const zodResult = PartialEventSchema.safeParse(input);\n if (!zodResult.success) {\n for (const issue of zodResult.error.issues) {\n const issuePath = issue.path.join('.');\n if (issuePath === 'name') continue; // handled above with better messages\n errors.push({\n path: issuePath || 'root',\n message: issue.message,\n code: 'SCHEMA_VALIDATION',\n });\n }\n }\n\n // Strict only: consent warning + details\n if (level === 'strict') {\n if (!event.consent) {\n warnings.push({\n path: 'consent',\n message: 'No consent object provided',\n suggestion:\n 'Consider adding a consent object for GDPR/privacy compliance',\n });\n }\n details.hasConsent = !!event.consent;\n details.hasData = !!event.data;\n details.hasContext = !!event.context;\n }\n\n return { valid: errors.length === 0, errors, warnings, details };\n}\n","/**\n * Configuration Type Guards and Validators\n *\n * Type checking utilities for configuration validation.\n * Uses Zod schemas from @walkeros/core for Flow.Json validation.\n */\n\nimport type { Flow } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\nimport { schemas } from '@walkeros/core/dev';\n\nconst { safeParseConfig } = schemas;\n\n/**\n * Detect platform from a single flow.\n *\n * Platform is determined by `flow.config.platform`.\n */\nexport function detectPlatform(\n flow: Record<string, unknown>,\n): 'web' | 'server' | undefined {\n const config = flow.config;\n if (!isObject(config)) return undefined;\n const platform = config.platform;\n if (platform === 'web' || platform === 'server') return platform;\n return undefined;\n}\n\n/**\n * Type guard: Check if config is a valid Flow.Json structure.\n *\n * @remarks\n * Uses Zod validation from @walkeros/core.\n * Returns false instead of throwing on invalid input.\n *\n * @example\n * ```typescript\n * if (isFlowConfig(config)) {\n * const flowSettings = getFlowSettings(config, 'production');\n * }\n * ```\n */\nexport function isFlowConfig(data: unknown): data is Flow.Json {\n const result = safeParseConfig(data);\n return result.success;\n}\n\n/**\n * Validate Flow.Json and throw descriptive error if invalid.\n *\n * @remarks\n * Uses Zod validation from @walkeros/core.\n * Provides detailed error messages from Zod.\n *\n * @param data - Raw configuration data\n * @returns Validated Flow.Json\n * @throws Error with descriptive message if validation fails\n */\nexport function validateFlowConfig(data: unknown): Flow.Json {\n // After successful Zod parse, the data conforms structurally to Flow.Json.\n // The existing `isFlowConfig` guard re-narrows `unknown` to `Flow.Json` via\n // the same schema — TS can't see through `safeParseConfig` because it\n // returns a Zod-inferred type that differs nominally (not structurally)\n // from the Flow.Json interface.\n if (isFlowConfig(data)) return data;\n\n // Not parseable — format Zod errors for CLI display.\n const result = safeParseConfig(data);\n if (result.success) {\n // Defensive: isFlowConfig and safeParseConfig disagreed (cannot happen).\n throw new Error('Invalid configuration: failed Flow.Json type guard.');\n }\n const errors = result.error.issues\n .map((issue) => {\n const path =\n issue.path.length > 0 ? issue.path.map(String).join('.') : 'root';\n return ` - ${path}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(`Invalid configuration:\\n${errors}`);\n}\n\n/**\n * Get available flow names from a Flow.Json.\n *\n * @param config - Flow.Json configuration\n * @returns Array of flow names\n */\nexport function getAvailableFlows(config: Flow.Json): string[] {\n return Object.keys(config.flows);\n}\n","/**\n * Configuration Loader\n *\n * Loads and parses Flow.Json configurations using core getFlowSettings().\n * Build options are determined by static platform defaults.\n */\n\nimport path from 'path';\nimport fs from 'fs-extra';\nimport { getFlowSettings, getPlatform, type Flow } from '@walkeros/core';\nimport { validateFlowStructure } from '@walkeros/core/dev';\nimport type { BuildOptions } from '../types/bundle.js';\nimport {\n validateFlowConfig,\n isFlowConfig,\n getAvailableFlows as getFlowNames,\n} from './validators.js';\nimport { getBuildDefaults, getDefaultOutput } from './build-defaults.js';\nimport { isUrl, loadJsonConfig } from './utils.js';\nimport { configDigest } from '../core/content-digest.js';\nimport {\n assertWindowCollector,\n type WindowCollectorSource,\n} from './window-collector.js';\n\n/** Default folder for includes if it exists */\nconst DEFAULT_INCLUDE_FOLDER = './shared';\n\n/**\n * Result of configuration loading.\n */\nexport interface LoadConfigResult {\n /** Runtime event processing configuration (resolved single Flow) */\n flowSettings: Flow;\n /** Build-time configuration */\n buildOptions: BuildOptions;\n /** Name of the selected flow */\n flowName: string;\n /** Whether multiple flows are available */\n isMultiFlow: boolean;\n /** All available flow names */\n availableFlows: string[];\n /**\n * Digest of the deferred flow settings, the config input of the default\n * release. Never carries an env or secret value.\n */\n configDigest: string;\n}\n\n/**\n * Options for loading configuration.\n */\nexport interface LoadConfigOptions {\n /** Path to config file */\n configPath: string;\n /** Flow name to load (required for multi-flow, optional for single-flow) */\n flowName?: string;\n /** CLI build overrides (future: --output, --minify, etc.) */\n buildOverrides?: Partial<BuildOptions>;\n /**\n * Base environment for web `$env` resolution, layered BENEATH the flow's\n * declared `config.bundle.env`. Omitted, it is the shell's `process.env`\n * (local builds). Pass `{}` for a hermetic build that resolves against the\n * declared values alone. Server flows are unaffected: their `$env` is read\n * at runtime.\n */\n buildEnv?: Record<string, string | undefined>;\n /** Logger for warnings */\n logger?: {\n warn: (message: string) => void;\n info: (message: string) => void;\n };\n}\n\n/**\n * The bundle preflight: the structural checks `walkeros validate` runs\n * (`validateFlowStructure`), on the flow being built. A flow validate\n * rejects (an unknown key, a mapping field with no effect at its position,\n * a malformed reference) stops here, before anything is resolved or built,\n * and the error names every path.\n */\nfunction assertFlowStructure(config: Flow.Json, flowName: string): void {\n const { errors } = validateFlowStructure({\n ...config,\n flows: { [flowName]: config.flows[flowName] },\n });\n if (errors.length === 0) return;\n const lines = errors.map((error) => ` - ${error.path}: ${error.message}`);\n throw new Error(`Invalid configuration:\\n${lines.join('\\n')}`);\n}\n\n/**\n * Load and parse bundle configuration.\n *\n * @remarks\n * Uses Flow.Json from @walkeros/core as the only config format.\n * - Validates config structure\n * - Uses core getFlowSettings() for variable/definition resolution\n * - Determines platform from resolved config\n * - Applies static build defaults based on platform\n *\n * @param rawConfig - Raw configuration object from JSON file\n * @param options - Loading options\n * @returns Parsed configuration with flow and build separated\n *\n * @example\n * ```typescript\n * const config = loadBundleConfig(rawConfig, {\n * configPath: './walkeros.config.json',\n * flowName: 'production',\n * });\n * ```\n */\nexport function loadBundleConfig(\n rawConfig: unknown,\n options: LoadConfigOptions,\n): LoadConfigResult {\n // Pre-validation scan: detect the legacy `flow.config.bundle.external`\n // field and strip it before schema validation. The strict Bundle schema\n // rejects unknown keys, so without this step the config would fail to\n // load with a confusing error. Emit a deprecation warning instead.\n const sanitized = stripLegacyBundleExternal(rawConfig, options.logger);\n\n // Validate as Flow.Json\n const config = validateFlowConfig(sanitized);\n const availableFlows = getFlowNames(config);\n\n // Determine which flow to use\n const flowName = resolveFlow(config, options.flowName, availableFlows);\n\n assertFlowStructure(config, flowName);\n assertLiteralBuildEnv(config, flowName);\n\n // Resolve with deferred mode first (markers don't affect platform detection)\n let flowSettings = getFlowSettings(config, flowName, { deferred: true });\n const platform = getPlatform(flowSettings);\n const flowConfigDigest = configDigest(flowSettings, flowName);\n if (!platform) {\n throw new Error(\n `Invalid configuration: flow \"${flowName}\" must have config.platform set to \"web\" or \"server\".`,\n );\n }\n\n // For web: re-resolve without deferred to bake values at build time. The\n // values come from an explicit map, never from an ambient read, so a build\n // inside a process holding its own secrets cannot inline them.\n if (platform === 'web') {\n flowSettings = getFlowSettings(config, flowName, {\n env: resolveBuildEnv(flowSettings.config?.bundle?.env, options.buildEnv),\n });\n }\n\n // Auto-inject validator transformers for any step-level `validate?:`\n // Get static build defaults based on platform\n const buildDefaults = getBuildDefaults(platform);\n\n // Extract packages + overrides + traceInclude from the canonical\n // `flow.<name>.config.bundle.{packages, overrides, traceInclude}` location.\n //\n // The `config.bundle.external` sub-field is no longer supported (replaced\n // by nft tracing); it is warned about and stripped pre-validation in\n // `stripLegacyBundleExternal` so it does not surface here.\n const bundle = flowSettings.config?.bundle;\n const packages: Record<string, Flow.BundlePackage> = bundle?.packages ?? {};\n const overrides: Record<string, string> = bundle?.overrides ?? {};\n const traceInclude = bundle?.traceInclude;\n\n // Output path: use --output if provided, otherwise default\n // Always relative to cwd, no dynamic resolution\n const output = options.buildOverrides?.output || getDefaultOutput(platform);\n\n // Get config directory for resolving includes and local packages\n // For URLs, use cwd since there's no local config directory\n const configDir = isUrl(options.configPath)\n ? process.cwd()\n : path.dirname(options.configPath);\n\n // Get includes from config or use default if ./shared exists\n let includes = config.include;\n if (!includes) {\n const defaultIncludePath = path.resolve(configDir, DEFAULT_INCLUDE_FOLDER);\n if (fs.pathExistsSync(defaultIncludePath)) {\n includes = [DEFAULT_INCLUDE_FOLDER];\n }\n }\n\n // Merge build options: defaults + flow settings + CLI overrides\n const buildOptions: BuildOptions = {\n ...buildDefaults,\n ...settingsToBuildOptions(platform, flowSettings.config?.settings, {\n path: `flows.${flowName}.config.settings.windowCollector`,\n written: config.flows[flowName]?.config?.settings?.windowCollector,\n }),\n packages,\n overrides,\n traceInclude,\n output,\n include: includes,\n configDir,\n ...options.buildOverrides,\n };\n\n // Log flow selection\n const isMultiFlow = availableFlows.length > 1;\n if (isMultiFlow && options.logger) {\n options.logger.info(\n `📦 Using flow: ${flowName} (${availableFlows.length} total)`,\n );\n }\n\n return {\n flowSettings,\n buildOptions,\n flowName,\n isMultiFlow,\n availableFlows,\n configDigest: flowConfigDigest,\n };\n}\n\n/**\n * The build options a flow's `config.settings` controls. Each key is mapped\n * explicitly, so an unknown settings key never reaches generated code.\n *\n * - `windowCollector` (web): the global the collector instance is assigned\n * to. It is interpolated into the generated entry, so the resolved value\n * must be a JavaScript identifier.\n */\nexport function settingsToBuildOptions(\n platform: 'web' | 'server',\n settings: Flow.Settings | undefined,\n source: WindowCollectorSource = {},\n): Pick<BuildOptions, 'windowCollector'> {\n if (platform !== 'web' || settings?.windowCollector === undefined) return {};\n return {\n windowCollector: assertWindowCollector(settings.windowCollector, source),\n };\n}\n\n/** Any reference or marker the resolver would act on inside a string. */\nconst REFERENCE_PATTERN =\n /\\$(?:env|var|secret|flow|contract)\\.|\\$code:|__WALKEROS_(?:ENV|SECRET):/;\n\n/**\n * `config.bundle.env` values are the declared build values themselves, so\n * each must be a literal string. A reference there would be resolved (or\n * turned into a runtime marker) instead of declared, so it is refused. The\n * message names keys only, never values.\n */\nfunction assertLiteralBuildEnv(config: Flow.Json, flowName: string): void {\n const declared = config.flows[flowName]?.config?.bundle?.env;\n if (!declared) return;\n const refs = Object.entries(declared)\n .filter(([, value]) => REFERENCE_PATTERN.test(value))\n .map(([key]) => key);\n if (refs.length > 0) {\n throw new Error(\n `config.bundle.env values must be literal strings; references are not allowed in: ${refs.join(', ')}`,\n );\n }\n}\n\n/**\n * The map web `$env` references resolve against: the base environment\n * (`process.env` unless the caller passes one) with the flow's declared\n * `config.bundle.env` on top.\n */\nexport function resolveBuildEnv(\n declared: Record<string, string> | undefined,\n base: Record<string, string | undefined> = process.env,\n): Record<string, string | undefined> {\n return { ...base, ...(declared ?? {}) };\n}\n\n/**\n * Resolve which flow to use.\n *\n * @param config - Flow.Json configuration\n * @param requestedFlow - Flow name from CLI (optional)\n * @param available - Available flow names\n * @returns Flow name to use\n * @throws Error if flow selection is invalid\n */\nfunction resolveFlow(\n config: Flow.Json,\n requestedFlow: string | undefined,\n available: string[],\n): string {\n // If only one flow, use it automatically\n if (available.length === 1) {\n return available[0];\n }\n\n // Multiple flows require explicit selection\n if (!requestedFlow) {\n throw new Error(\n `Multiple flows found. Please specify a flow using --flow flag.\\n` +\n `Available flows: ${available.join(', ')}`,\n );\n }\n\n // Validate the requested flow exists\n if (!available.includes(requestedFlow)) {\n throw new Error(\n `Flow \"${requestedFlow}\" not found in configuration.\\n` +\n `Available flows: ${available.join(', ')}`,\n );\n }\n\n return requestedFlow;\n}\n\n/**\n * Load all flows from a configuration.\n *\n * @remarks\n * Used by the --all flag to build all flows.\n *\n * @param rawConfig - Raw configuration object\n * @param options - Loading options (without flowName)\n * @returns Array of loaded configurations for all flows\n */\nexport function loadAllFlows(\n rawConfig: unknown,\n options: Omit<LoadConfigOptions, 'flowName'>,\n): LoadConfigResult[] {\n // Validate as Flow.Json\n const config = validateFlowConfig(rawConfig);\n const flows = getFlowNames(config);\n\n if (options.logger) {\n options.logger.info(\n `📦 Loading all ${flows.length} flows: ${flows.join(', ')}`,\n );\n }\n\n // Load each flow\n return flows.map((name) =>\n loadBundleConfig(rawConfig, {\n ...options,\n flowName: name,\n }),\n );\n}\n\n/**\n * Get list of available flows from configuration.\n *\n * @param rawConfig - Raw configuration object\n * @returns Array of flow names\n */\nexport function getAvailableFlows(rawConfig: unknown): string[] {\n if (isFlowConfig(rawConfig)) {\n return getFlowNames(rawConfig);\n }\n return [];\n}\n\n/**\n * Load flow configuration from file or URL.\n *\n * Single entry point for all commands (bundle, simulate, push).\n * Handles URL vs local path detection automatically.\n *\n * @param configPath - Path to config file or URL\n * @param options - Loading options (flowName, logger, buildOverrides)\n * @returns Parsed configuration with flow and build options\n *\n * @example\n * ```typescript\n * const { flowSettings, buildOptions } = await loadFlowConfig('./flow.json');\n * const { flowSettings } = await loadFlowConfig('https://example.com/flow.json');\n * ```\n */\nexport async function loadFlowConfig(\n configPath: string,\n options?: Omit<LoadConfigOptions, 'configPath'> & {\n /**\n * The config at `configPath`, already read by the caller: it is used\n * instead of reading the path again (`configPath` still anchors\n * relative paths).\n */\n raw?: unknown;\n },\n): Promise<LoadConfigResult> {\n const { raw, ...rest } = options ?? {};\n const rawConfig = raw !== undefined ? raw : await loadJsonConfig(configPath);\n return loadBundleConfig(rawConfig, { configPath, ...rest });\n}\n\n/**\n * Pre-validation pass: detect and strip the legacy\n * `flow.config.bundle.external` field. The strict Bundle schema rejects\n * unknown keys, so without this step a config containing the legacy\n * field would fail validation with a confusing error. Emit a one-time\n * deprecation warning per offending flow and return a sanitized clone.\n *\n * The clone is shallow at the levels we mutate (root, `flows`, each flow,\n * `config`, `bundle`); deeper structures are aliased into the clone, which\n * is fine because we only delete a top-level key on the bundle object.\n */\nfunction stripLegacyBundleExternal(\n rawConfig: unknown,\n logger: LoadConfigOptions['logger'],\n): unknown {\n if (!isPlainObject(rawConfig)) return rawConfig;\n const flows = rawConfig.flows;\n if (!isPlainObject(flows)) return rawConfig;\n\n let mutated: Record<string, unknown> | null = null;\n\n for (const [flowName, flowValue] of Object.entries(flows)) {\n if (!isPlainObject(flowValue)) continue;\n const flowConfig = flowValue.config;\n if (!isPlainObject(flowConfig)) continue;\n const bundle = flowConfig.bundle;\n if (!isPlainObject(bundle)) continue;\n if (!('external' in bundle)) continue;\n\n logger?.warn(\n `flow.config.bundle.external is no longer supported; @walkeros/cli@4.x traces server bundles automatically. Remove it from flows.${flowName}.config.bundle.`,\n );\n\n if (!mutated) {\n mutated = {\n ...rawConfig,\n flows: { ...flows },\n };\n }\n const mutatedFlows = mutated.flows as Record<string, unknown>;\n const newBundle = { ...bundle };\n delete newBundle.external;\n const newConfig = { ...flowConfig, bundle: newBundle };\n mutatedFlows[flowName] = { ...flowValue, config: newConfig };\n }\n\n return mutated ?? rawConfig;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/**\n * Content digests for build cache keys and the default release id.\n *\n * Every input is content the bundler consumes: the deferred flow settings,\n * the resolved package set (local packages by the bytes of their installed\n * copy) and the CLI version. Environment and secret values are never inputs,\n * only their markers, so a digest leaks none of them.\n */\n\nimport { createHash } from 'node:crypto';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport type { Flow } from '@walkeros/core';\n\nconst DIGEST_LENGTH = 12;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/** JSON with object keys sorted at every depth. Arrays keep their order. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(value, (_key, inner: unknown) => {\n if (!isRecord(inner)) return inner;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(inner).sort()) sorted[key] = inner[key];\n return sorted;\n });\n}\n\n/**\n * Hash a flow's deferred settings (its `$env` and `$secret` references are\n * still markers). `collector.release` is left out and `collector.name`\n * defaults to the flow name, so the digest names the config the release is\n * derived from. Declared `config.bundle.env` values are reduced to their keys:\n * they are the values web `$env` references resolve to.\n */\nexport function configDigest(flowSettings: Flow, flowName: string): string {\n const collector: Record<string, unknown> = {\n ...(flowSettings.collector ?? {}),\n };\n delete collector.release;\n collector.name ??= flowName;\n\n const config = flowSettings.config;\n const declaredEnv = config?.bundle?.env;\n const hashedConfig =\n config && declaredEnv\n ? {\n ...config,\n bundle: { ...config.bundle, env: Object.keys(declaredEnv).sort() },\n }\n : config;\n\n return createHash('sha256')\n .update(canonicalJson({ ...flowSettings, config: hashedConfig, collector }))\n .digest('hex');\n}\n\n/**\n * Hash an installed package directory: every file by relative path and bytes,\n * a symlink by its target. Paths are sorted, so file system order and mtimes\n * never change the result.\n */\nexport async function hashInstalledDir(dir: string): Promise<string> {\n const hash = createHash('sha256');\n for (const rel of await listFiles(dir, '')) {\n const abs = path.join(dir, rel);\n const stat = await fs.lstat(abs);\n hash.update(`${rel}\\0`);\n hash.update(\n stat.isSymbolicLink() ? await fs.readlink(abs) : await fs.readFile(abs),\n );\n hash.update('\\0');\n }\n return hash.digest('hex').slice(0, DIGEST_LENGTH);\n}\n\nasync function listFiles(root: string, rel: string): Promise<string[]> {\n const entries = await fs.readdir(path.join(root, rel), {\n withFileTypes: true,\n });\n const files: string[] = [];\n for (const entry of entries) {\n const entryRel = rel ? `${rel}/${entry.name}` : entry.name;\n if (entry.isDirectory()) files.push(...(await listFiles(root, entryRel)));\n else files.push(entryRel);\n }\n return files.sort();\n}\n\nexport interface ReleaseDigestInput {\n /** {@link configDigest} of the flow. */\n configDigest: string;\n /** Sorted `name@version` lines, a local package as `name@local:<hash>`. */\n versions: string[];\n /** CLI version, since generated code changes with it. */\n toolchain: string;\n}\n\n/** Default release id: 12 lowercase hex over config, packages and CLI. */\nexport function releaseDigest(input: ReleaseDigestInput): string {\n return createHash('sha256')\n .update(\n canonicalJson({\n config: input.configDigest,\n versions: input.versions,\n toolchain: input.toolchain,\n }),\n )\n .digest('hex')\n .slice(0, DIGEST_LENGTH);\n}\n","import { checkWindowCollector } from '@walkeros/core';\n\n/** Where a collector global name came from, for the error message. */\nexport interface WindowCollectorSource {\n /** Setting path, e.g. `flows.web.config.settings.windowCollector`. */\n path?: string;\n /** The value as written, when it was a `$var` / `$env` reference. */\n written?: unknown;\n}\n\n/**\n * Return `value` as the collector global name, or throw when it is not a\n * JavaScript identifier or names a reserved global. The name is interpolated\n * into generated code, so every build path checks it before use.\n */\nexport function assertWindowCollector(\n value: unknown,\n source: WindowCollectorSource = {},\n): string {\n const check = checkWindowCollector(value);\n if (check.ok) return check.name;\n const path = source.path ?? 'config.settings.windowCollector';\n const subject =\n source.written !== undefined && source.written !== value\n ? `${JSON.stringify(source.written)} resolved to ${JSON.stringify(value)}, which`\n : JSON.stringify(value);\n throw new Error(`${path}: ${subject} ${check.reason}.`);\n}\n","import { CONTRACT_RULE_KEYS, isObject } from '@walkeros/core';\nimport type {\n ValidateResult,\n ValidationError,\n ValidationWarning,\n} from '../types.js';\n\nconst KNOWN_KEYS = new Set<string>(CONTRACT_RULE_KEYS);\n\n/** Event parts once offered as contract sections; nothing enforces them. */\nconst SECTION_KEYS = ['globals', 'context', 'custom', 'user', 'consent'];\n\nexport function validateContract(input: unknown): ValidateResult {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n const details: Record<string, unknown> = {};\n\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n errors.push({\n path: 'root',\n message: 'Contract must be an object of named contract entries',\n code: 'INVALID_CONTRACT',\n });\n return { valid: false, type: 'contract', errors, warnings, details };\n }\n\n const contracts = input as Record<string, unknown>;\n const contractNames = Object.keys(contracts);\n details.contractCount = contractNames.length;\n\n // Check for $-prefixed root keys (flat app shape)\n for (const name of contractNames) {\n if (name.startsWith('$')) {\n errors.push({\n path: name,\n message:\n `\"${name}\" is not a named contract entry. This looks like the flat ` +\n 'shape; use named ContractRule entries, lift $tagging to the rule ' +\n 'field \"tagging\", and nest entity-action schemas under \"events\".',\n code: 'FLAT_CONTRACT_SHAPE',\n });\n }\n }\n\n // Check each named contract entry\n for (const [name, entry] of Object.entries(contracts)) {\n // Skip $-prefixed names (already reported above)\n if (name.startsWith('$')) {\n continue;\n }\n\n if (typeof entry !== 'object' || entry === null) {\n errors.push({\n path: name,\n message: `Contract \"${name}\" must be an object`,\n code: 'INVALID_CONTRACT_ENTRY',\n });\n continue;\n }\n\n const obj = entry as Record<string, unknown>;\n const keys = Object.keys(obj);\n const unknownKeys = keys.filter((k) => !KNOWN_KEYS.has(k));\n const hasKnownKey = keys.some((k) => KNOWN_KEYS.has(k));\n const objectValuedUnknown = unknownKeys.filter(\n (k) => isObject(obj[k]) && !SECTION_KEYS.includes(k),\n );\n\n // Check for flat entity-action maps (object-valued keys without known keys)\n if (!hasKnownKey && objectValuedUnknown.length > 0) {\n errors.push({\n path: name,\n message:\n `Contract \"${name}\" looks like a flat entity-action map. Nest ` +\n 'entity-action schemas under an \"events\" key of a named ContractRule.',\n code: 'FLAT_CONTRACT_SHAPE',\n });\n continue;\n }\n\n // Unknown keys are errors: the contract schema is closed, and the\n // validate transformer would never apply them.\n for (const key of unknownKeys) {\n errors.push({\n path: `${name}.${key}`,\n message: SECTION_KEYS.includes(key)\n ? `Contract section \"${key}\" is not enforced: the validate transformer reads only \"events\" and \"schema\". Put the rule into \"schema\" (a JSON Schema for the whole event), for example \"schema\": { \"properties\": { \"${key}\": { ... } } }.`\n : `Unknown contract key \"${key}\" is not supported. Known keys: ${[...KNOWN_KEYS].join(', ')}.`,\n code: 'UNKNOWN_CONTRACT_KEY',\n });\n }\n\n // Validate tagging\n if (obj.tagging !== undefined && typeof obj.tagging !== 'number') {\n errors.push({\n path: `${name}.tagging`,\n message: 'tagging must be a number',\n code: 'INVALID_TAGGING',\n });\n }\n\n // Validate schema\n if (obj.schema !== undefined && !isObject(obj.schema)) {\n errors.push({\n path: `${name}.schema`,\n message: 'schema must be a JSON Schema object',\n code: 'INVALID_SCHEMA',\n });\n }\n\n // Validate extend\n if (obj.extend !== undefined) {\n if (typeof obj.extend !== 'string') {\n errors.push({\n path: `${name}.extend`,\n message: 'extend must be a string',\n code: 'INVALID_EXTENDS',\n });\n } else if (!contractNames.includes(obj.extend)) {\n errors.push({\n path: `${name}.extend`,\n message: `extend references non-existent contract \"${obj.extend}\"`,\n value: obj.extend,\n code: 'INVALID_EXTENDS',\n });\n }\n }\n\n // Validate events\n if (obj.events !== undefined) {\n if (typeof obj.events !== 'object' || obj.events === null) {\n errors.push({\n path: `${name}.events`,\n message: 'events must be an object',\n code: 'INVALID_EVENTS',\n });\n } else {\n validateEntityActions(\n obj.events as Record<string, unknown>,\n `${name}.events`,\n errors,\n );\n }\n }\n }\n\n // Check for circular extend: one error per cycle, at its first member.\n const reportedCycles = new Set<string>();\n for (const name of contractNames) {\n const chain: string[] = [];\n let current = name;\n while (current) {\n const start = chain.indexOf(current);\n if (start !== -1) {\n const cycle = chain.slice(start);\n const key = [...cycle].sort().join('\\u0000');\n if (!reportedCycles.has(key)) {\n reportedCycles.add(key);\n errors.push({\n path: `${cycle[0]}.extend`,\n message: `Circular extend chain: ${[...cycle, current].join(' → ')}`,\n code: 'CIRCULAR_EXTENDS',\n });\n }\n break;\n }\n chain.push(current);\n const entry = contracts[current];\n current =\n isObject(entry) && typeof entry.extend === 'string' ? entry.extend : '';\n }\n }\n\n return {\n valid: errors.length === 0,\n type: 'contract',\n errors,\n warnings,\n details,\n };\n}\n\nfunction validateEntityActions(\n obj: Record<string, unknown>,\n prefix: string,\n errors: ValidationError[],\n): void {\n for (const [entityKey, entityValue] of Object.entries(obj)) {\n if (entityKey.trim() === '') {\n errors.push({\n path: `${prefix}.${entityKey}`,\n message: 'Entity key cannot be empty',\n code: 'INVALID_ENTITY_KEY',\n });\n continue;\n }\n\n if (typeof entityValue !== 'object' || entityValue === null) {\n errors.push({\n path: `${prefix}.${entityKey}`,\n message: `Entity \"${entityKey}\" must be an object`,\n value: entityValue,\n code: 'INVALID_ENTITY',\n });\n continue;\n }\n\n for (const [actionKey, actionValue] of Object.entries(\n entityValue as Record<string, unknown>,\n )) {\n if (actionKey.trim() === '') {\n errors.push({\n path: `${prefix}.${entityKey}.${actionKey}`,\n message: 'Action key cannot be empty',\n code: 'INVALID_ACTION_KEY',\n });\n continue;\n }\n\n if (\n typeof actionValue !== 'object' ||\n actionValue === null ||\n Array.isArray(actionValue)\n ) {\n errors.push({\n path: `${prefix}.${entityKey}.${actionKey}`,\n message: 'Contract entry must be a JSON Schema object',\n value: typeof actionValue,\n code: 'INVALID_SCHEMA_ENTRY',\n });\n }\n }\n }\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/event.ts\n\nimport { validateEvent as coreValidateEvent } from '../../../core/event-validation.js';\nimport type { ValidateResult } from '../types.js';\n\nexport function validateEvent(input: unknown): ValidateResult {\n const result = coreValidateEvent(input, 'strict');\n\n return {\n valid: result.valid,\n type: 'event',\n errors: result.errors,\n warnings: result.warnings,\n details: result.details as Record<string, unknown>,\n };\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/flow.ts\n\nimport type { Flow, Transformer, WalkerOS } from '@walkeros/core';\nimport {\n checkWindowCollector,\n ENV_MARKER_PREFIX,\n getByPath,\n getRouteGraph,\n isObject,\n REF_CONTRACT,\n resolveContracts,\n SECRET_MARKER_PREFIX,\n validateStepEntry,\n} from '@walkeros/core';\nimport {\n checkStepKeys,\n schemas,\n validateFlowStructure,\n} from '@walkeros/core/dev';\nimport {\n validateEventAgainstContract,\n type ContractSource,\n} from '@walkeros/transformer-validate';\nimport {\n describeScope,\n ENTRY_SECTIONS,\n flowOfPath,\n isFlowJson,\n resolveScope,\n type ResolvedScope,\n} from '../scope.js';\nimport type {\n ValidateCheck,\n ValidateDeferred,\n ValidateDetails,\n ValidateResult,\n ValidateSkip,\n ValidationError,\n ValidationWarning,\n} from '../types.js';\nimport { validateContract } from './contract.js';\nimport { resolveFlow, type FlowResolution } from './resolve.js';\nimport {\n checkStepSettings,\n createSettingsContext,\n type CheckedPackage,\n type SettingsOutcome,\n} from './settings.js';\n\nconst { validateFlowConfig } = schemas;\n\ninterface FlowValidateOptions {\n flow?: string;\n /** When true, contract violations are reported as errors instead of warnings. */\n strict?: boolean;\n}\n\ninterface FlowPackageOptions extends FlowValidateOptions {\n /** Skip the package settings check (no network); named in the scope. */\n offline?: boolean;\n /** Directory local `path` packages resolve against (the input file's). */\n configDir?: string;\n}\n\n/** Removed packages: reported as a warning for one minor (D3), then errors. */\nconst DEPRECATED_PACKAGES: Record<string, string> = {\n '@walkeros/store-memory':\n 'has been removed. Use the built-in cache by omitting cache.store, or remove the store declaration if it was only used as a cache target.',\n};\n\n/** Step sections and their kind, for the closed key check of every step. */\nconst STEP_KINDS = [\n ['sources', 'Source'],\n ['transformers', 'Transformer'],\n ['destinations', 'Destination'],\n ['stores', 'Store'],\n] as const;\n\n/** Everything the synchronous checks produced, for the package pass. */\ninterface FlowRun {\n result: ValidateResult;\n scope: ResolvedScope;\n typed?: Flow.Json;\n resolved: Map<string, Flow>;\n checks: ValidateCheck[];\n skipped: ValidateSkip[];\n gate: (name: string) => string | undefined;\n}\n\n/**\n * Validate a flow file without the network: schema and references, the\n * root contract, the bundler's structural preflight, routes, examples and\n * resolution of every flow in scope. `validate()` adds the package settings\n * check on top (see {@link validateFlowWithPackages}).\n */\nexport function validateFlow(\n input: unknown,\n options: FlowValidateOptions = {},\n): ValidateResult {\n return runFlowChecks(input, options).result;\n}\n\n/**\n * The whole-file run of `validate()`: {@link validateFlow} plus every step\n * with a `package` in scope checked against its package settings schema at\n * the pinned version. An unknown settings key is an error where the package\n * closes its schema (`additionalProperties: false`), as every closed schema\n * is; other findings are warnings for one minor (D3). A schema that cannot\n * be loaded is a skip; `offline` removes the check and says so.\n */\nexport async function validateFlowWithPackages(\n input: unknown,\n options: FlowPackageOptions = {},\n): Promise<ValidateResult> {\n const run = runFlowChecks(input, options);\n const { result, scope } = run;\n if (options.offline) {\n const described = describeScope(scope, run.checks);\n described.offline = true;\n result.details.scope = described;\n return result;\n }\n\n run.checks.push('flow:package-settings');\n const context = createSettingsContext(options.configDir);\n const deferred: ValidateDeferred[] = [];\n const packages: CheckedPackage[] = [];\n // Checks run in parallel; outcomes are applied in step order afterwards,\n // so the result does not depend on which schema arrives first.\n const pending: Array<Promise<SettingsOutcome>> = [];\n for (const name of scope.flows) {\n const flow = run.resolved.get(name);\n const rawFlow = run.typed?.flows[name];\n const reason = run.gate(name);\n if (!flow || !rawFlow || reason) {\n run.skipped.push({\n path: `flows.${name}`,\n check: 'flow:package-settings',\n reason: reason ?? `flows.${name} did not resolve`,\n code: 'GATED_BY_ERRORS',\n });\n continue;\n }\n for (const section of ENTRY_SECTIONS) {\n const steps = isObject(rawFlow[section]) ? rawFlow[section] : {};\n for (const [key, entry] of Object.entries(steps)) {\n if (!isObject(entry) || entry.package === undefined) continue;\n const target = { flow: name, section, key, entry };\n pending.push(\n checkStepSettings(target, flow, 'flow:package-settings', context),\n );\n }\n }\n }\n for (const outcome of await Promise.all(pending)) {\n for (const finding of outcome.findings) {\n if (finding.keyword === 'additionalProperties')\n result.errors.push(finding);\n else result.warnings.push(finding);\n }\n run.skipped.push(...outcome.skipped);\n deferred.push(...outcome.deferred);\n if (outcome.checked) packages.push(outcome.checked);\n }\n\n result.details.scope = describeScope(scope, run.checks);\n result.details.deferred = [...(result.details.deferred ?? []), ...deferred];\n if (packages.length > 0) result.details.packages = packages;\n result.valid = result.errors.length === 0;\n return result;\n}\n\nfunction runFlowChecks(input: unknown, options: FlowValidateOptions): FlowRun {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n const skipped: ValidateSkip[] = [];\n const checks: ValidateCheck[] = ['file:schema', 'flow:schema'];\n const details: ValidateDetails = {};\n const resolved = new Map<string, Flow>();\n // `valid` is set once every error-producing check has run; the package\n // pass only adds warnings and skips.\n const result: ValidateResult = {\n valid: false,\n type: 'flow',\n errors,\n warnings,\n details,\n };\n\n // 1. Serialize to JSON for core validator\n // Core's validateFlowConfig takes a JSON string, but CLI receives parsed objects.\n // Re-serializing is the bridge between the two interfaces.\n let json: string;\n const scope = resolveScope(input, { flow: options.flow });\n try {\n json = JSON.stringify(input, null, 2);\n } catch {\n errors.push({\n path: 'root',\n message: 'Input cannot be serialized to JSON',\n code: 'SERIALIZATION_ERROR',\n });\n return {\n result,\n scope,\n resolved,\n checks,\n skipped,\n gate: () => 'the input cannot be serialized',\n };\n }\n\n // 2. Scope: every per-flow check below runs on scope.flows only.\n errors.push(...scope.errors);\n const config: Record<string, unknown> = isObject(input) ? input : {};\n const flowsValue = config.flows;\n const flows: Record<string, unknown> | undefined = isObject(flowsValue)\n ? flowsValue\n : undefined;\n const allFlows = scope.allFlows;\n const inScope = (path: string): boolean => {\n const flow = flowOfPath(path, allFlows);\n return flow === undefined || scope.flows.includes(flow);\n };\n\n // 3. The root contract, validated as `-t contract` validates it, with\n // paths under `contract.`. A file-level check: it runs whatever --flow.\n let contractErrors = 0;\n if ('contract' in config) {\n checks.push('file:contract');\n const contract = validateContract(config.contract);\n contractErrors = contract.errors.length;\n for (const error of contract.errors)\n errors.push({ ...error, path: `contract.${error.path}` });\n for (const warning of contract.warnings)\n warnings.push({ ...warning, path: `contract.${warning.path}` });\n }\n\n // 4. Run core validation (Zod schema + reference checking). File-level\n // findings always count; per-flow findings only for flows in scope.\n // Core's own contract-resolution error at `contract` is the same\n // finding the contract check above already reported in detail.\n const coreResult = validateFlowConfig(json);\n for (const issue of coreResult.errors) {\n const path = issue.path || 'root';\n if (!inScope(path)) continue;\n if (\n (path === 'contract' || path.startsWith('contract.')) &&\n contractErrors > 0\n )\n continue;\n errors.push({\n path,\n message: issue.message,\n code: issue.code ?? 'SCHEMA_VALIDATION',\n });\n }\n for (const issue of coreResult.warnings) {\n const path = issue.path || 'root';\n if (!inScope(path)) continue;\n warnings.push({ path, message: issue.message, code: 'CONFIG_WARNING' });\n }\n\n // 5. CLI-specific: check for empty flows\n if (flows && allFlows.length === 0) {\n errors.push({\n path: 'flows',\n message: 'At least one flow is required',\n code: 'EMPTY_FLOWS',\n });\n }\n\n // 6. Step entries, closed (single source of truth in @walkeros/core):\n // a transformer entry's shape, and an unknown key on a destination or\n // store entry or in any step's `config`, which the runtime would\n // ignore (UNKNOWN_KEY errors). Removed packages warn\n // (DEPRECATED_PACKAGE) for one minor (D3).\n checks.push('flow:steps');\n for (const flowName of scope.flows) {\n const flowValue = flows?.[flowName];\n if (!isObject(flowValue)) continue;\n const transformersValue = flowValue.transformers;\n if (isObject(transformersValue)) {\n for (const [name, transformerValue] of Object.entries(\n transformersValue,\n )) {\n if (!isObject(transformerValue)) continue;\n const result = validateStepEntry(transformerValue, 'Transformer');\n if (!result.ok) {\n errors.push({\n path: `flows.${flowName}.transformers.${name}`,\n message: result.reason || 'Invalid transformer entry.',\n code: result.code,\n });\n }\n }\n }\n for (const [section, kind] of STEP_KINDS) {\n const steps = flowValue[section];\n if (!isObject(steps)) continue;\n for (const [name, step] of Object.entries(steps)) {\n if (!isObject(step)) continue;\n const at = `flows.${flowName}.${section}.${name}`;\n errors.push(...checkStepKeys(step, kind, at));\n const pkg = step.package;\n if (typeof pkg === 'string' && pkg in DEPRECATED_PACKAGES) {\n warnings.push({\n path: at,\n message: `${kind} \"${name}\" uses ${pkg}, which ${DEPRECATED_PACKAGES[pkg]}`,\n code: 'DEPRECATED_PACKAGE',\n });\n }\n }\n }\n checkWebLoggerFormat(flowName, flowValue, errors);\n }\n\n // 7. Extract flow details\n if (flows) {\n details.flowNames = allFlows;\n details.flowCount = allFlows.length;\n }\n\n // 8. CLI-specific: warn about packages without version (per-flow config.bundle.packages)\n checks.push('flow:package-versions');\n let totalPackageCount = 0;\n for (const flowName of scope.flows) {\n const flowValue = flows?.[flowName];\n if (!isObject(flowValue)) continue;\n const flowConfig = flowValue.config;\n if (!isObject(flowConfig)) continue;\n const bundle = flowConfig.bundle;\n if (!isObject(bundle)) continue;\n const packages = bundle.packages;\n if (!isObject(packages)) continue;\n\n for (const [pkgName, pkgConfigValue] of Object.entries(packages)) {\n if (!isObject(pkgConfigValue)) continue;\n if (!pkgConfigValue.version && !pkgConfigValue.path) {\n warnings.push({\n path: `flows.${flowName}.config.bundle.packages.${pkgName}`,\n message: `Package \"${pkgName}\" has no version specified`,\n suggestion: 'Consider specifying a version for reproducible builds',\n code: 'PACKAGE_VERSION_MISSING',\n });\n }\n }\n totalPackageCount += Object.keys(packages).length;\n }\n if (totalPackageCount > 0) {\n details.packageCount = totalPackageCount;\n }\n\n // 9. Expose core's IntelliSense context in details (bonus for MCP consumers)\n if (coreResult.context) {\n details.context = coreResult.context;\n }\n\n // 10. Deep checks run per flow on shapes core has already validated: a\n // flow with its own errors, or a file with file-level errors (its\n // shape, its contract), skips them, and every skip is listed.\n const typed = isFlowJson(input) ? input : undefined;\n const gate = (name: string): string | undefined => {\n if (\n !typed ||\n errors.some((e) => flowOfPath(e.path, allFlows) === undefined)\n )\n return 'file-level errors: the file shape or its contract is not valid';\n if (errors.some((e) => flowOfPath(e.path, allFlows) === name))\n return `earlier errors in flows.${name}`;\n return undefined;\n };\n const perFlow = (\n stageChecks: ValidateCheck[],\n run: (name: string, flow: Flow, file: Flow.Json) => void,\n ): void => {\n checks.push(...stageChecks);\n for (const name of scope.flows) {\n const reason = gate(name);\n const flow = typed?.flows[name];\n if (reason || !typed || !flow) {\n for (const check of stageChecks) {\n skipped.push({\n path: `flows.${name}`,\n check,\n reason: reason ?? `flows.${name} is not a flow object`,\n code: 'GATED_BY_ERRORS',\n });\n }\n continue;\n }\n run(name, flow, typed);\n }\n };\n\n // 10a. The bundler's own structural preflight (identifier names, package\n // xor code, per-flow $store targets, closed keys, mapping fields that\n // do nothing at their position, mapping values, whole-value\n // references), so a valid result implies the bundle preflight passes.\n // A flow with earlier errors skips it, so nothing is reported twice.\n perFlow(['flow:structure'], (name, flow, file) => {\n const structure = validateFlowStructure({\n ...file,\n flows: { [name]: flow },\n });\n errors.push(...structure.errors);\n warnings.push(...structure.warnings);\n });\n\n // 10b. Route checks on every chain field, read through core's\n // getRouteGraph. Unknown targets are errors; shapes the schema\n // accepts but the author probably did not intend are warnings.\n perFlow(['flow:routes'], (name, flow) => {\n lintFlowRoutes(name, flow, errors, warnings);\n });\n\n // Each flow is resolved once, as the bundler resolves it: the validate\n // step check below reads its settings from there, 10d reports failures.\n const resolutions = new Map<string, FlowResolution>();\n const resolutionOf = (name: string, file: Flow.Json): FlowResolution => {\n let resolution = resolutions.get(name);\n if (!resolution) {\n resolution = resolveFlow(file, name);\n resolutions.set(name, resolution);\n }\n return resolution;\n };\n\n // 10c. Cross-step example compatibility, validate step examples against\n // their contract, and flat dot-separated mapping keys.\n let totalConnections: number | undefined;\n perFlow(\n ['flow:examples', 'flow:contract-examples', 'flow:mapping-keys'],\n (name, flow, file) => {\n const connections = buildConnectionGraph(flow);\n for (const conn of connections) {\n checkCompatibility(name, conn, errors, warnings);\n }\n totalConnections = (totalConnections ?? 0) + connections.length;\n\n // Contracts bind only where a transformer-validate step links them,\n // with exactly that step's settings, as at runtime. The config-level\n // contract block binds nothing by itself.\n const resolution = resolutionOf(name, file);\n for (const [stepName, transformer] of Object.entries(\n flow.transformers || {},\n )) {\n if (!isValidateStep(transformer)) continue;\n const resolvedConfig = resolution.ok\n ? resolution.flow.transformers?.[stepName]?.config\n : undefined;\n checkValidateStepExamples(\n `flows.${name}.transformers.${stepName}`,\n transformer,\n file.contract,\n errors,\n warnings,\n options.strict === true,\n skipped,\n isObject(resolvedConfig) && isObject(resolvedConfig.settings)\n ? resolvedConfig.settings\n : undefined,\n );\n }\n\n for (const [destName, dest] of Object.entries(flow.destinations || {})) {\n if (!isObject(dest.config)) continue;\n const mapping = dest.config.mapping;\n if (!isObject(mapping)) continue;\n\n for (const key of Object.keys(mapping)) {\n if (key.includes('.') && !key.includes(' ')) {\n const parts = key.split('.');\n warnings.push({\n path: `flows.${name}.destinations.${destName}.config.mapping`,\n message: `Mapping key \"${key}\" looks like dot-notation. Mapping uses nested entity → action structure.`,\n suggestion: `Use nested format: { \"${parts[0]}\": { \"${parts.slice(1).join('.')}\": { ... } } }`,\n code: 'MAPPING_DOT_KEY',\n });\n }\n }\n }\n },\n );\n if (totalConnections !== undefined) {\n details.connectionsChecked = totalConnections;\n }\n\n // 10d. Resolve every flow as the bundler does. Every resolver failure\n // (unknown $var, unresolvable $flow, unknown contract, cycles) is an\n // error, since the bundle of that flow fails the same way. Values\n // known only at runtime ($env without default, $secret) stay deferred.\n // The collector global is checked on its resolved value; an `$env`\n // value is known only at build time and stays deferred.\n const deferred: ValidateDeferred[] = [];\n perFlow(['flow:resolve'], (name, flow, file) => {\n const resolution = resolutionOf(name, file);\n if (!resolution.ok) {\n errors.push(resolution.error);\n return;\n }\n resolved.set(name, resolution.flow);\n checkResolvedWindowCollector(name, flow, resolution.flow, errors, deferred);\n });\n\n details.scope = describeScope(scope, checks);\n details.skipped = skipped;\n details.deferred = deferred;\n result.valid = errors.length === 0;\n\n return {\n result,\n scope,\n typed,\n resolved,\n checks,\n skipped,\n gate,\n };\n}\n\n/**\n * A `$var` / `$env` collector global, checked on its resolved value as the\n * build checks it. A literal is already checked by the schema; a value that\n * needs the build env is deferred to the build.\n */\nfunction checkResolvedWindowCollector(\n name: string,\n raw: Flow,\n flow: Flow,\n errors: ValidationError[],\n deferred: ValidateDeferred[],\n): void {\n const written = raw.config?.settings?.windowCollector;\n const value = flow.config?.settings?.windowCollector;\n if (\n flow.config?.platform !== 'web' ||\n typeof written !== 'string' ||\n !/\\$(?:var|env)\\./.test(written)\n )\n return;\n const path = `flows.${name}.config.settings.windowCollector`;\n if (typeof value === 'string' && value.includes(ENV_MARKER_PREFIX)) {\n deferred.push({ path, reference: written });\n return;\n }\n const check = checkWindowCollector(value);\n if (check.ok) return;\n errors.push({\n path,\n message: `${JSON.stringify(written)} resolved to ${JSON.stringify(value)}, which ${check.reason}`,\n code: 'SCHEMA_VALIDATION',\n });\n}\n\n/**\n * `logger.format` chooses text or JSON lines for a server flow's output; a\n * web flow logs to the browser console, where it has no effect. Checked on\n * the collector's logger and on every step's `config.logger`.\n */\nfunction checkWebLoggerFormat(\n flowName: string,\n flow: Record<string, unknown>,\n errors: ValidationError[],\n): void {\n if (!isObject(flow.config) || flow.config.platform !== 'web') return;\n const at = `flows.${flowName}`;\n const loggers: Array<[string, unknown]> = [];\n if (isObject(flow.collector))\n loggers.push([`${at}.collector.logger`, flow.collector.logger]);\n for (const [section] of STEP_KINDS) {\n const steps = flow[section];\n if (!isObject(steps)) continue;\n for (const [name, step] of Object.entries(steps)) {\n if (isObject(step) && isObject(step.config))\n loggers.push([\n `${at}.${section}.${name}.config.logger`,\n step.config.logger,\n ]);\n }\n }\n for (const [path, logger] of loggers) {\n if (!isObject(logger) || logger.format === undefined) continue;\n errors.push({\n path: `${path}.format`,\n message:\n 'logger.format applies to server flows only: a web flow logs to the browser console, which has no output format. Remove it.',\n code: 'WEB_LOGGER_FORMAT',\n });\n }\n}\n\n// --- Deep validation helpers ---\n\ninterface StepInfo {\n type: 'source' | 'transformer' | 'destination';\n name: string;\n examples: Flow.StepExamples;\n}\n\ninterface StepConnection {\n from: StepInfo;\n to: StepInfo;\n}\n\n/**\n * Every transformer id a route can reach, read through core's\n * `getRouteGraph` (the one enumerator over the compiled route form). An\n * over-approximation by design: static validation has no event, so every\n * branch counts.\n */\nfunction routeTargets(spec: Transformer.Route | undefined): string[] {\n if (spec === undefined) return [];\n const targets = new Set<string>();\n for (const node of getRouteGraph(spec)) {\n for (const target of node.targets) targets.add(target);\n }\n return [...targets];\n}\n\nfunction buildConnectionGraph(config: Flow): StepConnection[] {\n const connections: StepConnection[] = [];\n\n // Source → next transformer\n for (const [name, source] of Object.entries(config.sources || {})) {\n if (!source.next || !source.examples) continue;\n const nextNames = routeTargets(source.next);\n for (const nextName of nextNames) {\n const transformer = config.transformers?.[nextName];\n if (transformer?.examples) {\n connections.push({\n from: { type: 'source', name, examples: source.examples },\n to: {\n type: 'transformer',\n name: nextName,\n examples: transformer.examples,\n },\n });\n }\n }\n }\n\n // Transformer → next transformer\n for (const [name, transformer] of Object.entries(config.transformers || {})) {\n if (!transformer.next || !transformer.examples) continue;\n const nextNames = routeTargets(transformer.next);\n for (const nextName of nextNames) {\n const nextTransformer = config.transformers?.[nextName];\n if (nextTransformer?.examples) {\n connections.push({\n from: {\n type: 'transformer',\n name,\n examples: transformer.examples,\n },\n to: {\n type: 'transformer',\n name: nextName,\n examples: nextTransformer.examples,\n },\n });\n }\n }\n }\n\n // Destination.before → transformer chain → destination\n for (const [name, dest] of Object.entries(config.destinations || {})) {\n if (!dest.before || !dest.examples) continue;\n const beforeNames = routeTargets(dest.before);\n for (const beforeName of beforeNames) {\n const transformer = config.transformers?.[beforeName];\n if (transformer?.examples) {\n connections.push({\n from: {\n type: 'transformer',\n name: beforeName,\n examples: transformer.examples,\n },\n to: { type: 'destination', name, examples: dest.examples },\n });\n }\n }\n }\n\n return connections;\n}\n\nfunction checkCompatibility(\n flowName: string,\n conn: StepConnection,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n): void {\n const fromOuts = Object.entries(conn.from.examples)\n .filter(([, ex]) => hasComparableOut(ex.out))\n .map(([name, ex]) => ({ name, value: ex.out }));\n\n // A command example's `in` is a walker command payload, never a pushed event\n const toIns = Object.entries(conn.to.examples)\n .filter(([, ex]) => ex.in !== undefined && !ex.command)\n .map(([name, ex]) => ({ name, value: ex.in }));\n\n const path = `flows.${flowName}.${conn.from.type}s.${conn.from.name} → ${conn.to.type}s.${conn.to.name}`;\n\n if (fromOuts.length === 0 || toIns.length === 0) {\n warnings.push({\n path,\n message: 'Cannot check compatibility: missing out or in examples',\n suggestion:\n 'Add out examples to the source step or in examples to the target step',\n code: 'EXAMPLES_INCOMPLETE',\n });\n return;\n }\n\n const hasMatch = fromOuts.some((out) =>\n outEvents(out.value).some((event) =>\n toIns.some((inp) => isStructurallyCompatible(event, inp.value)),\n ),\n );\n\n if (!hasMatch) {\n errors.push({\n path,\n message: 'No compatible out/in pair found between connected steps',\n code: 'INCOMPATIBLE_EXAMPLES',\n });\n }\n}\n\n/** An effect tuple of a StepOut: a string head, then the call arguments. */\ntype StepOutEffect = [string, ...unknown[]];\n\n/**\n * A StepOut is an array whose every entry is an array with a string head\n * (`[]` included): `[['elb', event]]`, `[['return', { event }]]`, ...\n */\nfunction isStepOut(value: unknown): value is StepOutEffect[] {\n return (\n Array.isArray(value) &&\n value.every((entry) => Array.isArray(entry) && typeof entry[0] === 'string')\n );\n}\n\n/**\n * Events a `return` effect passes on: `{ event }` yields the event, a bare\n * object is the event itself, an array is a fan-out read item by item by the\n * same rule. `false` and other values pass nothing on.\n */\nfunction returnedEvents(value: unknown): unknown[] {\n if (isObject(value)) return [isObject(value.event) ? value.event : value];\n if (Array.isArray(value)) return value.flatMap(returnedEvents);\n return [];\n}\n\n/**\n * The events an example `out` hands to the next step. A StepOut yields the\n * object argument of each `elb` effect and the events of each `return`\n * effect; every other head (`message.ack`, `response`, vendor calls) yields\n * nothing. A non-StepOut value is the output itself (back compat), when it\n * is a non-empty array, string or object.\n */\nfunction outEvents(out: unknown): unknown[] {\n if (isStepOut(out)) {\n return out.flatMap(([head, first]) => {\n if (head === 'elb') return isObject(first) ? [first] : [];\n if (head === 'return') return returnedEvents(first);\n return [];\n });\n }\n if (Array.isArray(out) || typeof out === 'string')\n return out.length > 0 ? [out] : [];\n return isObject(out) && Object.keys(out).length > 0 ? [out] : [];\n}\n\nfunction hasComparableOut(out: unknown): boolean {\n return outEvents(out).length > 0;\n}\n\nfunction isStructurallyCompatible(a: unknown, b: unknown): boolean {\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) && Array.isArray(b)) return true;\n if (typeof a === 'object' && typeof b === 'object') {\n const keysA = Object.keys(a as object);\n const keysB = Object.keys(b as object);\n const shared = keysA.filter((k) => keysB.includes(k));\n return shared.length >= Math.min(keysA.length, keysB.length) * 0.5;\n }\n return true;\n}\n\n/**\n * Checks every chain field of a flow (source `before`/`next`, transformer\n * `before`/`next`, destination `before`/`next`, `collector.next`):\n *\n * - error: a route target that is not a transformer of the flow. At runtime\n * an unknown id is skipped with a warning, so a misspelled step (for\n * example a redaction branch) would otherwise fail open.\n * - warning: entries after an unconditional `stop` never run.\n * - warning: `many: []` selects nothing; `many: [\"x\"]` is just `next`.\n * - warning: an array made only of route configs is first-match.\n */\nfunction lintFlowRoutes(\n flowName: string,\n flow: Flow,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n): void {\n const known = new Set(Object.keys(flow.transformers || {}));\n const check = (\n spec: Transformer.Route | undefined,\n position: string,\n ): void => {\n if (spec !== undefined) lintRoute(spec, position, known, errors, warnings);\n };\n const at = `flows.${flowName}`;\n\n for (const [name, source] of Object.entries(flow.sources || {})) {\n check(source.before, `${at}.sources.${name}.before`);\n check(source.next, `${at}.sources.${name}.next`);\n }\n for (const [name, transformer] of Object.entries(flow.transformers || {})) {\n check(transformer.before, `${at}.transformers.${name}.before`);\n check(transformer.next, `${at}.transformers.${name}.next`);\n }\n check(flow.collector?.next, `${at}.collector.next`);\n for (const [name, dest] of Object.entries(flow.destinations || {})) {\n check(dest.before, `${at}.destinations.${name}.before`);\n check(dest.next, `${at}.destinations.${name}.next`);\n }\n}\n\ntype RoutePath = (string | number)[];\n\n/** The raw value at `path` inside a route (a plain lookup, no grammar). */\nfunction routeAt(spec: Transformer.Route, path: RoutePath): unknown {\n let current: unknown = spec;\n for (const key of path) {\n if (typeof key === 'number')\n current = Array.isArray(current) ? current[key] : undefined;\n else current = isObject(current) ? current[key] : undefined;\n }\n return current;\n}\n\n/**\n * Lints one route through `getRouteGraph`. Every branch node, and every\n * enclosing decision it lists in `via`, is looked at once by its path.\n */\nfunction lintRoute(\n spec: Transformer.Route,\n position: string,\n known: Set<string>,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n): void {\n const where = (path: RoutePath) =>\n path.length > 0 ? `${position}.${path.join('.')}` : position;\n const reported = new Set<string>();\n const once = (key: string): boolean => {\n if (reported.has(key)) return false;\n reported.add(key);\n return true;\n };\n\n for (const node of getRouteGraph(spec)) {\n for (const target of node.targets) {\n if (known.has(target) || !once(`unknown:${target}`)) continue;\n errors.push({\n path: position,\n message: `Unknown transformer \"${target}\" in route at ${position}`,\n code: 'UNKNOWN_ROUTE_TARGET',\n });\n }\n\n // Entries after an unconditional stop never run (a `many` entry's stop\n // ends only its own copy, so it does not shadow its siblings).\n const last = node.path[node.path.length - 1];\n if (node.stop && !node.match && node.kind !== 'many') {\n const self = routeAt(spec, node.path);\n const list = routeAt(spec, node.path.slice(0, -1));\n if (\n isObject(self) &&\n self.stop === true &&\n typeof last === 'number' &&\n Array.isArray(list) &&\n last < list.length - 1 &&\n once(`stop:${where(node.path)}`)\n ) {\n const at = where(node.path);\n warnings.push({\n path: at,\n message: `dead code after stop at ${at}: entries after an unconditional stop never run`,\n code: 'ROUTE_DEAD_CODE',\n suggestion:\n 'Remove the entries after the stop, or give the stop a match.',\n });\n }\n }\n\n if (\n node.kind === 'many' &&\n last === 'many' &&\n node.targets.length === 0 &&\n once(`empty:${where(node.path)}`)\n ) {\n const at = where(node.path.slice(0, -1));\n warnings.push({\n path: at,\n message: `empty many at ${at}: selects no branch, so it has no effect`,\n code: 'ROUTE_EMPTY_MANY',\n suggestion: 'Add two or more branch targets to many, or remove it.',\n });\n }\n\n for (const decision of [...(node.via ?? []), node]) {\n const index = decision.path[decision.path.length - 1];\n if (typeof index !== 'number') continue;\n const listPath = decision.path.slice(0, -1);\n const list = routeAt(spec, listPath);\n if (!Array.isArray(list)) continue;\n const at = where(listPath);\n\n if (\n decision.kind === 'many' &&\n list.length === 1 &&\n once(`single:${at}`)\n ) {\n const only = list[0];\n const hint =\n typeof only === 'string'\n ? `use 'next: \"${only}\"' for clarity`\n : `use 'next' for clarity`;\n warnings.push({\n path: at,\n message: `single-entry many at ${at}: ${hint}`,\n code: 'ROUTE_SINGLE_MANY',\n suggestion: 'Replace many with next when only one branch exists.',\n });\n }\n\n // A pure route-config array is compiled as an implicit `one`.\n const explicitOne = listPath[listPath.length - 1] === 'one';\n if (\n decision.kind === 'one' &&\n !explicitOne &&\n list.length > 1 &&\n once(`first-match:${at}`)\n ) {\n warnings.push({\n path: at,\n message: `first-match array at ${at}: an array made only of route configs is an implicit one, the first matching entry wins`,\n code: 'ROUTE_FIRST_MATCH',\n suggestion:\n 'Write { \"one\": [...] } explicitly, or add the step ids as a sequence to run every entry in order.',\n });\n }\n }\n }\n}\n\n/**\n * Whether a value holds a `$env` or `$secret` value: resolved to the\n * resolver's deferred marker, or still written as the reference.\n */\nfunction hasRuntimeValue(value: unknown): boolean {\n if (typeof value === 'string')\n return (\n value.includes(ENV_MARKER_PREFIX) ||\n value.startsWith(SECRET_MARKER_PREFIX) ||\n /\\$(?:env|secret)\\./.test(value)\n );\n if (Array.isArray(value)) return value.some(hasRuntimeValue);\n return isObject(value) && Object.values(value).some(hasRuntimeValue);\n}\n\nconst VALIDATE_PACKAGE = '@walkeros/transformer-validate';\n\n/** A transformer step that runs `@walkeros/transformer-validate`. */\nexport function isValidateStep(step: Flow.Transformer): boolean {\n return step.package === VALIDATE_PACKAGE;\n}\n\ntype StepContracts =\n | { ok: true; sources: ContractSource[] }\n | { ok: false; index?: number; message: string };\n\n/**\n * The contract sources a validate step runs with, as at runtime: a whole\n * `$contract.<name>(.<path>)` string resolves against the config-level\n * contract definitions, an inline schema is used as given. An entry that\n * does not resolve says which one and why; the step is then not judged.\n */\nfunction resolveStepContracts(\n entries: unknown[],\n contract: Flow.Contract | undefined,\n): StepContracts {\n let resolved: Record<string, Flow.ContractRule> = {};\n if (contract) {\n try {\n resolved = resolveContracts(contract);\n } catch (error) {\n return {\n ok: false,\n message: `the root contract does not resolve (${error instanceof Error ? error.message : String(error)})`,\n };\n }\n }\n\n const sources: ContractSource[] = [];\n for (const [index, entry] of entries.entries()) {\n if (isObject(entry)) {\n sources.push(entry);\n continue;\n }\n const match = typeof entry === 'string' ? entry.match(REF_CONTRACT) : null;\n if (!match)\n return {\n ok: false,\n index,\n message: 'an entry must be a $contract reference or a JSON Schema',\n };\n if (!(match[1] in resolved))\n return {\n ok: false,\n index,\n message: `contract \"${match[1]}\" is not defined in the root contract (defined: ${Object.keys(resolved).join(', ') || 'none'})`,\n };\n const rule: unknown = match[2]\n ? getByPath(resolved[match[1]], match[2])\n : resolved[match[1]];\n if (!isObject(rule))\n return {\n ok: false,\n index,\n message: `${match[0]} does not point at a contract rule object`,\n };\n sources.push(rule);\n }\n return { ok: true, sources };\n}\n\n/**\n * Check a validate step's own examples against its own contract and\n * settings: the verdict the step reaches on `in` must agree with what `out`\n * shows. A valid event goes on (its `output.isValid` flag, when shown, is\n * true); an invalid one is dropped in `strict` mode and otherwise goes on\n * with the flag false. A disagreement is CONTRACT_VIOLATION (error when\n * {@link strict}, else warning). Deterministic: the verdict comes from the\n * shared {@link validateEventAgainstContract} runtime authority.\n * `stepPath` is the step's result path, `flows.<flow>.transformers.<name>`.\n * A contract entry that does not resolve is an error (UNRESOLVED_CONTRACT),\n * examples or not; a contract setting that is not a list is a skip.\n * `resolvedSettings` are the step's settings as the resolver produced them\n * (`$var` and `$contract` resolved, as the runtime sees them); without them\n * the settings as written are read.\n */\nexport function checkValidateStepExamples(\n stepPath: string,\n step: Flow.Transformer,\n contract: Flow.Contract | undefined,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n strict: boolean,\n skipped?: ValidateSkip[],\n resolvedSettings?: Record<string, unknown>,\n): void {\n const settings =\n resolvedSettings ??\n (isObject(step.config) && isObject(step.config.settings)\n ? step.config.settings\n : {});\n const contractPath = `${stepPath}.config.settings.contract`;\n // A contract that is set but not a list cannot be judged statically.\n if (settings.contract !== undefined && !Array.isArray(settings.contract)) {\n if (step.examples)\n skipped?.push({\n path: stepPath,\n check: 'flow:contract-examples',\n reason:\n 'config.settings.contract is not a list, so the step cannot be judged statically',\n code: 'CONTRACT_NOT_STATIC',\n });\n return;\n }\n const resolution: StepContracts = Array.isArray(settings.contract)\n ? resolveStepContracts(settings.contract, contract)\n : { ok: true, sources: [] };\n if (!resolution.ok) {\n errors.push({\n path:\n resolution.index === undefined\n ? contractPath\n : `${contractPath}.${resolution.index}`,\n message: `The validate step's contract does not resolve: ${resolution.message}`,\n code: 'UNRESOLVED_CONTRACT',\n });\n return;\n }\n if (!step.examples) return;\n const contracts = resolution.sources;\n // A `$env` or `$secret` value in the contract is known only when the\n // bundle builds or runs; judged on its literal text, JSON Schema would\n // ignore it. The check is deferred, and the skip says so.\n if (contracts.some(hasRuntimeValue)) {\n skipped?.push({\n path: stepPath,\n check: 'flow:contract-examples',\n reason:\n 'the contract uses a $env or $secret value, known only when the bundle builds or runs, so the examples cannot be judged statically',\n code: 'CONTRACT_NOT_STATIC',\n });\n return;\n }\n\n const mode = settings.mode === 'strict' ? 'strict' : 'pass';\n const format = settings.format === true;\n const output = isObject(settings.output) ? settings.output : {};\n const isValidPath =\n typeof output.isValid === 'string' ? output.isValid : 'source.valid';\n\n for (const [exName, example] of Object.entries(step.examples)) {\n if (example.command || !isObject(example.in)) continue;\n if (example.out === undefined) continue;\n\n const event: WalkerOS.DeepPartialEvent = example.in;\n const verdict = validateEventAgainstContract(event, undefined, {\n contracts,\n format,\n });\n const passed = outEvents(example.out);\n const dropped =\n isStepOut(example.out) &&\n example.out.some(([head, first]) => head === 'return' && first === false);\n\n let agrees: boolean;\n if (mode === 'strict' && !verdict.isValid) {\n agrees = dropped && passed.length === 0;\n } else {\n agrees =\n passed.length > 0 &&\n passed.every((out) => {\n if (!isValidPath) return true;\n const flag = getByPath(out, isValidPath);\n return verdict.isValid ? flag !== false : flag === false;\n });\n }\n if (agrees) continue;\n\n const reason = verdict.isValid\n ? 'the event satisfies the contract'\n : `the event breaks the contract (${verdict.errors\n .map((e) => `${e.path || '/'}: ${e.message}`)\n .join('; ')})`;\n const expected =\n mode === 'strict' && !verdict.isValid\n ? 'drops it ([\"return\", false])'\n : `passes it on${isValidPath ? ` with ${isValidPath} ${verdict.isValid}` : ''}`;\n const path = `${stepPath}.examples.${exName}.out`;\n const message = `Example out disagrees with the validate step: ${reason}, so with mode \"${mode}\" the step ${expected}.`;\n\n if (strict) {\n errors.push({ path, message, code: 'CONTRACT_VIOLATION' });\n } else {\n warnings.push({\n path,\n message,\n suggestion:\n 'Make the example out show what the validate step does with its settings.',\n code: 'CONTRACT_VIOLATION',\n });\n }\n }\n}\n","// walkerOS/packages/cli/src/commands/validate/scope.ts\n\nimport type { Flow } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\nimport type { ValidateCheck, ValidateScope, ValidationError } from './types.js';\n\n/**\n * A parsed Flow.Json shape (a `version` and a `flows` object), so the\n * validators can hand it to core's resolver and preflight without casts.\n */\nexport function isFlowJson(value: unknown): value is Flow.Json {\n return isObject(value) && 'version' in value && isObject(value.flows);\n}\n\n/** Step sections a `--path` can address. */\nexport const ENTRY_SECTIONS = [\n 'sources',\n 'destinations',\n 'transformers',\n 'stores',\n] as const;\n\nexport type EntrySection = (typeof ENTRY_SECTIONS)[number];\n\nfunction isEntrySection(value: string): value is EntrySection {\n return ENTRY_SECTIONS.some((section) => section === value);\n}\n\nexport interface ScopeOptions {\n flow?: string;\n path?: string;\n}\n\nexport interface ScopeEntry {\n flow: string;\n section: EntrySection;\n key: string;\n entry: Record<string, unknown>;\n}\n\nexport interface ResolvedScope {\n /** Every flow in the file, in file order. */\n allFlows: string[];\n /** Flows every per-flow check runs on. */\n flows: string[];\n /** Entries a `--path` addressed, one per flow that has it. */\n entries: ScopeEntry[];\n /** Flows searched for the `--path` entry (empty without a path). */\n searchedFlows: string[];\n /** The entry a `--path` names, when one was given and parsed. */\n target?: { section?: EntrySection; key: string };\n /** FLOW_NOT_FOUND, ENTRY_NOT_FOUND, AMBIGUOUS_ENTRY, UNSUPPORTED_SECTION, INVALID_PATH. */\n errors: ValidationError[];\n}\n\n/**\n * The one place validate picks flows and entries. `flow` narrows to that\n * flow; without it every flow is in scope. `path` (`section.key` or `key`)\n * addresses the entry in every flow in scope that has it.\n */\nexport function resolveScope(\n config: unknown,\n options: ScopeOptions = {},\n): ResolvedScope {\n const flowsValue = isObject(config) ? config.flows : undefined;\n const flowMap = isObject(flowsValue) ? flowsValue : {};\n const allFlows = Object.keys(flowMap);\n\n let flows = allFlows;\n if (options.flow !== undefined) {\n if (!allFlows.includes(options.flow)) {\n return {\n allFlows,\n flows: [],\n entries: [],\n searchedFlows: [],\n errors: [\n {\n path: 'flows',\n message: `Flow \"${options.flow}\" not found. Available: ${allFlows.join(', ') || 'none'}`,\n code: 'FLOW_NOT_FOUND',\n },\n ],\n };\n }\n flows = [options.flow];\n }\n\n const path = options.path;\n if (path === undefined) {\n return { allFlows, flows, entries: [], searchedFlows: [], errors: [] };\n }\n\n const parts = path.split('.');\n let sections: readonly EntrySection[] = ENTRY_SECTIONS;\n let section: EntrySection | undefined;\n let key: string;\n if (parts.length === 2 && parts[0] !== '' && parts[1] !== '') {\n if (!isEntrySection(parts[0])) {\n return {\n allFlows,\n flows,\n entries: [],\n searchedFlows: [],\n errors: [\n {\n path,\n message: `Section \"${parts[0]}\" cannot be addressed by --path. Use one of: ${ENTRY_SECTIONS.join(', ')}`,\n code: 'UNSUPPORTED_SECTION',\n },\n ],\n };\n }\n section = parts[0];\n sections = [section];\n key = parts[1];\n } else if (parts.length === 1 && parts[0] !== '') {\n key = parts[0];\n } else {\n return {\n allFlows,\n flows,\n entries: [],\n searchedFlows: [],\n errors: [\n {\n path,\n message: `Invalid path \"${path}\". Use \"section.key\" or just \"key\"`,\n code: 'INVALID_PATH',\n },\n ],\n };\n }\n\n const entries: ScopeEntry[] = [];\n for (const flow of flows) {\n const flowValue = flowMap[flow];\n if (!isObject(flowValue)) continue;\n for (const candidate of sections) {\n const sectionValue = flowValue[candidate];\n if (!isObject(sectionValue)) continue;\n const entry = sectionValue[key];\n if (isObject(entry))\n entries.push({ flow, section: candidate, key, entry });\n }\n }\n\n const matched = [...new Set(entries.map((entry) => entry.section))];\n if (matched.length > 1) {\n return {\n allFlows,\n flows,\n entries: [],\n searchedFlows: flows,\n target: { key },\n errors: [\n {\n path,\n message: `Ambiguous key \"${key}\" found in multiple sections: ${matched.join(', ')}. Use dot-notation (e.g., ${matched[0]}.${key})`,\n code: 'AMBIGUOUS_ENTRY',\n },\n ],\n };\n }\n\n const target = { section: section ?? matched[0], key };\n if (entries.length === 0) {\n return {\n allFlows,\n flows,\n entries,\n searchedFlows: flows,\n target,\n errors: [\n {\n path,\n message: `Entry \"${key}\" not found in ${section ?? 'any section'} of flow(s): ${flows.join(', ') || 'none'}`,\n code: 'ENTRY_NOT_FOUND',\n },\n ],\n };\n }\n\n return {\n allFlows,\n flows,\n entries,\n searchedFlows: flows,\n target,\n errors: [],\n };\n}\n\n/** The flow a result path belongs to, or undefined for a file-level path. */\nexport function flowOfPath(\n path: string,\n flowNames: readonly string[],\n): string | undefined {\n return flowNames.find(\n (name) => path === `flows.${name}` || path.startsWith(`flows.${name}.`),\n );\n}\n\n/** `details.scope` for a resolved scope and the checks the run included. */\nexport function describeScope(\n resolved: ResolvedScope,\n checks: ValidateCheck[],\n): ValidateScope {\n const scope: ValidateScope = { flows: resolved.flows, checks };\n if (resolved.target) {\n const { section, key } = resolved.target;\n scope.entry = {\n ...(section ? { section } : {}),\n key,\n flows: [...new Set(resolved.entries.map((entry) => entry.flow))],\n searchedFlows: resolved.searchedFlows,\n };\n }\n return scope;\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/resolve.ts\n\nimport type { Flow } from '@walkeros/core';\nimport {\n FlowCycleError,\n FlowReferenceError,\n getFlowSettings,\n getErrorMessage,\n} from '@walkeros/core';\nimport type { ValidateCode, ValidationError } from '../types.js';\n\nexport type FlowResolution =\n | { ok: true; flow: Flow }\n | { ok: false; error: ValidationError };\n\n/**\n * Resolve one flow exactly as the bundler does (`getFlowSettings` with\n * deferred `$env`/`$secret`, and `$flow` refs as strict as the bundle). A\n * resolver failure is an error at `flows.<name>`: the bundle of this flow\n * fails the same way.\n */\nexport function resolveFlow(file: Flow.Json, name: string): FlowResolution {\n try {\n const flow = getFlowSettings(file, name, { deferred: true });\n return { ok: true, flow };\n } catch (error) {\n return {\n ok: false,\n error: {\n path: `flows.${name}`,\n message: getErrorMessage(error),\n code: resolutionCode(error),\n },\n };\n }\n}\n\n/** The code of a resolver failure, read from its type. */\nfunction resolutionCode(error: unknown): ValidateCode {\n if (error instanceof FlowReferenceError) return error.code;\n if (error instanceof FlowCycleError) return error.code;\n return 'UNRESOLVED_REFERENCE';\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/settings.ts\n\nimport Ajv, { type ErrorObject, type ValidateFunction } from 'ajv';\nimport type { Flow } from '@walkeros/core';\nimport {\n createLogger,\n ENV_MARKER_PREFIX,\n getErrorMessage,\n isObject,\n REF_CODE_PREFIX,\n REF_STORE,\n SECRET_MARKER_PREFIX,\n} from '@walkeros/core';\nimport {\n resolveExportName,\n type ComponentKind,\n} from '../../../core/resolve-export-name.js';\nimport {\n resolveStepPackage,\n type ResolvedStepPackage,\n} from '../../../core/step-packages.js';\nimport { readStepPackageInfo } from '../../../core/package-info.js';\nimport type { EntrySection } from '../scope.js';\nimport type {\n ValidateCheck,\n ValidateDeferred,\n ValidateSkip,\n ValidationError,\n} from '../types.js';\n\n// __VERSION__ is replaced at build time by tsup's `define` (see tsup.config.ts).\n// In tests, it's set as a global by the shared jest config (@walkeros/config/jest).\ndeclare const __VERSION__: string;\n\nconst CLIENT_HEADER = 'walkeros-cli/' + __VERSION__;\n\n// Resolving a step's package logs only a warning (a bundle pin over a\n// disagreeing inline version); the version checked is in details.packages.\nconst LOGGER = createLogger();\n\n/** One step whose settings are checked against its package schema. */\nexport interface SettingsTarget {\n flow: string;\n section: EntrySection;\n key: string;\n /** The step as written in the file (package, bundle pins). */\n entry: Record<string, unknown>;\n}\n\nexport interface SettingsContext {\n /** Directory a local `path` package resolves against; unset: no disk reads. */\n configDir?: string;\n /** Schemas fetched in this run, by package source. */\n cache: Map<string, Promise<PackageSchema>>;\n /** Settings validators compiled in this run, by package source and export. */\n compiled: Map<string, CompiledSettings | undefined>;\n}\n\nexport interface CheckedPackage {\n path: string;\n package: string;\n version?: string;\n}\n\nexport interface SettingsOutcome {\n findings: ValidationError[];\n skipped: ValidateSkip[];\n deferred: ValidateDeferred[];\n checked?: CheckedPackage;\n}\n\nexport interface PackageSchema {\n version?: string;\n /** The default export's settings schema. */\n settings: unknown;\n /** Schemas per export, keyed by export name (multi-export packages). */\n exportSchemas?: Record<string, unknown>;\n /** Export names the package declares. */\n exports?: string[];\n}\n\nexport type SettingsSelection =\n | { ok: true; settings: unknown }\n | { ok: false; reason: string };\n\n/** A settings schema compiled once per package source, export and run. */\ntype CompiledSettings =\n | { ok: true; validate: ValidateFunction }\n | { ok: false; message: string };\n\nexport function createSettingsContext(configDir?: string): SettingsContext {\n return { configDir, cache: new Map(), compiled: new Map() };\n}\n\n/**\n * The settings schema for the export a step imports, by the same rule\n * simulate uses for dev examples: no export named takes `settings`; a\n * per-export map takes that export's entry and never another export's; a\n * package declaring at most one export takes `settings` for its only step\n * export; a multi-export package without a map, or a package that does not\n * publish its exports, cannot be checked.\n */\nexport function selectSettingsSchema(\n pkg: PackageSchema,\n packageLabel: string,\n exportName: string | undefined,\n): SettingsSelection {\n if (exportName === undefined) return { ok: true, settings: pkg.settings };\n if (pkg.exportSchemas) {\n const entry = Object.prototype.hasOwnProperty.call(\n pkg.exportSchemas,\n exportName,\n )\n ? pkg.exportSchemas[exportName]\n : undefined;\n if (!isObject(entry))\n return {\n ok: false,\n reason: `${packageLabel} has no export \"${exportName}\"`,\n };\n return { ok: true, settings: entry.settings };\n }\n if (pkg.exports && pkg.exports.length < 2)\n return { ok: true, settings: pkg.settings };\n return {\n ok: false,\n reason: `${packageLabel} predates per-export settings schemas; the step imports \"${exportName}\"`,\n };\n}\n\nconst SECTION_KIND: Record<EntrySection, ComponentKind> = {\n sources: 'source',\n transformers: 'transformer',\n destinations: 'destination',\n stores: 'store',\n};\n\n/** Segments of an Ajv instance path (a JSON pointer). */\nfunction pointerSegments(pointer: string): string[] {\n if (pointer === '') return [];\n return pointer\n .split('/')\n .slice(1)\n .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));\n}\n\nfunction valueAt(root: unknown, segments: string[]): unknown {\n let current = root;\n for (const segment of segments) {\n if (Array.isArray(current)) current = current[Number(segment)];\n else if (isObject(current)) current = current[segment];\n else return undefined;\n }\n return current;\n}\n\n// The bundler's deferred-$env marker grammar (see bundle/bundler.ts): a\n// default runs until whitespace, a quote or the next marker.\nconst MARKER = ENV_MARKER_PREFIX.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\nconst ENV_MARKER = new RegExp(\n `${MARKER}([a-zA-Z_][a-zA-Z0-9_]*)(?::((?:(?!${MARKER})[^\\\\s\"'])*))?`,\n 'g',\n);\n\ninterface PreparedSettings {\n value: unknown;\n /** Instance paths known only at runtime, with the reference as written. */\n deferred: Array<{ segments: string[]; reference: string }>;\n}\n\n/**\n * The placeholder rule, applied to settings already resolved by\n * `getFlowSettings(..., { deferred: true })`: `$var` and `$flow` values are\n * already resolved (an unresolvable one fails the flow first); `$env.NAME:default`\n * is checked as its default; `$env.NAME` without default, `$secret`, `$store`\n * and `$code` are known only at runtime and deferred.\n */\nfunction prepareSettings(settings: unknown): PreparedSettings {\n const deferred: PreparedSettings['deferred'] = [];\n const walk = (value: unknown, segments: string[]): unknown => {\n if (Array.isArray(value))\n return value.map((item, index) => walk(item, [...segments, `${index}`]));\n if (isObject(value))\n return Object.fromEntries(\n Object.entries(value).map(([key, item]) => [\n key,\n walk(item, [...segments, key]),\n ]),\n );\n if (typeof value !== 'string') return value;\n\n if (value.startsWith(SECRET_MARKER_PREFIX)) {\n deferred.push({\n segments,\n reference: `$secret.${value.slice(SECRET_MARKER_PREFIX.length)}`,\n });\n return value;\n }\n if (value.includes(ENV_MARKER_PREFIX)) {\n let runtimeOnly = false;\n const withDefaults = value.replace(\n ENV_MARKER,\n (_match, _name: string, fallback: string | undefined) => {\n if (fallback === undefined) runtimeOnly = true;\n return fallback ?? '';\n },\n );\n if (!runtimeOnly) return withDefaults;\n deferred.push({\n segments,\n reference: value.replace(\n ENV_MARKER,\n (_match, name: string, fallback: string | undefined) =>\n fallback === undefined\n ? `$env.${name}`\n : `$env.${name}:${fallback}`,\n ),\n });\n return value;\n }\n if (REF_STORE.test(value) || value.startsWith(REF_CODE_PREFIX)) {\n deferred.push({ segments, reference: value });\n }\n return value;\n };\n return { value: walk(settings, []), deferred };\n}\n\nfunction isUnder(segments: string[], prefix: string[]): boolean {\n return prefix.every((segment, index) => segments[index] === segment);\n}\n\nasync function loadSchema(\n resolved: ResolvedStepPackage,\n context: SettingsContext,\n): Promise<PackageSchema> {\n const info = await readStepPackageInfo(resolved, {\n configDir: context.configDir,\n client: CLIENT_HEADER,\n });\n return {\n version: info.version,\n settings: info.schemas.settings,\n ...(info.exportSchemas ? { exportSchemas: info.exportSchemas } : {}),\n ...(info.exports ? { exports: Object.keys(info.exports) } : {}),\n };\n}\n\n/**\n * Compile a package's settings schema. A schema Ajv cannot compile (an\n * unknown keyword, a bad `$ref`) cannot be checked against, so it is a\n * failed compile the caller reports as a skip, never a crash of the run.\n */\nfunction compileSettings(settings: unknown): CompiledSettings | undefined {\n if (!isObject(settings)) return undefined;\n // Formats such as `uri` and `email` are not bundled with Ajv; ignore them\n // instead of failing to compile. Every structural keyword is still checked.\n const ajv = new Ajv({ allErrors: true, validateFormats: false });\n try {\n return { ok: true, validate: ajv.compile(settings) };\n } catch (error) {\n return { ok: false, message: getErrorMessage(error) };\n }\n}\n\nfunction sourceKey({ packageName, spec }: ResolvedStepPackage): string {\n return spec.path !== undefined\n ? `path:${spec.path}`\n : `${packageName}@${spec.version ?? 'latest'}`;\n}\n\nfunction cachedSchema(\n resolved: ResolvedStepPackage,\n context: SettingsContext,\n): Promise<PackageSchema> {\n const key = sourceKey(resolved);\n let pending = context.cache.get(key);\n if (!pending) {\n pending = loadSchema(resolved, context);\n context.cache.set(key, pending);\n }\n return pending;\n}\n\nfunction cachedCompile(\n resolved: ResolvedStepPackage,\n exportName: string | undefined,\n settings: unknown,\n context: SettingsContext,\n): CompiledSettings | undefined {\n const key = `${sourceKey(resolved)}#${exportName ?? ''}`;\n if (context.compiled.has(key)) return context.compiled.get(key);\n const compiled = compileSettings(settings);\n context.compiled.set(key, compiled);\n return compiled;\n}\n\n/**\n * Check one step's resolved `config.settings` against its package's\n * settings schema, at the version the bundle would install. Findings are\n * returned for the caller to report as errors or warnings; a check that\n * cannot run is a skip; values known only at runtime are deferred.\n */\nexport async function checkStepSettings(\n target: SettingsTarget,\n resolvedFlow: Flow,\n check: ValidateCheck,\n context: SettingsContext,\n): Promise<SettingsOutcome> {\n const at = `flows.${target.flow}.${target.section}.${target.key}`;\n const outcome: SettingsOutcome = { findings: [], skipped: [], deferred: [] };\n const spec =\n typeof target.entry.package === 'string' ? target.entry.package : '';\n if (!spec) {\n outcome.skipped.push({\n path: at,\n check,\n reason: 'No package field, so there is no settings schema to fetch',\n code: 'NO_PACKAGE',\n });\n return outcome;\n }\n\n // The package source the bundle would install for this step.\n let resolved: ResolvedStepPackage;\n try {\n resolved = resolveStepPackage(\n resolvedFlow,\n SECTION_KIND[target.section],\n target.key,\n LOGGER,\n );\n } catch (error) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `Schema for ${spec} could not be loaded: ${getErrorMessage(error)}`,\n code: 'SCHEMA_UNAVAILABLE',\n });\n return outcome;\n }\n const { label } = resolved;\n const pinned =\n resolved.spec.path === undefined ? resolved.spec.version : undefined;\n\n let schema: PackageSchema;\n try {\n schema = await cachedSchema(resolved, context);\n } catch (error) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `Schema for ${label}${pinned ? `@${pinned}` : ''} could not be loaded: ${getErrorMessage(error)}`,\n code: 'SCHEMA_UNAVAILABLE',\n });\n return outcome;\n }\n outcome.checked = {\n path: at,\n package: label,\n ...(schema.version ? { version: schema.version } : {}),\n };\n\n // The export the step imports, resolved as the bundler resolves it.\n const { exportName } = resolveExportName(\n resolved.normalizedFlow,\n SECTION_KIND[target.section],\n target.key,\n );\n const version = schema.version ?? pinned;\n const selection = selectSettingsSchema(\n schema,\n version ? `${label}@${version}` : label,\n exportName,\n );\n if (!selection.ok) {\n outcome.skipped.push({\n path: at,\n check,\n reason: selection.reason,\n code: 'NO_SETTINGS_SCHEMA',\n });\n return outcome;\n }\n\n const compiled = cachedCompile(\n resolved,\n exportName,\n selection.settings,\n context,\n );\n if (!compiled) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `Package ${label} has no settings schema`,\n code: 'NO_SETTINGS_SCHEMA',\n });\n return outcome;\n }\n if (!compiled.ok) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `The settings schema of ${label} cannot be compiled: ${compiled.message}`,\n code: 'SCHEMA_UNAVAILABLE',\n });\n return outcome;\n }\n const validate = compiled.validate;\n\n const step = stepOf(resolvedFlow, target.section, target.key);\n const config = isObject(step?.config) ? step.config : {};\n const prepared = prepareSettings(config.settings ?? {});\n const base = `${at}.config.settings`;\n for (const { segments, reference } of prepared.deferred) {\n outcome.deferred.push({\n path: [base, ...segments].join('.'),\n reference,\n });\n }\n\n if (validate(prepared.value)) return outcome;\n\n for (const e of validate.errors || []) {\n const segments = pointerSegments(e.instancePath);\n if (prepared.deferred.some((d) => isUnder(segments, d.segments))) continue;\n const finding: ValidationError = {\n path: [base, ...segments].join('.'),\n message: describeAjvError(e),\n code: 'ENTRY_SCHEMA',\n keyword: e.keyword,\n };\n if (segments.length > 0) finding.value = valueAt(prepared.value, segments);\n outcome.findings.push(finding);\n }\n return outcome;\n}\n\n/** Ajv's message, naming the offending key where Ajv only reports it in params. */\nfunction describeAjvError(error: ErrorObject): string {\n const message = error.message || 'Unknown error';\n const extra: unknown = error.params.additionalProperty;\n return error.keyword === 'additionalProperties' && typeof extra === 'string'\n ? `${message}: \"${extra}\"`\n : message;\n}\n\nfunction stepOf(\n flow: Flow,\n section: EntrySection,\n key: string,\n): Flow.Step | undefined {\n switch (section) {\n case 'sources':\n return flow.sources?.[key];\n case 'destinations':\n return flow.destinations?.[key];\n case 'transformers':\n return flow.transformers?.[key];\n case 'stores':\n return flow.stores?.[key];\n }\n}\n","/**\n * Shared resolver for the export name to import from a flow component's\n * package.\n *\n * Both `walkeros bundle` and `walkeros setup` need to know which named export\n * (or default) to pull off a package referenced by a flow component. The\n * precedence chain is:\n *\n * 1. `flow.<kind>.<id>.import` if set (named export selector at the step).\n * 2. `flow.config.bundle.packages[<package>].imports[0]` if set.\n * 3. Otherwise undefined; the caller falls back to the package's `default`\n * export.\n *\n * This lives in `cli/src/core/` so both `commands/bundle/*` and\n * `commands/setup/*` import the same function. Duplicating the chain is how\n * setup ended up out of sync with bundle (Bug F: `setup destination.pubsub`\n * routed through BigQuery's setup because it ignored the `code` field and\n * always grabbed `mod.default`).\n */\n\nimport type { Flow, StepType } from '@walkeros/core';\n\nexport type ComponentKind = StepType;\n\nexport type ResolveSource = 'import' | 'imports' | 'default';\n\nexport interface ResolvedExportName {\n /**\n * The named export to pull off the imported module, or `undefined` to\n * indicate the caller should use the package's `default` export.\n */\n exportName: string | undefined;\n /** Which precedence step produced the result (useful for diagnostics). */\n source: ResolveSource;\n}\n\ninterface FlowStepView {\n package?: string;\n import?: string;\n}\n\nfunction getStep(\n flow: Flow,\n kind: ComponentKind,\n id: string,\n): FlowStepView | undefined {\n switch (kind) {\n case 'source':\n return flow.sources?.[id];\n case 'transformer':\n return flow.transformers?.[id];\n case 'destination':\n return flow.destinations?.[id];\n case 'store':\n return flow.stores?.[id];\n }\n}\n\nfunction getBundlePackages(\n flow: Flow,\n): Record<string, Flow.BundlePackage> | undefined {\n return flow.config?.bundle?.packages;\n}\n\n/**\n * Read the first entry of `bundle.packages[<package>].imports` if it is a\n * non-empty string. Returns `undefined` otherwise.\n */\nfunction readFirstImport(\n packages: Record<string, Flow.BundlePackage> | undefined,\n packageName: string | undefined,\n): string | undefined {\n if (!packages || !packageName) return undefined;\n const pkg = packages[packageName];\n if (!pkg || !pkg.imports || pkg.imports.length === 0) return undefined;\n const first = pkg.imports[0];\n return typeof first === 'string' && first.length > 0 ? first : undefined;\n}\n\n/**\n * Resolve the export name to import for a flow component's package.\n *\n * See module docs for the precedence chain. Inline code (`code` is an object)\n * doesn't enter this resolver at all — setup rejects inline code at a higher\n * layer.\n */\nexport function resolveExportName(\n flow: Flow,\n kind: ComponentKind,\n id: string,\n): ResolvedExportName {\n const step = getStep(flow, kind, id);\n if (!step) return { exportName: undefined, source: 'default' };\n\n const packageName = step.package;\n const importName = step.import;\n\n // 1. Explicit `import` field on the step wins.\n if (typeof importName === 'string' && importName.length > 0) {\n return { exportName: importName, source: 'import' };\n }\n\n // 2. Fallback to bundle.packages[pkg].imports[0].\n const firstImport = readFirstImport(getBundlePackages(flow), packageName);\n if (firstImport) {\n return { exportName: firstImport, source: 'imports' };\n }\n\n // 3. Default export.\n return { exportName: undefined, source: 'default' };\n}\n","/**\n * Step-package normalization shared by bundle, setup, validate and the MCP's\n * flow_examples.\n *\n * Pure flow-config logic: which packages do the flow's steps declare, and\n * what is the effective {name, version | path} for each after merging inline\n * step specs with config.bundle.packages pins. Lives in core/ so every\n * command and tool that reads a step's package runs the exact same precedence\n * (a bundle pin always wins; an unversioned bundle entry is filled from the\n * first inline version; conflicting inline versions with no pin throw;\n * local paths become synthetic packageNameToVariable keys).\n */\nimport type { Flow, Logger } from '@walkeros/core';\nimport { packageNameToVariable } from '@walkeros/core';\nimport type { ComponentKind } from './resolve-export-name.js';\n\n/**\n * Type-narrowed accessor for a Flow section. Returns the typed step record\n * (or undefined) — exhaustive switch over the literal-union parameter avoids\n * a generic indexed-access cast.\n *\n * Returns the union of all section types when the caller passes a runtime\n * variable. Call sites that need a specific section type access the field\n * directly (e.g. `flow.sources`).\n */\nexport type FlowStepRecord =\n | Record<string, Flow.Source>\n | Record<string, Flow.Destination>\n | Record<string, Flow.Transformer>\n | Record<string, Flow.Store>;\nexport function getFlowSection(\n flow: Flow,\n section: 'sources' | 'destinations' | 'transformers' | 'stores',\n): FlowStepRecord | undefined {\n switch (section) {\n case 'sources':\n return flow.sources;\n case 'destinations':\n return flow.destinations;\n case 'transformers':\n return flow.transformers;\n case 'stores':\n return flow.stores;\n }\n}\n\n/** A step package spec that is a local path (`./x`, `../x`, `/abs/x`). */\nfunction isLocalPath(spec: string): boolean {\n return spec.startsWith('.') || spec.startsWith('/');\n}\n\n/**\n * Split a step package spec into bare name and optional version suffix.\n * `@walkeros/x@1.2.3` → { name: '@walkeros/x', version: '1.2.3' }.\n * The scope `@` at index 0 is never a separator. A trailing `@` is ignored.\n * Alias/git/file suffixes are not interpreted — the resolver handles or\n * rejects them downstream.\n */\nexport function parsePackageSpec(spec: string): {\n name: string;\n version?: string;\n} {\n const at = spec.lastIndexOf('@');\n if (at <= 0) return { name: spec };\n const version = spec.slice(at + 1);\n return version\n ? { name: spec.slice(0, at), version }\n : { name: spec.slice(0, at) };\n}\n\n/**\n * Detects destination packages from flow configuration.\n * Extracts package names from destinations that have explicit 'package' field.\n */\n/**\n * Detects packages from a flow config section (sources, destinations, transformers, stores).\n * Extracts package names from steps that have an explicit 'package' field.\n */\nexport function detectStepPackages(\n flowSettings: Flow,\n section: 'sources' | 'destinations' | 'transformers' | 'stores',\n): Set<string> {\n const packages = new Set<string>();\n const steps = getFlowSection(flowSettings, section);\n\n if (steps) {\n for (const [, stepConfig] of Object.entries(steps)) {\n if (typeof stepConfig !== 'object' || stepConfig === null) continue;\n // Require explicit package field\n if (typeof stepConfig.package === 'string') {\n packages.add(stepConfig.package);\n }\n }\n }\n\n return packages;\n}\n\n/**\n * Collects all package names declared in flow steps.\n * Returns both npm packages and local paths — caller handles routing.\n */\nexport function collectAllStepPackages(flowSettings: Flow): Set<string> {\n const allPackages = new Set<string>();\n const sections = [\n 'sources',\n 'destinations',\n 'transformers',\n 'stores',\n ] as const;\n\n for (const section of sections) {\n for (const pkg of detectStepPackages(flowSettings, section)) {\n allPackages.add(pkg);\n }\n }\n\n return allPackages;\n}\n\n/**\n * Auto-adds every step-declared package (sources, destinations, transformers,\n * stores) to `packages`, mutating `flowSettings` in place so each step's\n * `package` field points at the key that ends up in `packages`.\n *\n * Local paths (`.` or `/` prefixed) are normalized to a synthetic\n * `packageNameToVariable` key so the regular default-import codegen wires\n * them up automatically.\n *\n * npm specs go through `parsePackageSpec` to split an inline version\n * (`@walkeros/x@1.2.3`) from the bare name. Precedence policy:\n * - An explicit `config.bundle.packages` version always wins; a disagreeing\n * inline version only warns (bundle pin is authoritative).\n * - An unversioned bundle entry is filled from the first inline version seen\n * for that bare name, preserving any other fields already on the entry.\n * - Two different inline versions for the same bare name, with no bundle\n * pin to arbitrate, are ambiguous — throw naming both steps' versions\n * rather than silently picking one.\n * - Identical inline versions across steps are fine (no-op).\n * Alias/git/file suffixes are not special-cased: `parsePackageSpec` splits on\n * the last `@` and the resolver handles or rejects the rest downstream.\n */\nexport function applyStepPackages(\n flowSettings: Flow,\n packages: Record<string, Flow.BundlePackage>,\n logger: Logger.Instance,\n): void {\n const stepPackages = collectAllStepPackages(flowSettings);\n // Bundle-pinned version per bare name, captured the first time each name is\n // encountered — i.e. before this function's own fill-ins can be mistaken\n // for a real `config.bundle.packages` pin on a later iteration.\n const originalPins = new Map<string, boolean>();\n // Inline version already seen per bare name (with no real bundle pin),\n // used to detect a second, different inline version for the same name.\n const inlineSeen = new Map<string, string>();\n\n const rewriteSteps = (from: string, to: string): void => {\n for (const section of [\n 'sources',\n 'destinations',\n 'transformers',\n 'stores',\n ] as const) {\n const steps = getFlowSection(flowSettings, section);\n if (!steps) continue;\n for (const step of Object.values(steps)) {\n if (step.package === from) {\n step.package = to;\n }\n }\n }\n };\n\n for (const pkg of stepPackages) {\n if (isLocalPath(pkg)) {\n // Normalize: convert path-based package: to packages section entry.\n // The synthetic key acts as the package name for downstream codegen,\n // so the regular default-import flow wires it up automatically.\n const varName = packageNameToVariable(pkg);\n if (!packages[varName]) {\n packages[varName] = {\n path: pkg,\n };\n }\n\n // Rewrite all components that reference the raw path to point at the\n // synthetic packages-section key instead.\n rewriteSteps(pkg, varName);\n continue;\n }\n\n const { name, version } = parsePackageSpec(pkg);\n\n if (!originalPins.has(name)) {\n const entry = packages[name];\n // A `path` entry is a pin too: local paths win at resolution, so the\n // inline versions the steps declare can never be used.\n originalPins.set(\n name,\n entry?.version !== undefined || entry?.path !== undefined,\n );\n }\n const hasBundlePin = originalPins.get(name) === true;\n\n if (name !== pkg) {\n // Rewrite every step that declared the versioned spec to the bare\n // name, mirroring the local-path rewrite above.\n rewriteSteps(pkg, name);\n }\n\n if (version && !hasBundlePin) {\n const seen = inlineSeen.get(name);\n if (seen !== undefined && seen !== version) {\n throw new Error(\n `Conflicting inline versions for ${name}: \"${seen}\" and \"${version}\" are ` +\n `declared by different steps. Pin one version in config.bundle.packages.`,\n );\n }\n inlineSeen.set(name, version);\n }\n\n const existing = packages[name];\n if (!existing) {\n packages[name] = version ? { version } : {};\n } else if (version && !existing.path) {\n // A path entry decides on its own; stamping a version onto it would\n // record a version that resolution never consults.\n if (!existing.version) {\n existing.version = version; // fill an unversioned bundle entry\n } else if (existing.version !== version) {\n logger.warn(\n `Package ${name}: config.bundle.packages pins ${existing.version}; ` +\n `a step declares ${version} inline. Using the bundle pin.`,\n );\n }\n }\n }\n}\n\nexport interface ResolvedStepPackage {\n /** Bare package name after normalization (inline version stripped, local path replaced by synthetic key). */\n packageName: string;\n /** The package as messages and results name it: the bare npm name, or a step's own local path as written. */\n label: string;\n /** Effective bundle entry: version pin and/or local path per bundle precedence. */\n spec: Flow.BundlePackage;\n /** flow.config.bundle.overrides, for transitive pins. */\n overrides: Record<string, string>;\n /** Deep clone of the flow with step.package rewritten to bare names; the input flow is never mutated. */\n normalizedFlow: Flow;\n}\n\nconst KIND_TO_SECTION = {\n source: 'sources',\n transformer: 'transformers',\n destination: 'destinations',\n store: 'stores',\n} as const;\n\n/**\n * Resolve the effective package spec for one step by running the SAME\n * normalization the bundler runs over the whole flow (applyStepPackages),\n * on a clone so the caller's flow stays untouched. The clone comes back as\n * `normalizedFlow` with `step.package` rewritten to the bare name, which is\n * also what resolveExportName needs to match `bundle.packages[pkg].imports`.\n */\nexport function resolveStepPackage(\n flow: Flow,\n kind: ComponentKind,\n id: string,\n logger: Logger.Instance,\n): ResolvedStepPackage {\n const normalizedFlow = structuredClone(flow);\n const packages: Record<string, Flow.BundlePackage> = structuredClone(\n normalizedFlow.config?.bundle?.packages ?? {},\n );\n applyStepPackages(normalizedFlow, packages, logger);\n\n const bucket = getFlowSection(normalizedFlow, KIND_TO_SECTION[kind]);\n const step = bucket?.[id];\n if (!step) {\n const available = Object.keys(bucket ?? {}).join(', ') || '(none)';\n throw new Error(\n `${kind} \"${id}\" not found in flow. Available: ${available}`,\n );\n }\n if (typeof step.package !== 'string') {\n throw new Error(\n `${kind}.${id} has no \"package\" (inline code cannot be set up).`,\n );\n }\n\n const declared = getFlowSection(flow, KIND_TO_SECTION[kind])?.[id]?.package;\n return {\n packageName: step.package,\n label:\n declared !== undefined && isLocalPath(declared) ? declared : step.package,\n spec: packages[step.package] ?? {},\n overrides: normalizedFlow.config?.bundle?.overrides ?? {},\n normalizedFlow,\n };\n}\n","/**\n * The walkerOS metadata of a step's package, read from the source\n * resolveStepPackage picked: a local path from its walkerOS.json on disk,\n * any other package from the CDN at the pinned version. Every tool that has\n * a flow reads a step's package here, so all of them read the same source\n * the bundle installs.\n *\n * Node built-ins only: this module is reachable from the `dev` and\n * `validate` entries, which a server process loads without the bundler or\n * the package manager.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { WalkerOSPackage } from '@walkeros/core';\nimport { fetchPackage, isObject, parseWalkerOSJson } from '@walkeros/core';\nimport type { ResolvedStepPackage } from './step-packages.js';\n\n/** A package's walkerOS.json content, plus the version read (absent for a local package whose package.json names none). */\nexport type StepPackageInfo = Omit<\n WalkerOSPackage,\n 'packageName' | 'version' | 'description'\n> & {\n version?: string;\n};\n\nfunction readJson(file: string): unknown {\n return JSON.parse(fs.readFileSync(file, 'utf-8'));\n}\n\n/**\n * Read the walkerOS metadata of a resolved step package. A local path\n * resolves against `configDir`; without one nothing is read from disk and\n * the read fails. `client` names the calling tool in the CDN request.\n */\nexport async function readStepPackageInfo(\n resolved: Pick<ResolvedStepPackage, 'packageName' | 'spec'>,\n options: { configDir?: string; client: string },\n): Promise<StepPackageInfo> {\n const { packageName, spec } = resolved;\n if (spec.path === undefined)\n return fetchPackage(packageName, {\n ...(spec.version ? { version: spec.version } : {}),\n client: options.client,\n });\n\n if (options.configDir === undefined)\n throw new Error(\n 'it is a local path package and the input is not a file, so there is no directory to read it from',\n );\n const root = path.isAbsolute(spec.path)\n ? spec.path\n : path.resolve(options.configDir, spec.path);\n const file = [\n path.join(root, 'dist', 'walkerOS.json'),\n path.join(root, 'walkerOS.json'),\n ].find((candidate) => fs.existsSync(candidate));\n if (!file) throw new Error(`no walkerOS.json under ${root}`);\n const meta = readJson(file);\n if (!isObject(meta)) throw new Error(`${file} is not a JSON object`);\n const pkgFile = path.join(root, 'package.json');\n const pkg = fs.existsSync(pkgFile) ? readJson(pkgFile) : undefined;\n const version =\n isObject(pkg) && typeof pkg.version === 'string' ? pkg.version : undefined;\n return { ...parseWalkerOSJson(meta), ...(version ? { version } : {}) };\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/mapping.ts\n\nimport { isObject } from '@walkeros/core';\nimport { schemas } from '@walkeros/core/dev';\nimport type {\n ValidateDetails,\n ValidateResult,\n ValidationError,\n ValidationWarning,\n} from '../types.js';\n\nconst { RuleSchema } = schemas.MappingSchemas;\n\nfunction isRule(value: unknown): boolean {\n return isObject(value) && RuleSchema.safeParse(value).success;\n}\n\n/**\n * The nested runtime shape: an entity key whose value maps actions to a rule\n * or a list of rules (`{ page: { view: { name: 'page_view' } } }`).\n */\nfunction isNestedActions(\n key: string,\n value: unknown,\n): value is Record<string, unknown> {\n if (key.includes(' ') || !isObject(value)) return false;\n const rules = Object.values(value);\n return (\n rules.length > 0 &&\n rules.every((rule) =>\n Array.isArray(rule)\n ? rule.length > 0 && rule.every(isRule)\n : isRule(rule),\n )\n );\n}\n\nexport function validateMapping(input: unknown): ValidateResult {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n const details: ValidateDetails = {};\n\n // Must be an object\n if (!isObject(input)) {\n errors.push({\n path: 'root',\n message: 'Mapping must be an object with event patterns as keys',\n code: 'INVALID_MAPPING_TYPE',\n });\n return { valid: false, type: 'mapping', errors, warnings, details };\n }\n\n const mapping = input;\n const nestedPatterns: string[] = [];\n const flatPatterns: string[] = [];\n for (const [key, value] of Object.entries(mapping)) {\n if (isNestedActions(key, value)) {\n for (const action of Object.keys(value))\n nestedPatterns.push(`${key} ${action}`);\n } else {\n flatPatterns.push(key);\n }\n }\n const patterns = [...nestedPatterns, ...flatPatterns];\n details.eventPatterns = patterns;\n details.patternCount = patterns.length;\n\n // Validate each flat \"entity action\" pattern (legacy flat shape)\n flatPatterns.forEach((pattern, index) => {\n // Check pattern format: must be \"entity action\", contain wildcard, or be \"*\"\n const isWildcard = pattern.includes('*');\n const hasSpace = pattern.includes(' ');\n\n if (!isWildcard && !hasSpace) {\n errors.push({\n path: pattern,\n message: `Invalid event pattern \"${pattern}\". Must be \"entity action\" format or contain wildcard (*)`,\n code: 'INVALID_EVENT_PATTERN',\n });\n }\n\n // Warn if catch-all is not last\n if (pattern === '*' && index !== flatPatterns.length - 1) {\n warnings.push({\n path: '*',\n code: 'CATCH_ALL_NOT_LAST',\n message: 'Catch-all pattern (*) should be last',\n suggestion:\n 'Move the catch-all pattern (*) to last position for predictable matching',\n });\n }\n\n // Validate rule structure (accepts single rule object or array of rules)\n const rule = mapping[pattern];\n const isValidRule = Array.isArray(rule)\n ? rule.every((r) => typeof r === 'object' && r !== null)\n : typeof rule === 'object' && rule !== null;\n\n if (!isValidRule) {\n errors.push({\n path: pattern,\n message: 'Mapping rule must be an object or array of objects',\n code: 'INVALID_RULE_TYPE',\n });\n }\n });\n\n return {\n valid: errors.length === 0,\n type: 'mapping',\n errors,\n warnings,\n details,\n };\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/entry.ts\n\nimport { describeScope, isFlowJson, resolveScope } from '../scope.js';\nimport type {\n ValidateDeferred,\n ValidateResult,\n ValidateSkip,\n ValidationError,\n} from '../types.js';\nimport { resolveFlow, type FlowResolution } from './resolve.js';\nimport {\n checkStepSettings,\n createSettingsContext,\n type CheckedPackage,\n} from './settings.js';\n\n/**\n * Validate an entry (source, destination, transformer or store) addressed by\n * `path` (`section.key` or `key`) against its package's published JSON\n * Schema, in every flow that has it, or only in `options.flow`. Settings are\n * checked as the flow resolves them (the placeholder rule); a flow that does\n * not resolve is an error and its entry check a skip.\n */\nexport async function validateEntry(\n path: string,\n flowConfig: unknown,\n options: { flow?: string; configDir?: string } = {},\n): Promise<ValidateResult> {\n const resolved = resolveScope(flowConfig, { flow: options.flow, path });\n const errors: ValidationError[] = [...resolved.errors];\n const skipped: ValidateSkip[] = [];\n const deferred: ValidateDeferred[] = [];\n const packages: CheckedPackage[] = [];\n const context = createSettingsContext(options.configDir);\n const file = isFlowJson(flowConfig) ? flowConfig : undefined;\n const resolutions = new Map<string, FlowResolution>();\n\n for (const target of resolved.entries) {\n const at = `flows.${target.flow}.${target.section}.${target.key}`;\n let resolution = resolutions.get(target.flow);\n if (!resolution && file) {\n resolution = resolveFlow(file, target.flow);\n resolutions.set(target.flow, resolution);\n if (!resolution.ok) errors.push(resolution.error);\n }\n if (!resolution?.ok) {\n skipped.push({\n path: at,\n check: 'entry:settings',\n reason: `flows.${target.flow} does not resolve, so its settings cannot be checked`,\n code: 'GATED_BY_ERRORS',\n });\n continue;\n }\n const outcome = await checkStepSettings(\n target,\n resolution.flow,\n 'entry:settings',\n context,\n );\n errors.push(...outcome.findings);\n skipped.push(...outcome.skipped);\n deferred.push(...outcome.deferred);\n if (outcome.checked) packages.push(outcome.checked);\n }\n\n return {\n valid: errors.length === 0,\n type: 'entry',\n errors,\n warnings: [],\n details: {\n scope: describeScope(resolved, ['entry:settings']),\n skipped,\n deferred,\n ...(packages.length > 0 ? { packages } : {}),\n },\n };\n}\n","// walkerOS/packages/cli/src/commands/validate/types.ts\n\nimport type {\n ValidateResult as CoreValidateResult,\n ValidationError as CoreValidationError,\n ValidationType,\n ValidationWarning as CoreValidationWarning,\n} from '@walkeros/core/dev';\n\n// Base result types are declared in @walkeros/core/dev beside\n// validateFlowStructure, so consumers can type its return without the CLI.\n// The CLI narrows them: every code comes from VALIDATE_CODES, and the\n// details carry the scope, skips and deferred values of the run.\nexport type { ValidateResultType, ValidationType } from '@walkeros/core/dev';\n\n/**\n * Every code an error, warning or skip of `validate` can carry. Agents and\n * tests branch on these, never on message text.\n */\nexport const VALIDATE_CODES = [\n // Input and options\n 'INPUT_ERROR',\n 'SERIALIZATION_ERROR',\n 'OPTION_NOT_APPLICABLE',\n // Scope (--flow, --path)\n 'FLOW_NOT_FOUND',\n 'ENTRY_NOT_FOUND',\n 'AMBIGUOUS_ENTRY',\n 'UNSUPPORTED_SECTION',\n 'INVALID_PATH',\n // Flow file\n 'SCHEMA_VALIDATION',\n 'CONFIG_WARNING',\n 'EMPTY_FLOWS',\n 'UNKNOWN_KEY',\n 'CONFLICT',\n 'MISSING_PACKAGE',\n 'OBSOLETE_CODE_STRING',\n 'INVALID_IMPORT',\n 'INVALID_CODE_SHAPE',\n 'PACKAGE_VERSION_MISSING',\n 'DEPRECATED_PACKAGE',\n 'UNKNOWN_ROUTE_TARGET',\n 'ROUTE_DEAD_CODE',\n 'ROUTE_EMPTY_MANY',\n 'ROUTE_SINGLE_MANY',\n 'ROUTE_FIRST_MATCH',\n 'INCOMPATIBLE_EXAMPLES',\n 'EXAMPLES_INCOMPLETE',\n 'CONTRACT_VIOLATION',\n 'MAPPING_DOT_KEY',\n 'FLOW_REF_WARNING',\n 'WEB_LOGGER_FORMAT',\n 'FLOW_CYCLE',\n 'VAR_CYCLE',\n // Structure (the bundler's own preflight, validateFlowStructure)\n 'INVALID_COMPONENT_NAME',\n 'INVALID_REFERENCE',\n 'STORE_REFERENCE_NOT_FOUND',\n 'STATE_STORE_UNKNOWN',\n 'STATE_STORE_FILE',\n 'MAPPING_NO_OP',\n 'MAPPING_VALUE_NO_OPERATOR',\n 'MAPPING_VALUE_PRODUCERS',\n 'BATCH_WITHOUT_PUSH_BATCH',\n 'MALFORMED_REFERENCE',\n // Resolution (getFlowSettings, as the bundler resolves the flow)\n 'UNRESOLVED_REFERENCE',\n 'MISSING_ENV',\n 'WEB_SECRET_REF',\n 'UNRESOLVED_CONTRACT',\n // Entry settings against the package schema\n 'ENTRY_SCHEMA',\n // Contract\n 'NO_CONTRACT_SECTION',\n 'INVALID_CONTRACT',\n 'FLAT_CONTRACT_SHAPE',\n 'INVALID_CONTRACT_ENTRY',\n 'UNKNOWN_CONTRACT_KEY',\n 'INVALID_TAGGING',\n 'INVALID_SCHEMA',\n 'INVALID_EXTENDS',\n 'INVALID_EVENTS',\n 'CIRCULAR_EXTENDS',\n 'INVALID_ENTITY_KEY',\n 'INVALID_ENTITY',\n 'INVALID_ACTION_KEY',\n 'INVALID_SCHEMA_ENTRY',\n // Event\n 'NOT_AN_OBJECT',\n 'MISSING_EVENT_NAME',\n 'EMPTY_EVENT_NAME',\n 'INVALID_EVENT_NAME',\n // Mapping\n 'INVALID_MAPPING_TYPE',\n 'INVALID_EVENT_PATTERN',\n 'INVALID_RULE_TYPE',\n 'CATCH_ALL_NOT_LAST',\n // Skips (details.skipped[].code)\n 'GATED_BY_ERRORS',\n 'CONTRACT_NOT_STATIC',\n 'NO_PACKAGE',\n 'NO_SETTINGS_SCHEMA',\n 'SCHEMA_UNAVAILABLE',\n] as const;\n\nexport type ValidateCode = (typeof VALIDATE_CODES)[number];\n\n/** Codes that mean validate could not run as asked (CLI exit 3). */\nexport const USAGE_CODES: readonly ValidateCode[] = ['OPTION_NOT_APPLICABLE'];\n\n/**\n * Named checks a run includes (`details.scope.checks`). `file:` checks run\n * on the whole file whatever `--flow` says; `flow:` checks run per flow in\n * scope; `entry:` checks run per entry `--path` addressed.\n */\nexport type ValidateCheck =\n | 'file:schema'\n | 'file:contract'\n | 'flow:schema'\n | 'flow:steps'\n | 'flow:structure'\n | 'flow:package-versions'\n | 'flow:routes'\n | 'flow:examples'\n | 'flow:contract-examples'\n | 'flow:mapping-keys'\n | 'flow:resolve'\n | 'flow:package-settings'\n | 'entry:settings'\n | 'contract'\n | 'event'\n | 'mapping';\n\nexport interface ValidateScopeEntry {\n /** Section of the addressed entry, when the path or the match names one. */\n section?: string;\n key: string;\n /** Flows that have the entry and were checked. */\n flows: string[];\n /** Flows searched for the entry. */\n searchedFlows: string[];\n}\n\n/** What a run covered (C6). */\nexport interface ValidateScope {\n flows: string[];\n entry?: ValidateScopeEntry;\n checks: ValidateCheck[];\n /** Set when `offline` removed the package settings check from `checks`. */\n offline?: true;\n}\n\n/** A check the scope includes that did not run (C5). */\nexport interface ValidateSkip {\n path: string;\n check: ValidateCheck;\n reason: string;\n code: ValidateCode;\n}\n\n/** A value known only at runtime, not schema-checked (info, not a skip). */\nexport interface ValidateDeferred {\n path: string;\n reference: string;\n}\n\nexport interface ValidateDetails {\n scope?: ValidateScope;\n skipped?: ValidateSkip[];\n deferred?: ValidateDeferred[];\n [key: string]: unknown;\n}\n\n// `code` stays core's `string` for consumers; validate emits only\n// ValidateCode values.\nexport interface ValidationError extends CoreValidationError {\n /** The JSON Schema keyword of an ENTRY_SCHEMA error. */\n keyword?: string;\n}\n\nexport interface ValidationWarning extends CoreValidationWarning {\n keyword?: string;\n}\n\nexport interface ValidateResult extends CoreValidateResult {\n errors: ValidationError[];\n warnings: ValidationWarning[];\n details: ValidateDetails;\n}\n\nexport interface ValidateCommandOptions {\n type: ValidationType;\n input?: string;\n output?: string;\n flow?: string; // Flow name for multi-flow configs\n path?: string; // Entry path for dot-notation validation (e.g., \"destinations.snowplow\")\n json?: boolean;\n verbose?: boolean;\n strict?: boolean;\n /** Skip every package settings check (no network); named in the scope. */\n offline?: boolean;\n silent?: boolean;\n}\n"],"mappings":";AAEA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;AACtB,OAAOC,YAAW;AAClB,SAAS,YAAAC,iBAAgB;;;ACLzB,OAAO,WAAW;AAClB;AAAA,EACE,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,OAGpB;AAUP,IAAM,aAA8B,EAAE,OAAO,MAAM,IAAI;;;AChBvD,SAAS,mBAA+B;;;ACIxC,OAAO,QAAQ;;;ACAf;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;;;ACLP,OAAOC,SAAQ;AACf,OAAO,UAAU;;;ACLjB,SAAS,SAAS,OAAkD;AAClE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAOO,SAAS,mBAAmB,OAAgB,WAA4B;AAC7E,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO,OAAO,KAAK;AACzC,MAAI,MAAM,SAAS;AACjB,WAAO,cAAc,SACjB,YACA,iBAAiB,SAAS;AAChC,QAAM,UACJ,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU,OAAO,KAAK;AAClE,QAAM,QAAQ,MAAM;AACpB,QAAM,OACJ,SAAS,KAAK,KAAK,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;AACnE,SAAO,OAAO,GAAG,OAAO,KAAK,IAAI,MAAM;AACzC;;;ADPO,SAAS,MAAM,KAAsB;AAC1C,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,GAAG;AACvB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACjC;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,SAAiB,KAAa,QAAiB;AACzD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,MAAM;AACX,SAAK,SAAS;AAAA,EAChB;AACF;AAaA,eAAsB,mBAAmB,KAA8B;AACrE,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,GAAG;AAAA,EAC5B,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,mBAAmB,GAAG,KAAK,mBAAmB,KAAK,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI;AAAA,MACR,mBAAmB,GAAG,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU,GAAG,QAAQ;AAAA,MAC5E;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO,SAAS,KAAK;AACvB;AAoCA,eAAe,eAAe,OAAgC;AAC5D,QAAM,UAAU,MAAM,KAAK;AAG3B,MAAI,MAAM,OAAO,GAAG;AAClB,WAAO,mBAAmB,OAAO;AAAA,EACnC;AAGA,QAAM,eAAe,KAAK,QAAQ,OAAO;AACzC,MAAI,MAAMC,IAAG,WAAW,YAAY,GAAG;AACrC,WAAOA,IAAG,SAAS,cAAc,OAAO;AAAA,EAC1C;AAGA,MAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,GAAG;AACtD,WAAO;AAAA,EACT;AAGA,QAAM,IAAI,MAAM,iCAAiC,YAAY,EAAE;AACjE;AA+BA,eAAsB,WACpB,OACA,SACqB;AACrB,QAAM,OAAO,SAAS,SAAS;AAE/B,MAAI,CAAC,MAAM;AACT,WAAO,eAAe,KAAK;AAAA,EAC7B;AAGA,QAAM,UAAU,MAAM,KAAK;AAE3B,MAAI;AACF,UAAM,UAAU,MAAM,eAAe,OAAO;AAG5C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,OAAO;AAEd,UAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAI1E,QAAI,iBAAiB,oBAAoB,aAAa,SAAS,WAAW;AACxE,YAAM;AAGR,QAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,GAAG;AACtD,YAAM,IAAI;AAAA,QACR,iDAAiD,YAAY;AAAA,MAC/D;AAAA,IACF;AAGA,UAAM,IAAI,MAAM,gCAAgC,KAAK,KAAK,YAAY,EAAE;AAAA,EAC1E;AACF;AA4BA,eAAsB,eAAkB,YAAgC;AACtE,SAAO,WAAc,YAAY,EAAE,MAAM,KAAK,CAAC;AACjD;;;AE7NA,SAAS,uBAAuB;;;ACChC,OAAOC,SAAQ;;;ACMf,OAAOC,SAAQ;;;ACPf,OAAOC,SAAQ;;;ACAf,SAAS,eAAe;AAExB,IAAM,EAAE,mBAAmB,IAAI;AAuCxB,SAAS,cACd,OACA,QAAyB,UACF;AACvB,QAAM,SAAiC,CAAC;AACxC,QAAM,WAAqC,CAAC;AAC5C,QAAM,UAAkC,CAAC;AAGzC,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AACD,WAAO,EAAE,OAAO,OAAO,QAAQ,UAAU,QAAQ;AAAA,EACnD;AAEA,QAAM,QAAQ;AAGd,MAAI,EAAE,UAAU,UAAU,MAAM,SAAS,QAAW;AAClD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH,WAAW,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,MAAM,IAAI;AACrE,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO,MAAM;AAAA,MACb,MAAM;AAAA,IACR,CAAC;AAAA,EACH,OAAO;AAEL,UAAM,OAAO,MAAM;AACnB,QAAI,CAAC,KAAK,SAAS,GAAG,GAAG;AACvB,UAAI,UAAU,UAAU;AACtB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,SACE;AAAA,UACF,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AACD,gBAAQ,SAAS;AACjB,gBAAQ,SAAS;AAAA,MACnB,WAAW,UAAU,YAAY;AAC/B,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,eAAe,IAAI;AAAA,QAC9B,CAAC;AACD,gBAAQ,SAAS;AACjB,gBAAQ,SAAS;AAAA,MACnB;AAAA,IAEF,OAAO;AACL,YAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK;AACrC,YAAM,SAAS,MAAM,IAAI;AACzB,YAAM,SAAS,MAAM,KAAK,GAAG;AAC7B,cAAQ,SAAS;AACjB,cAAQ,SAAS;AAAA,IACnB;AAAA,EACF;AAGA,MAAI,UAAU,WAAW;AACvB,WAAO,EAAE,OAAO,OAAO,WAAW,GAAG,QAAQ,UAAU,QAAQ;AAAA,EACjE;AAGA,QAAM,YAAY,mBAAmB,UAAU,KAAK;AACpD,MAAI,CAAC,UAAU,SAAS;AACtB,eAAW,SAAS,UAAU,MAAM,QAAQ;AAC1C,YAAM,YAAY,MAAM,KAAK,KAAK,GAAG;AACrC,UAAI,cAAc,OAAQ;AAC1B,aAAO,KAAK;AAAA,QACV,MAAM,aAAa;AAAA,QACnB,SAAS,MAAM;AAAA,QACf,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,UAAU,UAAU;AACtB,QAAI,CAAC,MAAM,SAAS;AAClB,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YACE;AAAA,MACJ,CAAC;AAAA,IACH;AACA,YAAQ,aAAa,CAAC,CAAC,MAAM;AAC7B,YAAQ,UAAU,CAAC,CAAC,MAAM;AAC1B,YAAQ,aAAa,CAAC,CAAC,MAAM;AAAA,EAC/B;AAEA,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,QAAQ,UAAU,QAAQ;AACjE;;;ACtIA,SAAS,gBAAgB;AACzB,SAAS,WAAAC,gBAAe;AAExB,IAAM,EAAE,gBAAgB,IAAIA;;;ACH5B,OAAOC,SAAQ;AACf,SAAS,iBAAiB,mBAA8B;AACxD,SAAS,6BAA6B;;;ACDtC,SAAS,kBAAkB;AAE3B,OAAOC,SAAQ;;;ACXf,SAAS,4BAA4B;;;ACArC,SAAS,oBAAoB,YAAAC,iBAAgB;AAO7C,IAAM,aAAa,IAAI,IAAY,kBAAkB;AAGrD,IAAM,eAAe,CAAC,WAAW,WAAW,UAAU,QAAQ,SAAS;AAEhE,SAAS,iBAAiB,OAAgC;AAC/D,QAAM,SAA4B,CAAC;AACnC,QAAM,WAAgC,CAAC;AACvC,QAAM,UAAmC,CAAC;AAE1C,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AACD,WAAO,EAAE,OAAO,OAAO,MAAM,YAAY,QAAQ,UAAU,QAAQ;AAAA,EACrE;AAEA,QAAM,YAAY;AAClB,QAAM,gBAAgB,OAAO,KAAK,SAAS;AAC3C,UAAQ,gBAAgB,cAAc;AAGtC,aAAW,QAAQ,eAAe;AAChC,QAAI,KAAK,WAAW,GAAG,GAAG;AACxB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE,IAAI,IAAI;AAAA,QAGV,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF;AAGA,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AAErD,QAAI,KAAK,WAAW,GAAG,GAAG;AACxB;AAAA,IACF;AAEA,QAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,aAAa,IAAI;AAAA,QAC1B,MAAM;AAAA,MACR,CAAC;AACD;AAAA,IACF;AAEA,UAAM,MAAM;AACZ,UAAM,OAAO,OAAO,KAAK,GAAG;AAC5B,UAAM,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC;AACzD,UAAM,cAAc,KAAK,KAAK,CAAC,MAAM,WAAW,IAAI,CAAC,CAAC;AACtD,UAAM,sBAAsB,YAAY;AAAA,MACtC,CAAC,MAAMA,UAAS,IAAI,CAAC,CAAC,KAAK,CAAC,aAAa,SAAS,CAAC;AAAA,IACrD;AAGA,QAAI,CAAC,eAAe,oBAAoB,SAAS,GAAG;AAClD,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE,aAAa,IAAI;AAAA,QAEnB,MAAM;AAAA,MACR,CAAC;AACD;AAAA,IACF;AAIA,eAAW,OAAO,aAAa;AAC7B,aAAO,KAAK;AAAA,QACV,MAAM,GAAG,IAAI,IAAI,GAAG;AAAA,QACpB,SAAS,aAAa,SAAS,GAAG,IAC9B,qBAAqB,GAAG,0LAA0L,GAAG,oBACrN,yBAAyB,GAAG,mCAAmC,CAAC,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC;AAAA,QAC7F,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAGA,QAAI,IAAI,YAAY,UAAa,OAAO,IAAI,YAAY,UAAU;AAChE,aAAO,KAAK;AAAA,QACV,MAAM,GAAG,IAAI;AAAA,QACb,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAGA,QAAI,IAAI,WAAW,UAAa,CAACA,UAAS,IAAI,MAAM,GAAG;AACrD,aAAO,KAAK;AAAA,QACV,MAAM,GAAG,IAAI;AAAA,QACb,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAGA,QAAI,IAAI,WAAW,QAAW;AAC5B,UAAI,OAAO,IAAI,WAAW,UAAU;AAClC,eAAO,KAAK;AAAA,UACV,MAAM,GAAG,IAAI;AAAA,UACb,SAAS;AAAA,UACT,MAAM;AAAA,QACR,CAAC;AAAA,MACH,WAAW,CAAC,cAAc,SAAS,IAAI,MAAM,GAAG;AAC9C,eAAO,KAAK;AAAA,UACV,MAAM,GAAG,IAAI;AAAA,UACb,SAAS,4CAA4C,IAAI,MAAM;AAAA,UAC/D,OAAO,IAAI;AAAA,UACX,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,IAAI,WAAW,QAAW;AAC5B,UAAI,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,MAAM;AACzD,eAAO,KAAK;AAAA,UACV,MAAM,GAAG,IAAI;AAAA,UACb,SAAS;AAAA,UACT,MAAM;AAAA,QACR,CAAC;AAAA,MACH,OAAO;AACL;AAAA,UACE,IAAI;AAAA,UACJ,GAAG,IAAI;AAAA,UACP;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiB,oBAAI,IAAY;AACvC,aAAW,QAAQ,eAAe;AAChC,UAAM,QAAkB,CAAC;AACzB,QAAI,UAAU;AACd,WAAO,SAAS;AACd,YAAM,QAAQ,MAAM,QAAQ,OAAO;AACnC,UAAI,UAAU,IAAI;AAChB,cAAM,QAAQ,MAAM,MAAM,KAAK;AAC/B,cAAM,MAAM,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,IAAQ;AAC3C,YAAI,CAAC,eAAe,IAAI,GAAG,GAAG;AAC5B,yBAAe,IAAI,GAAG;AACtB,iBAAO,KAAK;AAAA,YACV,MAAM,GAAG,MAAM,CAAC,CAAC;AAAA,YACjB,SAAS,0BAA0B,CAAC,GAAG,OAAO,OAAO,EAAE,KAAK,UAAK,CAAC;AAAA,YAClE,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACA,YAAM,KAAK,OAAO;AAClB,YAAM,QAAQ,UAAU,OAAO;AAC/B,gBACEA,UAAS,KAAK,KAAK,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AAAA,IACzE;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,sBACP,KACA,QACA,QACM;AACN,aAAW,CAAC,WAAW,WAAW,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC1D,QAAI,UAAU,KAAK,MAAM,IAAI;AAC3B,aAAO,KAAK;AAAA,QACV,MAAM,GAAG,MAAM,IAAI,SAAS;AAAA,QAC5B,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AACD;AAAA,IACF;AAEA,QAAI,OAAO,gBAAgB,YAAY,gBAAgB,MAAM;AAC3D,aAAO,KAAK;AAAA,QACV,MAAM,GAAG,MAAM,IAAI,SAAS;AAAA,QAC5B,SAAS,WAAW,SAAS;AAAA,QAC7B,OAAO;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AACD;AAAA,IACF;AAEA,eAAW,CAAC,WAAW,WAAW,KAAK,OAAO;AAAA,MAC5C;AAAA,IACF,GAAG;AACD,UAAI,UAAU,KAAK,MAAM,IAAI;AAC3B,eAAO,KAAK;AAAA,UACV,MAAM,GAAG,MAAM,IAAI,SAAS,IAAI,SAAS;AAAA,UACzC,SAAS;AAAA,UACT,MAAM;AAAA,QACR,CAAC;AACD;AAAA,MACF;AAEA,UACE,OAAO,gBAAgB,YACvB,gBAAgB,QAChB,MAAM,QAAQ,WAAW,GACzB;AACA,eAAO,KAAK;AAAA,UACV,MAAM,GAAG,MAAM,IAAI,SAAS,IAAI,SAAS;AAAA,UACzC,SAAS;AAAA,UACT,OAAO,OAAO;AAAA,UACd,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;;;ACpOO,SAASC,eAAc,OAAgC;AAC5D,QAAM,SAAS,cAAkB,OAAO,QAAQ;AAEhD,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,MAAM;AAAA,IACN,QAAQ,OAAO;AAAA,IACf,UAAU,OAAO;AAAA,IACjB,SAAS,OAAO;AAAA,EAClB;AACF;;;ACZA;AAAA,EACE,wBAAAC;AAAA,EACA,qBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA,wBAAAC;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA,WAAAC;AAAA,EACA,yBAAAC;AAAA,OACK;AACP;AAAA,EACE;AAAA,OAEK;;;ACnBP,SAAS,YAAAC,iBAAgB;AAOlB,SAAS,WAAW,OAAoC;AAC7D,SAAOA,UAAS,KAAK,KAAK,aAAa,SAASA,UAAS,MAAM,KAAK;AACtE;AAGO,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,SAAS,eAAe,OAAsC;AAC5D,SAAO,eAAe,KAAK,CAAC,YAAY,YAAY,KAAK;AAC3D;AAkCO,SAAS,aACd,QACA,UAAwB,CAAC,GACV;AACf,QAAM,aAAaA,UAAS,MAAM,IAAI,OAAO,QAAQ;AACrD,QAAM,UAAUA,UAAS,UAAU,IAAI,aAAa,CAAC;AACrD,QAAM,WAAW,OAAO,KAAK,OAAO;AAEpC,MAAI,QAAQ;AACZ,MAAI,QAAQ,SAAS,QAAW;AAC9B,QAAI,CAAC,SAAS,SAAS,QAAQ,IAAI,GAAG;AACpC,aAAO;AAAA,QACL;AAAA,QACA,OAAO,CAAC;AAAA,QACR,SAAS,CAAC;AAAA,QACV,eAAe,CAAC;AAAA,QAChB,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,SAAS,SAAS,QAAQ,IAAI,2BAA2B,SAAS,KAAK,IAAI,KAAK,MAAM;AAAA,YACtF,MAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,YAAQ,CAAC,QAAQ,IAAI;AAAA,EACvB;AAEA,QAAMC,QAAO,QAAQ;AACrB,MAAIA,UAAS,QAAW;AACtB,WAAO,EAAE,UAAU,OAAO,SAAS,CAAC,GAAG,eAAe,CAAC,GAAG,QAAQ,CAAC,EAAE;AAAA,EACvE;AAEA,QAAM,QAAQA,MAAK,MAAM,GAAG;AAC5B,MAAI,WAAoC;AACxC,MAAI;AACJ,MAAI;AACJ,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC,MAAM,IAAI;AAC5D,QAAI,CAAC,eAAe,MAAM,CAAC,CAAC,GAAG;AAC7B,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,SAAS,CAAC;AAAA,QACV,eAAe,CAAC;AAAA,QAChB,QAAQ;AAAA,UACN;AAAA,YACE,MAAAA;AAAA,YACA,SAAS,YAAY,MAAM,CAAC,CAAC,gDAAgD,eAAe,KAAK,IAAI,CAAC;AAAA,YACtG,MAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,cAAU,MAAM,CAAC;AACjB,eAAW,CAAC,OAAO;AACnB,UAAM,MAAM,CAAC;AAAA,EACf,WAAW,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,IAAI;AAChD,UAAM,MAAM,CAAC;AAAA,EACf,OAAO;AACL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS,CAAC;AAAA,MACV,eAAe,CAAC;AAAA,MAChB,QAAQ;AAAA,QACN;AAAA,UACE,MAAAA;AAAA,UACA,SAAS,iBAAiBA,KAAI;AAAA,UAC9B,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAwB,CAAC;AAC/B,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,QAAQ,IAAI;AAC9B,QAAI,CAACD,UAAS,SAAS,EAAG;AAC1B,eAAW,aAAa,UAAU;AAChC,YAAM,eAAe,UAAU,SAAS;AACxC,UAAI,CAACA,UAAS,YAAY,EAAG;AAC7B,YAAM,QAAQ,aAAa,GAAG;AAC9B,UAAIA,UAAS,KAAK;AAChB,gBAAQ,KAAK,EAAE,MAAM,SAAS,WAAW,KAAK,MAAM,CAAC;AAAA,IACzD;AAAA,EACF;AAEA,QAAM,UAAU,CAAC,GAAG,IAAI,IAAI,QAAQ,IAAI,CAAC,UAAU,MAAM,OAAO,CAAC,CAAC;AAClE,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS,CAAC;AAAA,MACV,eAAe;AAAA,MACf,QAAQ,EAAE,IAAI;AAAA,MACd,QAAQ;AAAA,QACN;AAAA,UACE,MAAAC;AAAA,UACA,SAAS,kBAAkB,GAAG,iCAAiC,QAAQ,KAAK,IAAI,CAAC,6BAA6B,QAAQ,CAAC,CAAC,IAAI,GAAG;AAAA,UAC/H,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,EAAE,SAAS,WAAW,QAAQ,CAAC,GAAG,IAAI;AACrD,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf;AAAA,MACA,QAAQ;AAAA,QACN;AAAA,UACE,MAAAA;AAAA,UACA,SAAS,UAAU,GAAG,kBAAkB,WAAW,aAAa,gBAAgB,MAAM,KAAK,IAAI,KAAK,MAAM;AAAA,UAC1G,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA,QAAQ,CAAC;AAAA,EACX;AACF;AAGO,SAAS,WACdA,OACA,WACoB;AACpB,SAAO,UAAU;AAAA,IACf,CAAC,SAASA,UAAS,SAAS,IAAI,MAAMA,MAAK,WAAW,SAAS,IAAI,GAAG;AAAA,EACxE;AACF;AAGO,SAAS,cACd,UACA,QACe;AACf,QAAM,QAAuB,EAAE,OAAO,SAAS,OAAO,OAAO;AAC7D,MAAI,SAAS,QAAQ;AACnB,UAAM,EAAE,SAAS,IAAI,IAAI,SAAS;AAClC,UAAM,QAAQ;AAAA,MACZ,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC7B;AAAA,MACA,OAAO,CAAC,GAAG,IAAI,IAAI,SAAS,QAAQ,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC,CAAC;AAAA,MAC/D,eAAe,SAAS;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;;;ACxNA;AAAA,EACE;AAAA,EACA;AAAA,EACA,mBAAAC;AAAA,EACA,mBAAAC;AAAA,OACK;AAaA,SAAS,YAAY,MAAiB,MAA8B;AACzE,MAAI;AACF,UAAM,OAAOD,iBAAgB,MAAM,MAAM,EAAE,UAAU,KAAK,CAAC;AAC3D,WAAO,EAAE,IAAI,MAAM,KAAK;AAAA,EAC1B,SAAS,OAAO;AACd,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM,SAAS,IAAI;AAAA,QACnB,SAASC,iBAAgB,KAAK;AAAA,QAC9B,MAAM,eAAe,KAAK;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AACF;AAGA,SAAS,eAAe,OAA8B;AACpD,MAAI,iBAAiB,mBAAoB,QAAO,MAAM;AACtD,MAAI,iBAAiB,eAAgB,QAAO,MAAM;AAClD,SAAO;AACT;;;ACxCA,OAAO,SAAsD;AAE7D;AAAA,EACE;AAAA,EACA;AAAA,EACA,mBAAAC;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;AC6BP,SAAS,QACP,MACA,MACA,IAC0B;AAC1B,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,KAAK,UAAU,EAAE;AAAA,IAC1B,KAAK;AACH,aAAO,KAAK,eAAe,EAAE;AAAA,IAC/B,KAAK;AACH,aAAO,KAAK,eAAe,EAAE;AAAA,IAC/B,KAAK;AACH,aAAO,KAAK,SAAS,EAAE;AAAA,EAC3B;AACF;AAEA,SAAS,kBACP,MACgD;AAChD,SAAO,KAAK,QAAQ,QAAQ;AAC9B;AAMA,SAAS,gBACP,UACA,aACoB;AACpB,MAAI,CAAC,YAAY,CAAC,YAAa,QAAO;AACtC,QAAM,MAAM,SAAS,WAAW;AAChC,MAAI,CAAC,OAAO,CAAC,IAAI,WAAW,IAAI,QAAQ,WAAW,EAAG,QAAO;AAC7D,QAAM,QAAQ,IAAI,QAAQ,CAAC;AAC3B,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AASO,SAAS,kBACd,MACA,MACA,IACoB;AACpB,QAAM,OAAO,QAAQ,MAAM,MAAM,EAAE;AACnC,MAAI,CAAC,KAAM,QAAO,EAAE,YAAY,QAAW,QAAQ,UAAU;AAE7D,QAAM,cAAc,KAAK;AACzB,QAAM,aAAa,KAAK;AAGxB,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,GAAG;AAC3D,WAAO,EAAE,YAAY,YAAY,QAAQ,SAAS;AAAA,EACpD;AAGA,QAAM,cAAc,gBAAgB,kBAAkB,IAAI,GAAG,WAAW;AACxE,MAAI,aAAa;AACf,WAAO,EAAE,YAAY,aAAa,QAAQ,UAAU;AAAA,EACtD;AAGA,SAAO,EAAE,YAAY,QAAW,QAAQ,UAAU;AACpD;;;ACjGA,SAAS,6BAA6B;AAiB/B,SAAS,eACd,MACA,SAC4B;AAC5B,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,KAAK;AAAA,IACd,KAAK;AACH,aAAO,KAAK;AAAA,IACd,KAAK;AACH,aAAO,KAAK;AAAA,IACd,KAAK;AACH,aAAO,KAAK;AAAA,EAChB;AACF;AAGA,SAAS,YAAY,MAAuB;AAC1C,SAAO,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG;AACpD;AASO,SAAS,iBAAiB,MAG/B;AACA,QAAM,KAAK,KAAK,YAAY,GAAG;AAC/B,MAAI,MAAM,EAAG,QAAO,EAAE,MAAM,KAAK;AACjC,QAAM,UAAU,KAAK,MAAM,KAAK,CAAC;AACjC,SAAO,UACH,EAAE,MAAM,KAAK,MAAM,GAAG,EAAE,GAAG,QAAQ,IACnC,EAAE,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE;AAChC;AAUO,SAAS,mBACd,cACA,SACa;AACb,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,QAAQ,eAAe,cAAc,OAAO;AAElD,MAAI,OAAO;AACT,eAAW,CAAC,EAAE,UAAU,KAAK,OAAO,QAAQ,KAAK,GAAG;AAClD,UAAI,OAAO,eAAe,YAAY,eAAe,KAAM;AAE3D,UAAI,OAAO,WAAW,YAAY,UAAU;AAC1C,iBAAS,IAAI,WAAW,OAAO;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,uBAAuB,cAAiC;AACtE,QAAM,cAAc,oBAAI,IAAY;AACpC,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,WAAW,UAAU;AAC9B,eAAW,OAAO,mBAAmB,cAAc,OAAO,GAAG;AAC3D,kBAAY,IAAI,GAAG;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AACT;AAwBO,SAAS,kBACd,cACA,UACA,QACM;AACN,QAAM,eAAe,uBAAuB,YAAY;AAIxD,QAAM,eAAe,oBAAI,IAAqB;AAG9C,QAAM,aAAa,oBAAI,IAAoB;AAE3C,QAAM,eAAe,CAAC,MAAc,OAAqB;AACvD,eAAW,WAAW;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,GAAY;AACV,YAAM,QAAQ,eAAe,cAAc,OAAO;AAClD,UAAI,CAAC,MAAO;AACZ,iBAAW,QAAQ,OAAO,OAAO,KAAK,GAAG;AACvC,YAAI,KAAK,YAAY,MAAM;AACzB,eAAK,UAAU;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,aAAW,OAAO,cAAc;AAC9B,QAAI,YAAY,GAAG,GAAG;AAIpB,YAAM,UAAU,sBAAsB,GAAG;AACzC,UAAI,CAAC,SAAS,OAAO,GAAG;AACtB,iBAAS,OAAO,IAAI;AAAA,UAClB,MAAM;AAAA,QACR;AAAA,MACF;AAIA,mBAAa,KAAK,OAAO;AACzB;AAAA,IACF;AAEA,UAAM,EAAE,MAAM,QAAQ,IAAI,iBAAiB,GAAG;AAE9C,QAAI,CAAC,aAAa,IAAI,IAAI,GAAG;AAC3B,YAAM,QAAQ,SAAS,IAAI;AAG3B,mBAAa;AAAA,QACX;AAAA,QACA,OAAO,YAAY,UAAa,OAAO,SAAS;AAAA,MAClD;AAAA,IACF;AACA,UAAM,eAAe,aAAa,IAAI,IAAI,MAAM;AAEhD,QAAI,SAAS,KAAK;AAGhB,mBAAa,KAAK,IAAI;AAAA,IACxB;AAEA,QAAI,WAAW,CAAC,cAAc;AAC5B,YAAM,OAAO,WAAW,IAAI,IAAI;AAChC,UAAI,SAAS,UAAa,SAAS,SAAS;AAC1C,cAAM,IAAI;AAAA,UACR,mCAAmC,IAAI,MAAM,IAAI,UAAU,OAAO;AAAA,QAEpE;AAAA,MACF;AACA,iBAAW,IAAI,MAAM,OAAO;AAAA,IAC9B;AAEA,UAAM,WAAW,SAAS,IAAI;AAC9B,QAAI,CAAC,UAAU;AACb,eAAS,IAAI,IAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5C,WAAW,WAAW,CAAC,SAAS,MAAM;AAGpC,UAAI,CAAC,SAAS,SAAS;AACrB,iBAAS,UAAU;AAAA,MACrB,WAAW,SAAS,YAAY,SAAS;AACvC,eAAO;AAAA,UACL,WAAW,IAAI,iCAAiC,SAAS,OAAO,qBAC3C,OAAO;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAeA,IAAM,kBAAkB;AAAA,EACtB,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,aAAa;AAAA,EACb,OAAO;AACT;AASO,SAAS,mBACd,MACA,MACA,IACA,QACqB;AACrB,QAAM,iBAAiB,gBAAgB,IAAI;AAC3C,QAAM,WAA+C;AAAA,IACnD,eAAe,QAAQ,QAAQ,YAAY,CAAC;AAAA,EAC9C;AACA,oBAAkB,gBAAgB,UAAU,MAAM;AAElD,QAAM,SAAS,eAAe,gBAAgB,gBAAgB,IAAI,CAAC;AACnE,QAAM,OAAO,SAAS,EAAE;AACxB,MAAI,CAAC,MAAM;AACT,UAAM,YAAY,OAAO,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,KAAK;AAC1D,UAAM,IAAI;AAAA,MACR,GAAG,IAAI,KAAK,EAAE,mCAAmC,SAAS;AAAA,IAC5D;AAAA,EACF;AACA,MAAI,OAAO,KAAK,YAAY,UAAU;AACpC,UAAM,IAAI;AAAA,MACR,GAAG,IAAI,IAAI,EAAE;AAAA,IACf;AAAA,EACF;AAEA,QAAM,WAAW,eAAe,MAAM,gBAAgB,IAAI,CAAC,IAAI,EAAE,GAAG;AACpE,SAAO;AAAA,IACL,aAAa,KAAK;AAAA,IAClB,OACE,aAAa,UAAa,YAAY,QAAQ,IAAI,WAAW,KAAK;AAAA,IACpE,MAAM,SAAS,KAAK,OAAO,KAAK,CAAC;AAAA,IACjC,WAAW,eAAe,QAAQ,QAAQ,aAAa,CAAC;AAAA,IACxD;AAAA,EACF;AACF;;;AClSA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAEtB,SAAS,cAAc,YAAAC,WAAU,yBAAyB;AAW1D,SAAS,SAAS,MAAuB;AACvC,SAAO,KAAK,MAAS,iBAAa,MAAM,OAAO,CAAC;AAClD;AAOA,eAAsB,oBACpB,UACA,SAC0B;AAC1B,QAAM,EAAE,aAAa,KAAK,IAAI;AAC9B,MAAI,KAAK,SAAS;AAChB,WAAO,aAAa,aAAa;AAAA,MAC/B,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,MAChD,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAEH,MAAI,QAAQ,cAAc;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AACF,QAAM,OAAY,iBAAW,KAAK,IAAI,IAClC,KAAK,OACA,cAAQ,QAAQ,WAAW,KAAK,IAAI;AAC7C,QAAM,OAAO;AAAA,IACN,WAAK,MAAM,QAAQ,eAAe;AAAA,IAClC,WAAK,MAAM,eAAe;AAAA,EACjC,EAAE,KAAK,CAAC,cAAiB,eAAW,SAAS,CAAC;AAC9C,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,0BAA0B,IAAI,EAAE;AAC3D,QAAM,OAAO,SAAS,IAAI;AAC1B,MAAI,CAACA,UAAS,IAAI,EAAG,OAAM,IAAI,MAAM,GAAG,IAAI,uBAAuB;AACnE,QAAM,UAAe,WAAK,MAAM,cAAc;AAC9C,QAAM,MAAS,eAAW,OAAO,IAAI,SAAS,OAAO,IAAI;AACzD,QAAM,UACJA,UAAS,GAAG,KAAK,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;AACnE,SAAO,EAAE,GAAG,kBAAkB,IAAI,GAAG,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AACvE;;;AH9BA,IAAM,gBAAgB;AAItB,IAAM,SAAS,aAAa;AAoDrB,SAAS,sBAAsB,WAAqC;AACzE,SAAO,EAAE,WAAW,OAAO,oBAAI,IAAI,GAAG,UAAU,oBAAI,IAAI,EAAE;AAC5D;AAUO,SAAS,qBACd,KACA,cACA,YACmB;AACnB,MAAI,eAAe,OAAW,QAAO,EAAE,IAAI,MAAM,UAAU,IAAI,SAAS;AACxE,MAAI,IAAI,eAAe;AACrB,UAAM,QAAQ,OAAO,UAAU,eAAe;AAAA,MAC5C,IAAI;AAAA,MACJ;AAAA,IACF,IACI,IAAI,cAAc,UAAU,IAC5B;AACJ,QAAI,CAACC,UAAS,KAAK;AACjB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,GAAG,YAAY,mBAAmB,UAAU;AAAA,MACtD;AACF,WAAO,EAAE,IAAI,MAAM,UAAU,MAAM,SAAS;AAAA,EAC9C;AACA,MAAI,IAAI,WAAW,IAAI,QAAQ,SAAS;AACtC,WAAO,EAAE,IAAI,MAAM,UAAU,IAAI,SAAS;AAC5C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QAAQ,GAAG,YAAY,4DAA4D,UAAU;AAAA,EAC/F;AACF;AAEA,IAAM,eAAoD;AAAA,EACxD,SAAS;AAAA,EACT,cAAc;AAAA,EACd,cAAc;AAAA,EACd,QAAQ;AACV;AAGA,SAAS,gBAAgB,SAA2B;AAClD,MAAI,YAAY,GAAI,QAAO,CAAC;AAC5B,SAAO,QACJ,MAAM,GAAG,EACT,MAAM,CAAC,EACP,IAAI,CAAC,YAAY,QAAQ,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,CAAC;AACrE;AAEA,SAAS,QAAQ,MAAe,UAA6B;AAC3D,MAAI,UAAU;AACd,aAAW,WAAW,UAAU;AAC9B,QAAI,MAAM,QAAQ,OAAO,EAAG,WAAU,QAAQ,OAAO,OAAO,CAAC;AAAA,aACpDA,UAAS,OAAO,EAAG,WAAU,QAAQ,OAAO;AAAA,QAChD,QAAO;AAAA,EACd;AACA,SAAO;AACT;AAIA,IAAM,SAAS,kBAAkB,QAAQ,uBAAuB,MAAM;AACtE,IAAM,aAAa,IAAI;AAAA,EACrB,GAAG,MAAM,sCAAsC,MAAM;AAAA,EACrD;AACF;AAeA,SAAS,gBAAgB,UAAqC;AAC5D,QAAM,WAAyC,CAAC;AAChD,QAAM,OAAO,CAAC,OAAgB,aAAgC;AAC5D,QAAI,MAAM,QAAQ,KAAK;AACrB,aAAO,MAAM,IAAI,CAAC,MAAM,UAAU,KAAK,MAAM,CAAC,GAAG,UAAU,GAAG,KAAK,EAAE,CAAC,CAAC;AACzE,QAAIA,UAAS,KAAK;AAChB,aAAO,OAAO;AAAA,QACZ,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM;AAAA,UACzC;AAAA,UACA,KAAK,MAAM,CAAC,GAAG,UAAU,GAAG,CAAC;AAAA,QAC/B,CAAC;AAAA,MACH;AACF,QAAI,OAAO,UAAU,SAAU,QAAO;AAEtC,QAAI,MAAM,WAAW,oBAAoB,GAAG;AAC1C,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,WAAW,WAAW,MAAM,MAAM,qBAAqB,MAAM,CAAC;AAAA,MAChE,CAAC;AACD,aAAO;AAAA,IACT;AACA,QAAI,MAAM,SAAS,iBAAiB,GAAG;AACrC,UAAI,cAAc;AAClB,YAAM,eAAe,MAAM;AAAA,QACzB;AAAA,QACA,CAAC,QAAQ,OAAe,aAAiC;AACvD,cAAI,aAAa,OAAW,eAAc;AAC1C,iBAAO,YAAY;AAAA,QACrB;AAAA,MACF;AACA,UAAI,CAAC,YAAa,QAAO;AACzB,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,WAAW,MAAM;AAAA,UACf;AAAA,UACA,CAAC,QAAQ,MAAc,aACrB,aAAa,SACT,QAAQ,IAAI,KACZ,QAAQ,IAAI,IAAI,QAAQ;AAAA,QAChC;AAAA,MACF,CAAC;AACD,aAAO;AAAA,IACT;AACA,QAAI,UAAU,KAAK,KAAK,KAAK,MAAM,WAAW,eAAe,GAAG;AAC9D,eAAS,KAAK,EAAE,UAAU,WAAW,MAAM,CAAC;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AACA,SAAO,EAAE,OAAO,KAAK,UAAU,CAAC,CAAC,GAAG,SAAS;AAC/C;AAEA,SAAS,QAAQ,UAAoB,QAA2B;AAC9D,SAAO,OAAO,MAAM,CAAC,SAAS,UAAU,SAAS,KAAK,MAAM,OAAO;AACrE;AAEA,eAAe,WACb,UACA,SACwB;AACxB,QAAM,OAAO,MAAM,oBAAoB,UAAU;AAAA,IAC/C,WAAW,QAAQ;AAAA,IACnB,QAAQ;AAAA,EACV,CAAC;AACD,SAAO;AAAA,IACL,SAAS,KAAK;AAAA,IACd,UAAU,KAAK,QAAQ;AAAA,IACvB,GAAI,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,IAAI,CAAC;AAAA,IAClE,GAAI,KAAK,UAAU,EAAE,SAAS,OAAO,KAAK,KAAK,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/D;AACF;AAOA,SAAS,gBAAgB,UAAiD;AACxE,MAAI,CAACA,UAAS,QAAQ,EAAG,QAAO;AAGhC,QAAM,MAAM,IAAI,IAAI,EAAE,WAAW,MAAM,iBAAiB,MAAM,CAAC;AAC/D,MAAI;AACF,WAAO,EAAE,IAAI,MAAM,UAAU,IAAI,QAAQ,QAAQ,EAAE;AAAA,EACrD,SAAS,OAAO;AACd,WAAO,EAAE,IAAI,OAAO,SAASC,iBAAgB,KAAK,EAAE;AAAA,EACtD;AACF;AAEA,SAAS,UAAU,EAAE,aAAa,KAAK,GAAgC;AACrE,SAAO,KAAK,SAAS,SACjB,QAAQ,KAAK,IAAI,KACjB,GAAG,WAAW,IAAI,KAAK,WAAW,QAAQ;AAChD;AAEA,SAAS,aACP,UACA,SACwB;AACxB,QAAM,MAAM,UAAU,QAAQ;AAC9B,MAAI,UAAU,QAAQ,MAAM,IAAI,GAAG;AACnC,MAAI,CAAC,SAAS;AACZ,cAAU,WAAW,UAAU,OAAO;AACtC,YAAQ,MAAM,IAAI,KAAK,OAAO;AAAA,EAChC;AACA,SAAO;AACT;AAEA,SAAS,cACP,UACA,YACA,UACA,SAC8B;AAC9B,QAAM,MAAM,GAAG,UAAU,QAAQ,CAAC,IAAI,cAAc,EAAE;AACtD,MAAI,QAAQ,SAAS,IAAI,GAAG,EAAG,QAAO,QAAQ,SAAS,IAAI,GAAG;AAC9D,QAAM,WAAW,gBAAgB,QAAQ;AACzC,UAAQ,SAAS,IAAI,KAAK,QAAQ;AAClC,SAAO;AACT;AAQA,eAAsB,kBACpB,QACA,cACA,OACA,SAC0B;AAC1B,QAAM,KAAK,SAAS,OAAO,IAAI,IAAI,OAAO,OAAO,IAAI,OAAO,GAAG;AAC/D,QAAM,UAA2B,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC,EAAE;AAC3E,QAAM,OACJ,OAAO,OAAO,MAAM,YAAY,WAAW,OAAO,MAAM,UAAU;AACpE,MAAI,CAAC,MAAM;AACT,YAAQ,QAAQ,KAAK;AAAA,MACnB,MAAM;AAAA,MACN;AAAA,MACA,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,eAAW;AAAA,MACT;AAAA,MACA,aAAa,OAAO,OAAO;AAAA,MAC3B,OAAO;AAAA,MACP;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,QAAQ,KAAK;AAAA,MACnB,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,cAAc,IAAI,yBAAyBA,iBAAgB,KAAK,CAAC;AAAA,MACzE,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AACA,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,SACJ,SAAS,KAAK,SAAS,SAAY,SAAS,KAAK,UAAU;AAE7D,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,aAAa,UAAU,OAAO;AAAA,EAC/C,SAAS,OAAO;AACd,YAAQ,QAAQ,KAAK;AAAA,MACnB,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,cAAc,KAAK,GAAG,SAAS,IAAI,MAAM,KAAK,EAAE,yBAAyBA,iBAAgB,KAAK,CAAC;AAAA,MACvG,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AACA,UAAQ,UAAU;AAAA,IAChB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,GAAI,OAAO,UAAU,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,EACtD;AAGA,QAAM,EAAE,WAAW,IAAI;AAAA,IACrB,SAAS;AAAA,IACT,aAAa,OAAO,OAAO;AAAA,IAC3B,OAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,YAAY;AAAA,IAChB;AAAA,IACA,UAAU,GAAG,KAAK,IAAI,OAAO,KAAK;AAAA,IAClC;AAAA,EACF;AACA,MAAI,CAAC,UAAU,IAAI;AACjB,YAAQ,QAAQ,KAAK;AAAA,MACnB,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,UAAU;AAAA,MAClB,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AAEA,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,EACF;AACA,MAAI,CAAC,UAAU;AACb,YAAQ,QAAQ,KAAK;AAAA,MACnB,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,WAAW,KAAK;AAAA,MACxB,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,IAAI;AAChB,YAAQ,QAAQ,KAAK;AAAA,MACnB,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,0BAA0B,KAAK,wBAAwB,SAAS,OAAO;AAAA,MAC/E,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AACA,QAAMC,YAAW,SAAS;AAE1B,QAAM,OAAO,OAAO,cAAc,OAAO,SAAS,OAAO,GAAG;AAC5D,QAAM,SAASF,UAAS,MAAM,MAAM,IAAI,KAAK,SAAS,CAAC;AACvD,QAAM,WAAW,gBAAgB,OAAO,YAAY,CAAC,CAAC;AACtD,QAAM,OAAO,GAAG,EAAE;AAClB,aAAW,EAAE,UAAU,UAAU,KAAK,SAAS,UAAU;AACvD,YAAQ,SAAS,KAAK;AAAA,MACpB,MAAM,CAAC,MAAM,GAAG,QAAQ,EAAE,KAAK,GAAG;AAAA,MAClC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAIE,UAAS,SAAS,KAAK,EAAG,QAAO;AAErC,aAAW,KAAKA,UAAS,UAAU,CAAC,GAAG;AACrC,UAAM,WAAW,gBAAgB,EAAE,YAAY;AAC/C,QAAI,SAAS,SAAS,KAAK,CAAC,MAAM,QAAQ,UAAU,EAAE,QAAQ,CAAC,EAAG;AAClE,UAAM,UAA2B;AAAA,MAC/B,MAAM,CAAC,MAAM,GAAG,QAAQ,EAAE,KAAK,GAAG;AAAA,MAClC,SAAS,iBAAiB,CAAC;AAAA,MAC3B,MAAM;AAAA,MACN,SAAS,EAAE;AAAA,IACb;AACA,QAAI,SAAS,SAAS,EAAG,SAAQ,QAAQ,QAAQ,SAAS,OAAO,QAAQ;AACzE,YAAQ,SAAS,KAAK,OAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAGA,SAAS,iBAAiB,OAA4B;AACpD,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,QAAiB,MAAM,OAAO;AACpC,SAAO,MAAM,YAAY,0BAA0B,OAAO,UAAU,WAChE,GAAG,OAAO,MAAM,KAAK,MACrB;AACN;AAEA,SAAS,OACP,MACA,SACA,KACuB;AACvB,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,KAAK,UAAU,GAAG;AAAA,IAC3B,KAAK;AACH,aAAO,KAAK,eAAe,GAAG;AAAA,IAChC,KAAK;AACH,aAAO,KAAK,eAAe,GAAG;AAAA,IAChC,KAAK;AACH,aAAO,KAAK,SAAS,GAAG;AAAA,EAC5B;AACF;;;AH9ZA,IAAM,EAAE,oBAAAC,oBAAmB,IAAIC;AAgB/B,IAAM,sBAA8C;AAAA,EAClD,0BACE;AACJ;AAGA,IAAM,aAAa;AAAA,EACjB,CAAC,WAAW,QAAQ;AAAA,EACpB,CAAC,gBAAgB,aAAa;AAAA,EAC9B,CAAC,gBAAgB,aAAa;AAAA,EAC9B,CAAC,UAAU,OAAO;AACpB;AAkCA,eAAsB,yBACpB,OACA,UAA8B,CAAC,GACN;AACzB,QAAM,MAAM,cAAc,OAAO,OAAO;AACxC,QAAM,EAAE,QAAQ,MAAM,IAAI;AAC1B,MAAI,QAAQ,SAAS;AACnB,UAAM,YAAY,cAAc,OAAO,IAAI,MAAM;AACjD,cAAU,UAAU;AACpB,WAAO,QAAQ,QAAQ;AACvB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,KAAK,uBAAuB;AACvC,QAAM,UAAU,sBAAsB,QAAQ,SAAS;AACvD,QAAM,WAA+B,CAAC;AACtC,QAAM,WAA6B,CAAC;AAGpC,QAAM,UAA2C,CAAC;AAClD,aAAW,QAAQ,MAAM,OAAO;AAC9B,UAAM,OAAO,IAAI,SAAS,IAAI,IAAI;AAClC,UAAM,UAAU,IAAI,OAAO,MAAM,IAAI;AACrC,UAAM,SAAS,IAAI,KAAK,IAAI;AAC5B,QAAI,CAAC,QAAQ,CAAC,WAAW,QAAQ;AAC/B,UAAI,QAAQ,KAAK;AAAA,QACf,MAAM,SAAS,IAAI;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ,UAAU,SAAS,IAAI;AAAA,QAC/B,MAAM;AAAA,MACR,CAAC;AACD;AAAA,IACF;AACA,eAAW,WAAW,gBAAgB;AACpC,YAAM,QAAQC,UAAS,QAAQ,OAAO,CAAC,IAAI,QAAQ,OAAO,IAAI,CAAC;AAC/D,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAI,CAACA,UAAS,KAAK,KAAK,MAAM,YAAY,OAAW;AACrD,cAAM,SAAS,EAAE,MAAM,MAAM,SAAS,KAAK,MAAM;AACjD,gBAAQ;AAAA,UACN,kBAAkB,QAAQ,MAAM,yBAAyB,OAAO;AAAA,QAClE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,aAAW,WAAW,MAAM,QAAQ,IAAI,OAAO,GAAG;AAChD,eAAW,WAAW,QAAQ,UAAU;AACtC,UAAI,QAAQ,YAAY;AACtB,eAAO,OAAO,KAAK,OAAO;AAAA,UACvB,QAAO,SAAS,KAAK,OAAO;AAAA,IACnC;AACA,QAAI,QAAQ,KAAK,GAAG,QAAQ,OAAO;AACnC,aAAS,KAAK,GAAG,QAAQ,QAAQ;AACjC,QAAI,QAAQ,QAAS,UAAS,KAAK,QAAQ,OAAO;AAAA,EACpD;AAEA,SAAO,QAAQ,QAAQ,cAAc,OAAO,IAAI,MAAM;AACtD,SAAO,QAAQ,WAAW,CAAC,GAAI,OAAO,QAAQ,YAAY,CAAC,GAAI,GAAG,QAAQ;AAC1E,MAAI,SAAS,SAAS,EAAG,QAAO,QAAQ,WAAW;AACnD,SAAO,QAAQ,OAAO,OAAO,WAAW;AACxC,SAAO;AACT;AAEA,SAAS,cAAc,OAAgB,SAAuC;AAC5E,QAAM,SAA4B,CAAC;AACnC,QAAM,WAAgC,CAAC;AACvC,QAAM,UAA0B,CAAC;AACjC,QAAM,SAA0B,CAAC,eAAe,aAAa;AAC7D,QAAM,UAA2B,CAAC;AAClC,QAAM,WAAW,oBAAI,IAAkB;AAGvC,QAAM,SAAyB;AAAA,IAC7B,OAAO;AAAA,IACP,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAKA,MAAI;AACJ,QAAM,QAAQ,aAAa,OAAO,EAAE,MAAM,QAAQ,KAAK,CAAC;AACxD,MAAI;AACF,WAAO,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,EACtC,QAAQ;AACN,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM,MAAM;AAAA,IACd;AAAA,EACF;AAGA,SAAO,KAAK,GAAG,MAAM,MAAM;AAC3B,QAAM,SAAkCA,UAAS,KAAK,IAAI,QAAQ,CAAC;AACnE,QAAM,aAAa,OAAO;AAC1B,QAAM,QAA6CA,UAAS,UAAU,IAClE,aACA;AACJ,QAAM,WAAW,MAAM;AACvB,QAAM,UAAU,CAACC,UAA0B;AACzC,UAAM,OAAO,WAAWA,OAAM,QAAQ;AACtC,WAAO,SAAS,UAAa,MAAM,MAAM,SAAS,IAAI;AAAA,EACxD;AAIA,MAAI,iBAAiB;AACrB,MAAI,cAAc,QAAQ;AACxB,WAAO,KAAK,eAAe;AAC3B,UAAM,WAAW,iBAAiB,OAAO,QAAQ;AACjD,qBAAiB,SAAS,OAAO;AACjC,eAAW,SAAS,SAAS;AAC3B,aAAO,KAAK,EAAE,GAAG,OAAO,MAAM,YAAY,MAAM,IAAI,GAAG,CAAC;AAC1D,eAAW,WAAW,SAAS;AAC7B,eAAS,KAAK,EAAE,GAAG,SAAS,MAAM,YAAY,QAAQ,IAAI,GAAG,CAAC;AAAA,EAClE;AAMA,QAAM,aAAaC,oBAAmB,IAAI;AAC1C,aAAW,SAAS,WAAW,QAAQ;AACrC,UAAMD,QAAO,MAAM,QAAQ;AAC3B,QAAI,CAAC,QAAQA,KAAI,EAAG;AACpB,SACGA,UAAS,cAAcA,MAAK,WAAW,WAAW,MACnD,iBAAiB;AAEjB;AACF,WAAO,KAAK;AAAA,MACV,MAAAA;AAAA,MACA,SAAS,MAAM;AAAA,MACf,MAAM,MAAM,QAAQ;AAAA,IACtB,CAAC;AAAA,EACH;AACA,aAAW,SAAS,WAAW,UAAU;AACvC,UAAMA,QAAO,MAAM,QAAQ;AAC3B,QAAI,CAAC,QAAQA,KAAI,EAAG;AACpB,aAAS,KAAK,EAAE,MAAAA,OAAM,SAAS,MAAM,SAAS,MAAM,iBAAiB,CAAC;AAAA,EACxE;AAGA,MAAI,SAAS,SAAS,WAAW,GAAG;AAClC,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAOA,SAAO,KAAK,YAAY;AACxB,aAAW,YAAY,MAAM,OAAO;AAClC,UAAM,YAAY,QAAQ,QAAQ;AAClC,QAAI,CAACD,UAAS,SAAS,EAAG;AAC1B,UAAM,oBAAoB,UAAU;AACpC,QAAIA,UAAS,iBAAiB,GAAG;AAC/B,iBAAW,CAAC,MAAM,gBAAgB,KAAK,OAAO;AAAA,QAC5C;AAAA,MACF,GAAG;AACD,YAAI,CAACA,UAAS,gBAAgB,EAAG;AACjC,cAAMG,UAAS,kBAAkB,kBAAkB,aAAa;AAChE,YAAI,CAACA,QAAO,IAAI;AACd,iBAAO,KAAK;AAAA,YACV,MAAM,SAAS,QAAQ,iBAAiB,IAAI;AAAA,YAC5C,SAASA,QAAO,UAAU;AAAA,YAC1B,MAAMA,QAAO;AAAA,UACf,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AACA,eAAW,CAAC,SAAS,IAAI,KAAK,YAAY;AACxC,YAAM,QAAQ,UAAU,OAAO;AAC/B,UAAI,CAACH,UAAS,KAAK,EAAG;AACtB,iBAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAI,CAACA,UAAS,IAAI,EAAG;AACrB,cAAM,KAAK,SAAS,QAAQ,IAAI,OAAO,IAAI,IAAI;AAC/C,eAAO,KAAK,GAAG,cAAc,MAAM,MAAM,EAAE,CAAC;AAC5C,cAAM,MAAM,KAAK;AACjB,YAAI,OAAO,QAAQ,YAAY,OAAO,qBAAqB;AACzD,mBAAS,KAAK;AAAA,YACZ,MAAM;AAAA,YACN,SAAS,GAAG,IAAI,KAAK,IAAI,UAAU,GAAG,WAAW,oBAAoB,GAAG,CAAC;AAAA,YACzE,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AACA,yBAAqB,UAAU,WAAW,MAAM;AAAA,EAClD;AAGA,MAAI,OAAO;AACT,YAAQ,YAAY;AACpB,YAAQ,YAAY,SAAS;AAAA,EAC/B;AAGA,SAAO,KAAK,uBAAuB;AACnC,MAAI,oBAAoB;AACxB,aAAW,YAAY,MAAM,OAAO;AAClC,UAAM,YAAY,QAAQ,QAAQ;AAClC,QAAI,CAACA,UAAS,SAAS,EAAG;AAC1B,UAAM,aAAa,UAAU;AAC7B,QAAI,CAACA,UAAS,UAAU,EAAG;AAC3B,UAAM,SAAS,WAAW;AAC1B,QAAI,CAACA,UAAS,MAAM,EAAG;AACvB,UAAM,WAAW,OAAO;AACxB,QAAI,CAACA,UAAS,QAAQ,EAAG;AAEzB,eAAW,CAAC,SAAS,cAAc,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAChE,UAAI,CAACA,UAAS,cAAc,EAAG;AAC/B,UAAI,CAAC,eAAe,WAAW,CAAC,eAAe,MAAM;AACnD,iBAAS,KAAK;AAAA,UACZ,MAAM,SAAS,QAAQ,2BAA2B,OAAO;AAAA,UACzD,SAAS,YAAY,OAAO;AAAA,UAC5B,YAAY;AAAA,UACZ,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AACA,yBAAqB,OAAO,KAAK,QAAQ,EAAE;AAAA,EAC7C;AACA,MAAI,oBAAoB,GAAG;AACzB,YAAQ,eAAe;AAAA,EACzB;AAGA,MAAI,WAAW,SAAS;AACtB,YAAQ,UAAU,WAAW;AAAA,EAC/B;AAKA,QAAM,QAAQ,WAAW,KAAK,IAAI,QAAQ;AAC1C,QAAM,OAAO,CAAC,SAAqC;AACjD,QACE,CAAC,SACD,OAAO,KAAK,CAAC,MAAM,WAAW,EAAE,MAAM,QAAQ,MAAM,MAAS;AAE7D,aAAO;AACT,QAAI,OAAO,KAAK,CAAC,MAAM,WAAW,EAAE,MAAM,QAAQ,MAAM,IAAI;AAC1D,aAAO,2BAA2B,IAAI;AACxC,WAAO;AAAA,EACT;AACA,QAAM,UAAU,CACd,aACA,QACS;AACT,WAAO,KAAK,GAAG,WAAW;AAC1B,eAAW,QAAQ,MAAM,OAAO;AAC9B,YAAM,SAAS,KAAK,IAAI;AACxB,YAAM,OAAO,OAAO,MAAM,IAAI;AAC9B,UAAI,UAAU,CAAC,SAAS,CAAC,MAAM;AAC7B,mBAAW,SAAS,aAAa;AAC/B,kBAAQ,KAAK;AAAA,YACX,MAAM,SAAS,IAAI;AAAA,YACnB;AAAA,YACA,QAAQ,UAAU,SAAS,IAAI;AAAA,YAC/B,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACA,UAAI,MAAM,MAAM,KAAK;AAAA,IACvB;AAAA,EACF;AAOA,UAAQ,CAAC,gBAAgB,GAAG,CAAC,MAAM,MAAM,SAAS;AAChD,UAAM,YAAYI,uBAAsB;AAAA,MACtC,GAAG;AAAA,MACH,OAAO,EAAE,CAAC,IAAI,GAAG,KAAK;AAAA,IACxB,CAAC;AACD,WAAO,KAAK,GAAG,UAAU,MAAM;AAC/B,aAAS,KAAK,GAAG,UAAU,QAAQ;AAAA,EACrC,CAAC;AAKD,UAAQ,CAAC,aAAa,GAAG,CAAC,MAAM,SAAS;AACvC,mBAAe,MAAM,MAAM,QAAQ,QAAQ;AAAA,EAC7C,CAAC;AAID,QAAM,cAAc,oBAAI,IAA4B;AACpD,QAAM,eAAe,CAAC,MAAc,SAAoC;AACtE,QAAI,aAAa,YAAY,IAAI,IAAI;AACrC,QAAI,CAAC,YAAY;AACf,mBAAa,YAAY,MAAM,IAAI;AACnC,kBAAY,IAAI,MAAM,UAAU;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AAIA,MAAI;AACJ;AAAA,IACE,CAAC,iBAAiB,0BAA0B,mBAAmB;AAAA,IAC/D,CAAC,MAAM,MAAM,SAAS;AACpB,YAAM,cAAc,qBAAqB,IAAI;AAC7C,iBAAW,QAAQ,aAAa;AAC9B,2BAAmB,MAAM,MAAM,QAAQ,QAAQ;AAAA,MACjD;AACA,0BAAoB,oBAAoB,KAAK,YAAY;AAKzD,YAAM,aAAa,aAAa,MAAM,IAAI;AAC1C,iBAAW,CAAC,UAAU,WAAW,KAAK,OAAO;AAAA,QAC3C,KAAK,gBAAgB,CAAC;AAAA,MACxB,GAAG;AACD,YAAI,CAAC,eAAe,WAAW,EAAG;AAClC,cAAM,iBAAiB,WAAW,KAC9B,WAAW,KAAK,eAAe,QAAQ,GAAG,SAC1C;AACJ;AAAA,UACE,SAAS,IAAI,iBAAiB,QAAQ;AAAA,UACtC;AAAA,UACA,KAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,QAAQ,WAAW;AAAA,UACnB;AAAA,UACAJ,UAAS,cAAc,KAAKA,UAAS,eAAe,QAAQ,IACxD,eAAe,WACf;AAAA,QACN;AAAA,MACF;AAEA,iBAAW,CAAC,UAAU,IAAI,KAAK,OAAO,QAAQ,KAAK,gBAAgB,CAAC,CAAC,GAAG;AACtE,YAAI,CAACA,UAAS,KAAK,MAAM,EAAG;AAC5B,cAAM,UAAU,KAAK,OAAO;AAC5B,YAAI,CAACA,UAAS,OAAO,EAAG;AAExB,mBAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,cAAI,IAAI,SAAS,GAAG,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG;AAC3C,kBAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,qBAAS,KAAK;AAAA,cACZ,MAAM,SAAS,IAAI,iBAAiB,QAAQ;AAAA,cAC5C,SAAS,gBAAgB,GAAG;AAAA,cAC5B,YAAY,yBAAyB,MAAM,CAAC,CAAC,SAAS,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,cAC9E,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,qBAAqB,QAAW;AAClC,YAAQ,qBAAqB;AAAA,EAC/B;AAQA,QAAM,WAA+B,CAAC;AACtC,UAAQ,CAAC,cAAc,GAAG,CAAC,MAAM,MAAM,SAAS;AAC9C,UAAM,aAAa,aAAa,MAAM,IAAI;AAC1C,QAAI,CAAC,WAAW,IAAI;AAClB,aAAO,KAAK,WAAW,KAAK;AAC5B;AAAA,IACF;AACA,aAAS,IAAI,MAAM,WAAW,IAAI;AAClC,iCAA6B,MAAM,MAAM,WAAW,MAAM,QAAQ,QAAQ;AAAA,EAC5E,CAAC;AAED,UAAQ,QAAQ,cAAc,OAAO,MAAM;AAC3C,UAAQ,UAAU;AAClB,UAAQ,WAAW;AACnB,SAAO,QAAQ,OAAO,WAAW;AAEjC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAOA,SAAS,6BACP,MACA,KACA,MACA,QACA,UACM;AACN,QAAM,UAAU,IAAI,QAAQ,UAAU;AACtC,QAAM,QAAQ,KAAK,QAAQ,UAAU;AACrC,MACE,KAAK,QAAQ,aAAa,SAC1B,OAAO,YAAY,YACnB,CAAC,kBAAkB,KAAK,OAAO;AAE/B;AACF,QAAMC,QAAO,SAAS,IAAI;AAC1B,MAAI,OAAO,UAAU,YAAY,MAAM,SAASI,kBAAiB,GAAG;AAClE,aAAS,KAAK,EAAE,MAAAJ,OAAM,WAAW,QAAQ,CAAC;AAC1C;AAAA,EACF;AACA,QAAM,QAAQK,sBAAqB,KAAK;AACxC,MAAI,MAAM,GAAI;AACd,SAAO,KAAK;AAAA,IACV,MAAAL;AAAA,IACA,SAAS,GAAG,KAAK,UAAU,OAAO,CAAC,gBAAgB,KAAK,UAAU,KAAK,CAAC,WAAW,MAAM,MAAM;AAAA,IAC/F,MAAM;AAAA,EACR,CAAC;AACH;AAOA,SAAS,qBACP,UACA,MACA,QACM;AACN,MAAI,CAACD,UAAS,KAAK,MAAM,KAAK,KAAK,OAAO,aAAa,MAAO;AAC9D,QAAM,KAAK,SAAS,QAAQ;AAC5B,QAAM,UAAoC,CAAC;AAC3C,MAAIA,UAAS,KAAK,SAAS;AACzB,YAAQ,KAAK,CAAC,GAAG,EAAE,qBAAqB,KAAK,UAAU,MAAM,CAAC;AAChE,aAAW,CAAC,OAAO,KAAK,YAAY;AAClC,UAAM,QAAQ,KAAK,OAAO;AAC1B,QAAI,CAACA,UAAS,KAAK,EAAG;AACtB,eAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAIA,UAAS,IAAI,KAAKA,UAAS,KAAK,MAAM;AACxC,gBAAQ,KAAK;AAAA,UACX,GAAG,EAAE,IAAI,OAAO,IAAI,IAAI;AAAA,UACxB,KAAK,OAAO;AAAA,QACd,CAAC;AAAA,IACL;AAAA,EACF;AACA,aAAW,CAACC,OAAM,MAAM,KAAK,SAAS;AACpC,QAAI,CAACD,UAAS,MAAM,KAAK,OAAO,WAAW,OAAW;AACtD,WAAO,KAAK;AAAA,MACV,MAAM,GAAGC,KAAI;AAAA,MACb,SACE;AAAA,MACF,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AAqBA,SAAS,aAAa,MAA+C;AACnE,MAAI,SAAS,OAAW,QAAO,CAAC;AAChC,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,QAAQ,cAAc,IAAI,GAAG;AACtC,eAAW,UAAU,KAAK,QAAS,SAAQ,IAAI,MAAM;AAAA,EACvD;AACA,SAAO,CAAC,GAAG,OAAO;AACpB;AAEA,SAAS,qBAAqB,QAAgC;AAC5D,QAAM,cAAgC,CAAC;AAGvC,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC,GAAG;AACjE,QAAI,CAAC,OAAO,QAAQ,CAAC,OAAO,SAAU;AACtC,UAAM,YAAY,aAAa,OAAO,IAAI;AAC1C,eAAW,YAAY,WAAW;AAChC,YAAM,cAAc,OAAO,eAAe,QAAQ;AAClD,UAAI,aAAa,UAAU;AACzB,oBAAY,KAAK;AAAA,UACf,MAAM,EAAE,MAAM,UAAU,MAAM,UAAU,OAAO,SAAS;AAAA,UACxD,IAAI;AAAA,YACF,MAAM;AAAA,YACN,MAAM;AAAA,YACN,UAAU,YAAY;AAAA,UACxB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,CAAC,MAAM,WAAW,KAAK,OAAO,QAAQ,OAAO,gBAAgB,CAAC,CAAC,GAAG;AAC3E,QAAI,CAAC,YAAY,QAAQ,CAAC,YAAY,SAAU;AAChD,UAAM,YAAY,aAAa,YAAY,IAAI;AAC/C,eAAW,YAAY,WAAW;AAChC,YAAM,kBAAkB,OAAO,eAAe,QAAQ;AACtD,UAAI,iBAAiB,UAAU;AAC7B,oBAAY,KAAK;AAAA,UACf,MAAM;AAAA,YACJ,MAAM;AAAA,YACN;AAAA,YACA,UAAU,YAAY;AAAA,UACxB;AAAA,UACA,IAAI;AAAA,YACF,MAAM;AAAA,YACN,MAAM;AAAA,YACN,UAAU,gBAAgB;AAAA,UAC5B;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,OAAO,gBAAgB,CAAC,CAAC,GAAG;AACpE,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,SAAU;AACpC,UAAM,cAAc,aAAa,KAAK,MAAM;AAC5C,eAAW,cAAc,aAAa;AACpC,YAAM,cAAc,OAAO,eAAe,UAAU;AACpD,UAAI,aAAa,UAAU;AACzB,oBAAY,KAAK;AAAA,UACf,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,MAAM;AAAA,YACN,UAAU,YAAY;AAAA,UACxB;AAAA,UACA,IAAI,EAAE,MAAM,eAAe,MAAM,UAAU,KAAK,SAAS;AAAA,QAC3D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,mBACP,UACA,MACA,QACA,UACM;AACN,QAAM,WAAW,OAAO,QAAQ,KAAK,KAAK,QAAQ,EAC/C,OAAO,CAAC,CAAC,EAAE,EAAE,MAAM,iBAAiB,GAAG,GAAG,CAAC,EAC3C,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,EAAE;AAGhD,QAAM,QAAQ,OAAO,QAAQ,KAAK,GAAG,QAAQ,EAC1C,OAAO,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,UAAa,CAAC,GAAG,OAAO,EACrD,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,GAAG,EAAE;AAE/C,QAAMA,QAAO,SAAS,QAAQ,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,WAAM,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,IAAI;AAEtG,MAAI,SAAS,WAAW,KAAK,MAAM,WAAW,GAAG;AAC/C,aAAS,KAAK;AAAA,MACZ,MAAAA;AAAA,MACA,SAAS;AAAA,MACT,YACE;AAAA,MACF,MAAM;AAAA,IACR,CAAC;AACD;AAAA,EACF;AAEA,QAAM,WAAW,SAAS;AAAA,IAAK,CAAC,QAC9B,UAAU,IAAI,KAAK,EAAE;AAAA,MAAK,CAAC,UACzB,MAAM,KAAK,CAAC,QAAQ,yBAAyB,OAAO,IAAI,KAAK,CAAC;AAAA,IAChE;AAAA,EACF;AAEA,MAAI,CAAC,UAAU;AACb,WAAO,KAAK;AAAA,MACV,MAAAA;AAAA,MACA,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AASA,SAAS,UAAU,OAA0C;AAC3D,SACE,MAAM,QAAQ,KAAK,KACnB,MAAM,MAAM,CAAC,UAAU,MAAM,QAAQ,KAAK,KAAK,OAAO,MAAM,CAAC,MAAM,QAAQ;AAE/E;AAOA,SAAS,eAAe,OAA2B;AACjD,MAAID,UAAS,KAAK,EAAG,QAAO,CAACA,UAAS,MAAM,KAAK,IAAI,MAAM,QAAQ,KAAK;AACxE,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,QAAQ,cAAc;AAC7D,SAAO,CAAC;AACV;AASA,SAAS,UAAU,KAAyB;AAC1C,MAAI,UAAU,GAAG,GAAG;AAClB,WAAO,IAAI,QAAQ,CAAC,CAAC,MAAM,KAAK,MAAM;AACpC,UAAI,SAAS,MAAO,QAAOA,UAAS,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC;AACxD,UAAI,SAAS,SAAU,QAAO,eAAe,KAAK;AAClD,aAAO,CAAC;AAAA,IACV,CAAC;AAAA,EACH;AACA,MAAI,MAAM,QAAQ,GAAG,KAAK,OAAO,QAAQ;AACvC,WAAO,IAAI,SAAS,IAAI,CAAC,GAAG,IAAI,CAAC;AACnC,SAAOA,UAAS,GAAG,KAAK,OAAO,KAAK,GAAG,EAAE,SAAS,IAAI,CAAC,GAAG,IAAI,CAAC;AACjE;AAEA,SAAS,iBAAiB,KAAuB;AAC/C,SAAO,UAAU,GAAG,EAAE,SAAS;AACjC;AAEA,SAAS,yBAAyB,GAAY,GAAqB;AACjE,MAAI,OAAO,MAAM,OAAO,EAAG,QAAO;AAClC,MAAI,MAAM,QAAQ,MAAM,KAAM,QAAO,MAAM;AAC3C,MAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,EAAG,QAAO;AACjD,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AAClD,UAAM,QAAQ,OAAO,KAAK,CAAW;AACrC,UAAM,QAAQ,OAAO,KAAK,CAAW;AACrC,UAAM,SAAS,MAAM,OAAO,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC;AACpD,WAAO,OAAO,UAAU,KAAK,IAAI,MAAM,QAAQ,MAAM,MAAM,IAAI;AAAA,EACjE;AACA,SAAO;AACT;AAaA,SAAS,eACP,UACA,MACA,QACA,UACM;AACN,QAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,KAAK,gBAAgB,CAAC,CAAC,CAAC;AAC1D,QAAM,QAAQ,CACZ,MACA,aACS;AACT,QAAI,SAAS,OAAW,WAAU,MAAM,UAAU,OAAO,QAAQ,QAAQ;AAAA,EAC3E;AACA,QAAM,KAAK,SAAS,QAAQ;AAE5B,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,GAAG;AAC/D,UAAM,OAAO,QAAQ,GAAG,EAAE,YAAY,IAAI,SAAS;AACnD,UAAM,OAAO,MAAM,GAAG,EAAE,YAAY,IAAI,OAAO;AAAA,EACjD;AACA,aAAW,CAAC,MAAM,WAAW,KAAK,OAAO,QAAQ,KAAK,gBAAgB,CAAC,CAAC,GAAG;AACzE,UAAM,YAAY,QAAQ,GAAG,EAAE,iBAAiB,IAAI,SAAS;AAC7D,UAAM,YAAY,MAAM,GAAG,EAAE,iBAAiB,IAAI,OAAO;AAAA,EAC3D;AACA,QAAM,KAAK,WAAW,MAAM,GAAG,EAAE,iBAAiB;AAClD,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,KAAK,gBAAgB,CAAC,CAAC,GAAG;AAClE,UAAM,KAAK,QAAQ,GAAG,EAAE,iBAAiB,IAAI,SAAS;AACtD,UAAM,KAAK,MAAM,GAAG,EAAE,iBAAiB,IAAI,OAAO;AAAA,EACpD;AACF;AAKA,SAAS,QAAQ,MAAyBC,OAA0B;AAClE,MAAI,UAAmB;AACvB,aAAW,OAAOA,OAAM;AACtB,QAAI,OAAO,QAAQ;AACjB,gBAAU,MAAM,QAAQ,OAAO,IAAI,QAAQ,GAAG,IAAI;AAAA,QAC/C,WAAUD,UAAS,OAAO,IAAI,QAAQ,GAAG,IAAI;AAAA,EACpD;AACA,SAAO;AACT;AAMA,SAAS,UACP,MACA,UACA,OACA,QACA,UACM;AACN,QAAM,QAAQ,CAACC,UACbA,MAAK,SAAS,IAAI,GAAG,QAAQ,IAAIA,MAAK,KAAK,GAAG,CAAC,KAAK;AACtD,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,OAAO,CAAC,QAAyB;AACrC,QAAI,SAAS,IAAI,GAAG,EAAG,QAAO;AAC9B,aAAS,IAAI,GAAG;AAChB,WAAO;AAAA,EACT;AAEA,aAAW,QAAQ,cAAc,IAAI,GAAG;AACtC,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,MAAM,IAAI,MAAM,KAAK,CAAC,KAAK,WAAW,MAAM,EAAE,EAAG;AACrD,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,wBAAwB,MAAM,iBAAiB,QAAQ;AAAA,QAChE,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAIA,UAAM,OAAO,KAAK,KAAK,KAAK,KAAK,SAAS,CAAC;AAC3C,QAAI,KAAK,QAAQ,CAAC,KAAK,SAAS,KAAK,SAAS,QAAQ;AACpD,YAAM,OAAO,QAAQ,MAAM,KAAK,IAAI;AACpC,YAAM,OAAO,QAAQ,MAAM,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AACjD,UACED,UAAS,IAAI,KACb,KAAK,SAAS,QACd,OAAO,SAAS,YAChB,MAAM,QAAQ,IAAI,KAClB,OAAO,KAAK,SAAS,KACrB,KAAK,QAAQ,MAAM,KAAK,IAAI,CAAC,EAAE,GAC/B;AACA,cAAM,KAAK,MAAM,KAAK,IAAI;AAC1B,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,2BAA2B,EAAE;AAAA,UACtC,MAAM;AAAA,UACN,YACE;AAAA,QACJ,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QACE,KAAK,SAAS,UACd,SAAS,UACT,KAAK,QAAQ,WAAW,KACxB,KAAK,SAAS,MAAM,KAAK,IAAI,CAAC,EAAE,GAChC;AACA,YAAM,KAAK,MAAM,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AACvC,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,iBAAiB,EAAE;AAAA,QAC5B,MAAM;AAAA,QACN,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAEA,eAAW,YAAY,CAAC,GAAI,KAAK,OAAO,CAAC,GAAI,IAAI,GAAG;AAClD,YAAM,QAAQ,SAAS,KAAK,SAAS,KAAK,SAAS,CAAC;AACpD,UAAI,OAAO,UAAU,SAAU;AAC/B,YAAM,WAAW,SAAS,KAAK,MAAM,GAAG,EAAE;AAC1C,YAAM,OAAO,QAAQ,MAAM,QAAQ;AACnC,UAAI,CAAC,MAAM,QAAQ,IAAI,EAAG;AAC1B,YAAM,KAAK,MAAM,QAAQ;AAEzB,UACE,SAAS,SAAS,UAClB,KAAK,WAAW,KAChB,KAAK,UAAU,EAAE,EAAE,GACnB;AACA,cAAM,OAAO,KAAK,CAAC;AACnB,cAAM,OACJ,OAAO,SAAS,WACZ,eAAe,IAAI,mBACnB;AACN,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,wBAAwB,EAAE,KAAK,IAAI;AAAA,UAC5C,MAAM;AAAA,UACN,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAGA,YAAM,cAAc,SAAS,SAAS,SAAS,CAAC,MAAM;AACtD,UACE,SAAS,SAAS,SAClB,CAAC,eACD,KAAK,SAAS,KACd,KAAK,eAAe,EAAE,EAAE,GACxB;AACA,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,wBAAwB,EAAE;AAAA,UACnC,MAAM;AAAA,UACN,YACE;AAAA,QACJ,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,gBAAgB,OAAyB;AAChD,MAAI,OAAO,UAAU;AACnB,WACE,MAAM,SAASK,kBAAiB,KAChC,MAAM,WAAWE,qBAAoB,KACrC,qBAAqB,KAAK,KAAK;AAEnC,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,eAAe;AAC3D,SAAOP,UAAS,KAAK,KAAK,OAAO,OAAO,KAAK,EAAE,KAAK,eAAe;AACrE;AAEA,IAAM,mBAAmB;AAGlB,SAAS,eAAe,MAAiC;AAC9D,SAAO,KAAK,YAAY;AAC1B;AAYA,SAAS,qBACP,SACA,UACe;AACf,MAAI,WAA8C,CAAC;AACnD,MAAI,UAAU;AACZ,QAAI;AACF,iBAAW,iBAAiB,QAAQ;AAAA,IACtC,SAAS,OAAO;AACd,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS,uCAAuC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MACxG;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAA4B,CAAC;AACnC,aAAW,CAAC,OAAO,KAAK,KAAK,QAAQ,QAAQ,GAAG;AAC9C,QAAIA,UAAS,KAAK,GAAG;AACnB,cAAQ,KAAK,KAAK;AAClB;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,UAAU,WAAW,MAAM,MAAM,YAAY,IAAI;AACtE,QAAI,CAAC;AACH,aAAO;AAAA,QACL,IAAI;AAAA,QACJ;AAAA,QACA,SAAS;AAAA,MACX;AACF,QAAI,EAAE,MAAM,CAAC,KAAK;AAChB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ;AAAA,QACA,SAAS,aAAa,MAAM,CAAC,CAAC,mDAAmD,OAAO,KAAK,QAAQ,EAAE,KAAK,IAAI,KAAK,MAAM;AAAA,MAC7H;AACF,UAAM,OAAgB,MAAM,CAAC,IACzB,UAAU,SAAS,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,IACtC,SAAS,MAAM,CAAC,CAAC;AACrB,QAAI,CAACA,UAAS,IAAI;AAChB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ;AAAA,QACA,SAAS,GAAG,MAAM,CAAC,CAAC;AAAA,MACtB;AACF,YAAQ,KAAK,IAAI;AAAA,EACnB;AACA,SAAO,EAAE,IAAI,MAAM,QAAQ;AAC7B;AAiBO,SAAS,0BACd,UACA,MACA,UACA,QACA,UACA,QACA,SACA,kBACM;AACN,QAAM,WACJ,qBACCA,UAAS,KAAK,MAAM,KAAKA,UAAS,KAAK,OAAO,QAAQ,IACnD,KAAK,OAAO,WACZ,CAAC;AACP,QAAM,eAAe,GAAG,QAAQ;AAEhC,MAAI,SAAS,aAAa,UAAa,CAAC,MAAM,QAAQ,SAAS,QAAQ,GAAG;AACxE,QAAI,KAAK;AACP,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QACE;AAAA,QACF,MAAM;AAAA,MACR,CAAC;AACH;AAAA,EACF;AACA,QAAM,aAA4B,MAAM,QAAQ,SAAS,QAAQ,IAC7D,qBAAqB,SAAS,UAAU,QAAQ,IAChD,EAAE,IAAI,MAAM,SAAS,CAAC,EAAE;AAC5B,MAAI,CAAC,WAAW,IAAI;AAClB,WAAO,KAAK;AAAA,MACV,MACE,WAAW,UAAU,SACjB,eACA,GAAG,YAAY,IAAI,WAAW,KAAK;AAAA,MACzC,SAAS,kDAAkD,WAAW,OAAO;AAAA,MAC7E,MAAM;AAAA,IACR,CAAC;AACD;AAAA,EACF;AACA,MAAI,CAAC,KAAK,SAAU;AACpB,QAAM,YAAY,WAAW;AAI7B,MAAI,UAAU,KAAK,eAAe,GAAG;AACnC,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QACE;AAAA,MACF,MAAM;AAAA,IACR,CAAC;AACD;AAAA,EACF;AAEA,QAAM,OAAO,SAAS,SAAS,WAAW,WAAW;AACrD,QAAM,SAAS,SAAS,WAAW;AACnC,QAAM,SAASA,UAAS,SAAS,MAAM,IAAI,SAAS,SAAS,CAAC;AAC9D,QAAM,cACJ,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;AAExD,aAAW,CAAC,QAAQ,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,GAAG;AAC7D,QAAI,QAAQ,WAAW,CAACA,UAAS,QAAQ,EAAE,EAAG;AAC9C,QAAI,QAAQ,QAAQ,OAAW;AAE/B,UAAM,QAAmC,QAAQ;AACjD,UAAM,UAAU,6BAA6B,OAAO,QAAW;AAAA,MAC7D;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,SAAS,UAAU,QAAQ,GAAG;AACpC,UAAM,UACJ,UAAU,QAAQ,GAAG,KACrB,QAAQ,IAAI,KAAK,CAAC,CAAC,MAAM,KAAK,MAAM,SAAS,YAAY,UAAU,KAAK;AAE1E,QAAI;AACJ,QAAI,SAAS,YAAY,CAAC,QAAQ,SAAS;AACzC,eAAS,WAAW,OAAO,WAAW;AAAA,IACxC,OAAO;AACL,eACE,OAAO,SAAS,KAChB,OAAO,MAAM,CAAC,QAAQ;AACpB,YAAI,CAAC,YAAa,QAAO;AACzB,cAAM,OAAO,UAAU,KAAK,WAAW;AACvC,eAAO,QAAQ,UAAU,SAAS,QAAQ,SAAS;AAAA,MACrD,CAAC;AAAA,IACL;AACA,QAAI,OAAQ;AAEZ,UAAM,SAAS,QAAQ,UACnB,qCACA,kCAAkC,QAAQ,OACvC,IAAI,CAAC,MAAM,GAAG,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,EAAE,EAC3C,KAAK,IAAI,CAAC;AACjB,UAAM,WACJ,SAAS,YAAY,CAAC,QAAQ,UAC1B,iCACA,eAAe,cAAc,SAAS,WAAW,IAAI,QAAQ,OAAO,KAAK,EAAE;AACjF,UAAMC,QAAO,GAAG,QAAQ,aAAa,MAAM;AAC3C,UAAM,UAAU,iDAAiD,MAAM,mBAAmB,IAAI,cAAc,QAAQ;AAEpH,QAAI,QAAQ;AACV,aAAO,KAAK,EAAE,MAAAA,OAAM,SAAS,MAAM,qBAAqB,CAAC;AAAA,IAC3D,OAAO;AACL,eAAS,KAAK;AAAA,QACZ,MAAAA;AAAA,QACA;AAAA,QACA,YACE;AAAA,QACF,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AOppCA,SAAS,YAAAO,iBAAgB;AACzB,SAAS,WAAAC,gBAAe;AAQxB,IAAM,EAAE,WAAW,IAAIA,SAAQ;AAE/B,SAAS,OAAO,OAAyB;AACvC,SAAOD,UAAS,KAAK,KAAK,WAAW,UAAU,KAAK,EAAE;AACxD;AAMA,SAAS,gBACP,KACA,OACkC;AAClC,MAAI,IAAI,SAAS,GAAG,KAAK,CAACA,UAAS,KAAK,EAAG,QAAO;AAClD,QAAM,QAAQ,OAAO,OAAO,KAAK;AACjC,SACE,MAAM,SAAS,KACf,MAAM;AAAA,IAAM,CAAC,SACX,MAAM,QAAQ,IAAI,IACd,KAAK,SAAS,KAAK,KAAK,MAAM,MAAM,IACpC,OAAO,IAAI;AAAA,EACjB;AAEJ;AAEO,SAAS,gBAAgB,OAAgC;AAC9D,QAAM,SAA4B,CAAC;AACnC,QAAM,WAAgC,CAAC;AACvC,QAAM,UAA2B,CAAC;AAGlC,MAAI,CAACA,UAAS,KAAK,GAAG;AACpB,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AACD,WAAO,EAAE,OAAO,OAAO,MAAM,WAAW,QAAQ,UAAU,QAAQ;AAAA,EACpE;AAEA,QAAM,UAAU;AAChB,QAAM,iBAA2B,CAAC;AAClC,QAAM,eAAyB,CAAC;AAChC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,gBAAgB,KAAK,KAAK,GAAG;AAC/B,iBAAW,UAAU,OAAO,KAAK,KAAK;AACpC,uBAAe,KAAK,GAAG,GAAG,IAAI,MAAM,EAAE;AAAA,IAC1C,OAAO;AACL,mBAAa,KAAK,GAAG;AAAA,IACvB;AAAA,EACF;AACA,QAAM,WAAW,CAAC,GAAG,gBAAgB,GAAG,YAAY;AACpD,UAAQ,gBAAgB;AACxB,UAAQ,eAAe,SAAS;AAGhC,eAAa,QAAQ,CAAC,SAAS,UAAU;AAEvC,UAAM,aAAa,QAAQ,SAAS,GAAG;AACvC,UAAM,WAAW,QAAQ,SAAS,GAAG;AAErC,QAAI,CAAC,cAAc,CAAC,UAAU;AAC5B,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,0BAA0B,OAAO;AAAA,QAC1C,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAGA,QAAI,YAAY,OAAO,UAAU,aAAa,SAAS,GAAG;AACxD,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YACE;AAAA,MACJ,CAAC;AAAA,IACH;AAGA,UAAM,OAAO,QAAQ,OAAO;AAC5B,UAAM,cAAc,MAAM,QAAQ,IAAI,IAClC,KAAK,MAAM,CAAC,MAAM,OAAO,MAAM,YAAY,MAAM,IAAI,IACrD,OAAO,SAAS,YAAY,SAAS;AAEzC,QAAI,CAAC,aAAa;AAChB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC3FA,eAAsB,cACpBE,OACA,YACA,UAAiD,CAAC,GACzB;AACzB,QAAM,WAAW,aAAa,YAAY,EAAE,MAAM,QAAQ,MAAM,MAAAA,MAAK,CAAC;AACtE,QAAM,SAA4B,CAAC,GAAG,SAAS,MAAM;AACrD,QAAM,UAA0B,CAAC;AACjC,QAAM,WAA+B,CAAC;AACtC,QAAM,WAA6B,CAAC;AACpC,QAAM,UAAU,sBAAsB,QAAQ,SAAS;AACvD,QAAM,OAAO,WAAW,UAAU,IAAI,aAAa;AACnD,QAAM,cAAc,oBAAI,IAA4B;AAEpD,aAAW,UAAU,SAAS,SAAS;AACrC,UAAM,KAAK,SAAS,OAAO,IAAI,IAAI,OAAO,OAAO,IAAI,OAAO,GAAG;AAC/D,QAAI,aAAa,YAAY,IAAI,OAAO,IAAI;AAC5C,QAAI,CAAC,cAAc,MAAM;AACvB,mBAAa,YAAY,MAAM,OAAO,IAAI;AAC1C,kBAAY,IAAI,OAAO,MAAM,UAAU;AACvC,UAAI,CAAC,WAAW,GAAI,QAAO,KAAK,WAAW,KAAK;AAAA,IAClD;AACA,QAAI,CAAC,YAAY,IAAI;AACnB,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QAAQ,SAAS,OAAO,IAAI;AAAA,QAC5B,MAAM;AAAA,MACR,CAAC;AACD;AAAA,IACF;AACA,UAAM,UAAU,MAAM;AAAA,MACpB;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,IACF;AACA,WAAO,KAAK,GAAG,QAAQ,QAAQ;AAC/B,YAAQ,KAAK,GAAG,QAAQ,OAAO;AAC/B,aAAS,KAAK,GAAG,QAAQ,QAAQ;AACjC,QAAI,QAAQ,QAAS,UAAS,KAAK,QAAQ,OAAO;AAAA,EACpD;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,MAAM;AAAA,IACN;AAAA,IACA,UAAU,CAAC;AAAA,IACX,SAAS;AAAA,MACP,OAAO,cAAc,UAAU,CAAC,gBAAgB,CAAC;AAAA,MACjD;AAAA,MACA;AAAA,MACA,GAAI,SAAS,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,IAC5C;AAAA,EACF;AACF;;;AC3DO,IAAM,iBAAiB;AAAA;AAAA,EAE5B;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;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;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;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;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;A3BnEA,SAAS,oBACP,MACA,SACmB;AACnB,QAAM,SAA4B,CAAC;AACnC,MAAI,QAAQ,YAAY,SAAS,SAAS,UAAU,QAAQ,OAAO;AACjE,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SACE,SAAS,SACL,+FACA,mEAAmE,IAAI;AAAA,MAC7E,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,MAAI,SAAS,OAAQ,QAAO;AAC5B,MAAI,QAAQ,SAAS,QAAW;AAC9B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,qEAAqE,IAAI;AAAA,MAClF,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,MAAI,QAAQ,SAAS,QAAW;AAC9B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,+DAA+D,IAAI;AAAA,MAC5E,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAMA,SAAS,sBAAsB,OAAgC;AAC7D,MAAI,CAAC,WAAW,KAAK,EAAG,QAAO,iBAAiB,KAAK;AACrD,QAAM,WAAWC,UAAS,KAAK,IAAI,MAAM,WAAW;AACpD,MAAI,aAAa,QAAW;AAC1B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QACR;AAAA,MACF;AAAA,MACA,UAAU,CAAC;AAAA,MACX,SAAS,CAAC;AAAA,IACZ;AAAA,EACF;AACA,SAAO,iBAAiB,QAAQ;AAClC;AAMA,SAAS,YAAY,OAAoC;AACvD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,YAAiB,cAAQ,KAAK;AACpC,SAAU,eAAW,SAAS,KAAQ,aAAS,SAAS,EAAE,OAAO,IACxD,cAAQ,SAAS,IACtB;AACN;AAGA,SAAS,kBACP,QACA,OACgB;AAChB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS;AAAA,MACP,GAAG,OAAO;AAAA,MACV,OAAO,OAAO,QAAQ,SAAS,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE;AAAA,MAC5D,SAAS,OAAO,QAAQ,WAAW,CAAC;AAAA,IACtC;AAAA,EACF;AACF;AAQA,eAAsB,SACpB,MACA,OACA,UAA2B,CAAC,GACH;AAGzB,MAAI,WAAW;AACf,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,MAAM,KAAK,MAAM,GAAI,OAAM,IAAI,MAAM,GAAG,IAAI,cAAc;AAC9D,eAAW,MAAM,eAAe,KAAK;AAAA,EACvC;AAEA,QAAM,SAAS,oBAAoB,MAAM,OAAO;AAChD,MAAI,OAAO,SAAS,GAAG;AACrB,WAAO;AAAA,MACL,OAAO;AAAA,MACP;AAAA,MACA,QAAQ;AAAA,MACR,UAAU,CAAC;AAAA,MACX,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE;AAAA,IAC3D;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,eAAe,MAAM,OAAO,UAAU,OAAO;AAClE,SAAO,QAAQ,WAAW,OAAO,eAAe,MAAM,IAAI;AAC5D;AAMA,SAAS,eAAe,QAAwC;AAC9D,QAAM,UAAU,OAAO,QAAQ,WAAW,CAAC;AAC3C,MAAI,OAAO,SAAS,WAAW,KAAK,QAAQ,WAAW,EAAG,QAAO;AACjE,SAAO,EAAE,GAAG,QAAQ,OAAO,MAAM;AACnC;AAEA,eAAe,eACb,MACA,OACA,UACA,SACyB;AACzB,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,kBAAkB,sBAAsB,QAAQ,GAAG,UAAU;AAAA,IACtE,KAAK;AACH,aAAO,kBAAkBC,eAAc,QAAQ,GAAG,OAAO;AAAA,IAC3D,KAAK;AACH,UAAI,QAAQ,SAAS,QAAW;AAC9B,eAAO,cAAc,QAAQ,MAAM,UAAU;AAAA,UAC3C,MAAM,QAAQ;AAAA,UACd,WAAW,QAAQ,aAAa,YAAY,KAAK;AAAA,QACnD,CAAC;AAAA,MACH;AACA,aAAO,yBAAyB,UAAU;AAAA,QACxC,MAAM,QAAQ;AAAA,QACd,QAAQ,QAAQ;AAAA,QAChB,SAAS,QAAQ;AAAA,QACjB,WAAW,QAAQ,aAAa,YAAY,KAAK;AAAA,MACnD,CAAC;AAAA,IACH,KAAK;AACH,aAAO,kBAAkB,gBAAgB,QAAQ,GAAG,SAAS;AAAA,IAC/D;AACE,YAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;AAAA,EACtD;AACF;","names":["fs","path","chalk","isObject","fs","fs","fs","fs","fs","schemas","fs","fs","isObject","validateEvent","checkWindowCollector","ENV_MARKER_PREFIX","isObject","SECRET_MARKER_PREFIX","schemas","validateFlowStructure","isObject","path","getFlowSettings","getErrorMessage","getErrorMessage","isObject","fs","path","isObject","isObject","getErrorMessage","validate","validateFlowConfig","schemas","isObject","path","validateFlowConfig","result","validateFlowStructure","ENV_MARKER_PREFIX","checkWindowCollector","SECRET_MARKER_PREFIX","isObject","schemas","path","isObject","validateEvent"]}
1
+ {"version":3,"sources":["../src/commands/validate/index.ts","../src/core/cli-logger.ts","../src/core/timer.ts","../src/core/output.ts","../src/core/tmp.ts","../src/config/utils.ts","../src/core/fetch-failure.ts","../src/core/utils.ts","../src/core/local-packages.ts","../src/core/input-detector.ts","../src/core/stdin.ts","../src/lib/credentials.ts","../src/core/oauth-login.ts","../src/core/app-fetch.ts","../src/core/event-validation.ts","../src/config/validators.ts","../src/config/loader.ts","../src/core/content-digest.ts","../src/config/window-collector.ts","../src/commands/validate/validators/contract.ts","../src/commands/validate/validators/event.ts","../src/commands/validate/validators/flow.ts","../src/commands/validate/scope.ts","../src/commands/validate/validators/resolve.ts","../src/commands/validate/validators/settings.ts","../src/core/resolve-export-name.ts","../src/core/step-packages.ts","../src/core/package-info.ts","../src/commands/validate/validators/mapping.ts","../src/commands/validate/validators/entry.ts","../src/commands/validate/types.ts"],"sourcesContent":["// walkerOS/packages/cli/src/commands/validate/index.ts\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport chalk from 'chalk';\nimport { isObject } from '@walkeros/core';\nimport { createCLILogger } from '../../core/cli-logger.js';\nimport {\n getErrorMessage,\n isStdinPiped,\n readStdin,\n writeResult,\n} from '../../core/index.js';\nimport { loadJsonConfig } from '../../config/index.js';\nimport {\n validateContract,\n validateEvent,\n validateMapping,\n} from './validators/index.js';\nimport { validateEntry } from './validators/entry.js';\nimport { isFlowJson } from './scope.js';\nimport { validateFlowWithPackages } from './validators/flow.js';\nimport type { ValidateOptions } from '../../schemas/validate.js';\nimport {\n USAGE_CODES,\n type ValidateCheck,\n type ValidateCommandOptions,\n type ValidateResult,\n type ValidationError,\n type ValidationType,\n} from './types.js';\n\n/**\n * Options that do not apply to the validation type: `--path` and `--flow`\n * address flow files only, `--offline` removes the package settings check\n * of a whole-file flow run. Never ignored silently (OPTION_NOT_APPLICABLE).\n */\nfunction inapplicableOptions(\n type: ValidationType,\n options: ValidateOptions,\n): ValidationError[] {\n const errors: ValidationError[] = [];\n if (options.offline === true && (type !== 'flow' || options.path)) {\n errors.push({\n path: 'options.offline',\n message:\n type === 'flow'\n ? '--offline removes the package settings check, which is all --path checks; drop one of them'\n : `--offline applies to a whole flow file and does not apply to -t ${type}`,\n code: 'OPTION_NOT_APPLICABLE',\n });\n }\n if (type === 'flow') return errors;\n if (options.path !== undefined) {\n errors.push({\n path: 'options.path',\n message: `--path addresses an entry in a flow file and does not apply to -t ${type}`,\n code: 'OPTION_NOT_APPLICABLE',\n });\n }\n if (options.flow !== undefined) {\n errors.push({\n path: 'options.flow',\n message: `--flow names a flow in a flow file and does not apply to -t ${type}`,\n code: 'OPTION_NOT_APPLICABLE',\n });\n }\n return errors;\n}\n\n/**\n * `-t contract` on a flow file validates its `contract` section, exactly as\n * on the section alone (M-dispatch).\n */\nfunction validateContractInput(input: unknown): ValidateResult {\n if (!isFlowJson(input)) return validateContract(input);\n const contract = isObject(input) ? input.contract : undefined;\n if (contract === undefined) {\n return {\n valid: false,\n type: 'contract',\n errors: [\n {\n path: 'contract',\n message: 'The flow file has no contract section to validate',\n code: 'NO_CONTRACT_SECTION',\n },\n ],\n warnings: [],\n details: {},\n };\n }\n return validateContract(contract);\n}\n\n/**\n * The directory a local `path` package resolves against: the input file's,\n * when the input is a file on disk. Otherwise none, and no disk reads.\n */\nfunction configDirOf(input: unknown): string | undefined {\n if (typeof input !== 'string') return undefined;\n const candidate = path.resolve(input);\n return fs.existsSync(candidate) && fs.statSync(candidate).isFile()\n ? path.dirname(candidate)\n : undefined;\n}\n\n/** Whole-document validations state their one check as their scope. */\nfunction withDocumentScope(\n result: ValidateResult,\n check: ValidateCheck,\n): ValidateResult {\n return {\n ...result,\n details: {\n ...result.details,\n scope: result.details.scope ?? { flows: [], checks: [check] },\n skipped: result.details.skipped ?? [],\n },\n };\n}\n\n/**\n * Programmatic API for validation.\n * Can be called directly from code or MCP server.\n *\n * Accepts parsed objects, JSON strings, file paths, or URLs as input.\n */\nexport async function validate(\n type: ValidationType,\n input: unknown,\n options: ValidateOptions = {},\n): Promise<ValidateResult> {\n // Resolve string inputs (file paths, URLs, JSON strings) to parsed objects.\n // A path that cannot be read or parsed is an input error, never a document.\n let resolved = input;\n if (typeof input === 'string') {\n if (input.trim() === '') throw new Error(`${type} is required`);\n resolved = await loadJsonConfig(input);\n }\n\n const misuse = inapplicableOptions(type, options);\n if (misuse.length > 0) {\n return {\n valid: false,\n type,\n errors: misuse,\n warnings: [],\n details: { scope: { flows: [], checks: [] }, skipped: [] },\n };\n }\n\n const result = await validateByType(type, input, resolved, options);\n return options.strict === true ? escalateStrict(result) : result;\n}\n\n/**\n * `strict` turns warnings and skipped checks into failures (C4), in every\n * entry point (C9): the result is invalid, with the findings unchanged.\n */\nfunction escalateStrict(result: ValidateResult): ValidateResult {\n const skipped = result.details.skipped ?? [];\n if (result.warnings.length === 0 && skipped.length === 0) return result;\n return { ...result, valid: false };\n}\n\nasync function validateByType(\n type: ValidationType,\n input: unknown,\n resolved: unknown,\n options: ValidateOptions,\n): Promise<ValidateResult> {\n switch (type) {\n case 'contract':\n return withDocumentScope(validateContractInput(resolved), 'contract');\n case 'event':\n return withDocumentScope(validateEvent(resolved), 'event');\n case 'flow':\n if (options.path !== undefined) {\n return validateEntry(options.path, resolved, {\n flow: options.flow,\n configDir: options.configDir ?? configDirOf(input),\n });\n }\n return validateFlowWithPackages(resolved, {\n flow: options.flow,\n strict: options.strict,\n offline: options.offline,\n configDir: options.configDir ?? configDirOf(input),\n });\n case 'mapping':\n return withDocumentScope(validateMapping(resolved), 'mapping');\n default:\n throw new Error(`Unknown validation type: ${type}`);\n }\n}\n\n/** One line naming what the run covered (C6). */\nfunction formatScope(result: ValidateResult): string | undefined {\n const scope = result.details.scope;\n if (!scope) return undefined;\n const parts: string[] = [];\n if (scope.flows.length > 0) parts.push(`flows ${scope.flows.join(', ')}`);\n if (scope.entry) {\n const { section, key, flows, searchedFlows } = scope.entry;\n const name = section ? `${section}.${key}` : key;\n parts.push(\n `entry ${name} in ${flows.join(', ') || 'no flow'} (searched ${searchedFlows.join(', ') || 'none'})`,\n );\n }\n parts.push(`${scope.checks.length} check(s): ${scope.checks.join(', ')}`);\n if (scope.offline) parts.push('offline: package settings not checked');\n return `Scope: ${parts.join('; ')}`;\n}\n\n/**\n * Format validation result for CLI output.\n */\nfunction formatResult(\n result: ValidateResult,\n options: { json?: boolean; verbose?: boolean; exitCode: number },\n): string {\n if (options.json) {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push('');\n lines.push(`Validating ${result.type}...`);\n lines.push('');\n\n const scopeLine = formatScope(result);\n if (scopeLine) {\n lines.push(scopeLine);\n lines.push('');\n }\n\n // Show details if verbose\n if (options.verbose && Object.keys(result.details).length > 0) {\n lines.push('Details:');\n for (const [key, value] of Object.entries(result.details)) {\n lines.push(` ${key}: ${JSON.stringify(value)}`);\n }\n lines.push('');\n }\n\n lines.push('Validation Results:');\n\n // Errors\n for (const error of result.errors) {\n lines.push(chalk.red(` ✗ ${error.path}: ${error.message}`));\n }\n\n // Warnings\n for (const warning of result.warnings) {\n lines.push(chalk.yellow(` ⚠ ${warning.path}: ${warning.message}`));\n if (warning.suggestion) {\n lines.push(chalk.gray(` → ${warning.suggestion}`));\n }\n }\n\n // Skips are listed whatever the verbosity (C5)\n const skipped = result.details.skipped ?? [];\n for (const skip of skipped) {\n lines.push(\n chalk.gray(` - skipped ${skip.check} at ${skip.path}: ${skip.reason}`),\n );\n }\n\n // The verdict line follows the exit code: a run that fails under\n // --strict (exit 2) never reads as passed.\n if (options.exitCode === 2) {\n lines.push(\n chalk.red(\n ` ✗ Failed under --strict: ${result.warnings.length} warning(s) and ${skipped.length} skipped check(s) count as failures`,\n ),\n );\n } else if (result.valid && skipped.length === 0) {\n lines.push(chalk.green(` ✓ All checks passed`));\n } else if (result.valid) {\n lines.push(\n chalk.green(\n ` ✓ No errors in checked scope; ${skipped.length} check(s) skipped`,\n ),\n );\n }\n\n lines.push('');\n lines.push(\n `Summary: ${result.errors.length} error(s), ${result.warnings.length} warning(s), ${skipped.length} skipped`,\n );\n\n return lines.join('\\n');\n}\n\n/**\n * CLI command handler for validate command.\n */\nexport async function validateCommand(\n options: ValidateCommandOptions,\n): Promise<void> {\n // Result always goes to stdout; logs to stderr\n const logger = createCLILogger({ ...options, stderr: true });\n\n let exitCode: number;\n try {\n // Load input: stdin > argument > error\n let input: unknown;\n if (isStdinPiped() && !options.input) {\n const stdinContent = await readStdin();\n try {\n input = JSON.parse(stdinContent);\n } catch {\n throw new Error('Invalid JSON received on stdin');\n }\n } else {\n input = options.input;\n }\n\n // Run validation\n const result = await validate(options.type, input, {\n flow: options.flow,\n path: options.path,\n strict: options.strict,\n offline: options.offline,\n });\n\n exitCode = exitCodeOf(result, options.strict === true);\n\n // Format and write result; --silent suppresses stdout, never a file\n if (!options.silent || options.output) {\n const formatted = formatResult(result, {\n json: options.json,\n verbose: options.verbose,\n exitCode,\n });\n await writeResult(formatted + '\\n', { output: options.output });\n }\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n\n if (options.json) {\n const errorOutput = JSON.stringify(\n {\n valid: false,\n type: options.type,\n errors: [\n { path: 'input', message: errorMessage, code: 'INPUT_ERROR' },\n ],\n warnings: [],\n details: { scope: { flows: [], checks: [] }, skipped: [] },\n },\n null,\n 2,\n );\n await writeResult(errorOutput + '\\n', { output: options.output });\n } else {\n logger.error(`Error: ${errorMessage}`);\n }\n\n exitCode = 3;\n }\n process.exit(exitCode);\n}\n\n/**\n * Exit code (C7): 3 could not run, 1 errors, 2 warnings or skips under\n * --strict, 0 valid.\n */\nfunction exitCodeOf(result: ValidateResult, strict: boolean): number {\n if (\n result.errors.some((e) =>\n USAGE_CODES.some((usageCode) => usageCode === e.code),\n )\n )\n return 3;\n if (result.errors.length > 0) return 1;\n const skipped = result.details.skipped ?? [];\n if (strict && (result.warnings.length > 0 || skipped.length > 0)) return 2;\n return result.valid ? 0 : 1;\n}\n\n// Re-export types\nexport * from './types.js';\nexport {\n validateContract,\n validateEvent,\n validateFlow,\n validateMapping,\n} from './validators/index.js';\nexport { validateEntry } from './validators/entry.js';\n","import chalk from 'chalk';\nimport {\n createCLILogger as createCoreCLILogger,\n createCLILoggerConfig as createCoreCLILoggerConfig,\n type CLILoggerColors,\n type CLILoggerOptions,\n} from '@walkeros/core/node';\nimport type { Logger } from '@walkeros/core';\n\nexport type { CLILoggerOptions };\n\n/**\n * The CLI's terminal colours. Binding them here matters: the shared factory in\n * `@walkeros/core/node` defaults every level to identity, so a plain re-export\n * would silently drop red ERROR output at every CLI call site.\n */\nconst CLI_COLORS: CLILoggerColors = { error: chalk.red };\n\n/**\n * Build the `Logger.Config` backing the CLI logger, with the CLI's colours.\n * See `createCLILoggerConfig` in `@walkeros/core/node` for the handler contract.\n */\nexport function createCLILoggerConfig(\n options: CLILoggerOptions = {},\n): Logger.Config {\n return createCoreCLILoggerConfig(options, CLI_COLORS);\n}\n\n/**\n * Create a core Logger.Instance with CLI-appropriate behavior.\n *\n * Behavior:\n * - ERROR: always shown (chalk red, via console.error) unless --json\n * - WARN: shown unless --silent or --json\n * - INFO: shown unless --silent or --json\n * - DEBUG: shown only with --verbose (and not --silent/--json)\n * - json(): shown unless --silent\n */\nexport function createCLILogger(\n options: CLILoggerOptions = {},\n): Logger.Instance {\n return createCoreCLILogger(options, CLI_COLORS);\n}\n","export { createTimer, type Timer } from '@walkeros/core';\n","/**\n * Output formatting utilities for CLI commands\n */\n\nimport fs from 'fs-extra';\nimport path from 'path';\n\n/**\n * Write command result to stdout or file.\n * Used by all commands to implement -o/--output flag.\n */\nexport async function writeResult(\n content: string | Buffer,\n options: { output?: string },\n): Promise<void> {\n if (options.output) {\n const outputPath = path.resolve(options.output);\n await fs.ensureDir(path.dirname(outputPath));\n await fs.writeFile(outputPath, content);\n } else {\n process.stdout.write(content);\n process.stdout.write('\\n');\n }\n}\n\nexport interface JsonOutput<T = Record<string, unknown>> {\n success: boolean;\n data?: T;\n error?: string;\n duration?: number;\n}\n\n/**\n * Create standardized JSON output for CLI commands\n */\nexport function createJsonOutput<T = Record<string, unknown>>(\n success: boolean,\n data?: T,\n error?: string,\n duration?: number,\n): JsonOutput<T> {\n return {\n success,\n ...(data && { data }),\n ...(error && { error }),\n ...(duration && { duration }),\n };\n}\n\n/**\n * Create success JSON output\n */\nexport function createSuccessOutput<T = Record<string, unknown>>(\n data?: T,\n duration?: number,\n): JsonOutput<T> {\n return createJsonOutput(true, data, undefined, duration);\n}\n\n/**\n * Create error JSON output\n */\nexport function createErrorOutput(\n error: string,\n duration?: number,\n): JsonOutput<never> {\n return createJsonOutput<never>(false, undefined, error, duration);\n}\n\n/**\n * Format bytes to KB with 2 decimal places\n */\nexport function formatBytes(bytes: number): string {\n return (bytes / 1024).toFixed(2);\n}\n\n/**\n * Format file size with appropriate unit\n */\nexport function formatFileSize(bytes: number): string {\n const units = ['B', 'KB', 'MB', 'GB'];\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex++;\n }\n\n return `${size.toFixed(2)} ${units[unitIndex]}`;\n}\n","/**\n * Temp path helpers live in `@walkeros/core/node`, shared with the runtime.\n * All temp files go to os.tmpdir() by default.\n */\nexport {\n getTmpPath,\n createTmpResolver,\n getDefaultTmpRoot,\n type TmpResolver,\n} from '@walkeros/core/node';\n","/**\n * Configuration Utility Functions\n */\n\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { fetchFailureReason } from '../core/fetch-failure.js';\n\n/**\n * Check if a string is a valid URL\n *\n * @param str - String to check\n * @returns True if string is a valid HTTP/HTTPS URL\n */\nexport function isUrl(str: string): boolean {\n try {\n const url = new URL(str);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/** A config input named an http(s) URL that could not be read. */\nexport class ConfigFetchError extends Error {\n readonly url: string;\n /** The HTTP status, when the host answered. */\n readonly status?: number;\n\n constructor(message: string, url: string, status?: number) {\n super(message);\n this.name = 'ConfigFetchError';\n this.url = url;\n this.status = status;\n }\n}\n\n/**\n * Fetch content from a URL as a string.\n * Shared helper for all URL-loading paths.\n *\n * A plain GET with no credential for every host: a config input is any URL\n * the person names, and a link to a saved flow carries its own signature.\n *\n * @param url - HTTP/HTTPS URL to fetch\n * @returns Response body as a string\n * @throws ConfigFetchError if the host cannot be reached or answers non-OK\n */\nexport async function fetchContentString(url: string): Promise<string> {\n let response: Response;\n try {\n response = await fetch(url);\n } catch (error) {\n throw new ConfigFetchError(\n `Could not reach ${url}: ${fetchFailureReason(error)}`,\n url,\n );\n }\n if (!response.ok) {\n throw new ConfigFetchError(\n `Failed to fetch ${url}: ${response.status} ${response.statusText}`.trimEnd(),\n url,\n response.status,\n );\n }\n return response.text();\n}\n\n/**\n * Substitute environment variables in a string.\n *\n * @param value - String with ${VAR} placeholders\n * @returns String with environment variables substituted\n * @throws Error if environment variable is not found\n *\n * @example\n * ```typescript\n * substituteEnvVariables('${HOME}/config') // \"/Users/name/config\"\n * ```\n */\nexport function substituteEnvVariables(value: string): string {\n return value.replace(/\\${([^}]+)}/g, (_, envVar) => {\n const envValue = process.env[envVar];\n if (!envValue) {\n throw new Error(`Environment variable ${envVar} not found`);\n }\n return envValue;\n });\n}\n\n/**\n * Resolve raw string content from a URL, file path, or inline string.\n *\n * Detection priority:\n * 1. URL (http://, https://) — download content\n * 2. Existing file path — read file content\n * 3. Inline string — return as-is\n *\n * @param input - URL, file path, or inline string\n * @returns Raw string content and the resolved absolute path (if file-based)\n * @throws Error if file not found or download fails\n */\nasync function resolveContent(input: string): Promise<string> {\n const trimmed = input.trim();\n\n // 1. Check if input is a URL\n if (isUrl(trimmed)) {\n return fetchContentString(trimmed);\n }\n\n // 2. Check if file path exists\n const absolutePath = path.resolve(trimmed);\n if (await fs.pathExists(absolutePath)) {\n return fs.readFile(absolutePath, 'utf-8');\n }\n\n // 3. Inline content — return as-is\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return trimmed;\n }\n\n // 4. Nothing matched — file not found\n throw new Error(`Configuration file not found: ${absolutePath}`);\n}\n\n/**\n * Load configuration from a file path, URL, or inline string.\n *\n * Supports two modes:\n * - `json: true` (default) — resolves content and parses as JSON\n * - `json: false` — resolves content and returns raw string\n *\n * Detection priority:\n * 1. URL (http://, https://) — download content\n * 2. Existing file path — read file content\n * 3. Inline string (starting with { or [) — use directly\n *\n * @param input - Path to file, HTTP/HTTPS URL, or inline string\n * @param options - Optional settings\n * @param options.json - Parse as JSON (default: true)\n * @returns Parsed object (json: true) or raw string (json: false)\n * @throws Error if file not found, download fails, or invalid JSON\n *\n * @example\n * ```typescript\n * // JSON mode (default) — same as loadJsonConfig\n * const config = await loadConfig('./config.json')\n * const config = await loadConfig('https://example.com/config.json')\n * const config = await loadConfig('{\"version\":3,\"flows\":{}}')\n *\n * // Raw string mode — returns file/URL content as string\n * const code = await loadConfig('./bundle.js', { json: false })\n * ```\n */\nexport async function loadConfig<T = unknown>(\n input: string,\n options?: { json?: boolean },\n): Promise<T | string> {\n const json = options?.json !== false; // default true\n\n if (!json) {\n return resolveContent(input);\n }\n\n // JSON mode — resolve content then parse\n const trimmed = input.trim();\n\n try {\n const content = await resolveContent(trimmed);\n\n // Parse the resolved content as JSON\n return JSON.parse(content) as T;\n } catch (error) {\n // Distinguish between JSON parse errors and resolution errors\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Re-throw resolution errors (a URL that could not be read, a file not\n // found) as-is\n if (error instanceof ConfigFetchError || errorMessage.includes('not found'))\n throw error;\n\n // JSON-looking inline content with parse errors\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n throw new Error(\n `Input appears to be JSON but contains errors: ${errorMessage}`,\n );\n }\n\n // File or URL with invalid JSON content\n throw new Error(`Invalid JSON in config file: ${input}. ${errorMessage}`);\n }\n}\n\n/**\n * Load and parse JSON configuration from a file path, URL, or inline JSON string.\n *\n * Thin wrapper around `loadConfig` with `json: true` (default).\n *\n * Detection priority:\n * 1. URL (http://, https://) — download and parse\n * 2. Existing file path — read and parse\n * 3. Inline JSON string (starting with { or [) — parse directly\n *\n * @param configPath - Path to JSON file, HTTP/HTTPS URL, or inline JSON string\n * @returns Parsed configuration object\n * @throws Error if file not found, download fails, or invalid JSON\n *\n * @example\n * ```typescript\n * // Local file\n * const config = await loadJsonConfig('./config.json')\n *\n * // Remote URL\n * const config = await loadJsonConfig('https://example.com/config.json')\n *\n * // Inline JSON\n * const config = await loadJsonConfig('{\"version\":3,\"flows\":{}}')\n * ```\n */\nexport async function loadJsonConfig<T>(configPath: string): Promise<T> {\n return loadConfig<T>(configPath, { json: true }) as Promise<T>;\n}\n\n/**\n * Load JSON from inline string, file path, or URL.\n *\n * Supports three input formats:\n * 1. Inline JSON string - parsed directly\n * 2. Local file path - read and parsed\n * 3. HTTP/HTTPS URL - downloaded and parsed\n *\n * Detection priority:\n * 1. URL (http://, https://) → download and parse\n * 2. Existing file path → read and parse\n * 3. Valid JSON string → parse directly\n * 4. Simple string → treat as {name: string} for backward compatibility\n *\n * @param source - JSON string, file path, or URL\n * @param options - Optional configuration\n * @param options.name - Parameter name for error messages (e.g., \"event\", \"config\")\n * @param options.required - Throw error if source is empty (default: false)\n * @param options.fallback - Default value if source is empty\n * @returns Parsed JSON object\n * @throws Error if source is required but empty, or if loading/parsing fails\n *\n * @example\n * ```typescript\n * // Inline JSON\n * await loadJsonFromSource('{\"name\":\"order complete\",\"data\":{}}')\n *\n * // File path\n * await loadJsonFromSource('./examples/event.json')\n *\n * // URL\n * await loadJsonFromSource('https://example.com/event.json')\n *\n * // With options\n * await loadJsonFromSource(input, {\n * name: 'event',\n * required: true,\n * fallback: { name: 'default' }\n * })\n * ```\n */\nexport async function loadJsonFromSource<T = unknown>(\n source: string | undefined,\n options?: {\n name?: string;\n required?: boolean;\n fallback?: T;\n },\n): Promise<T> {\n const paramName = options?.name || 'input';\n\n // 1. Handle empty/undefined input (pre-check)\n if (!source || source.trim() === '') {\n if (options?.required) throw new Error(`${paramName} is required`);\n if (options?.fallback !== undefined) return options.fallback;\n return {} as T;\n }\n\n // 2. Try the strict loader (handles URL, file, inline JSON)\n try {\n return await loadJsonConfig<T>(source);\n } catch (error) {\n const trimmed = source.trim();\n\n // 3. Not JSON-looking? Treat as event name (backward compat)\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return { name: trimmed } as T;\n }\n\n // 4. JSON-looking but invalid -- re-throw with context\n throw new Error(\n `Failed to parse ${paramName}. ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n","function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Why a request did not reach its host: `timeout after <ms> ms`, or the\n * network error with its code, such as `fetch failed (ECONNREFUSED)`. Read by\n * shape, not instanceof: an abort's DOMException can come from another realm.\n */\nexport function fetchFailureReason(error: unknown, timeoutMs?: number): string {\n if (!isRecord(error)) return String(error);\n if (error.name === 'TimeoutError')\n return timeoutMs === undefined\n ? 'timeout'\n : `timeout after ${timeoutMs} ms`;\n const message =\n typeof error.message === 'string' ? error.message : String(error);\n const cause = error.cause;\n const code =\n isRecord(cause) && typeof cause.code === 'string' ? cause.code : undefined;\n return code ? `${message} (${code})` : message;\n}\n","export { getErrorMessage } from '@walkeros/core';\n","import path from 'path';\nimport fs from 'fs-extra';\nimport type { Logger } from '@walkeros/core';\n\nexport type LocalPackageInfo =\n | { name: string; absolutePath: string; type: 'file' }\n | { name: string; absolutePath: string; type: 'directory' }\n | {\n name: string;\n absolutePath: string;\n type: 'package';\n distPath: string;\n hasDistFolder: boolean;\n };\n\n/**\n * Resolve and validate a local package path.\n *\n * Handles three cases:\n * 1. Single file (with or without extension) → type: 'file'\n * 2. Directory without package.json → type: 'directory'\n * 3. Directory with package.json → type: 'package' (existing behavior)\n */\nexport async function resolveLocalPackage(\n packageName: string,\n localPath: string,\n configDir: string,\n logger: Logger.Instance,\n): Promise<LocalPackageInfo> {\n // Resolve relative to config file directory\n const absolutePath = path.isAbsolute(localPath)\n ? localPath\n : path.resolve(configDir, localPath);\n\n const stat = await fs.stat(absolutePath).catch(() => null);\n\n // Case 1a: Direct file reference (e.g., ./src/decoder.ts)\n if (stat?.isFile()) {\n return { name: packageName, absolutePath, type: 'file' };\n }\n\n // Case 1b: Try with extensions (e.g., ./src/decoder → ./src/decoder.ts)\n if (!stat) {\n for (const ext of ['.ts', '.mjs', '.js', '.json']) {\n const withExt = absolutePath + ext;\n if (await fs.pathExists(withExt)) {\n return { name: packageName, absolutePath: withExt, type: 'file' };\n }\n }\n throw new Error(\n `Local package path not found: ${localPath} (resolved to ${absolutePath})`,\n );\n }\n\n // Path is a directory\n if (stat.isDirectory()) {\n const hasPkgJson = await fs.pathExists(\n path.join(absolutePath, 'package.json'),\n );\n\n if (hasPkgJson) {\n // Case 3: Full package (existing behavior)\n const distPath = path.join(absolutePath, 'dist');\n const hasDistFolder = await fs.pathExists(distPath);\n\n if (!hasDistFolder) {\n logger.warn(\n `⚠️ ${packageName}: No dist/ folder found. Using package root.`,\n );\n }\n\n return {\n name: packageName,\n absolutePath,\n type: 'package',\n distPath: hasDistFolder ? distPath : absolutePath,\n hasDistFolder,\n };\n }\n\n // Case 2: Directory without package.json\n return { name: packageName, absolutePath, type: 'directory' };\n }\n\n throw new Error(\n `Local package path not found: ${localPath} (resolved to ${absolutePath})`,\n );\n}\n\n/** Top-level entries a copy of a package root leaves out. */\nconst ROOT_COPY_EXCLUDED = ['.turbo', '.git'];\n\n/**\n * Whether a copy of a package root leaves a path out: `node_modules` at any\n * depth (links are followed, so a nested one would pull in a whole tree) and\n * `.env` files, which hold secrets and are never code a flow loads.\n */\nfunction excludedFromCopy(root: string, file: string): boolean {\n const relative = path.relative(root, file);\n if (relative === '') return false;\n const parts = relative.split(path.sep);\n if (ROOT_COPY_EXCLUDED.includes(parts[0])) return true;\n if (parts.includes('node_modules')) return true;\n const name = parts[parts.length - 1];\n return name === '.env' || name.startsWith('.env.');\n}\n\n/** Copies a package root, links resolved: a workspace's relative symlinks break once copied. */\nasync function copyPackageRoot(source: string, target: string): Promise<void> {\n await fs.copy(source, target, {\n dereference: true,\n filter: (file) => !excludedFromCopy(source, file),\n });\n}\n\n/** Every string a manifest field holds, at any depth (`exports` conditions). */\nexport function stringLeaves(value: unknown, into: string[] = []): string[] {\n if (typeof value === 'string') into.push(value);\n else if (value && typeof value === 'object')\n for (const inner of Object.values(value)) stringLeaves(inner, into);\n return into;\n}\n\n/** Condition keys of `exports` that name type declarations, never code. */\nconst TYPE_CONDITIONS = ['types', 'typings'];\n\n/** The code leaves of an `exports` value: no type conditions, declarations or JSON. */\nfunction codeExports(value: unknown, into: string[] = []): string[] {\n if (typeof value === 'string') {\n if (!/\\.d\\.[cm]?ts$/.test(value) && !value.endsWith('.json'))\n into.push(value);\n } else if (value && typeof value === 'object') {\n for (const [key, inner] of Object.entries(value))\n if (!TYPE_CONDITIONS.includes(key)) codeExports(inner, into);\n }\n return into;\n}\n\n/**\n * Whether a manifest's entry points (`main`, `module`, the code leaves of\n * `exports`, `bin`) name a file outside `dist/`. A package without them\n * resolves nothing outside it either; type declarations and JSON a package\n * exports are no code to load.\n */\nexport function entriesOutsideDist(manifest: unknown): boolean {\n if (!manifest || typeof manifest !== 'object') return false;\n const fields = [\n ...['main', 'module', 'bin'].flatMap((field) =>\n stringLeaves(Reflect.get(manifest, field)),\n ),\n ...codeExports(Reflect.get(manifest, 'exports')),\n ];\n return fields.some((entry) => {\n const relative = path.posix.normalize(entry.replace(/^\\.\\//, ''));\n return (\n relative !== 'package.json' &&\n relative !== 'dist' &&\n !relative.startsWith('dist/')\n );\n });\n}\n\n/**\n * Copy local package to target node_modules directory.\n *\n * Handles three cases:\n * - file: Copy as index.{ext} with generated package.json\n * - directory: Copy contents with generated package.json\n * - package: package.json plus dist/, or the package root when it has no\n * dist/ or an entry point outside it (a `main` in `lib/` beside a browser\n * build in `dist/`)\n */\nexport async function copyLocalPackage(\n localPkg: LocalPackageInfo,\n targetDir: string,\n logger: Logger.Instance,\n): Promise<string> {\n const packageDir = path.join(targetDir, 'node_modules', localPkg.name);\n\n if (localPkg.type === 'file') {\n await fs.ensureDir(packageDir);\n\n // Copy the single file as index (esbuild will resolve it)\n const ext = path.extname(localPkg.absolutePath);\n await fs.copy(localPkg.absolutePath, path.join(packageDir, `index${ext}`));\n\n // Create minimal package.json for module resolution\n await fs.writeJson(path.join(packageDir, 'package.json'), {\n name: localPkg.name,\n main: `./index${ext}`,\n });\n\n logger.info(\n `📦 Using local file: ${localPkg.name} from ${localPkg.absolutePath}`,\n );\n return packageDir;\n }\n\n if (localPkg.type === 'directory') {\n await fs.ensureDir(path.dirname(packageDir));\n\n // Copy directory contents (without node_modules, .env files etc.)\n await copyPackageRoot(localPkg.absolutePath, packageDir);\n\n // Generate minimal package.json for module resolution\n await fs.writeJson(path.join(packageDir, 'package.json'), {\n name: localPkg.name,\n main: './index.ts',\n });\n\n logger.info(\n `📦 Using local dir: ${localPkg.name} from ${localPkg.absolutePath}`,\n );\n return packageDir;\n }\n\n // type === 'package': Existing behavior\n await fs.ensureDir(path.dirname(packageDir));\n\n // Always copy package.json for module resolution\n await fs.copy(\n path.join(localPkg.absolutePath, 'package.json'),\n path.join(packageDir, 'package.json'),\n );\n\n // Copy dist folder AS dist folder (preserving structure for exports like\n // ./dist/index.mjs), unless an entry point names a file outside it: then\n // dist/ alone would leave the package without the file it resolves to.\n const manifest: unknown = await fs\n .readJson(path.join(localPkg.absolutePath, 'package.json'))\n .catch(() => undefined);\n if (localPkg.hasDistFolder && !entriesOutsideDist(manifest)) {\n await fs.copy(localPkg.distPath, path.join(packageDir, 'dist'));\n } else {\n await copyPackageRoot(localPkg.absolutePath, packageDir);\n }\n\n logger.info(`📦 Using local: ${localPkg.name} from ${localPkg.absolutePath}`);\n\n return packageDir;\n}\n","/**\n * Input Detector\n *\n * Detects whether CLI input is a config JSON or pre-built bundle.\n * Supports both local files and URLs.\n */\n\nimport fs from 'fs-extra';\nimport { fetchContentString, isUrl } from '../config/utils.js';\n\nexport type Platform = 'web' | 'server';\n\nexport type DetectedInput =\n | { type: 'config'; content: string }\n | { type: 'bundle'; content: string; platform: Platform };\n\n/**\n * Detect if input is config JSON or pre-built bundle.\n *\n * Detection: Try JSON.parse, if fails = bundle\n * Platform: .mjs = server, .js = web\n *\n * @param inputPath - Path to file or URL\n * @param platformOverride - Optional platform override\n * @returns Detected input type with content\n */\nexport async function detectInput(\n inputPath: string,\n platformOverride?: Platform,\n): Promise<DetectedInput> {\n // Load content (URL or local file)\n const content = await loadContent(inputPath);\n\n // Try parsing as JSON\n try {\n JSON.parse(content);\n return { type: 'config', content };\n } catch {\n // Not JSON, treat as bundle\n const platform = platformOverride ?? detectPlatformFromPath(inputPath);\n return { type: 'bundle', content, platform };\n }\n}\n\n/**\n * Detect platform from file extension.\n *\n * @param inputPath - Path to file or URL\n * @returns Platform based on extension (.mjs = server, .js = web)\n */\nexport function detectPlatformFromPath(inputPath: string): Platform {\n // Remove query params for URLs\n const cleanPath = inputPath.split('?')[0];\n return cleanPath.endsWith('.mjs') ? 'server' : 'web';\n}\n\n/**\n * Load content from URL or local file.\n *\n * @param inputPath - Path to file or URL\n * @returns File content as string\n */\nasync function loadContent(inputPath: string): Promise<string> {\n if (isUrl(inputPath)) {\n return fetchContentString(inputPath);\n }\n // Inline JSON passthrough: JS bundles never start with { or [\n const trimmed = inputPath.trim();\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return trimmed;\n }\n\n return fs.readFile(inputPath, 'utf8');\n}\n","import fs from 'fs-extra';\nimport path from 'path';\nimport { getTmpPath } from './tmp.js';\n\nexport function isStdinPiped(): boolean {\n return !process.stdin.isTTY;\n}\n\nexport async function readStdin(\n stream: NodeJS.ReadableStream = process.stdin,\n): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n // A stream may yield strings as well as buffers.\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n const content = Buffer.concat(chunks).toString('utf-8');\n if (!content.trim()) {\n throw new Error('No input received on stdin');\n }\n return content;\n}\n\n/**\n * Read stdin content and write to a labeled temp file.\n * Returns the temp file path for downstream functions that expect file paths.\n */\nexport async function readStdinToTempFile(label: string): Promise<string> {\n const content = await readStdin();\n const tmpPath = getTmpPath(undefined, `stdin-${label}.json`);\n await fs.ensureDir(path.dirname(tmpPath));\n await fs.writeFile(tmpPath, content, 'utf-8');\n return tmpPath;\n}\n","import {\n closeSync,\n openSync,\n readFileSync,\n readdirSync,\n statSync,\n unlinkSync,\n writeSync,\n} from 'fs';\nimport { randomBytes } from 'crypto';\nimport { join } from 'path';\nimport { z } from 'zod';\nimport {\n ensureConfigDir,\n getConfigDir,\n writePrivateFile,\n} from './config-file.js';\n\n/**\n * Where `walkeros login` keeps what it signed in with, beside `config.json`:\n *\n * - `credentials.json`, one login per issuer (`{ \"<issuer>\": {...} }`), so a\n * person can stay signed in to stage and prod at once. Each login keeps the\n * endpoints discovered when it was made, and a refresh or a revocation goes\n * only to those, never to endpoints discovered again from whatever app URL\n * is set later.\n * - `pending-<state>.json`, one per `walkeros login --no-browser` run without\n * a terminal, waiting for `walkeros login --code`.\n * - `credentials.lock`, held around every read-refresh-write and every write\n * of `credentials.json`, so two commands never present one refresh token\n * twice (the app ends the whole sign-in when one is replayed).\n *\n * Every file is created exclusively with mode 0600 (`writePrivateFile`).\n */\n\nexport const LoginSchema = z.object({\n issuer: z.string(),\n client_id: z.string(),\n token_endpoint: z.string(),\n revocation_endpoint: z.string(),\n access_token: z.string(),\n /** Epoch milliseconds. */\n expires_at: z.number(),\n refresh_token: z.string().optional(),\n scope: z.string().optional(),\n});\nexport type StoredLogin = z.infer<typeof LoginSchema>;\n\nconst CredentialsSchema = z.record(z.string(), z.unknown());\n\nexport type Credentials = Record<string, StoredLogin>;\n\nexport function getCredentialsPath(): string {\n return join(getConfigDir(), 'credentials.json');\n}\n\nfunction getLockPath(): string {\n return join(getConfigDir(), 'credentials.lock');\n}\n\n/**\n * Every stored login, keyed by issuer. A login whose key is not its issuer, or\n * that does not parse, is left out: it is never sent anywhere.\n */\nexport function readCredentials(): Credentials {\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(getCredentialsPath(), 'utf-8'));\n } catch {\n return {};\n }\n const parsed = CredentialsSchema.safeParse(raw);\n if (!parsed.success) return {};\n const logins: Credentials = {};\n for (const [issuer, value] of Object.entries(parsed.data)) {\n const login = LoginSchema.safeParse(value);\n if (login.success && login.data.issuer === issuer)\n logins[issuer] = login.data;\n }\n return logins;\n}\n\n/** Replace `credentials.json`. Call it only while holding `withCredentialsLock`. */\nexport function writeCredentials(logins: Credentials): void {\n ensureConfigDir();\n writePrivateFile(getCredentialsPath(), JSON.stringify(logins, null, 2));\n}\n\nfunction originOf(url: string): string | null {\n try {\n return new URL(url).origin;\n } catch {\n return null;\n }\n}\n\n/**\n * The stored login an app URL may use: the one whose issuer has the app URL's\n * origin. A login is never attached to a request for another origin.\n */\nexport function loginForAppUrl(\n logins: Credentials,\n appUrl: string,\n): StoredLogin | undefined {\n const origin = originOf(appUrl);\n if (origin === null) return undefined;\n const exact = logins[appUrl.replace(/\\/+$/, '')];\n if (exact) return exact;\n return Object.values(logins).find(\n (login) => originOf(login.issuer) === origin,\n );\n}\n\n// === The lock ===\n\nexport class CredentialsBusyError extends Error {}\n\nexport interface LockTiming {\n /** How long to wait for another command's lock. */\n waitMs: number;\n /** A lock older than this was left by a command that died, and is broken. */\n staleMs: number;\n pollMs: number;\n}\n\n/**\n * 15 seconds of waiting, and 30 before a lock counts as left behind: a live\n * holder's refresh request times out after 10, so it never comes near it.\n */\nexport const LOCK_TIMING: LockTiming = {\n waitMs: 15_000,\n staleMs: 30_000,\n pollMs: 100,\n};\n\nconst sleep = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nfunction isErrnoCode(error: unknown, code: string): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n error.code === code\n );\n}\n\nfunction tryCreateLock(path: string, mark: string): boolean {\n let fd: number;\n try {\n fd = openSync(path, 'wx', 0o600);\n } catch (error) {\n if (isErrnoCode(error, 'EEXIST')) return false;\n throw error;\n }\n try {\n writeSync(fd, mark);\n } finally {\n closeSync(fd);\n }\n return true;\n}\n\nfunction readLock(path: string): { mark: string; ageMs: number } | null {\n try {\n const ageMs = Date.now() - statSync(path).mtimeMs;\n return { mark: readFileSync(path, 'utf-8'), ageMs };\n } catch {\n return null;\n }\n}\n\n/**\n * Run `fn` holding `credentials.lock`. The lock file holds the pid, the time\n * and a random mark, so only its own holder removes it.\n */\nexport async function withCredentialsLock<T>(\n fn: () => Promise<T>,\n timing: LockTiming = LOCK_TIMING,\n): Promise<T> {\n const dir = ensureConfigDir();\n const path = getLockPath();\n const mark = `${process.pid} ${new Date().toISOString()} ${randomBytes(8).toString('hex')}`;\n const deadline = Date.now() + timing.waitMs;\n for (;;) {\n if (tryCreateLock(path, mark)) break;\n const held = readLock(path);\n if (held && held.ageMs > timing.staleMs) {\n // Read it once more right before removing it, so a lock another command\n // just took in its place is left alone.\n const again = readLock(path);\n if (again && again.mark === held.mark) {\n try {\n unlinkSync(path);\n } catch {\n // Another command broke it first.\n }\n }\n continue;\n }\n if (Date.now() >= deadline)\n throw new CredentialsBusyError(\n `another walkeros command is updating the sign-in in ${dir}: try again`,\n );\n await sleep(timing.pollMs);\n }\n try {\n return await fn();\n } finally {\n const held = readLock(path);\n if (held && held.mark === mark) {\n try {\n unlinkSync(path);\n } catch {\n // Already gone.\n }\n }\n }\n}\n\n// === Pending sign-ins (walkeros login --no-browser without a terminal) ===\n\n/**\n * The 10 minutes the app keeps the consent request open plus the 5 a code\n * lives after Allow: a person may take minutes to sign in on the other device.\n */\nexport const PENDING_TTL_MS = 15 * 60 * 1000;\n\nexport const STATE_SHAPE = /^[A-Za-z0-9_-]{1,128}$/;\n\nexport const PendingSchema = z.object({\n code_verifier: z.string(),\n state: z.string().regex(STATE_SHAPE),\n redirect_uri: z.string(),\n issuer: z.string(),\n client_id: z.string(),\n token_endpoint: z.string(),\n /** Epoch milliseconds. */\n created_at: z.number(),\n});\nexport type PendingLogin = z.infer<typeof PendingSchema>;\n\nconst PENDING_FILE = /^pending-([A-Za-z0-9_-]{1,128})\\.json$/;\n\nfunction pendingPath(state: string): string {\n if (!STATE_SHAPE.test(state)) throw new Error('state is not base64url');\n return join(getConfigDir(), `pending-${state}.json`);\n}\n\nexport function writePending(pending: PendingLogin): void {\n ensureConfigDir();\n writePrivateFile(\n pendingPath(pending.state),\n JSON.stringify(pending, null, 2),\n );\n}\n\n/** The pending sign-in for a state, or null when there is none or it does not parse. */\nexport function readPending(state: string): PendingLogin | null {\n try {\n const parsed = PendingSchema.safeParse(\n JSON.parse(readFileSync(pendingPath(state), 'utf-8')),\n );\n return parsed.success && parsed.data.state === state ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\nexport function deletePending(state: string): void {\n try {\n unlinkSync(pendingPath(state));\n } catch {\n // Already gone.\n }\n}\n\nexport function isPendingExpired(\n pending: PendingLogin,\n now = Date.now(),\n): boolean {\n return now - pending.created_at > PENDING_TTL_MS;\n}\n\n/** Remove every pending sign-in older than its life (or unreadable). */\nexport function removeExpiredPending(now = Date.now()): void {\n let names: string[];\n try {\n names = readdirSync(getConfigDir());\n } catch {\n return;\n }\n for (const name of names) {\n const match = PENDING_FILE.exec(name);\n if (!match) continue;\n const pending = readPending(match[1]);\n if (pending === null || isPendingExpired(pending, now))\n deletePending(match[1]);\n }\n}\n","import { createHash, randomBytes } from 'crypto';\nimport { z } from 'zod';\nimport { appFetch } from './app-fetch.js';\nimport { APP_PATHS } from './app-paths.js';\nimport { clientContextHeaders } from './client-context.js';\nimport { fetchFailureReason } from './fetch-failure.js';\nimport { isSecureSignInUrl } from '../lib/secure-url.js';\nimport {\n CredentialsBusyError,\n readCredentials,\n withCredentialsLock,\n writeCredentials,\n type Credentials,\n type LockTiming,\n type StoredLogin,\n} from '../lib/credentials.js';\n\n/**\n * `walkeros login` speaks standard OAuth 2.1, code plus PKCE, to the app URL\n * it is configured with. Everything but one path comes from the app's own\n * documents: the authorization server metadata (RFC 8414) names the\n * endpoints, and the CLI's client document (`APP_PATHS.cliClient`) names its\n * client id and its two return addresses, a loopback on this machine and a\n * code page on the app for a sign-in finished on another device.\n */\n\n/** `offline_access` buys the refresh token the CLI keeps. */\nexport const LOGIN_SCOPE = 'read write offline_access';\n\n/** The loopback return address the client document registers; any port matches it. */\nexport const LOOPBACK_REDIRECT_URI = 'http://127.0.0.1/callback';\n\n/** An authorization code of the walkerOS app. */\nexport const CODE_SHAPE = /^[0-9a-f]{64}$/;\n\n/** What the code page shows: `<code>.<state>`. */\nexport const PASTED_CODE_SHAPE = /^([0-9a-f]{64})\\.([A-Za-z0-9_-]{1,128})$/;\n\n/** Every request a sign-in sends gives up after this long. */\nexport const REQUEST_TIMEOUT_MS = 10_000;\n\n/** An access token is used only while it stays valid this much longer. */\nconst FRESH_FOR_MS = 60_000;\n\n/** A sign-in step that stopped; its message names what to do next. */\nexport class LoginError extends Error {}\n\nexport interface Discovery {\n issuer: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n revocationEndpoint: string;\n clientId: string;\n /** The return address on the app that shows a code to paste. */\n codePage: string;\n}\n\nconst MetadataSchema = z.object({\n issuer: z.string(),\n authorization_endpoint: z.string(),\n token_endpoint: z.string(),\n revocation_endpoint: z.string(),\n code_challenge_methods_supported: z.array(z.string()).optional(),\n});\n\nconst ClientDocumentSchema = z.object({\n client_id: z.string(),\n redirect_uris: z.array(z.string()),\n});\n\nconst CONTROL_CHARACTERS = /[\\u0000-\\u001f\\u007f]/;\n\nfunction originOf(url: string): string | null {\n try {\n return new URL(url).origin;\n } catch {\n return null;\n }\n}\n\n/** The issuer an app URL stands for: the URL without trailing slashes. */\nexport function issuerOf(appUrl: string): string {\n return appUrl.replace(/\\/+$/, '');\n}\n\nconst CHECK_APP_URL =\n 'check the app URL (WALKEROS_APP_URL, or appUrl in the walkerOS config)';\n\nasync function readDocument(issuer: string, path: string): Promise<unknown> {\n let response: Response;\n try {\n response = await appFetch(path, {\n baseUrl: issuer,\n redirect: 'error',\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n } catch (error) {\n throw new LoginError(\n `could not reach ${issuer}: ${fetchFailureReason(error, REQUEST_TIMEOUT_MS)}: ${CHECK_APP_URL} and the connection`,\n );\n }\n if (!response.ok)\n throw new LoginError(\n `${issuer} answered HTTP ${response.status} for ${path}: ${CHECK_APP_URL}`,\n );\n try {\n const body: unknown = await response.json();\n return body;\n } catch {\n throw new LoginError(\n `${issuer} answered ${path} with something that is not JSON: ${CHECK_APP_URL}`,\n );\n }\n}\n\n/** The sign-in address of the endpoint, or why it may not be used. */\nfunction endpointProblem(\n name: string,\n value: string,\n issuer: string,\n): string | null {\n if (CONTROL_CHARACTERS.test(value))\n return `${name} holds a control character`;\n if (!isSecureSignInUrl(value))\n return `${name} ${value} is neither https nor http on this machine`;\n if (originOf(value) !== originOf(issuer))\n return `${name} ${value} is not on ${issuer}`;\n return null;\n}\n\nfunction isLoopbackEntry(uri: string): boolean {\n try {\n const url = new URL(uri);\n return (\n url.protocol === 'http:' &&\n url.hostname === '127.0.0.1' &&\n url.port === '' &&\n url.pathname === '/callback'\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Read and check the app's sign-in documents. Nothing is sent to an endpoint\n * before all of these hold: the metadata's `issuer` is the app URL itself\n * (RFC 8414 3.3); every endpoint is on the issuer's origin and is https or\n * http on this machine; the client document's id is\n * `<issuer>/oauth/walkeros-cli`, it registers the loopback, and one of its\n * return addresses is on the issuer's origin (picked by origin, never by\n * scheme: a local app is http like the loopback).\n */\nexport async function discover(appUrl: string): Promise<Discovery> {\n const issuer = issuerOf(appUrl);\n if (CONTROL_CHARACTERS.test(issuer) || !isSecureSignInUrl(issuer))\n throw new LoginError(\n `walkeros login signs in only over https, or http on this machine, and the app URL is ${issuer}: ${CHECK_APP_URL}`,\n );\n\n const metadata = MetadataSchema.safeParse(\n await readDocument(issuer, APP_PATHS.authorizationServer),\n );\n if (!metadata.success)\n throw new LoginError(\n `the sign-in settings of ${issuer} lack an issuer, an authorization, token or revocation endpoint: ${CHECK_APP_URL}`,\n );\n const m = metadata.data;\n if (m.issuer !== issuer)\n throw new LoginError(\n `the sign-in settings of ${issuer} name another issuer: ${CHECK_APP_URL}`,\n );\n const endpoints: Array<[string, string]> = [\n ['authorization_endpoint', m.authorization_endpoint],\n ['token_endpoint', m.token_endpoint],\n ['revocation_endpoint', m.revocation_endpoint],\n ];\n for (const [name, value] of endpoints) {\n const problem = endpointProblem(name, value, issuer);\n if (problem)\n throw new LoginError(\n `the sign-in settings of ${issuer} are refused: ${problem}: ${CHECK_APP_URL}`,\n );\n }\n if (\n m.code_challenge_methods_supported !== undefined &&\n !m.code_challenge_methods_supported.includes('S256')\n )\n throw new LoginError(\n `${issuer} does not offer PKCE with S256, which walkeros login needs: ${CHECK_APP_URL}`,\n );\n\n const clientId = `${issuer}${APP_PATHS.cliClient}`;\n const document = ClientDocumentSchema.safeParse(\n await readDocument(issuer, APP_PATHS.cliClient),\n );\n if (!document.success || document.data.client_id !== clientId)\n throw new LoginError(\n `${issuer} does not serve the walkerOS CLI client ${clientId}: ${CHECK_APP_URL}`,\n );\n const uris = document.data.redirect_uris;\n const codePage = uris.find(\n (uri) =>\n !isLoopbackEntry(uri) &&\n endpointProblem('code page', uri, issuer) === null,\n );\n if (!uris.some(isLoopbackEntry) || codePage === undefined)\n throw new LoginError(\n `the walkerOS CLI client of ${issuer} does not register the loopback and a code page on ${issuer}: ${CHECK_APP_URL}`,\n );\n\n return {\n issuer,\n authorizationEndpoint: m.authorization_endpoint,\n tokenEndpoint: m.token_endpoint,\n revocationEndpoint: m.revocation_endpoint,\n clientId,\n codePage,\n };\n}\n\n// === PKCE and the authorization request ===\n\nexport interface Pkce {\n verifier: string;\n /** BASE64URL(SHA256(verifier)), RFC 7636 4.2. */\n challenge: string;\n}\n\nexport function createPkce(): Pkce {\n const verifier = randomBytes(32).toString('base64url');\n const challenge = createHash('sha256').update(verifier).digest('base64url');\n return { verifier, challenge };\n}\n\n/** 32 base64url characters, well inside the app's 128. */\nexport function createState(): string {\n return randomBytes(24).toString('base64url');\n}\n\n/** The authorization address. No `resource`: the app gives the CLI's client the REST API only. */\nexport function authorizeUrl(\n discovery: Discovery,\n request: { redirectUri: string; challenge: string; state: string },\n): string {\n const url = new URL(discovery.authorizationEndpoint);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', discovery.clientId);\n url.searchParams.set('redirect_uri', request.redirectUri);\n url.searchParams.set('code_challenge', request.challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n url.searchParams.set('scope', LOGIN_SCOPE);\n url.searchParams.set('state', request.state);\n return url.toString();\n}\n\n/**\n * Why a URL may not be opened in a browser for a sign-in to `issuer`, or\n * null. Only an https address, or http on this machine, on the issuer's\n * origin and with no control character, is ever handed to an opener.\n */\nexport function browserUrlProblem(url: string, issuer: string): string | null {\n if (CONTROL_CHARACTERS.test(url)) return 'it holds a control character';\n if (!isSecureSignInUrl(url))\n return 'it is neither https nor http on this machine';\n if (originOf(url) !== originOf(issuer)) return `it is not on ${issuer}`;\n return null;\n}\n\n// === The token and revocation endpoints ===\n\nconst TokenSetSchema = z.object({\n access_token: z.string().min(1),\n token_type: z.string().refine((type) => type.toLowerCase() === 'bearer'),\n expires_in: z.number(),\n refresh_token: z.string().min(1).optional(),\n scope: z.string().optional(),\n});\nexport type TokenSet = z.infer<typeof TokenSetSchema>;\n\nconst OAuthErrorSchema = z.object({ error: z.string() });\n\nexport type EndpointAnswer =\n /** 200 with a token set (the token endpoint) or 200 (revocation). */\n | { kind: 'ok'; tokens?: TokenSet }\n /** An OAuth error answer, such as `invalid_grant`. */\n | { kind: 'refused'; status: number; error: string }\n /** Another answer: a status without an OAuth error, or a 200 that is no token set. */\n | { kind: 'failed'; status: number }\n /** Nothing came back: the network, the timeout, or a redirect (never followed). */\n | { kind: 'unreachable'; reason: string };\n\n/**\n * POST a form to a token or revocation endpoint. A redirect is an error, never\n * followed: a 307 or 308 would send the code verifier or the refresh token in\n * this body on to wherever it points.\n */\nasync function postForm(\n url: string,\n form: Record<string, string>,\n expectTokens: boolean,\n): Promise<EndpointAnswer> {\n if (!isSecureSignInUrl(url))\n throw new LoginError(\n `refusing to send a walkerOS sign-in to ${url}: it is neither https nor http on this machine: run walkeros login`,\n );\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n ...clientContextHeaders(),\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n },\n body: new URLSearchParams(form).toString(),\n redirect: 'error',\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n } catch (error) {\n return {\n kind: 'unreachable',\n reason: fetchFailureReason(error, REQUEST_TIMEOUT_MS),\n };\n }\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n body = undefined;\n }\n if (response.ok) {\n if (!expectTokens) return { kind: 'ok' };\n const tokens = TokenSetSchema.safeParse(body);\n return tokens.success\n ? { kind: 'ok', tokens: tokens.data }\n : { kind: 'failed', status: response.status };\n }\n const error = OAuthErrorSchema.safeParse(body);\n return error.success\n ? { kind: 'refused', status: response.status, error: error.data.error }\n : { kind: 'failed', status: response.status };\n}\n\nexport function exchangeCode(request: {\n tokenEndpoint: string;\n clientId: string;\n code: string;\n redirectUri: string;\n verifier: string;\n}): Promise<EndpointAnswer> {\n return postForm(\n request.tokenEndpoint,\n {\n grant_type: 'authorization_code',\n client_id: request.clientId,\n code: request.code,\n redirect_uri: request.redirectUri,\n code_verifier: request.verifier,\n },\n true,\n );\n}\n\n/** RFC 7009: revoking the refresh token ends its whole sign-in, the access token too. */\nexport function revokeLogin(login: StoredLogin): Promise<EndpointAnswer> {\n if (login.refresh_token === undefined)\n return postForm(\n login.revocation_endpoint,\n {\n client_id: login.client_id,\n token: login.access_token,\n token_type_hint: 'access_token',\n },\n false,\n );\n return postForm(\n login.revocation_endpoint,\n {\n client_id: login.client_id,\n token: login.refresh_token,\n token_type_hint: 'refresh_token',\n },\n false,\n );\n}\n\n/** One answer's words for a message: `HTTP 400 invalid_request`. */\nexport function answerText(answer: EndpointAnswer): string {\n switch (answer.kind) {\n case 'ok':\n return 'HTTP 200';\n case 'refused':\n return `HTTP ${answer.status} ${answer.error}`;\n case 'failed':\n return `HTTP ${answer.status}`;\n case 'unreachable':\n return answer.reason;\n }\n}\n\n// === Stored logins ===\n\nexport function isFresh(login: StoredLogin, now = Date.now()): boolean {\n return login.expires_at - now > FRESH_FOR_MS;\n}\n\nexport function loginOf(\n endpoints: Pick<\n Discovery,\n 'issuer' | 'clientId' | 'tokenEndpoint' | 'revocationEndpoint'\n >,\n tokens: TokenSet,\n now = Date.now(),\n): StoredLogin {\n return {\n issuer: endpoints.issuer,\n client_id: endpoints.clientId,\n token_endpoint: endpoints.tokenEndpoint,\n revocation_endpoint: endpoints.revocationEndpoint,\n access_token: tokens.access_token,\n expires_at: now + tokens.expires_in * 1000,\n ...(tokens.refresh_token === undefined\n ? {}\n : { refresh_token: tokens.refresh_token }),\n ...(tokens.scope === undefined ? {} : { scope: tokens.scope }),\n };\n}\n\n/**\n * Write a new login over the one for its issuer and answer the one it\n * replaced. Call it only while holding `withCredentialsLock`.\n */\nexport function replaceLoginLocked(\n login: StoredLogin,\n): StoredLogin | undefined {\n const logins = readCredentials();\n const previous = logins[login.issuer];\n writeCredentials({ ...logins, [login.issuer]: login });\n return previous;\n}\n\n/**\n * Revoke the login a new one replaced, best effort, after the new one is\n * written: it would otherwise stay live for its refresh token's life.\n */\nexport async function revokeReplaced(\n replaced: StoredLogin | undefined,\n login: StoredLogin,\n): Promise<void> {\n if (replaced !== undefined && replaced.refresh_token !== login.refresh_token)\n await revokeLogin(replaced).catch(() => undefined);\n}\n\n/** Keep a new login, under the lock, and revoke the one it replaces. */\nexport async function storeLogin(\n login: StoredLogin,\n timing?: LockTiming,\n): Promise<void> {\n const replaced = await withCredentialsLock(\n async () => replaceLoginLocked(login),\n timing,\n );\n await revokeReplaced(replaced, login);\n}\n\n/** Why a stored login gives no token; the message names the next step. */\nexport class SignInError extends Error {\n constructor(\n message: string,\n readonly reason: 'ended' | 'unreachable' | 'elsewhere' | 'busy',\n ) {\n super(message);\n }\n}\n\n/** Every login but the one for `issuer`. */\nfunction without(logins: Credentials, issuer: string): Credentials {\n return Object.fromEntries(\n Object.entries(logins).filter(([key]) => key !== issuer),\n );\n}\n\nfunction endedError(issuer: string): SignInError {\n return new SignInError(\n `your sign-in to ${issuer} ended: run walkeros login`,\n 'ended',\n );\n}\n\n/**\n * A usable access token of the stored login for `issuer`, refreshed when it\n * is about to expire, or was refused (`rejected`, the token a 401 answered).\n *\n * The whole read, refresh and write runs under the credentials lock, and the\n * file is read again once the lock is held: a command that waited behind\n * another one's refresh finds the rotated token and sends nothing. The app\n * answers a replayed refresh token by ending the whole sign-in.\n *\n * The refresh goes to the token endpoint stored with the login, never to one\n * discovered again. `invalid_grant` or `invalid_client` removes the login; a\n * network failure keeps it.\n */\nexport async function refreshStoredLogin(\n issuer: string,\n options: { rejected?: string; timing?: LockTiming } = {},\n): Promise<StoredLogin> {\n try {\n return await withCredentialsLock(async () => {\n const logins = readCredentials();\n const login = logins[issuer];\n if (login === undefined) throw endedError(issuer);\n if (login.access_token !== options.rejected && isFresh(login))\n return login;\n\n const remove = () => writeCredentials(without(logins, issuer));\n if (login.refresh_token === undefined) {\n remove();\n throw endedError(issuer);\n }\n\n const answer = await postForm(\n login.token_endpoint,\n {\n grant_type: 'refresh_token',\n client_id: login.client_id,\n refresh_token: login.refresh_token,\n },\n true,\n );\n if (answer.kind === 'ok' && answer.tokens) {\n const tokens = answer.tokens;\n const refreshed = loginOf(\n {\n issuer: login.issuer,\n clientId: login.client_id,\n tokenEndpoint: login.token_endpoint,\n revocationEndpoint: login.revocation_endpoint,\n },\n {\n ...tokens,\n refresh_token: tokens.refresh_token ?? login.refresh_token,\n scope: tokens.scope ?? login.scope,\n },\n );\n writeCredentials({ ...logins, [issuer]: refreshed });\n return refreshed;\n }\n if (\n answer.kind === 'refused' &&\n (answer.error === 'invalid_grant' || answer.error === 'invalid_client')\n ) {\n remove();\n throw endedError(issuer);\n }\n if (answer.kind === 'unreachable')\n throw new SignInError(\n `could not reach ${issuer} to renew the sign-in: ${answer.reason}: try again`,\n 'unreachable',\n );\n throw new SignInError(\n `${issuer} did not renew the sign-in (${answerText(answer)}): try again, or run walkeros login`,\n 'unreachable',\n );\n }, options.timing);\n } catch (error) {\n if (error instanceof SignInError) throw error;\n if (error instanceof LoginError)\n throw new SignInError(error.message, 'ended');\n if (error instanceof CredentialsBusyError)\n throw new SignInError(error.message, 'busy');\n throw error;\n }\n}\n\nexport type LogoutOutcome =\n | { kind: 'revoked'; issuer: string }\n | { kind: 'unconfirmed'; issuer: string; answer: string }\n | { kind: 'none' };\n\n/**\n * Sign the login for `issuer` out: revoke its refresh token at the stored\n * revocation endpoint first, then remove it, under the lock. It is removed\n * whatever the app answers, so a person can always sign this machine out; an\n * answer other than 200 or `invalid_client` (the client itself is gone) is\n * reported, since the sign-in may then still work.\n */\nexport async function logoutStoredLogin(\n issuer: string,\n timing?: LockTiming,\n): Promise<LogoutOutcome> {\n return withCredentialsLock(async () => {\n const logins = readCredentials();\n const login = logins[issuer];\n if (login === undefined) return { kind: 'none' };\n const answer = await revokeLogin(login).catch(\n (error: unknown): EndpointAnswer => ({\n kind: 'unreachable',\n reason: error instanceof Error ? error.message : String(error),\n }),\n );\n writeCredentials(without(logins, issuer));\n if (\n answer.kind === 'ok' ||\n (answer.kind === 'refused' && answer.error === 'invalid_client')\n )\n return { kind: 'revoked', issuer };\n return { kind: 'unconfirmed', issuer, answer: answerText(answer) };\n }, timing);\n}\n","import { z } from 'zod';\nimport { resolveAppUrl } from '../lib/config-file.js';\nimport { requireSecureUrl } from '../lib/secure-url.js';\nimport { clientContextHeaders } from './client-context.js';\n\nexport interface AppFetchInit {\n method?: string;\n /** The app, default `resolveAppUrl()`; trailing slashes are trimmed. */\n baseUrl?: string;\n /** Sent as `Authorization: Bearer <token>` unless the caller set Authorization. */\n token?: string | null;\n headers?: Readonly<Record<string, string>>;\n body?: string;\n signal?: AbortSignal;\n redirect?: RequestRedirect;\n fetch?: typeof fetch;\n}\n\nconst AppErrorSchema = z.object({\n error: z.object({ message: z.string().min(1) }),\n});\n\n/** The message of the app's error envelope `{ error: { message } }`, or undefined. */\nexport function appErrorMessage(body: unknown): string | undefined {\n const parsed = AppErrorSchema.safeParse(body);\n return parsed.success ? parsed.data.error.message : undefined;\n}\n\n/**\n * Call the walkerOS app. Every request carries the client-context headers\n * (`User-Agent`, `X-WalkerOS-Client`, `X-WalkerOS-Client-Version`); caller\n * headers win over them; a request carrying Authorization goes only to a\n * secure URL (`requireSecureUrl`). `path` starts with `/` and may carry a query.\n */\nexport async function appFetch(\n path: string,\n init: AppFetchInit = {},\n): Promise<Response> {\n const url = `${(init.baseUrl ?? resolveAppUrl()).replace(/\\/+$/, '')}${path}`;\n const headers = new Headers(clientContextHeaders());\n for (const [name, value] of Object.entries(init.headers ?? {}))\n headers.set(name, value);\n if (init.token && !headers.has('Authorization'))\n headers.set('Authorization', `Bearer ${init.token}`);\n if (headers.has('Authorization')) requireSecureUrl(url);\n const request: RequestInit = { headers };\n if (init.method && init.method !== 'GET') request.method = init.method;\n if (init.body !== undefined) request.body = init.body;\n if (init.signal) request.signal = init.signal;\n if (init.redirect) request.redirect = init.redirect;\n return (init.fetch ?? fetch)(url, request);\n}\n","import { schemas } from '@walkeros/core/dev';\n\nconst { PartialEventSchema } = schemas;\n\nexport type ValidationLevel = 'strict' | 'standard' | 'minimal';\n\nexport type EventValidationCode =\n | 'NOT_AN_OBJECT'\n | 'MISSING_EVENT_NAME'\n | 'EMPTY_EVENT_NAME'\n | 'INVALID_EVENT_NAME'\n | 'SCHEMA_VALIDATION';\n\nexport interface EventValidationError {\n path: string;\n message: string;\n value?: unknown;\n code: EventValidationCode;\n}\n\nexport interface EventValidationWarning {\n path: string;\n message: string;\n suggestion?: string;\n}\n\nexport interface EventValidationDetails {\n entity?: string | null;\n action?: string | null;\n hasConsent?: boolean;\n hasData?: boolean;\n hasContext?: boolean;\n}\n\nexport interface EventValidationResult {\n valid: boolean;\n errors: EventValidationError[];\n warnings: EventValidationWarning[];\n details: EventValidationDetails;\n}\n\nexport function validateEvent(\n input: unknown,\n level: ValidationLevel = 'strict',\n): EventValidationResult {\n const errors: EventValidationError[] = [];\n const warnings: EventValidationWarning[] = [];\n const details: EventValidationDetails = {};\n\n // All levels: must be an object\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n errors.push({\n path: 'root',\n message: 'Event must be an object',\n code: 'NOT_AN_OBJECT',\n });\n return { valid: false, errors, warnings, details };\n }\n\n const event = input as Record<string, unknown>;\n\n // All levels: name field checks\n if (!('name' in event) || event.name === undefined) {\n errors.push({\n path: 'name',\n message: 'Event must have a name field',\n code: 'MISSING_EVENT_NAME',\n });\n } else if (typeof event.name !== 'string' || event.name.trim() === '') {\n errors.push({\n path: 'name',\n message: 'Event name cannot be empty',\n value: event.name,\n code: 'EMPTY_EVENT_NAME',\n });\n } else {\n // Entity-action format check (strict + standard only)\n const name = event.name;\n if (!name.includes(' ')) {\n if (level === 'strict') {\n errors.push({\n path: 'name',\n message:\n 'Event name must be \"entity action\" format with space (e.g., \"page view\")',\n value: name,\n code: 'INVALID_EVENT_NAME',\n });\n details.entity = null;\n details.action = null;\n } else if (level === 'standard') {\n warnings.push({\n path: 'name',\n message: `Event name \"${name}\" should follow \"ENTITY ACTION\" format (e.g., \"page view\")`,\n });\n details.entity = null;\n details.action = null;\n }\n // minimal: skip entirely\n } else {\n const parts = name.trim().split(/\\s+/);\n const action = parts.pop()!;\n const entity = parts.join(' ');\n details.entity = entity;\n details.action = action;\n }\n }\n\n // Minimal stops here\n if (level === 'minimal') {\n return { valid: errors.length === 0, errors, warnings, details };\n }\n\n // Standard + Strict: Zod schema validation\n const zodResult = PartialEventSchema.safeParse(input);\n if (!zodResult.success) {\n for (const issue of zodResult.error.issues) {\n const issuePath = issue.path.join('.');\n if (issuePath === 'name') continue; // handled above with better messages\n errors.push({\n path: issuePath || 'root',\n message: issue.message,\n code: 'SCHEMA_VALIDATION',\n });\n }\n }\n\n // Strict only: consent warning + details\n if (level === 'strict') {\n if (!event.consent) {\n warnings.push({\n path: 'consent',\n message: 'No consent object provided',\n suggestion:\n 'Consider adding a consent object for GDPR/privacy compliance',\n });\n }\n details.hasConsent = !!event.consent;\n details.hasData = !!event.data;\n details.hasContext = !!event.context;\n }\n\n return { valid: errors.length === 0, errors, warnings, details };\n}\n","/**\n * Configuration Type Guards and Validators\n *\n * Type checking utilities for configuration validation.\n * Uses Zod schemas from @walkeros/core for Flow.Json validation.\n */\n\nimport type { Flow } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\nimport { schemas } from '@walkeros/core/dev';\n\nconst { safeParseConfig } = schemas;\n\n/**\n * Detect platform from a single flow.\n *\n * Platform is determined by `flow.config.platform`.\n */\nexport function detectPlatform(\n flow: Record<string, unknown>,\n): 'web' | 'server' | undefined {\n const config = flow.config;\n if (!isObject(config)) return undefined;\n const platform = config.platform;\n if (platform === 'web' || platform === 'server') return platform;\n return undefined;\n}\n\n/**\n * Type guard: Check if config is a valid Flow.Json structure.\n *\n * @remarks\n * Uses Zod validation from @walkeros/core.\n * Returns false instead of throwing on invalid input.\n *\n * @example\n * ```typescript\n * if (isFlowConfig(config)) {\n * const flowSettings = getFlowSettings(config, 'production');\n * }\n * ```\n */\nexport function isFlowConfig(data: unknown): data is Flow.Json {\n const result = safeParseConfig(data);\n return result.success;\n}\n\n/**\n * Validate Flow.Json and throw descriptive error if invalid.\n *\n * @remarks\n * Uses Zod validation from @walkeros/core.\n * Provides detailed error messages from Zod.\n *\n * @param data - Raw configuration data\n * @returns Validated Flow.Json\n * @throws Error with descriptive message if validation fails\n */\nexport function validateFlowConfig(data: unknown): Flow.Json {\n // After successful Zod parse, the data conforms structurally to Flow.Json.\n // The existing `isFlowConfig` guard re-narrows `unknown` to `Flow.Json` via\n // the same schema — TS can't see through `safeParseConfig` because it\n // returns a Zod-inferred type that differs nominally (not structurally)\n // from the Flow.Json interface.\n if (isFlowConfig(data)) return data;\n\n // Not parseable — format Zod errors for CLI display.\n const result = safeParseConfig(data);\n if (result.success) {\n // Defensive: isFlowConfig and safeParseConfig disagreed (cannot happen).\n throw new Error('Invalid configuration: failed Flow.Json type guard.');\n }\n const errors = result.error.issues\n .map((issue) => {\n const path =\n issue.path.length > 0 ? issue.path.map(String).join('.') : 'root';\n return ` - ${path}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(`Invalid configuration:\\n${errors}`);\n}\n\n/**\n * Get available flow names from a Flow.Json.\n *\n * @param config - Flow.Json configuration\n * @returns Array of flow names\n */\nexport function getAvailableFlows(config: Flow.Json): string[] {\n return Object.keys(config.flows);\n}\n","/**\n * Configuration Loader\n *\n * Loads and parses Flow.Json configurations using core getFlowSettings().\n * Build options are determined by static platform defaults.\n */\n\nimport path from 'path';\nimport fs from 'fs-extra';\nimport { getFlowSettings, getPlatform, type Flow } from '@walkeros/core';\nimport { validateFlowStructure } from '@walkeros/core/dev';\nimport type { BuildOptions } from '../types/bundle.js';\nimport {\n validateFlowConfig,\n isFlowConfig,\n getAvailableFlows as getFlowNames,\n} from './validators.js';\nimport { getBuildDefaults, getDefaultOutput } from './build-defaults.js';\nimport { isUrl, loadJsonConfig } from './utils.js';\nimport { configDigest } from '../core/content-digest.js';\nimport {\n assertWindowCollector,\n type WindowCollectorSource,\n} from './window-collector.js';\n\n/** Default folder for includes if it exists */\nconst DEFAULT_INCLUDE_FOLDER = './shared';\n\n/**\n * Result of configuration loading.\n */\nexport interface LoadConfigResult {\n /** Runtime event processing configuration (resolved single Flow) */\n flowSettings: Flow;\n /** Build-time configuration */\n buildOptions: BuildOptions;\n /** Name of the selected flow */\n flowName: string;\n /** Whether multiple flows are available */\n isMultiFlow: boolean;\n /** All available flow names */\n availableFlows: string[];\n /**\n * Digest of the deferred flow settings, the config input of the default\n * release. Never carries an env or secret value.\n */\n configDigest: string;\n}\n\n/**\n * Options for loading configuration.\n */\nexport interface LoadConfigOptions {\n /** Path to config file */\n configPath: string;\n /** Flow name to load (required for multi-flow, optional for single-flow) */\n flowName?: string;\n /** CLI build overrides (future: --output, --minify, etc.) */\n buildOverrides?: Partial<BuildOptions>;\n /**\n * Base environment for web `$env` resolution, layered BENEATH the flow's\n * declared `config.bundle.env`. Omitted, it is the shell's `process.env`\n * (local builds). Pass `{}` for a hermetic build that resolves against the\n * declared values alone. Server flows are unaffected: their `$env` is read\n * at runtime.\n */\n buildEnv?: Record<string, string | undefined>;\n /** Logger for warnings */\n logger?: {\n warn: (message: string) => void;\n info: (message: string) => void;\n };\n}\n\n/**\n * The bundle preflight: the structural checks `walkeros validate` runs\n * (`validateFlowStructure`), on the flow being built. A flow validate\n * rejects (an unknown key, a mapping field with no effect at its position,\n * a malformed reference) stops here, before anything is resolved or built,\n * and the error names every path.\n */\nfunction assertFlowStructure(config: Flow.Json, flowName: string): void {\n const { errors } = validateFlowStructure({\n ...config,\n flows: { [flowName]: config.flows[flowName] },\n });\n if (errors.length === 0) return;\n const lines = errors.map((error) => ` - ${error.path}: ${error.message}`);\n throw new Error(`Invalid configuration:\\n${lines.join('\\n')}`);\n}\n\n/**\n * Load and parse bundle configuration.\n *\n * @remarks\n * Uses Flow.Json from @walkeros/core as the only config format.\n * - Validates config structure\n * - Uses core getFlowSettings() for variable/definition resolution\n * - Determines platform from resolved config\n * - Applies static build defaults based on platform\n *\n * @param rawConfig - Raw configuration object from JSON file\n * @param options - Loading options\n * @returns Parsed configuration with flow and build separated\n *\n * @example\n * ```typescript\n * const config = loadBundleConfig(rawConfig, {\n * configPath: './walkeros.config.json',\n * flowName: 'production',\n * });\n * ```\n */\nexport function loadBundleConfig(\n rawConfig: unknown,\n options: LoadConfigOptions,\n): LoadConfigResult {\n // Pre-validation scan: detect the legacy `flow.config.bundle.external`\n // field and strip it before schema validation. The strict Bundle schema\n // rejects unknown keys, so without this step the config would fail to\n // load with a confusing error. Emit a deprecation warning instead.\n const sanitized = stripLegacyBundleExternal(rawConfig, options.logger);\n\n // Validate as Flow.Json\n const config = validateFlowConfig(sanitized);\n const availableFlows = getFlowNames(config);\n\n // Determine which flow to use\n const flowName = resolveFlow(config, options.flowName, availableFlows);\n\n assertFlowStructure(config, flowName);\n assertLiteralBuildEnv(config, flowName);\n\n // Resolve with deferred mode first (markers don't affect platform detection)\n let flowSettings = getFlowSettings(config, flowName, { deferred: true });\n const platform = getPlatform(flowSettings);\n const flowConfigDigest = configDigest(flowSettings, flowName);\n if (!platform) {\n throw new Error(\n `Invalid configuration: flow \"${flowName}\" must have config.platform set to \"web\" or \"server\".`,\n );\n }\n\n // For web: re-resolve without deferred to bake values at build time. The\n // values come from an explicit map, never from an ambient read, so a build\n // inside a process holding its own secrets cannot inline them.\n if (platform === 'web') {\n flowSettings = getFlowSettings(config, flowName, {\n env: resolveBuildEnv(flowSettings.config?.bundle?.env, options.buildEnv),\n });\n }\n\n // Auto-inject validator transformers for any step-level `validate?:`\n // Get static build defaults based on platform\n const buildDefaults = getBuildDefaults(platform);\n\n // Extract packages + overrides + traceInclude from the canonical\n // `flow.<name>.config.bundle.{packages, overrides, traceInclude}` location.\n //\n // The `config.bundle.external` sub-field is no longer supported (replaced\n // by nft tracing); it is warned about and stripped pre-validation in\n // `stripLegacyBundleExternal` so it does not surface here.\n const bundle = flowSettings.config?.bundle;\n const packages: Record<string, Flow.BundlePackage> = bundle?.packages ?? {};\n const overrides: Record<string, string> = bundle?.overrides ?? {};\n const traceInclude = bundle?.traceInclude;\n\n // Output path: use --output if provided, otherwise default\n // Always relative to cwd, no dynamic resolution\n const output = options.buildOverrides?.output || getDefaultOutput(platform);\n\n // Get config directory for resolving includes and local packages\n // For URLs, use cwd since there's no local config directory\n const configDir = isUrl(options.configPath)\n ? process.cwd()\n : path.dirname(options.configPath);\n\n // Get includes from config or use default if ./shared exists\n let includes = config.include;\n if (!includes) {\n const defaultIncludePath = path.resolve(configDir, DEFAULT_INCLUDE_FOLDER);\n if (fs.pathExistsSync(defaultIncludePath)) {\n includes = [DEFAULT_INCLUDE_FOLDER];\n }\n }\n\n // Merge build options: defaults + flow settings + CLI overrides\n const buildOptions: BuildOptions = {\n ...buildDefaults,\n ...settingsToBuildOptions(platform, flowSettings.config?.settings, {\n path: `flows.${flowName}.config.settings.windowCollector`,\n written: config.flows[flowName]?.config?.settings?.windowCollector,\n }),\n packages,\n overrides,\n traceInclude,\n output,\n include: includes,\n configDir,\n ...options.buildOverrides,\n };\n\n // Log flow selection\n const isMultiFlow = availableFlows.length > 1;\n if (isMultiFlow && options.logger) {\n options.logger.info(\n `📦 Using flow: ${flowName} (${availableFlows.length} total)`,\n );\n }\n\n return {\n flowSettings,\n buildOptions,\n flowName,\n isMultiFlow,\n availableFlows,\n configDigest: flowConfigDigest,\n };\n}\n\n/**\n * The build options a flow's `config.settings` controls. Each key is mapped\n * explicitly, so an unknown settings key never reaches generated code.\n *\n * - `windowCollector` (web): the global the collector instance is assigned\n * to. It is interpolated into the generated entry, so the resolved value\n * must be a JavaScript identifier.\n */\nexport function settingsToBuildOptions(\n platform: 'web' | 'server',\n settings: Flow.Settings | undefined,\n source: WindowCollectorSource = {},\n): Pick<BuildOptions, 'windowCollector'> {\n if (platform !== 'web' || settings?.windowCollector === undefined) return {};\n return {\n windowCollector: assertWindowCollector(settings.windowCollector, source),\n };\n}\n\n/** Any reference or marker the resolver would act on inside a string. */\nconst REFERENCE_PATTERN =\n /\\$(?:env|var|secret|flow|contract)\\.|\\$code:|__WALKEROS_(?:ENV|SECRET):/;\n\n/**\n * `config.bundle.env` values are the declared build values themselves, so\n * each must be a literal string. A reference there would be resolved (or\n * turned into a runtime marker) instead of declared, so it is refused. The\n * message names keys only, never values.\n */\nfunction assertLiteralBuildEnv(config: Flow.Json, flowName: string): void {\n const declared = config.flows[flowName]?.config?.bundle?.env;\n if (!declared) return;\n const refs = Object.entries(declared)\n .filter(([, value]) => REFERENCE_PATTERN.test(value))\n .map(([key]) => key);\n if (refs.length > 0) {\n throw new Error(\n `config.bundle.env values must be literal strings; references are not allowed in: ${refs.join(', ')}`,\n );\n }\n}\n\n/**\n * The map web `$env` references resolve against: the base environment\n * (`process.env` unless the caller passes one) with the flow's declared\n * `config.bundle.env` on top.\n */\nexport function resolveBuildEnv(\n declared: Record<string, string> | undefined,\n base: Record<string, string | undefined> = process.env,\n): Record<string, string | undefined> {\n return { ...base, ...(declared ?? {}) };\n}\n\n/**\n * Resolve which flow to use.\n *\n * @param config - Flow.Json configuration\n * @param requestedFlow - Flow name from CLI (optional)\n * @param available - Available flow names\n * @returns Flow name to use\n * @throws Error if flow selection is invalid\n */\nfunction resolveFlow(\n config: Flow.Json,\n requestedFlow: string | undefined,\n available: string[],\n): string {\n // If only one flow, use it automatically\n if (available.length === 1) {\n return available[0];\n }\n\n // Multiple flows require explicit selection\n if (!requestedFlow) {\n throw new Error(\n `Multiple flows found. Please specify a flow using --flow flag.\\n` +\n `Available flows: ${available.join(', ')}`,\n );\n }\n\n // Validate the requested flow exists\n if (!available.includes(requestedFlow)) {\n throw new Error(\n `Flow \"${requestedFlow}\" not found in configuration.\\n` +\n `Available flows: ${available.join(', ')}`,\n );\n }\n\n return requestedFlow;\n}\n\n/**\n * Load all flows from a configuration.\n *\n * @remarks\n * Used by the --all flag to build all flows.\n *\n * @param rawConfig - Raw configuration object\n * @param options - Loading options (without flowName)\n * @returns Array of loaded configurations for all flows\n */\nexport function loadAllFlows(\n rawConfig: unknown,\n options: Omit<LoadConfigOptions, 'flowName'>,\n): LoadConfigResult[] {\n // Validate as Flow.Json\n const config = validateFlowConfig(rawConfig);\n const flows = getFlowNames(config);\n\n if (options.logger) {\n options.logger.info(\n `📦 Loading all ${flows.length} flows: ${flows.join(', ')}`,\n );\n }\n\n // Load each flow\n return flows.map((name) =>\n loadBundleConfig(rawConfig, {\n ...options,\n flowName: name,\n }),\n );\n}\n\n/**\n * Get list of available flows from configuration.\n *\n * @param rawConfig - Raw configuration object\n * @returns Array of flow names\n */\nexport function getAvailableFlows(rawConfig: unknown): string[] {\n if (isFlowConfig(rawConfig)) {\n return getFlowNames(rawConfig);\n }\n return [];\n}\n\n/**\n * Load flow configuration from file or URL.\n *\n * Single entry point for all commands (bundle, simulate, push).\n * Handles URL vs local path detection automatically.\n *\n * @param configPath - Path to config file or URL\n * @param options - Loading options (flowName, logger, buildOverrides)\n * @returns Parsed configuration with flow and build options\n *\n * @example\n * ```typescript\n * const { flowSettings, buildOptions } = await loadFlowConfig('./flow.json');\n * const { flowSettings } = await loadFlowConfig('https://example.com/flow.json');\n * ```\n */\nexport async function loadFlowConfig(\n configPath: string,\n options?: Omit<LoadConfigOptions, 'configPath'> & {\n /**\n * The config at `configPath`, already read by the caller: it is used\n * instead of reading the path again (`configPath` still anchors\n * relative paths).\n */\n raw?: unknown;\n },\n): Promise<LoadConfigResult> {\n const { raw, ...rest } = options ?? {};\n const rawConfig = raw !== undefined ? raw : await loadJsonConfig(configPath);\n return loadBundleConfig(rawConfig, { configPath, ...rest });\n}\n\n/**\n * Pre-validation pass: detect and strip the legacy\n * `flow.config.bundle.external` field. The strict Bundle schema rejects\n * unknown keys, so without this step a config containing the legacy\n * field would fail validation with a confusing error. Emit a one-time\n * deprecation warning per offending flow and return a sanitized clone.\n *\n * The clone is shallow at the levels we mutate (root, `flows`, each flow,\n * `config`, `bundle`); deeper structures are aliased into the clone, which\n * is fine because we only delete a top-level key on the bundle object.\n */\nfunction stripLegacyBundleExternal(\n rawConfig: unknown,\n logger: LoadConfigOptions['logger'],\n): unknown {\n if (!isPlainObject(rawConfig)) return rawConfig;\n const flows = rawConfig.flows;\n if (!isPlainObject(flows)) return rawConfig;\n\n let mutated: Record<string, unknown> | null = null;\n\n for (const [flowName, flowValue] of Object.entries(flows)) {\n if (!isPlainObject(flowValue)) continue;\n const flowConfig = flowValue.config;\n if (!isPlainObject(flowConfig)) continue;\n const bundle = flowConfig.bundle;\n if (!isPlainObject(bundle)) continue;\n if (!('external' in bundle)) continue;\n\n logger?.warn(\n `flow.config.bundle.external is no longer supported; @walkeros/cli@4.x traces server bundles automatically. Remove it from flows.${flowName}.config.bundle.`,\n );\n\n if (!mutated) {\n mutated = {\n ...rawConfig,\n flows: { ...flows },\n };\n }\n const mutatedFlows = mutated.flows as Record<string, unknown>;\n const newBundle = { ...bundle };\n delete newBundle.external;\n const newConfig = { ...flowConfig, bundle: newBundle };\n mutatedFlows[flowName] = { ...flowValue, config: newConfig };\n }\n\n return mutated ?? rawConfig;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/**\n * Content digests for build cache keys and the default release id.\n *\n * Every input is content the bundler consumes: the deferred flow settings,\n * the resolved package set (local packages by the bytes of their installed\n * copy) and the CLI version. Environment and secret values are never inputs,\n * only their markers, so a digest leaks none of them.\n */\n\nimport { createHash } from 'node:crypto';\nimport path from 'path';\nimport fs from 'fs-extra';\nimport type { Flow } from '@walkeros/core';\n\nconst DIGEST_LENGTH = 12;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/** JSON with object keys sorted at every depth. Arrays keep their order. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(value, (_key, inner: unknown) => {\n if (!isRecord(inner)) return inner;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(inner).sort()) sorted[key] = inner[key];\n return sorted;\n });\n}\n\n/**\n * Hash a flow's deferred settings (its `$env` and `$secret` references are\n * still markers). `collector.release` is left out and `collector.name`\n * defaults to the flow name, so the digest names the config the release is\n * derived from. Declared `config.bundle.env` values are reduced to their keys:\n * they are the values web `$env` references resolve to.\n */\nexport function configDigest(flowSettings: Flow, flowName: string): string {\n const collector: Record<string, unknown> = {\n ...(flowSettings.collector ?? {}),\n };\n delete collector.release;\n collector.name ??= flowName;\n\n const config = flowSettings.config;\n const declaredEnv = config?.bundle?.env;\n const hashedConfig =\n config && declaredEnv\n ? {\n ...config,\n bundle: { ...config.bundle, env: Object.keys(declaredEnv).sort() },\n }\n : config;\n\n return createHash('sha256')\n .update(canonicalJson({ ...flowSettings, config: hashedConfig, collector }))\n .digest('hex');\n}\n\n/**\n * Hash an installed package directory: every file by relative path and bytes,\n * a symlink by its target. Paths are sorted, so file system order and mtimes\n * never change the result.\n */\nexport async function hashInstalledDir(dir: string): Promise<string> {\n const hash = createHash('sha256');\n for (const rel of await listFiles(dir, '')) {\n const abs = path.join(dir, rel);\n const stat = await fs.lstat(abs);\n hash.update(`${rel}\\0`);\n hash.update(\n stat.isSymbolicLink() ? await fs.readlink(abs) : await fs.readFile(abs),\n );\n hash.update('\\0');\n }\n return hash.digest('hex').slice(0, DIGEST_LENGTH);\n}\n\nasync function listFiles(root: string, rel: string): Promise<string[]> {\n const entries = await fs.readdir(path.join(root, rel), {\n withFileTypes: true,\n });\n const files: string[] = [];\n for (const entry of entries) {\n const entryRel = rel ? `${rel}/${entry.name}` : entry.name;\n if (entry.isDirectory()) files.push(...(await listFiles(root, entryRel)));\n else files.push(entryRel);\n }\n return files.sort();\n}\n\nexport interface ReleaseDigestInput {\n /** {@link configDigest} of the flow. */\n configDigest: string;\n /** Sorted `name@version` lines, a local package as `name@local:<hash>`. */\n versions: string[];\n /** CLI version, since generated code changes with it. */\n toolchain: string;\n}\n\n/** Default release id: 12 lowercase hex over config, packages and CLI. */\nexport function releaseDigest(input: ReleaseDigestInput): string {\n return createHash('sha256')\n .update(\n canonicalJson({\n config: input.configDigest,\n versions: input.versions,\n toolchain: input.toolchain,\n }),\n )\n .digest('hex')\n .slice(0, DIGEST_LENGTH);\n}\n","import { checkWindowCollector } from '@walkeros/core';\n\n/** Where a collector global name came from, for the error message. */\nexport interface WindowCollectorSource {\n /** Setting path, e.g. `flows.web.config.settings.windowCollector`. */\n path?: string;\n /** The value as written, when it was a `$var` / `$env` reference. */\n written?: unknown;\n}\n\n/**\n * Return `value` as the collector global name, or throw when it is not a\n * JavaScript identifier or names a reserved global. The name is interpolated\n * into generated code, so every build path checks it before use.\n */\nexport function assertWindowCollector(\n value: unknown,\n source: WindowCollectorSource = {},\n): string {\n const check = checkWindowCollector(value);\n if (check.ok) return check.name;\n const path = source.path ?? 'config.settings.windowCollector';\n const subject =\n source.written !== undefined && source.written !== value\n ? `${JSON.stringify(source.written)} resolved to ${JSON.stringify(value)}, which`\n : JSON.stringify(value);\n throw new Error(`${path}: ${subject} ${check.reason}.`);\n}\n","import { CONTRACT_RULE_KEYS, isObject } from '@walkeros/core';\nimport type {\n ValidateResult,\n ValidationError,\n ValidationWarning,\n} from '../types.js';\n\nconst KNOWN_KEYS = new Set<string>(CONTRACT_RULE_KEYS);\n\n/** Event parts once offered as contract sections; nothing enforces them. */\nconst SECTION_KEYS = ['globals', 'context', 'custom', 'user', 'consent'];\n\nexport function validateContract(input: unknown): ValidateResult {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n const details: Record<string, unknown> = {};\n\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n errors.push({\n path: 'root',\n message: 'Contract must be an object of named contract entries',\n code: 'INVALID_CONTRACT',\n });\n return { valid: false, type: 'contract', errors, warnings, details };\n }\n\n const contracts = input as Record<string, unknown>;\n const contractNames = Object.keys(contracts);\n details.contractCount = contractNames.length;\n\n // Check for $-prefixed root keys (flat app shape)\n for (const name of contractNames) {\n if (name.startsWith('$')) {\n errors.push({\n path: name,\n message:\n `\"${name}\" is not a named contract entry. This looks like the flat ` +\n 'shape; use named ContractRule entries, lift $tagging to the rule ' +\n 'field \"tagging\", and nest entity-action schemas under \"events\".',\n code: 'FLAT_CONTRACT_SHAPE',\n });\n }\n }\n\n // Check each named contract entry\n for (const [name, entry] of Object.entries(contracts)) {\n // Skip $-prefixed names (already reported above)\n if (name.startsWith('$')) {\n continue;\n }\n\n if (typeof entry !== 'object' || entry === null) {\n errors.push({\n path: name,\n message: `Contract \"${name}\" must be an object`,\n code: 'INVALID_CONTRACT_ENTRY',\n });\n continue;\n }\n\n const obj = entry as Record<string, unknown>;\n const keys = Object.keys(obj);\n const unknownKeys = keys.filter((k) => !KNOWN_KEYS.has(k));\n const hasKnownKey = keys.some((k) => KNOWN_KEYS.has(k));\n const objectValuedUnknown = unknownKeys.filter(\n (k) => isObject(obj[k]) && !SECTION_KEYS.includes(k),\n );\n\n // Check for flat entity-action maps (object-valued keys without known keys)\n if (!hasKnownKey && objectValuedUnknown.length > 0) {\n errors.push({\n path: name,\n message:\n `Contract \"${name}\" looks like a flat entity-action map. Nest ` +\n 'entity-action schemas under an \"events\" key of a named ContractRule.',\n code: 'FLAT_CONTRACT_SHAPE',\n });\n continue;\n }\n\n // Unknown keys are errors: the contract schema is closed, and the\n // validate transformer would never apply them.\n for (const key of unknownKeys) {\n errors.push({\n path: `${name}.${key}`,\n message: SECTION_KEYS.includes(key)\n ? `Contract section \"${key}\" is not enforced: the validate transformer reads only \"events\" and \"schema\". Put the rule into \"schema\" (a JSON Schema for the whole event), for example \"schema\": { \"properties\": { \"${key}\": { ... } } }.`\n : `Unknown contract key \"${key}\" is not supported. Known keys: ${[...KNOWN_KEYS].join(', ')}.`,\n code: 'UNKNOWN_CONTRACT_KEY',\n });\n }\n\n // Validate tagging\n if (obj.tagging !== undefined && typeof obj.tagging !== 'number') {\n errors.push({\n path: `${name}.tagging`,\n message: 'tagging must be a number',\n code: 'INVALID_TAGGING',\n });\n }\n\n // Validate schema\n if (obj.schema !== undefined && !isObject(obj.schema)) {\n errors.push({\n path: `${name}.schema`,\n message: 'schema must be a JSON Schema object',\n code: 'INVALID_SCHEMA',\n });\n }\n\n // Validate extend\n if (obj.extend !== undefined) {\n if (typeof obj.extend !== 'string') {\n errors.push({\n path: `${name}.extend`,\n message: 'extend must be a string',\n code: 'INVALID_EXTENDS',\n });\n } else if (!contractNames.includes(obj.extend)) {\n errors.push({\n path: `${name}.extend`,\n message: `extend references non-existent contract \"${obj.extend}\"`,\n value: obj.extend,\n code: 'INVALID_EXTENDS',\n });\n }\n }\n\n // Validate events\n if (obj.events !== undefined) {\n if (typeof obj.events !== 'object' || obj.events === null) {\n errors.push({\n path: `${name}.events`,\n message: 'events must be an object',\n code: 'INVALID_EVENTS',\n });\n } else {\n validateEntityActions(\n obj.events as Record<string, unknown>,\n `${name}.events`,\n errors,\n );\n }\n }\n }\n\n // Check for circular extend: one error per cycle, at its first member.\n const reportedCycles = new Set<string>();\n for (const name of contractNames) {\n const chain: string[] = [];\n let current = name;\n while (current) {\n const start = chain.indexOf(current);\n if (start !== -1) {\n const cycle = chain.slice(start);\n const key = [...cycle].sort().join('\\u0000');\n if (!reportedCycles.has(key)) {\n reportedCycles.add(key);\n errors.push({\n path: `${cycle[0]}.extend`,\n message: `Circular extend chain: ${[...cycle, current].join(' → ')}`,\n code: 'CIRCULAR_EXTENDS',\n });\n }\n break;\n }\n chain.push(current);\n const entry = contracts[current];\n current =\n isObject(entry) && typeof entry.extend === 'string' ? entry.extend : '';\n }\n }\n\n return {\n valid: errors.length === 0,\n type: 'contract',\n errors,\n warnings,\n details,\n };\n}\n\nfunction validateEntityActions(\n obj: Record<string, unknown>,\n prefix: string,\n errors: ValidationError[],\n): void {\n for (const [entityKey, entityValue] of Object.entries(obj)) {\n if (entityKey.trim() === '') {\n errors.push({\n path: `${prefix}.${entityKey}`,\n message: 'Entity key cannot be empty',\n code: 'INVALID_ENTITY_KEY',\n });\n continue;\n }\n\n if (typeof entityValue !== 'object' || entityValue === null) {\n errors.push({\n path: `${prefix}.${entityKey}`,\n message: `Entity \"${entityKey}\" must be an object`,\n value: entityValue,\n code: 'INVALID_ENTITY',\n });\n continue;\n }\n\n for (const [actionKey, actionValue] of Object.entries(\n entityValue as Record<string, unknown>,\n )) {\n if (actionKey.trim() === '') {\n errors.push({\n path: `${prefix}.${entityKey}.${actionKey}`,\n message: 'Action key cannot be empty',\n code: 'INVALID_ACTION_KEY',\n });\n continue;\n }\n\n if (\n typeof actionValue !== 'object' ||\n actionValue === null ||\n Array.isArray(actionValue)\n ) {\n errors.push({\n path: `${prefix}.${entityKey}.${actionKey}`,\n message: 'Contract entry must be a JSON Schema object',\n value: typeof actionValue,\n code: 'INVALID_SCHEMA_ENTRY',\n });\n }\n }\n }\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/event.ts\n\nimport { validateEvent as coreValidateEvent } from '../../../core/event-validation.js';\nimport type { ValidateResult } from '../types.js';\n\nexport function validateEvent(input: unknown): ValidateResult {\n const result = coreValidateEvent(input, 'strict');\n\n return {\n valid: result.valid,\n type: 'event',\n errors: result.errors,\n warnings: result.warnings,\n details: result.details as Record<string, unknown>,\n };\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/flow.ts\n\nimport type { Flow, Transformer, WalkerOS } from '@walkeros/core';\nimport {\n checkWindowCollector,\n ENV_MARKER_PREFIX,\n getByPath,\n getRouteGraph,\n isObject,\n REF_CONTRACT,\n resolveContracts,\n SECRET_MARKER_PREFIX,\n validateStepEntry,\n} from '@walkeros/core';\nimport {\n checkStepKeys,\n schemas,\n validateFlowStructure,\n} from '@walkeros/core/dev';\nimport {\n validateEventAgainstContract,\n type ContractSource,\n} from '@walkeros/transformer-validate';\nimport {\n describeScope,\n ENTRY_SECTIONS,\n flowOfPath,\n isFlowJson,\n resolveScope,\n type ResolvedScope,\n} from '../scope.js';\nimport type {\n ValidateCheck,\n ValidateDeferred,\n ValidateDetails,\n ValidateResult,\n ValidateSkip,\n ValidationError,\n ValidationWarning,\n} from '../types.js';\nimport { validateContract } from './contract.js';\nimport { resolveFlow, type FlowResolution } from './resolve.js';\nimport {\n checkStepSettings,\n createSettingsContext,\n type CheckedPackage,\n type SettingsOutcome,\n} from './settings.js';\n\nconst { validateFlowConfig } = schemas;\n\ninterface FlowValidateOptions {\n flow?: string;\n /** When true, contract violations are reported as errors instead of warnings. */\n strict?: boolean;\n}\n\ninterface FlowPackageOptions extends FlowValidateOptions {\n /** Skip the package settings check (no network); named in the scope. */\n offline?: boolean;\n /** Directory local `path` packages resolve against (the input file's). */\n configDir?: string;\n}\n\n/** Removed packages: reported as a warning for one minor (D3), then errors. */\nconst DEPRECATED_PACKAGES: Record<string, string> = {\n '@walkeros/store-memory':\n 'has been removed. Use the built-in cache by omitting cache.store, or remove the store declaration if it was only used as a cache target.',\n};\n\n/** Step sections and their kind, for the closed key check of every step. */\nconst STEP_KINDS = [\n ['sources', 'Source'],\n ['transformers', 'Transformer'],\n ['destinations', 'Destination'],\n ['stores', 'Store'],\n] as const;\n\n/** Everything the synchronous checks produced, for the package pass. */\ninterface FlowRun {\n result: ValidateResult;\n scope: ResolvedScope;\n typed?: Flow.Json;\n resolved: Map<string, Flow>;\n checks: ValidateCheck[];\n skipped: ValidateSkip[];\n gate: (name: string) => string | undefined;\n}\n\n/**\n * Validate a flow file without the network: schema and references, the\n * root contract, the bundler's structural preflight, routes, examples and\n * resolution of every flow in scope. `validate()` adds the package settings\n * check on top (see {@link validateFlowWithPackages}).\n */\nexport function validateFlow(\n input: unknown,\n options: FlowValidateOptions = {},\n): ValidateResult {\n return runFlowChecks(input, options).result;\n}\n\n/**\n * The whole-file run of `validate()`: {@link validateFlow} plus every step\n * with a `package` in scope checked against its package settings schema at\n * the pinned version. An unknown settings key is an error where the package\n * closes its schema (`additionalProperties: false`), as every closed schema\n * is; other findings are warnings for one minor (D3). A schema that cannot\n * be loaded is a skip; `offline` removes the check and says so.\n */\nexport async function validateFlowWithPackages(\n input: unknown,\n options: FlowPackageOptions = {},\n): Promise<ValidateResult> {\n const run = runFlowChecks(input, options);\n const { result, scope } = run;\n if (options.offline) {\n const described = describeScope(scope, run.checks);\n described.offline = true;\n result.details.scope = described;\n return result;\n }\n\n run.checks.push('flow:package-settings');\n const context = createSettingsContext(options.configDir);\n const deferred: ValidateDeferred[] = [];\n const packages: CheckedPackage[] = [];\n // Checks run in parallel; outcomes are applied in step order afterwards,\n // so the result does not depend on which schema arrives first.\n const pending: Array<Promise<SettingsOutcome>> = [];\n for (const name of scope.flows) {\n const flow = run.resolved.get(name);\n const rawFlow = run.typed?.flows[name];\n const reason = run.gate(name);\n if (!flow || !rawFlow || reason) {\n run.skipped.push({\n path: `flows.${name}`,\n check: 'flow:package-settings',\n reason: reason ?? `flows.${name} did not resolve`,\n code: 'GATED_BY_ERRORS',\n });\n continue;\n }\n for (const section of ENTRY_SECTIONS) {\n const steps = isObject(rawFlow[section]) ? rawFlow[section] : {};\n for (const [key, entry] of Object.entries(steps)) {\n if (!isObject(entry) || entry.package === undefined) continue;\n const target = { flow: name, section, key, entry };\n pending.push(\n checkStepSettings(target, flow, 'flow:package-settings', context),\n );\n }\n }\n }\n for (const outcome of await Promise.all(pending)) {\n for (const finding of outcome.findings) {\n if (finding.keyword === 'additionalProperties')\n result.errors.push(finding);\n else result.warnings.push(finding);\n }\n run.skipped.push(...outcome.skipped);\n deferred.push(...outcome.deferred);\n if (outcome.checked) packages.push(outcome.checked);\n }\n\n result.details.scope = describeScope(scope, run.checks);\n result.details.deferred = [...(result.details.deferred ?? []), ...deferred];\n if (packages.length > 0) result.details.packages = packages;\n result.valid = result.errors.length === 0;\n return result;\n}\n\nfunction runFlowChecks(input: unknown, options: FlowValidateOptions): FlowRun {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n const skipped: ValidateSkip[] = [];\n const checks: ValidateCheck[] = ['file:schema', 'flow:schema'];\n const details: ValidateDetails = {};\n const resolved = new Map<string, Flow>();\n // `valid` is set once every error-producing check has run; the package\n // pass only adds warnings and skips.\n const result: ValidateResult = {\n valid: false,\n type: 'flow',\n errors,\n warnings,\n details,\n };\n\n // 1. Serialize to JSON for core validator\n // Core's validateFlowConfig takes a JSON string, but CLI receives parsed objects.\n // Re-serializing is the bridge between the two interfaces.\n let json: string;\n const scope = resolveScope(input, { flow: options.flow });\n try {\n json = JSON.stringify(input, null, 2);\n } catch {\n errors.push({\n path: 'root',\n message: 'Input cannot be serialized to JSON',\n code: 'SERIALIZATION_ERROR',\n });\n return {\n result,\n scope,\n resolved,\n checks,\n skipped,\n gate: () => 'the input cannot be serialized',\n };\n }\n\n // 2. Scope: every per-flow check below runs on scope.flows only.\n errors.push(...scope.errors);\n const config: Record<string, unknown> = isObject(input) ? input : {};\n const flowsValue = config.flows;\n const flows: Record<string, unknown> | undefined = isObject(flowsValue)\n ? flowsValue\n : undefined;\n const allFlows = scope.allFlows;\n const inScope = (path: string): boolean => {\n const flow = flowOfPath(path, allFlows);\n return flow === undefined || scope.flows.includes(flow);\n };\n\n // 3. The root contract, validated as `-t contract` validates it, with\n // paths under `contract.`. A file-level check: it runs whatever --flow.\n let contractErrors = 0;\n if ('contract' in config) {\n checks.push('file:contract');\n const contract = validateContract(config.contract);\n contractErrors = contract.errors.length;\n for (const error of contract.errors)\n errors.push({ ...error, path: `contract.${error.path}` });\n for (const warning of contract.warnings)\n warnings.push({ ...warning, path: `contract.${warning.path}` });\n }\n\n // 4. Run core validation (Zod schema + reference checking). File-level\n // findings always count; per-flow findings only for flows in scope.\n // Core's own contract-resolution error at `contract` is the same\n // finding the contract check above already reported in detail.\n const coreResult = validateFlowConfig(json);\n for (const issue of coreResult.errors) {\n const path = issue.path || 'root';\n if (!inScope(path)) continue;\n if (\n (path === 'contract' || path.startsWith('contract.')) &&\n contractErrors > 0\n )\n continue;\n errors.push({\n path,\n message: issue.message,\n code: issue.code ?? 'SCHEMA_VALIDATION',\n });\n }\n for (const issue of coreResult.warnings) {\n const path = issue.path || 'root';\n if (!inScope(path)) continue;\n warnings.push({ path, message: issue.message, code: 'CONFIG_WARNING' });\n }\n\n // 5. CLI-specific: check for empty flows\n if (flows && allFlows.length === 0) {\n errors.push({\n path: 'flows',\n message: 'At least one flow is required',\n code: 'EMPTY_FLOWS',\n });\n }\n\n // 6. Step entries, closed (single source of truth in @walkeros/core):\n // a transformer entry's shape, and an unknown key on a destination or\n // store entry or in any step's `config`, which the runtime would\n // ignore (UNKNOWN_KEY errors). Removed packages warn\n // (DEPRECATED_PACKAGE) for one minor (D3).\n checks.push('flow:steps');\n for (const flowName of scope.flows) {\n const flowValue = flows?.[flowName];\n if (!isObject(flowValue)) continue;\n const transformersValue = flowValue.transformers;\n if (isObject(transformersValue)) {\n for (const [name, transformerValue] of Object.entries(\n transformersValue,\n )) {\n if (!isObject(transformerValue)) continue;\n const result = validateStepEntry(transformerValue, 'Transformer');\n if (!result.ok) {\n errors.push({\n path: `flows.${flowName}.transformers.${name}`,\n message: result.reason || 'Invalid transformer entry.',\n code: result.code,\n });\n }\n }\n }\n for (const [section, kind] of STEP_KINDS) {\n const steps = flowValue[section];\n if (!isObject(steps)) continue;\n for (const [name, step] of Object.entries(steps)) {\n if (!isObject(step)) continue;\n const at = `flows.${flowName}.${section}.${name}`;\n errors.push(...checkStepKeys(step, kind, at));\n const pkg = step.package;\n if (typeof pkg === 'string' && pkg in DEPRECATED_PACKAGES) {\n warnings.push({\n path: at,\n message: `${kind} \"${name}\" uses ${pkg}, which ${DEPRECATED_PACKAGES[pkg]}`,\n code: 'DEPRECATED_PACKAGE',\n });\n }\n }\n }\n checkWebLoggerFormat(flowName, flowValue, errors);\n }\n\n // 7. Extract flow details\n if (flows) {\n details.flowNames = allFlows;\n details.flowCount = allFlows.length;\n }\n\n // 8. CLI-specific: warn about packages without version (per-flow config.bundle.packages)\n checks.push('flow:package-versions');\n let totalPackageCount = 0;\n for (const flowName of scope.flows) {\n const flowValue = flows?.[flowName];\n if (!isObject(flowValue)) continue;\n const flowConfig = flowValue.config;\n if (!isObject(flowConfig)) continue;\n const bundle = flowConfig.bundle;\n if (!isObject(bundle)) continue;\n const packages = bundle.packages;\n if (!isObject(packages)) continue;\n\n for (const [pkgName, pkgConfigValue] of Object.entries(packages)) {\n if (!isObject(pkgConfigValue)) continue;\n if (!pkgConfigValue.version && !pkgConfigValue.path) {\n warnings.push({\n path: `flows.${flowName}.config.bundle.packages.${pkgName}`,\n message: `Package \"${pkgName}\" has no version specified`,\n suggestion: 'Consider specifying a version for reproducible builds',\n code: 'PACKAGE_VERSION_MISSING',\n });\n }\n }\n totalPackageCount += Object.keys(packages).length;\n }\n if (totalPackageCount > 0) {\n details.packageCount = totalPackageCount;\n }\n\n // 9. Expose core's IntelliSense context in details (bonus for MCP consumers)\n if (coreResult.context) {\n details.context = coreResult.context;\n }\n\n // 10. Deep checks run per flow on shapes core has already validated: a\n // flow with its own errors, or a file with file-level errors (its\n // shape, its contract), skips them, and every skip is listed.\n const typed = isFlowJson(input) ? input : undefined;\n const gate = (name: string): string | undefined => {\n if (\n !typed ||\n errors.some((e) => flowOfPath(e.path, allFlows) === undefined)\n )\n return 'file-level errors: the file shape or its contract is not valid';\n if (errors.some((e) => flowOfPath(e.path, allFlows) === name))\n return `earlier errors in flows.${name}`;\n return undefined;\n };\n const perFlow = (\n stageChecks: ValidateCheck[],\n run: (name: string, flow: Flow, file: Flow.Json) => void,\n ): void => {\n checks.push(...stageChecks);\n for (const name of scope.flows) {\n const reason = gate(name);\n const flow = typed?.flows[name];\n if (reason || !typed || !flow) {\n for (const check of stageChecks) {\n skipped.push({\n path: `flows.${name}`,\n check,\n reason: reason ?? `flows.${name} is not a flow object`,\n code: 'GATED_BY_ERRORS',\n });\n }\n continue;\n }\n run(name, flow, typed);\n }\n };\n\n // 10a. The bundler's own structural preflight (identifier names, package\n // xor code, per-flow $store targets, closed keys, mapping fields that\n // do nothing at their position, mapping values, whole-value\n // references), so a valid result implies the bundle preflight passes.\n // A flow with earlier errors skips it, so nothing is reported twice.\n perFlow(['flow:structure'], (name, flow, file) => {\n const structure = validateFlowStructure({\n ...file,\n flows: { [name]: flow },\n });\n errors.push(...structure.errors);\n warnings.push(...structure.warnings);\n });\n\n // 10b. Route checks on every chain field, read through core's\n // getRouteGraph. Unknown targets are errors; shapes the schema\n // accepts but the author probably did not intend are warnings.\n perFlow(['flow:routes'], (name, flow) => {\n lintFlowRoutes(name, flow, errors, warnings);\n });\n\n // Each flow is resolved once, as the bundler resolves it: the validate\n // step check below reads its settings from there, 10d reports failures.\n const resolutions = new Map<string, FlowResolution>();\n const resolutionOf = (name: string, file: Flow.Json): FlowResolution => {\n let resolution = resolutions.get(name);\n if (!resolution) {\n resolution = resolveFlow(file, name);\n resolutions.set(name, resolution);\n }\n return resolution;\n };\n\n // 10c. Cross-step example compatibility, validate step examples against\n // their contract, and flat dot-separated mapping keys.\n let totalConnections: number | undefined;\n perFlow(\n ['flow:examples', 'flow:contract-examples', 'flow:mapping-keys'],\n (name, flow, file) => {\n const connections = buildConnectionGraph(flow);\n for (const conn of connections) {\n checkCompatibility(name, conn, errors, warnings);\n }\n totalConnections = (totalConnections ?? 0) + connections.length;\n\n // Contracts bind only where a transformer-validate step links them,\n // with exactly that step's settings, as at runtime. The config-level\n // contract block binds nothing by itself.\n const resolution = resolutionOf(name, file);\n for (const [stepName, transformer] of Object.entries(\n flow.transformers || {},\n )) {\n if (!isValidateStep(transformer)) continue;\n const resolvedConfig = resolution.ok\n ? resolution.flow.transformers?.[stepName]?.config\n : undefined;\n checkValidateStepExamples(\n `flows.${name}.transformers.${stepName}`,\n transformer,\n file.contract,\n errors,\n warnings,\n options.strict === true,\n skipped,\n isObject(resolvedConfig) && isObject(resolvedConfig.settings)\n ? resolvedConfig.settings\n : undefined,\n );\n }\n\n for (const [destName, dest] of Object.entries(flow.destinations || {})) {\n if (!isObject(dest.config)) continue;\n const mapping = dest.config.mapping;\n if (!isObject(mapping)) continue;\n\n for (const key of Object.keys(mapping)) {\n if (key.includes('.') && !key.includes(' ')) {\n const parts = key.split('.');\n warnings.push({\n path: `flows.${name}.destinations.${destName}.config.mapping`,\n message: `Mapping key \"${key}\" looks like dot-notation. Mapping uses nested entity → action structure.`,\n suggestion: `Use nested format: { \"${parts[0]}\": { \"${parts.slice(1).join('.')}\": { ... } } }`,\n code: 'MAPPING_DOT_KEY',\n });\n }\n }\n }\n },\n );\n if (totalConnections !== undefined) {\n details.connectionsChecked = totalConnections;\n }\n\n // 10d. Resolve every flow as the bundler does. Every resolver failure\n // (unknown $var, unresolvable $flow, unknown contract, cycles) is an\n // error, since the bundle of that flow fails the same way. Values\n // known only at runtime ($env without default, $secret) stay deferred.\n // The collector global is checked on its resolved value; an `$env`\n // value is known only at build time and stays deferred.\n const deferred: ValidateDeferred[] = [];\n perFlow(['flow:resolve'], (name, flow, file) => {\n const resolution = resolutionOf(name, file);\n if (!resolution.ok) {\n errors.push(resolution.error);\n return;\n }\n resolved.set(name, resolution.flow);\n checkResolvedWindowCollector(name, flow, resolution.flow, errors, deferred);\n });\n\n details.scope = describeScope(scope, checks);\n details.skipped = skipped;\n details.deferred = deferred;\n result.valid = errors.length === 0;\n\n return {\n result,\n scope,\n typed,\n resolved,\n checks,\n skipped,\n gate,\n };\n}\n\n/**\n * A `$var` / `$env` collector global, checked on its resolved value as the\n * build checks it. A literal is already checked by the schema; a value that\n * needs the build env is deferred to the build.\n */\nfunction checkResolvedWindowCollector(\n name: string,\n raw: Flow,\n flow: Flow,\n errors: ValidationError[],\n deferred: ValidateDeferred[],\n): void {\n const written = raw.config?.settings?.windowCollector;\n const value = flow.config?.settings?.windowCollector;\n if (\n flow.config?.platform !== 'web' ||\n typeof written !== 'string' ||\n !/\\$(?:var|env)\\./.test(written)\n )\n return;\n const path = `flows.${name}.config.settings.windowCollector`;\n if (typeof value === 'string' && value.includes(ENV_MARKER_PREFIX)) {\n deferred.push({ path, reference: written });\n return;\n }\n const check = checkWindowCollector(value);\n if (check.ok) return;\n errors.push({\n path,\n message: `${JSON.stringify(written)} resolved to ${JSON.stringify(value)}, which ${check.reason}`,\n code: 'SCHEMA_VALIDATION',\n });\n}\n\n/**\n * `logger.format` chooses text or JSON lines for a server flow's output; a\n * web flow logs to the browser console, where it has no effect. Checked on\n * the collector's logger and on every step's `config.logger`.\n */\nfunction checkWebLoggerFormat(\n flowName: string,\n flow: Record<string, unknown>,\n errors: ValidationError[],\n): void {\n if (!isObject(flow.config) || flow.config.platform !== 'web') return;\n const at = `flows.${flowName}`;\n const loggers: Array<[string, unknown]> = [];\n if (isObject(flow.collector))\n loggers.push([`${at}.collector.logger`, flow.collector.logger]);\n for (const [section] of STEP_KINDS) {\n const steps = flow[section];\n if (!isObject(steps)) continue;\n for (const [name, step] of Object.entries(steps)) {\n if (isObject(step) && isObject(step.config))\n loggers.push([\n `${at}.${section}.${name}.config.logger`,\n step.config.logger,\n ]);\n }\n }\n for (const [path, logger] of loggers) {\n if (!isObject(logger) || logger.format === undefined) continue;\n errors.push({\n path: `${path}.format`,\n message:\n 'logger.format applies to server flows only: a web flow logs to the browser console, which has no output format. Remove it.',\n code: 'WEB_LOGGER_FORMAT',\n });\n }\n}\n\n// --- Deep validation helpers ---\n\ninterface StepInfo {\n type: 'source' | 'transformer' | 'destination';\n name: string;\n examples: Flow.StepExamples;\n}\n\ninterface StepConnection {\n from: StepInfo;\n to: StepInfo;\n}\n\n/**\n * Every transformer id a route can reach, read through core's\n * `getRouteGraph` (the one enumerator over the compiled route form). An\n * over-approximation by design: static validation has no event, so every\n * branch counts.\n */\nfunction routeTargets(spec: Transformer.Route | undefined): string[] {\n if (spec === undefined) return [];\n const targets = new Set<string>();\n for (const node of getRouteGraph(spec)) {\n for (const target of node.targets) targets.add(target);\n }\n return [...targets];\n}\n\nfunction buildConnectionGraph(config: Flow): StepConnection[] {\n const connections: StepConnection[] = [];\n\n // Source → next transformer\n for (const [name, source] of Object.entries(config.sources || {})) {\n if (!source.next || !source.examples) continue;\n const nextNames = routeTargets(source.next);\n for (const nextName of nextNames) {\n const transformer = config.transformers?.[nextName];\n if (transformer?.examples) {\n connections.push({\n from: { type: 'source', name, examples: source.examples },\n to: {\n type: 'transformer',\n name: nextName,\n examples: transformer.examples,\n },\n });\n }\n }\n }\n\n // Transformer → next transformer\n for (const [name, transformer] of Object.entries(config.transformers || {})) {\n if (!transformer.next || !transformer.examples) continue;\n const nextNames = routeTargets(transformer.next);\n for (const nextName of nextNames) {\n const nextTransformer = config.transformers?.[nextName];\n if (nextTransformer?.examples) {\n connections.push({\n from: {\n type: 'transformer',\n name,\n examples: transformer.examples,\n },\n to: {\n type: 'transformer',\n name: nextName,\n examples: nextTransformer.examples,\n },\n });\n }\n }\n }\n\n // Destination.before → transformer chain → destination\n for (const [name, dest] of Object.entries(config.destinations || {})) {\n if (!dest.before || !dest.examples) continue;\n const beforeNames = routeTargets(dest.before);\n for (const beforeName of beforeNames) {\n const transformer = config.transformers?.[beforeName];\n if (transformer?.examples) {\n connections.push({\n from: {\n type: 'transformer',\n name: beforeName,\n examples: transformer.examples,\n },\n to: { type: 'destination', name, examples: dest.examples },\n });\n }\n }\n }\n\n return connections;\n}\n\nfunction checkCompatibility(\n flowName: string,\n conn: StepConnection,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n): void {\n const fromOuts = Object.entries(conn.from.examples)\n .filter(([, ex]) => hasComparableOut(ex.out))\n .map(([name, ex]) => ({ name, value: ex.out }));\n\n // A command example's `in` is a walker command payload, never a pushed event\n const toIns = Object.entries(conn.to.examples)\n .filter(([, ex]) => ex.in !== undefined && !ex.command)\n .map(([name, ex]) => ({ name, value: ex.in }));\n\n const path = `flows.${flowName}.${conn.from.type}s.${conn.from.name} → ${conn.to.type}s.${conn.to.name}`;\n\n if (fromOuts.length === 0 || toIns.length === 0) {\n warnings.push({\n path,\n message: 'Cannot check compatibility: missing out or in examples',\n suggestion:\n 'Add out examples to the source step or in examples to the target step',\n code: 'EXAMPLES_INCOMPLETE',\n });\n return;\n }\n\n const hasMatch = fromOuts.some((out) =>\n outEvents(out.value).some((event) =>\n toIns.some((inp) => isStructurallyCompatible(event, inp.value)),\n ),\n );\n\n if (!hasMatch) {\n errors.push({\n path,\n message: 'No compatible out/in pair found between connected steps',\n code: 'INCOMPATIBLE_EXAMPLES',\n });\n }\n}\n\n/** An effect tuple of a StepOut: a string head, then the call arguments. */\ntype StepOutEffect = [string, ...unknown[]];\n\n/**\n * A StepOut is an array whose every entry is an array with a string head\n * (`[]` included): `[['elb', event]]`, `[['return', { event }]]`, ...\n */\nfunction isStepOut(value: unknown): value is StepOutEffect[] {\n return (\n Array.isArray(value) &&\n value.every((entry) => Array.isArray(entry) && typeof entry[0] === 'string')\n );\n}\n\n/**\n * Events a `return` effect passes on: `{ event }` yields the event, a bare\n * object is the event itself, an array is a fan-out read item by item by the\n * same rule. `false` and other values pass nothing on.\n */\nfunction returnedEvents(value: unknown): unknown[] {\n if (isObject(value)) return [isObject(value.event) ? value.event : value];\n if (Array.isArray(value)) return value.flatMap(returnedEvents);\n return [];\n}\n\n/**\n * The events an example `out` hands to the next step. A StepOut yields the\n * object argument of each `elb` effect and the events of each `return`\n * effect; every other head (`message.ack`, `response`, vendor calls) yields\n * nothing. A non-StepOut value is the output itself (back compat), when it\n * is a non-empty array, string or object.\n */\nfunction outEvents(out: unknown): unknown[] {\n if (isStepOut(out)) {\n return out.flatMap(([head, first]) => {\n if (head === 'elb') return isObject(first) ? [first] : [];\n if (head === 'return') return returnedEvents(first);\n return [];\n });\n }\n if (Array.isArray(out) || typeof out === 'string')\n return out.length > 0 ? [out] : [];\n return isObject(out) && Object.keys(out).length > 0 ? [out] : [];\n}\n\nfunction hasComparableOut(out: unknown): boolean {\n return outEvents(out).length > 0;\n}\n\nfunction isStructurallyCompatible(a: unknown, b: unknown): boolean {\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) && Array.isArray(b)) return true;\n if (typeof a === 'object' && typeof b === 'object') {\n const keysA = Object.keys(a as object);\n const keysB = Object.keys(b as object);\n const shared = keysA.filter((k) => keysB.includes(k));\n return shared.length >= Math.min(keysA.length, keysB.length) * 0.5;\n }\n return true;\n}\n\n/**\n * Checks every chain field of a flow (source `before`/`next`, transformer\n * `before`/`next`, destination `before`/`next`, `collector.next`):\n *\n * - error: a route target that is not a transformer of the flow. At runtime\n * an unknown id is skipped with a warning, so a misspelled step (for\n * example a redaction branch) would otherwise fail open.\n * - warning: entries after an unconditional `stop` never run.\n * - warning: `many: []` selects nothing; `many: [\"x\"]` is just `next`.\n * - warning: an array made only of route configs is first-match.\n */\nfunction lintFlowRoutes(\n flowName: string,\n flow: Flow,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n): void {\n const known = new Set(Object.keys(flow.transformers || {}));\n const check = (\n spec: Transformer.Route | undefined,\n position: string,\n ): void => {\n if (spec !== undefined) lintRoute(spec, position, known, errors, warnings);\n };\n const at = `flows.${flowName}`;\n\n for (const [name, source] of Object.entries(flow.sources || {})) {\n check(source.before, `${at}.sources.${name}.before`);\n check(source.next, `${at}.sources.${name}.next`);\n }\n for (const [name, transformer] of Object.entries(flow.transformers || {})) {\n check(transformer.before, `${at}.transformers.${name}.before`);\n check(transformer.next, `${at}.transformers.${name}.next`);\n }\n check(flow.collector?.next, `${at}.collector.next`);\n for (const [name, dest] of Object.entries(flow.destinations || {})) {\n check(dest.before, `${at}.destinations.${name}.before`);\n check(dest.next, `${at}.destinations.${name}.next`);\n }\n}\n\ntype RoutePath = (string | number)[];\n\n/** The raw value at `path` inside a route (a plain lookup, no grammar). */\nfunction routeAt(spec: Transformer.Route, path: RoutePath): unknown {\n let current: unknown = spec;\n for (const key of path) {\n if (typeof key === 'number')\n current = Array.isArray(current) ? current[key] : undefined;\n else current = isObject(current) ? current[key] : undefined;\n }\n return current;\n}\n\n/**\n * Lints one route through `getRouteGraph`. Every branch node, and every\n * enclosing decision it lists in `via`, is looked at once by its path.\n */\nfunction lintRoute(\n spec: Transformer.Route,\n position: string,\n known: Set<string>,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n): void {\n const where = (path: RoutePath) =>\n path.length > 0 ? `${position}.${path.join('.')}` : position;\n const reported = new Set<string>();\n const once = (key: string): boolean => {\n if (reported.has(key)) return false;\n reported.add(key);\n return true;\n };\n\n for (const node of getRouteGraph(spec)) {\n for (const target of node.targets) {\n if (known.has(target) || !once(`unknown:${target}`)) continue;\n errors.push({\n path: position,\n message: `Unknown transformer \"${target}\" in route at ${position}`,\n code: 'UNKNOWN_ROUTE_TARGET',\n });\n }\n\n // Entries after an unconditional stop never run (a `many` entry's stop\n // ends only its own copy, so it does not shadow its siblings).\n const last = node.path[node.path.length - 1];\n if (node.stop && !node.match && node.kind !== 'many') {\n const self = routeAt(spec, node.path);\n const list = routeAt(spec, node.path.slice(0, -1));\n if (\n isObject(self) &&\n self.stop === true &&\n typeof last === 'number' &&\n Array.isArray(list) &&\n last < list.length - 1 &&\n once(`stop:${where(node.path)}`)\n ) {\n const at = where(node.path);\n warnings.push({\n path: at,\n message: `dead code after stop at ${at}: entries after an unconditional stop never run`,\n code: 'ROUTE_DEAD_CODE',\n suggestion:\n 'Remove the entries after the stop, or give the stop a match.',\n });\n }\n }\n\n if (\n node.kind === 'many' &&\n last === 'many' &&\n node.targets.length === 0 &&\n once(`empty:${where(node.path)}`)\n ) {\n const at = where(node.path.slice(0, -1));\n warnings.push({\n path: at,\n message: `empty many at ${at}: selects no branch, so it has no effect`,\n code: 'ROUTE_EMPTY_MANY',\n suggestion: 'Add two or more branch targets to many, or remove it.',\n });\n }\n\n for (const decision of [...(node.via ?? []), node]) {\n const index = decision.path[decision.path.length - 1];\n if (typeof index !== 'number') continue;\n const listPath = decision.path.slice(0, -1);\n const list = routeAt(spec, listPath);\n if (!Array.isArray(list)) continue;\n const at = where(listPath);\n\n if (\n decision.kind === 'many' &&\n list.length === 1 &&\n once(`single:${at}`)\n ) {\n const only = list[0];\n const hint =\n typeof only === 'string'\n ? `use 'next: \"${only}\"' for clarity`\n : `use 'next' for clarity`;\n warnings.push({\n path: at,\n message: `single-entry many at ${at}: ${hint}`,\n code: 'ROUTE_SINGLE_MANY',\n suggestion: 'Replace many with next when only one branch exists.',\n });\n }\n\n // A pure route-config array is compiled as an implicit `one`.\n const explicitOne = listPath[listPath.length - 1] === 'one';\n if (\n decision.kind === 'one' &&\n !explicitOne &&\n list.length > 1 &&\n once(`first-match:${at}`)\n ) {\n warnings.push({\n path: at,\n message: `first-match array at ${at}: an array made only of route configs is an implicit one, the first matching entry wins`,\n code: 'ROUTE_FIRST_MATCH',\n suggestion:\n 'Write { \"one\": [...] } explicitly, or add the step ids as a sequence to run every entry in order.',\n });\n }\n }\n }\n}\n\n/**\n * Whether a value holds a `$env` or `$secret` value: resolved to the\n * resolver's deferred marker, or still written as the reference.\n */\nfunction hasRuntimeValue(value: unknown): boolean {\n if (typeof value === 'string')\n return (\n value.includes(ENV_MARKER_PREFIX) ||\n value.startsWith(SECRET_MARKER_PREFIX) ||\n /\\$(?:env|secret)\\./.test(value)\n );\n if (Array.isArray(value)) return value.some(hasRuntimeValue);\n return isObject(value) && Object.values(value).some(hasRuntimeValue);\n}\n\nconst VALIDATE_PACKAGE = '@walkeros/transformer-validate';\n\n/** A transformer step that runs `@walkeros/transformer-validate`. */\nexport function isValidateStep(step: Flow.Transformer): boolean {\n return step.package === VALIDATE_PACKAGE;\n}\n\ntype StepContracts =\n | { ok: true; sources: ContractSource[] }\n | { ok: false; index?: number; message: string };\n\n/**\n * The contract sources a validate step runs with, as at runtime: a whole\n * `$contract.<name>(.<path>)` string resolves against the config-level\n * contract definitions, an inline schema is used as given. An entry that\n * does not resolve says which one and why; the step is then not judged.\n */\nfunction resolveStepContracts(\n entries: unknown[],\n contract: Flow.Contract | undefined,\n): StepContracts {\n let resolved: Record<string, Flow.ContractRule> = {};\n if (contract) {\n try {\n resolved = resolveContracts(contract);\n } catch (error) {\n return {\n ok: false,\n message: `the root contract does not resolve (${error instanceof Error ? error.message : String(error)})`,\n };\n }\n }\n\n const sources: ContractSource[] = [];\n for (const [index, entry] of entries.entries()) {\n if (isObject(entry)) {\n sources.push(entry);\n continue;\n }\n const match = typeof entry === 'string' ? entry.match(REF_CONTRACT) : null;\n if (!match)\n return {\n ok: false,\n index,\n message: 'an entry must be a $contract reference or a JSON Schema',\n };\n if (!(match[1] in resolved))\n return {\n ok: false,\n index,\n message: `contract \"${match[1]}\" is not defined in the root contract (defined: ${Object.keys(resolved).join(', ') || 'none'})`,\n };\n const rule: unknown = match[2]\n ? getByPath(resolved[match[1]], match[2])\n : resolved[match[1]];\n if (!isObject(rule))\n return {\n ok: false,\n index,\n message: `${match[0]} does not point at a contract rule object`,\n };\n sources.push(rule);\n }\n return { ok: true, sources };\n}\n\n/**\n * Check a validate step's own examples against its own contract and\n * settings: the verdict the step reaches on `in` must agree with what `out`\n * shows. A valid event goes on (its `output.isValid` flag, when shown, is\n * true); an invalid one is dropped in `strict` mode and otherwise goes on\n * with the flag false. A disagreement is CONTRACT_VIOLATION (error when\n * {@link strict}, else warning). Deterministic: the verdict comes from the\n * shared {@link validateEventAgainstContract} runtime authority.\n * `stepPath` is the step's result path, `flows.<flow>.transformers.<name>`.\n * A contract entry that does not resolve is an error (UNRESOLVED_CONTRACT),\n * examples or not; a contract setting that is not a list is a skip.\n * `resolvedSettings` are the step's settings as the resolver produced them\n * (`$var` and `$contract` resolved, as the runtime sees them); without them\n * the settings as written are read.\n */\nexport function checkValidateStepExamples(\n stepPath: string,\n step: Flow.Transformer,\n contract: Flow.Contract | undefined,\n errors: ValidationError[],\n warnings: ValidationWarning[],\n strict: boolean,\n skipped?: ValidateSkip[],\n resolvedSettings?: Record<string, unknown>,\n): void {\n const settings =\n resolvedSettings ??\n (isObject(step.config) && isObject(step.config.settings)\n ? step.config.settings\n : {});\n const contractPath = `${stepPath}.config.settings.contract`;\n // A contract that is set but not a list cannot be judged statically.\n if (settings.contract !== undefined && !Array.isArray(settings.contract)) {\n if (step.examples)\n skipped?.push({\n path: stepPath,\n check: 'flow:contract-examples',\n reason:\n 'config.settings.contract is not a list, so the step cannot be judged statically',\n code: 'CONTRACT_NOT_STATIC',\n });\n return;\n }\n const resolution: StepContracts = Array.isArray(settings.contract)\n ? resolveStepContracts(settings.contract, contract)\n : { ok: true, sources: [] };\n if (!resolution.ok) {\n errors.push({\n path:\n resolution.index === undefined\n ? contractPath\n : `${contractPath}.${resolution.index}`,\n message: `The validate step's contract does not resolve: ${resolution.message}`,\n code: 'UNRESOLVED_CONTRACT',\n });\n return;\n }\n if (!step.examples) return;\n const contracts = resolution.sources;\n // A `$env` or `$secret` value in the contract is known only when the\n // bundle builds or runs; judged on its literal text, JSON Schema would\n // ignore it. The check is deferred, and the skip says so.\n if (contracts.some(hasRuntimeValue)) {\n skipped?.push({\n path: stepPath,\n check: 'flow:contract-examples',\n reason:\n 'the contract uses a $env or $secret value, known only when the bundle builds or runs, so the examples cannot be judged statically',\n code: 'CONTRACT_NOT_STATIC',\n });\n return;\n }\n\n const mode = settings.mode === 'strict' ? 'strict' : 'pass';\n const format = settings.format === true;\n const output = isObject(settings.output) ? settings.output : {};\n const isValidPath =\n typeof output.isValid === 'string' ? output.isValid : 'source.valid';\n\n for (const [exName, example] of Object.entries(step.examples)) {\n if (example.command || !isObject(example.in)) continue;\n if (example.out === undefined) continue;\n\n const event: WalkerOS.DeepPartialEvent = example.in;\n const verdict = validateEventAgainstContract(event, undefined, {\n contracts,\n format,\n });\n const passed = outEvents(example.out);\n const dropped =\n isStepOut(example.out) &&\n example.out.some(([head, first]) => head === 'return' && first === false);\n\n let agrees: boolean;\n if (mode === 'strict' && !verdict.isValid) {\n agrees = dropped && passed.length === 0;\n } else {\n agrees =\n passed.length > 0 &&\n passed.every((out) => {\n if (!isValidPath) return true;\n const flag = getByPath(out, isValidPath);\n return verdict.isValid ? flag !== false : flag === false;\n });\n }\n if (agrees) continue;\n\n const reason = verdict.isValid\n ? 'the event satisfies the contract'\n : `the event breaks the contract (${verdict.errors\n .map((e) => `${e.path || '/'}: ${e.message}`)\n .join('; ')})`;\n const expected =\n mode === 'strict' && !verdict.isValid\n ? 'drops it ([\"return\", false])'\n : `passes it on${isValidPath ? ` with ${isValidPath} ${verdict.isValid}` : ''}`;\n const path = `${stepPath}.examples.${exName}.out`;\n const message = `Example out disagrees with the validate step: ${reason}, so with mode \"${mode}\" the step ${expected}.`;\n\n if (strict) {\n errors.push({ path, message, code: 'CONTRACT_VIOLATION' });\n } else {\n warnings.push({\n path,\n message,\n suggestion:\n 'Make the example out show what the validate step does with its settings.',\n code: 'CONTRACT_VIOLATION',\n });\n }\n }\n}\n","// walkerOS/packages/cli/src/commands/validate/scope.ts\n\nimport type { Flow } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\nimport type { ValidateCheck, ValidateScope, ValidationError } from './types.js';\n\n/**\n * A parsed Flow.Json shape (a `version` and a `flows` object), so the\n * validators can hand it to core's resolver and preflight without casts.\n */\nexport function isFlowJson(value: unknown): value is Flow.Json {\n return isObject(value) && 'version' in value && isObject(value.flows);\n}\n\n/** Step sections a `--path` can address. */\nexport const ENTRY_SECTIONS = [\n 'sources',\n 'destinations',\n 'transformers',\n 'stores',\n] as const;\n\nexport type EntrySection = (typeof ENTRY_SECTIONS)[number];\n\nfunction isEntrySection(value: string): value is EntrySection {\n return ENTRY_SECTIONS.some((section) => section === value);\n}\n\nexport interface ScopeOptions {\n flow?: string;\n path?: string;\n}\n\nexport interface ScopeEntry {\n flow: string;\n section: EntrySection;\n key: string;\n entry: Record<string, unknown>;\n}\n\nexport interface ResolvedScope {\n /** Every flow in the file, in file order. */\n allFlows: string[];\n /** Flows every per-flow check runs on. */\n flows: string[];\n /** Entries a `--path` addressed, one per flow that has it. */\n entries: ScopeEntry[];\n /** Flows searched for the `--path` entry (empty without a path). */\n searchedFlows: string[];\n /** The entry a `--path` names, when one was given and parsed. */\n target?: { section?: EntrySection; key: string };\n /** FLOW_NOT_FOUND, ENTRY_NOT_FOUND, AMBIGUOUS_ENTRY, UNSUPPORTED_SECTION, INVALID_PATH. */\n errors: ValidationError[];\n}\n\n/**\n * The one place validate picks flows and entries. `flow` narrows to that\n * flow; without it every flow is in scope. `path` (`section.key` or `key`)\n * addresses the entry in every flow in scope that has it.\n */\nexport function resolveScope(\n config: unknown,\n options: ScopeOptions = {},\n): ResolvedScope {\n const flowsValue = isObject(config) ? config.flows : undefined;\n const flowMap = isObject(flowsValue) ? flowsValue : {};\n const allFlows = Object.keys(flowMap);\n\n let flows = allFlows;\n if (options.flow !== undefined) {\n if (!allFlows.includes(options.flow)) {\n return {\n allFlows,\n flows: [],\n entries: [],\n searchedFlows: [],\n errors: [\n {\n path: 'flows',\n message: `Flow \"${options.flow}\" not found. Available: ${allFlows.join(', ') || 'none'}`,\n code: 'FLOW_NOT_FOUND',\n },\n ],\n };\n }\n flows = [options.flow];\n }\n\n const path = options.path;\n if (path === undefined) {\n return { allFlows, flows, entries: [], searchedFlows: [], errors: [] };\n }\n\n const parts = path.split('.');\n let sections: readonly EntrySection[] = ENTRY_SECTIONS;\n let section: EntrySection | undefined;\n let key: string;\n if (parts.length === 2 && parts[0] !== '' && parts[1] !== '') {\n if (!isEntrySection(parts[0])) {\n return {\n allFlows,\n flows,\n entries: [],\n searchedFlows: [],\n errors: [\n {\n path,\n message: `Section \"${parts[0]}\" cannot be addressed by --path. Use one of: ${ENTRY_SECTIONS.join(', ')}`,\n code: 'UNSUPPORTED_SECTION',\n },\n ],\n };\n }\n section = parts[0];\n sections = [section];\n key = parts[1];\n } else if (parts.length === 1 && parts[0] !== '') {\n key = parts[0];\n } else {\n return {\n allFlows,\n flows,\n entries: [],\n searchedFlows: [],\n errors: [\n {\n path,\n message: `Invalid path \"${path}\". Use \"section.key\" or just \"key\"`,\n code: 'INVALID_PATH',\n },\n ],\n };\n }\n\n const entries: ScopeEntry[] = [];\n for (const flow of flows) {\n const flowValue = flowMap[flow];\n if (!isObject(flowValue)) continue;\n for (const candidate of sections) {\n const sectionValue = flowValue[candidate];\n if (!isObject(sectionValue)) continue;\n const entry = sectionValue[key];\n if (isObject(entry))\n entries.push({ flow, section: candidate, key, entry });\n }\n }\n\n const matched = [...new Set(entries.map((entry) => entry.section))];\n if (matched.length > 1) {\n return {\n allFlows,\n flows,\n entries: [],\n searchedFlows: flows,\n target: { key },\n errors: [\n {\n path,\n message: `Ambiguous key \"${key}\" found in multiple sections: ${matched.join(', ')}. Use dot-notation (e.g., ${matched[0]}.${key})`,\n code: 'AMBIGUOUS_ENTRY',\n },\n ],\n };\n }\n\n const target = { section: section ?? matched[0], key };\n if (entries.length === 0) {\n return {\n allFlows,\n flows,\n entries,\n searchedFlows: flows,\n target,\n errors: [\n {\n path,\n message: `Entry \"${key}\" not found in ${section ?? 'any section'} of flow(s): ${flows.join(', ') || 'none'}`,\n code: 'ENTRY_NOT_FOUND',\n },\n ],\n };\n }\n\n return {\n allFlows,\n flows,\n entries,\n searchedFlows: flows,\n target,\n errors: [],\n };\n}\n\n/** The flow a result path belongs to, or undefined for a file-level path. */\nexport function flowOfPath(\n path: string,\n flowNames: readonly string[],\n): string | undefined {\n return flowNames.find(\n (name) => path === `flows.${name}` || path.startsWith(`flows.${name}.`),\n );\n}\n\n/** `details.scope` for a resolved scope and the checks the run included. */\nexport function describeScope(\n resolved: ResolvedScope,\n checks: ValidateCheck[],\n): ValidateScope {\n const scope: ValidateScope = { flows: resolved.flows, checks };\n if (resolved.target) {\n const { section, key } = resolved.target;\n scope.entry = {\n ...(section ? { section } : {}),\n key,\n flows: [...new Set(resolved.entries.map((entry) => entry.flow))],\n searchedFlows: resolved.searchedFlows,\n };\n }\n return scope;\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/resolve.ts\n\nimport type { Flow } from '@walkeros/core';\nimport {\n FlowCycleError,\n FlowReferenceError,\n getFlowSettings,\n getErrorMessage,\n} from '@walkeros/core';\nimport type { ValidateCode, ValidationError } from '../types.js';\n\nexport type FlowResolution =\n | { ok: true; flow: Flow }\n | { ok: false; error: ValidationError };\n\n/**\n * Resolve one flow exactly as the bundler does (`getFlowSettings` with\n * deferred `$env`/`$secret`, and `$flow` refs as strict as the bundle). A\n * resolver failure is an error at `flows.<name>`: the bundle of this flow\n * fails the same way.\n */\nexport function resolveFlow(file: Flow.Json, name: string): FlowResolution {\n try {\n const flow = getFlowSettings(file, name, { deferred: true });\n return { ok: true, flow };\n } catch (error) {\n return {\n ok: false,\n error: {\n path: `flows.${name}`,\n message: getErrorMessage(error),\n code: resolutionCode(error),\n },\n };\n }\n}\n\n/** The code of a resolver failure, read from its type. */\nfunction resolutionCode(error: unknown): ValidateCode {\n if (error instanceof FlowReferenceError) return error.code;\n if (error instanceof FlowCycleError) return error.code;\n return 'UNRESOLVED_REFERENCE';\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/settings.ts\n\nimport Ajv, { type ErrorObject, type ValidateFunction } from 'ajv';\nimport type { Flow } from '@walkeros/core';\nimport {\n createLogger,\n ENV_MARKER_PREFIX,\n getErrorMessage,\n isObject,\n REF_CODE_PREFIX,\n REF_STORE,\n SECRET_MARKER_PREFIX,\n} from '@walkeros/core';\nimport {\n resolveExportName,\n type ComponentKind,\n} from '../../../core/resolve-export-name.js';\nimport {\n resolveStepPackage,\n type ResolvedStepPackage,\n} from '../../../core/step-packages.js';\nimport { readStepPackageInfo } from '../../../core/package-info.js';\nimport type { EntrySection } from '../scope.js';\nimport type {\n ValidateCheck,\n ValidateDeferred,\n ValidateSkip,\n ValidationError,\n} from '../types.js';\n\n// __VERSION__ is replaced at build time by tsup's `define` (see tsup.config.ts).\n// In tests, it's set as a global by the shared jest config (@walkeros/config/jest).\ndeclare const __VERSION__: string;\n\nconst CLIENT_HEADER = 'walkeros-cli/' + __VERSION__;\n\n// Resolving a step's package logs only a warning (a bundle pin over a\n// disagreeing inline version); the version checked is in details.packages.\nconst LOGGER = createLogger();\n\n/** One step whose settings are checked against its package schema. */\nexport interface SettingsTarget {\n flow: string;\n section: EntrySection;\n key: string;\n /** The step as written in the file (package, bundle pins). */\n entry: Record<string, unknown>;\n}\n\nexport interface SettingsContext {\n /** Directory a local `path` package resolves against; unset: no disk reads. */\n configDir?: string;\n /** Schemas fetched in this run, by package source. */\n cache: Map<string, Promise<PackageSchema>>;\n /** Settings validators compiled in this run, by package source and export. */\n compiled: Map<string, CompiledSettings | undefined>;\n}\n\nexport interface CheckedPackage {\n path: string;\n package: string;\n version?: string;\n}\n\nexport interface SettingsOutcome {\n findings: ValidationError[];\n skipped: ValidateSkip[];\n deferred: ValidateDeferred[];\n checked?: CheckedPackage;\n}\n\nexport interface PackageSchema {\n version?: string;\n /** The default export's settings schema. */\n settings: unknown;\n /** Schemas per export, keyed by export name (multi-export packages). */\n exportSchemas?: Record<string, unknown>;\n /** Export names the package declares. */\n exports?: string[];\n}\n\nexport type SettingsSelection =\n | { ok: true; settings: unknown }\n | { ok: false; reason: string };\n\n/** A settings schema compiled once per package source, export and run. */\ntype CompiledSettings =\n | { ok: true; validate: ValidateFunction }\n | { ok: false; message: string };\n\nexport function createSettingsContext(configDir?: string): SettingsContext {\n return { configDir, cache: new Map(), compiled: new Map() };\n}\n\n/**\n * The settings schema for the export a step imports, by the same rule\n * simulate uses for dev examples: no export named takes `settings`; a\n * per-export map takes that export's entry and never another export's; a\n * package declaring at most one export takes `settings` for its only step\n * export; a multi-export package without a map, or a package that does not\n * publish its exports, cannot be checked.\n */\nexport function selectSettingsSchema(\n pkg: PackageSchema,\n packageLabel: string,\n exportName: string | undefined,\n): SettingsSelection {\n if (exportName === undefined) return { ok: true, settings: pkg.settings };\n if (pkg.exportSchemas) {\n const entry = Object.prototype.hasOwnProperty.call(\n pkg.exportSchemas,\n exportName,\n )\n ? pkg.exportSchemas[exportName]\n : undefined;\n if (!isObject(entry))\n return {\n ok: false,\n reason: `${packageLabel} has no export \"${exportName}\"`,\n };\n return { ok: true, settings: entry.settings };\n }\n if (pkg.exports && pkg.exports.length < 2)\n return { ok: true, settings: pkg.settings };\n return {\n ok: false,\n reason: `${packageLabel} predates per-export settings schemas; the step imports \"${exportName}\"`,\n };\n}\n\nconst SECTION_KIND: Record<EntrySection, ComponentKind> = {\n sources: 'source',\n transformers: 'transformer',\n destinations: 'destination',\n stores: 'store',\n};\n\n/** Segments of an Ajv instance path (a JSON pointer). */\nfunction pointerSegments(pointer: string): string[] {\n if (pointer === '') return [];\n return pointer\n .split('/')\n .slice(1)\n .map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'));\n}\n\nfunction valueAt(root: unknown, segments: string[]): unknown {\n let current = root;\n for (const segment of segments) {\n if (Array.isArray(current)) current = current[Number(segment)];\n else if (isObject(current)) current = current[segment];\n else return undefined;\n }\n return current;\n}\n\n// The bundler's deferred-$env marker grammar (see bundle/bundler.ts): a\n// default runs until whitespace, a quote or the next marker.\nconst MARKER = ENV_MARKER_PREFIX.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\nconst ENV_MARKER = new RegExp(\n `${MARKER}([a-zA-Z_][a-zA-Z0-9_]*)(?::((?:(?!${MARKER})[^\\\\s\"'])*))?`,\n 'g',\n);\n\ninterface PreparedSettings {\n value: unknown;\n /** Instance paths known only at runtime, with the reference as written. */\n deferred: Array<{ segments: string[]; reference: string }>;\n}\n\n/**\n * The placeholder rule, applied to settings already resolved by\n * `getFlowSettings(..., { deferred: true })`: `$var` and `$flow` values are\n * already resolved (an unresolvable one fails the flow first); `$env.NAME:default`\n * is checked as its default; `$env.NAME` without default, `$secret`, `$store`\n * and `$code` are known only at runtime and deferred.\n */\nfunction prepareSettings(settings: unknown): PreparedSettings {\n const deferred: PreparedSettings['deferred'] = [];\n const walk = (value: unknown, segments: string[]): unknown => {\n if (Array.isArray(value))\n return value.map((item, index) => walk(item, [...segments, `${index}`]));\n if (isObject(value))\n return Object.fromEntries(\n Object.entries(value).map(([key, item]) => [\n key,\n walk(item, [...segments, key]),\n ]),\n );\n if (typeof value !== 'string') return value;\n\n if (value.startsWith(SECRET_MARKER_PREFIX)) {\n deferred.push({\n segments,\n reference: `$secret.${value.slice(SECRET_MARKER_PREFIX.length)}`,\n });\n return value;\n }\n if (value.includes(ENV_MARKER_PREFIX)) {\n let runtimeOnly = false;\n const withDefaults = value.replace(\n ENV_MARKER,\n (_match, _name: string, fallback: string | undefined) => {\n if (fallback === undefined) runtimeOnly = true;\n return fallback ?? '';\n },\n );\n if (!runtimeOnly) return withDefaults;\n deferred.push({\n segments,\n reference: value.replace(\n ENV_MARKER,\n (_match, name: string, fallback: string | undefined) =>\n fallback === undefined\n ? `$env.${name}`\n : `$env.${name}:${fallback}`,\n ),\n });\n return value;\n }\n if (REF_STORE.test(value) || value.startsWith(REF_CODE_PREFIX)) {\n deferred.push({ segments, reference: value });\n }\n return value;\n };\n return { value: walk(settings, []), deferred };\n}\n\nfunction isUnder(segments: string[], prefix: string[]): boolean {\n return prefix.every((segment, index) => segments[index] === segment);\n}\n\nasync function loadSchema(\n resolved: ResolvedStepPackage,\n context: SettingsContext,\n): Promise<PackageSchema> {\n const info = await readStepPackageInfo(resolved, {\n configDir: context.configDir,\n client: CLIENT_HEADER,\n });\n return {\n version: info.version,\n settings: info.schemas.settings,\n ...(info.exportSchemas ? { exportSchemas: info.exportSchemas } : {}),\n ...(info.exports ? { exports: Object.keys(info.exports) } : {}),\n };\n}\n\n/**\n * Compile a package's settings schema. A schema Ajv cannot compile (an\n * unknown keyword, a bad `$ref`) cannot be checked against, so it is a\n * failed compile the caller reports as a skip, never a crash of the run.\n */\nfunction compileSettings(settings: unknown): CompiledSettings | undefined {\n if (!isObject(settings)) return undefined;\n // Formats such as `uri` and `email` are not bundled with Ajv; ignore them\n // instead of failing to compile. Every structural keyword is still checked.\n const ajv = new Ajv({ allErrors: true, validateFormats: false });\n try {\n return { ok: true, validate: ajv.compile(settings) };\n } catch (error) {\n return { ok: false, message: getErrorMessage(error) };\n }\n}\n\nfunction sourceKey({ packageName, spec }: ResolvedStepPackage): string {\n return spec.path !== undefined\n ? `path:${spec.path}`\n : `${packageName}@${spec.version ?? 'latest'}`;\n}\n\nfunction cachedSchema(\n resolved: ResolvedStepPackage,\n context: SettingsContext,\n): Promise<PackageSchema> {\n const key = sourceKey(resolved);\n let pending = context.cache.get(key);\n if (!pending) {\n pending = loadSchema(resolved, context);\n context.cache.set(key, pending);\n }\n return pending;\n}\n\nfunction cachedCompile(\n resolved: ResolvedStepPackage,\n exportName: string | undefined,\n settings: unknown,\n context: SettingsContext,\n): CompiledSettings | undefined {\n const key = `${sourceKey(resolved)}#${exportName ?? ''}`;\n if (context.compiled.has(key)) return context.compiled.get(key);\n const compiled = compileSettings(settings);\n context.compiled.set(key, compiled);\n return compiled;\n}\n\n/**\n * Check one step's resolved `config.settings` against its package's\n * settings schema, at the version the bundle would install. Findings are\n * returned for the caller to report as errors or warnings; a check that\n * cannot run is a skip; values known only at runtime are deferred.\n */\nexport async function checkStepSettings(\n target: SettingsTarget,\n resolvedFlow: Flow,\n check: ValidateCheck,\n context: SettingsContext,\n): Promise<SettingsOutcome> {\n const at = `flows.${target.flow}.${target.section}.${target.key}`;\n const outcome: SettingsOutcome = { findings: [], skipped: [], deferred: [] };\n const spec =\n typeof target.entry.package === 'string' ? target.entry.package : '';\n if (!spec) {\n outcome.skipped.push({\n path: at,\n check,\n reason: 'No package field, so there is no settings schema to fetch',\n code: 'NO_PACKAGE',\n });\n return outcome;\n }\n\n // The package source the bundle would install for this step.\n let resolved: ResolvedStepPackage;\n try {\n resolved = resolveStepPackage(\n resolvedFlow,\n SECTION_KIND[target.section],\n target.key,\n LOGGER,\n );\n } catch (error) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `Schema for ${spec} could not be loaded: ${getErrorMessage(error)}`,\n code: 'SCHEMA_UNAVAILABLE',\n });\n return outcome;\n }\n const { label } = resolved;\n const pinned =\n resolved.spec.path === undefined ? resolved.spec.version : undefined;\n\n let schema: PackageSchema;\n try {\n schema = await cachedSchema(resolved, context);\n } catch (error) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `Schema for ${label}${pinned ? `@${pinned}` : ''} could not be loaded: ${getErrorMessage(error)}`,\n code: 'SCHEMA_UNAVAILABLE',\n });\n return outcome;\n }\n outcome.checked = {\n path: at,\n package: label,\n ...(schema.version ? { version: schema.version } : {}),\n };\n\n // The export the step imports, resolved as the bundler resolves it.\n const { exportName } = resolveExportName(\n resolved.normalizedFlow,\n SECTION_KIND[target.section],\n target.key,\n );\n const version = schema.version ?? pinned;\n const selection = selectSettingsSchema(\n schema,\n version ? `${label}@${version}` : label,\n exportName,\n );\n if (!selection.ok) {\n outcome.skipped.push({\n path: at,\n check,\n reason: selection.reason,\n code: 'NO_SETTINGS_SCHEMA',\n });\n return outcome;\n }\n\n const compiled = cachedCompile(\n resolved,\n exportName,\n selection.settings,\n context,\n );\n if (!compiled) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `Package ${label} has no settings schema`,\n code: 'NO_SETTINGS_SCHEMA',\n });\n return outcome;\n }\n if (!compiled.ok) {\n outcome.skipped.push({\n path: at,\n check,\n reason: `The settings schema of ${label} cannot be compiled: ${compiled.message}`,\n code: 'SCHEMA_UNAVAILABLE',\n });\n return outcome;\n }\n const validate = compiled.validate;\n\n const step = stepOf(resolvedFlow, target.section, target.key);\n const config = isObject(step?.config) ? step.config : {};\n const prepared = prepareSettings(config.settings ?? {});\n const base = `${at}.config.settings`;\n for (const { segments, reference } of prepared.deferred) {\n outcome.deferred.push({\n path: [base, ...segments].join('.'),\n reference,\n });\n }\n\n if (validate(prepared.value)) return outcome;\n\n for (const e of validate.errors || []) {\n const segments = pointerSegments(e.instancePath);\n if (prepared.deferred.some((d) => isUnder(segments, d.segments))) continue;\n const finding: ValidationError = {\n path: [base, ...segments].join('.'),\n message: describeAjvError(e),\n code: 'ENTRY_SCHEMA',\n keyword: e.keyword,\n };\n if (segments.length > 0) finding.value = valueAt(prepared.value, segments);\n outcome.findings.push(finding);\n }\n return outcome;\n}\n\n/** Ajv's message, naming the offending key where Ajv only reports it in params. */\nfunction describeAjvError(error: ErrorObject): string {\n const message = error.message || 'Unknown error';\n const extra: unknown = error.params.additionalProperty;\n return error.keyword === 'additionalProperties' && typeof extra === 'string'\n ? `${message}: \"${extra}\"`\n : message;\n}\n\nfunction stepOf(\n flow: Flow,\n section: EntrySection,\n key: string,\n): Flow.Step | undefined {\n switch (section) {\n case 'sources':\n return flow.sources?.[key];\n case 'destinations':\n return flow.destinations?.[key];\n case 'transformers':\n return flow.transformers?.[key];\n case 'stores':\n return flow.stores?.[key];\n }\n}\n","/**\n * Shared resolver for the export name to import from a flow component's\n * package.\n *\n * Both `walkeros bundle` and `walkeros setup` need to know which named export\n * (or default) to pull off a package referenced by a flow component. The\n * precedence chain is:\n *\n * 1. `flow.<kind>.<id>.import` if set (named export selector at the step).\n * 2. `flow.config.bundle.packages[<package>].imports[0]` if set.\n * 3. Otherwise undefined; the caller falls back to the package's `default`\n * export.\n *\n * This lives in `cli/src/core/` so both `commands/bundle/*` and\n * `commands/setup/*` import the same function. Duplicating the chain is how\n * setup ended up out of sync with bundle (Bug F: `setup destination.pubsub`\n * routed through BigQuery's setup because it ignored the `code` field and\n * always grabbed `mod.default`).\n */\n\nimport type { Flow, StepType } from '@walkeros/core';\n\nexport type ComponentKind = StepType;\n\nexport type ResolveSource = 'import' | 'imports' | 'default';\n\nexport interface ResolvedExportName {\n /**\n * The named export to pull off the imported module, or `undefined` to\n * indicate the caller should use the package's `default` export.\n */\n exportName: string | undefined;\n /** Which precedence step produced the result (useful for diagnostics). */\n source: ResolveSource;\n}\n\ninterface FlowStepView {\n package?: string;\n import?: string;\n}\n\nfunction getStep(\n flow: Flow,\n kind: ComponentKind,\n id: string,\n): FlowStepView | undefined {\n switch (kind) {\n case 'source':\n return flow.sources?.[id];\n case 'transformer':\n return flow.transformers?.[id];\n case 'destination':\n return flow.destinations?.[id];\n case 'store':\n return flow.stores?.[id];\n }\n}\n\nfunction getBundlePackages(\n flow: Flow,\n): Record<string, Flow.BundlePackage> | undefined {\n return flow.config?.bundle?.packages;\n}\n\n/**\n * Read the first entry of `bundle.packages[<package>].imports` if it is a\n * non-empty string. Returns `undefined` otherwise.\n */\nfunction readFirstImport(\n packages: Record<string, Flow.BundlePackage> | undefined,\n packageName: string | undefined,\n): string | undefined {\n if (!packages || !packageName) return undefined;\n const pkg = packages[packageName];\n if (!pkg || !pkg.imports || pkg.imports.length === 0) return undefined;\n const first = pkg.imports[0];\n return typeof first === 'string' && first.length > 0 ? first : undefined;\n}\n\n/**\n * Resolve the export name to import for a flow component's package.\n *\n * See module docs for the precedence chain. Inline code (`code` is an object)\n * doesn't enter this resolver at all — setup rejects inline code at a higher\n * layer.\n */\nexport function resolveExportName(\n flow: Flow,\n kind: ComponentKind,\n id: string,\n): ResolvedExportName {\n const step = getStep(flow, kind, id);\n if (!step) return { exportName: undefined, source: 'default' };\n\n const packageName = step.package;\n const importName = step.import;\n\n // 1. Explicit `import` field on the step wins.\n if (typeof importName === 'string' && importName.length > 0) {\n return { exportName: importName, source: 'import' };\n }\n\n // 2. Fallback to bundle.packages[pkg].imports[0].\n const firstImport = readFirstImport(getBundlePackages(flow), packageName);\n if (firstImport) {\n return { exportName: firstImport, source: 'imports' };\n }\n\n // 3. Default export.\n return { exportName: undefined, source: 'default' };\n}\n","/**\n * Step-package normalization shared by bundle, setup, validate and the MCP's\n * flow_examples.\n *\n * Pure flow-config logic: which packages do the flow's steps declare, and\n * what is the effective {name, version | path} for each after merging inline\n * step specs with config.bundle.packages pins. Lives in core/ so every\n * command and tool that reads a step's package runs the exact same precedence\n * (a bundle pin always wins; an unversioned bundle entry is filled from the\n * first inline version; conflicting inline versions with no pin throw;\n * local paths become synthetic packageNameToVariable keys).\n */\nimport type { Flow, Logger } from '@walkeros/core';\nimport { packageNameToVariable } from '@walkeros/core';\nimport type { ComponentKind } from './resolve-export-name.js';\n\n/**\n * Type-narrowed accessor for a Flow section. Returns the typed step record\n * (or undefined) — exhaustive switch over the literal-union parameter avoids\n * a generic indexed-access cast.\n *\n * Returns the union of all section types when the caller passes a runtime\n * variable. Call sites that need a specific section type access the field\n * directly (e.g. `flow.sources`).\n */\nexport type FlowStepRecord =\n | Record<string, Flow.Source>\n | Record<string, Flow.Destination>\n | Record<string, Flow.Transformer>\n | Record<string, Flow.Store>;\nexport function getFlowSection(\n flow: Flow,\n section: 'sources' | 'destinations' | 'transformers' | 'stores',\n): FlowStepRecord | undefined {\n switch (section) {\n case 'sources':\n return flow.sources;\n case 'destinations':\n return flow.destinations;\n case 'transformers':\n return flow.transformers;\n case 'stores':\n return flow.stores;\n }\n}\n\n/** A step package spec that is a local path (`./x`, `../x`, `/abs/x`). */\nfunction isLocalPath(spec: string): boolean {\n return spec.startsWith('.') || spec.startsWith('/');\n}\n\n/**\n * Split a step package spec into bare name and optional version suffix.\n * `@walkeros/x@1.2.3` → { name: '@walkeros/x', version: '1.2.3' }.\n * The scope `@` at index 0 is never a separator. A trailing `@` is ignored.\n * Alias/git/file suffixes are not interpreted — the resolver handles or\n * rejects them downstream.\n */\nexport function parsePackageSpec(spec: string): {\n name: string;\n version?: string;\n} {\n const at = spec.lastIndexOf('@');\n if (at <= 0) return { name: spec };\n const version = spec.slice(at + 1);\n return version\n ? { name: spec.slice(0, at), version }\n : { name: spec.slice(0, at) };\n}\n\n/**\n * Detects destination packages from flow configuration.\n * Extracts package names from destinations that have explicit 'package' field.\n */\n/**\n * Detects packages from a flow config section (sources, destinations, transformers, stores).\n * Extracts package names from steps that have an explicit 'package' field.\n */\nexport function detectStepPackages(\n flowSettings: Flow,\n section: 'sources' | 'destinations' | 'transformers' | 'stores',\n): Set<string> {\n const packages = new Set<string>();\n const steps = getFlowSection(flowSettings, section);\n\n if (steps) {\n for (const [, stepConfig] of Object.entries(steps)) {\n if (typeof stepConfig !== 'object' || stepConfig === null) continue;\n // Require explicit package field\n if (typeof stepConfig.package === 'string') {\n packages.add(stepConfig.package);\n }\n }\n }\n\n return packages;\n}\n\n/**\n * Collects all package names declared in flow steps.\n * Returns both npm packages and local paths — caller handles routing.\n */\nexport function collectAllStepPackages(flowSettings: Flow): Set<string> {\n const allPackages = new Set<string>();\n const sections = [\n 'sources',\n 'destinations',\n 'transformers',\n 'stores',\n ] as const;\n\n for (const section of sections) {\n for (const pkg of detectStepPackages(flowSettings, section)) {\n allPackages.add(pkg);\n }\n }\n\n return allPackages;\n}\n\n/**\n * Auto-adds every step-declared package (sources, destinations, transformers,\n * stores) to `packages`, mutating `flowSettings` in place so each step's\n * `package` field points at the key that ends up in `packages`.\n *\n * Local paths (`.` or `/` prefixed) are normalized to a synthetic\n * `packageNameToVariable` key so the regular default-import codegen wires\n * them up automatically.\n *\n * npm specs go through `parsePackageSpec` to split an inline version\n * (`@walkeros/x@1.2.3`) from the bare name. Precedence policy:\n * - An explicit `config.bundle.packages` version always wins; a disagreeing\n * inline version only warns (bundle pin is authoritative).\n * - An unversioned bundle entry is filled from the first inline version seen\n * for that bare name, preserving any other fields already on the entry.\n * - Two different inline versions for the same bare name, with no bundle\n * pin to arbitrate, are ambiguous — throw naming both steps' versions\n * rather than silently picking one.\n * - Identical inline versions across steps are fine (no-op).\n * Alias/git/file suffixes are not special-cased: `parsePackageSpec` splits on\n * the last `@` and the resolver handles or rejects the rest downstream.\n */\nexport function applyStepPackages(\n flowSettings: Flow,\n packages: Record<string, Flow.BundlePackage>,\n logger: Logger.Instance,\n): void {\n const stepPackages = collectAllStepPackages(flowSettings);\n // Bundle-pinned version per bare name, captured the first time each name is\n // encountered — i.e. before this function's own fill-ins can be mistaken\n // for a real `config.bundle.packages` pin on a later iteration.\n const originalPins = new Map<string, boolean>();\n // Inline version already seen per bare name (with no real bundle pin),\n // used to detect a second, different inline version for the same name.\n const inlineSeen = new Map<string, string>();\n\n const rewriteSteps = (from: string, to: string): void => {\n for (const section of [\n 'sources',\n 'destinations',\n 'transformers',\n 'stores',\n ] as const) {\n const steps = getFlowSection(flowSettings, section);\n if (!steps) continue;\n for (const step of Object.values(steps)) {\n if (step.package === from) {\n step.package = to;\n }\n }\n }\n };\n\n for (const pkg of stepPackages) {\n if (isLocalPath(pkg)) {\n // Normalize: convert path-based package: to packages section entry.\n // The synthetic key acts as the package name for downstream codegen,\n // so the regular default-import flow wires it up automatically.\n const varName = packageNameToVariable(pkg);\n if (!packages[varName]) {\n packages[varName] = {\n path: pkg,\n };\n }\n\n // Rewrite all components that reference the raw path to point at the\n // synthetic packages-section key instead.\n rewriteSteps(pkg, varName);\n continue;\n }\n\n const { name, version } = parsePackageSpec(pkg);\n\n if (!originalPins.has(name)) {\n const entry = packages[name];\n // A `path` entry is a pin too: local paths win at resolution, so the\n // inline versions the steps declare can never be used.\n originalPins.set(\n name,\n entry?.version !== undefined || entry?.path !== undefined,\n );\n }\n const hasBundlePin = originalPins.get(name) === true;\n\n if (name !== pkg) {\n // Rewrite every step that declared the versioned spec to the bare\n // name, mirroring the local-path rewrite above.\n rewriteSteps(pkg, name);\n }\n\n if (version && !hasBundlePin) {\n const seen = inlineSeen.get(name);\n if (seen !== undefined && seen !== version) {\n throw new Error(\n `Conflicting inline versions for ${name}: \"${seen}\" and \"${version}\" are ` +\n `declared by different steps. Pin one version in config.bundle.packages.`,\n );\n }\n inlineSeen.set(name, version);\n }\n\n const existing = packages[name];\n if (!existing) {\n packages[name] = version ? { version } : {};\n } else if (version && !existing.path) {\n // A path entry decides on its own; stamping a version onto it would\n // record a version that resolution never consults.\n if (!existing.version) {\n existing.version = version; // fill an unversioned bundle entry\n } else if (existing.version !== version) {\n logger.warn(\n `Package ${name}: config.bundle.packages pins ${existing.version}; ` +\n `a step declares ${version} inline. Using the bundle pin.`,\n );\n }\n }\n }\n}\n\nexport interface ResolvedStepPackage {\n /** Bare package name after normalization (inline version stripped, local path replaced by synthetic key). */\n packageName: string;\n /** The package as messages and results name it: the bare npm name, or a step's own local path as written. */\n label: string;\n /** Effective bundle entry: version pin and/or local path per bundle precedence. */\n spec: Flow.BundlePackage;\n /** flow.config.bundle.overrides, for transitive pins. */\n overrides: Record<string, string>;\n /** Deep clone of the flow with step.package rewritten to bare names; the input flow is never mutated. */\n normalizedFlow: Flow;\n}\n\nconst KIND_TO_SECTION = {\n source: 'sources',\n transformer: 'transformers',\n destination: 'destinations',\n store: 'stores',\n} as const;\n\n/**\n * Resolve the effective package spec for one step by running the SAME\n * normalization the bundler runs over the whole flow (applyStepPackages),\n * on a clone so the caller's flow stays untouched. The clone comes back as\n * `normalizedFlow` with `step.package` rewritten to the bare name, which is\n * also what resolveExportName needs to match `bundle.packages[pkg].imports`.\n */\nexport function resolveStepPackage(\n flow: Flow,\n kind: ComponentKind,\n id: string,\n logger: Logger.Instance,\n): ResolvedStepPackage {\n const normalizedFlow = structuredClone(flow);\n const packages: Record<string, Flow.BundlePackage> = structuredClone(\n normalizedFlow.config?.bundle?.packages ?? {},\n );\n applyStepPackages(normalizedFlow, packages, logger);\n\n const bucket = getFlowSection(normalizedFlow, KIND_TO_SECTION[kind]);\n const step = bucket?.[id];\n if (!step) {\n const available = Object.keys(bucket ?? {}).join(', ') || '(none)';\n throw new Error(\n `${kind} \"${id}\" not found in flow. Available: ${available}`,\n );\n }\n if (typeof step.package !== 'string') {\n throw new Error(\n `${kind}.${id} has no \"package\" (inline code cannot be set up).`,\n );\n }\n\n const declared = getFlowSection(flow, KIND_TO_SECTION[kind])?.[id]?.package;\n return {\n packageName: step.package,\n label:\n declared !== undefined && isLocalPath(declared) ? declared : step.package,\n spec: packages[step.package] ?? {},\n overrides: normalizedFlow.config?.bundle?.overrides ?? {},\n normalizedFlow,\n };\n}\n","/**\n * The walkerOS metadata of a step's package, read from the source\n * resolveStepPackage picked: a local path from its walkerOS.json on disk,\n * any other package from the CDN at the pinned version. Every tool that has\n * a flow reads a step's package here, so all of them read the same source\n * the bundle installs.\n *\n * Node built-ins only: this module is reachable from the `dev` and\n * `validate` entries, which a server process loads without the bundler or\n * the package manager.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { WalkerOSPackage } from '@walkeros/core';\nimport { fetchPackage, isObject, parseWalkerOSJson } from '@walkeros/core';\nimport type { ResolvedStepPackage } from './step-packages.js';\n\n/** A package's walkerOS.json content, plus the version read (absent for a local package whose package.json names none). */\nexport type StepPackageInfo = Omit<\n WalkerOSPackage,\n 'packageName' | 'version' | 'description'\n> & {\n version?: string;\n};\n\nfunction readJson(file: string): unknown {\n return JSON.parse(fs.readFileSync(file, 'utf-8'));\n}\n\n/**\n * Read the walkerOS metadata of a resolved step package. A local path\n * resolves against `configDir`; without one nothing is read from disk and\n * the read fails. `client` names the calling tool in the CDN request.\n */\nexport async function readStepPackageInfo(\n resolved: Pick<ResolvedStepPackage, 'packageName' | 'spec'>,\n options: { configDir?: string; client: string },\n): Promise<StepPackageInfo> {\n const { packageName, spec } = resolved;\n if (spec.path === undefined)\n return fetchPackage(packageName, {\n ...(spec.version ? { version: spec.version } : {}),\n client: options.client,\n });\n\n if (options.configDir === undefined)\n throw new Error(\n 'it is a local path package and the input is not a file, so there is no directory to read it from',\n );\n const root = path.isAbsolute(spec.path)\n ? spec.path\n : path.resolve(options.configDir, spec.path);\n const file = [\n path.join(root, 'dist', 'walkerOS.json'),\n path.join(root, 'walkerOS.json'),\n ].find((candidate) => fs.existsSync(candidate));\n if (!file) throw new Error(`no walkerOS.json under ${root}`);\n const meta = readJson(file);\n if (!isObject(meta)) throw new Error(`${file} is not a JSON object`);\n const pkgFile = path.join(root, 'package.json');\n const pkg = fs.existsSync(pkgFile) ? readJson(pkgFile) : undefined;\n const version =\n isObject(pkg) && typeof pkg.version === 'string' ? pkg.version : undefined;\n return { ...parseWalkerOSJson(meta), ...(version ? { version } : {}) };\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/mapping.ts\n\nimport { isObject } from '@walkeros/core';\nimport { schemas } from '@walkeros/core/dev';\nimport type {\n ValidateDetails,\n ValidateResult,\n ValidationError,\n ValidationWarning,\n} from '../types.js';\n\nconst { RuleSchema } = schemas.MappingSchemas;\n\nfunction isRule(value: unknown): boolean {\n return isObject(value) && RuleSchema.safeParse(value).success;\n}\n\n/**\n * The nested runtime shape: an entity key whose value maps actions to a rule\n * or a list of rules (`{ page: { view: { name: 'page_view' } } }`).\n */\nfunction isNestedActions(\n key: string,\n value: unknown,\n): value is Record<string, unknown> {\n if (key.includes(' ') || !isObject(value)) return false;\n const rules = Object.values(value);\n return (\n rules.length > 0 &&\n rules.every((rule) =>\n Array.isArray(rule)\n ? rule.length > 0 && rule.every(isRule)\n : isRule(rule),\n )\n );\n}\n\nexport function validateMapping(input: unknown): ValidateResult {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n const details: ValidateDetails = {};\n\n // Must be an object\n if (!isObject(input)) {\n errors.push({\n path: 'root',\n message: 'Mapping must be an object with event patterns as keys',\n code: 'INVALID_MAPPING_TYPE',\n });\n return { valid: false, type: 'mapping', errors, warnings, details };\n }\n\n const mapping = input;\n const nestedPatterns: string[] = [];\n const flatPatterns: string[] = [];\n for (const [key, value] of Object.entries(mapping)) {\n if (isNestedActions(key, value)) {\n for (const action of Object.keys(value))\n nestedPatterns.push(`${key} ${action}`);\n } else {\n flatPatterns.push(key);\n }\n }\n const patterns = [...nestedPatterns, ...flatPatterns];\n details.eventPatterns = patterns;\n details.patternCount = patterns.length;\n\n // Validate each flat \"entity action\" pattern (legacy flat shape)\n flatPatterns.forEach((pattern, index) => {\n // Check pattern format: must be \"entity action\", contain wildcard, or be \"*\"\n const isWildcard = pattern.includes('*');\n const hasSpace = pattern.includes(' ');\n\n if (!isWildcard && !hasSpace) {\n errors.push({\n path: pattern,\n message: `Invalid event pattern \"${pattern}\". Must be \"entity action\" format or contain wildcard (*)`,\n code: 'INVALID_EVENT_PATTERN',\n });\n }\n\n // Warn if catch-all is not last\n if (pattern === '*' && index !== flatPatterns.length - 1) {\n warnings.push({\n path: '*',\n code: 'CATCH_ALL_NOT_LAST',\n message: 'Catch-all pattern (*) should be last',\n suggestion:\n 'Move the catch-all pattern (*) to last position for predictable matching',\n });\n }\n\n // Validate rule structure (accepts single rule object or array of rules)\n const rule = mapping[pattern];\n const isValidRule = Array.isArray(rule)\n ? rule.every((r) => typeof r === 'object' && r !== null)\n : typeof rule === 'object' && rule !== null;\n\n if (!isValidRule) {\n errors.push({\n path: pattern,\n message: 'Mapping rule must be an object or array of objects',\n code: 'INVALID_RULE_TYPE',\n });\n }\n });\n\n return {\n valid: errors.length === 0,\n type: 'mapping',\n errors,\n warnings,\n details,\n };\n}\n","// walkerOS/packages/cli/src/commands/validate/validators/entry.ts\n\nimport { describeScope, isFlowJson, resolveScope } from '../scope.js';\nimport type {\n ValidateDeferred,\n ValidateResult,\n ValidateSkip,\n ValidationError,\n} from '../types.js';\nimport { resolveFlow, type FlowResolution } from './resolve.js';\nimport {\n checkStepSettings,\n createSettingsContext,\n type CheckedPackage,\n} from './settings.js';\n\n/**\n * Validate an entry (source, destination, transformer or store) addressed by\n * `path` (`section.key` or `key`) against its package's published JSON\n * Schema, in every flow that has it, or only in `options.flow`. Settings are\n * checked as the flow resolves them (the placeholder rule); a flow that does\n * not resolve is an error and its entry check a skip.\n */\nexport async function validateEntry(\n path: string,\n flowConfig: unknown,\n options: { flow?: string; configDir?: string } = {},\n): Promise<ValidateResult> {\n const resolved = resolveScope(flowConfig, { flow: options.flow, path });\n const errors: ValidationError[] = [...resolved.errors];\n const skipped: ValidateSkip[] = [];\n const deferred: ValidateDeferred[] = [];\n const packages: CheckedPackage[] = [];\n const context = createSettingsContext(options.configDir);\n const file = isFlowJson(flowConfig) ? flowConfig : undefined;\n const resolutions = new Map<string, FlowResolution>();\n\n for (const target of resolved.entries) {\n const at = `flows.${target.flow}.${target.section}.${target.key}`;\n let resolution = resolutions.get(target.flow);\n if (!resolution && file) {\n resolution = resolveFlow(file, target.flow);\n resolutions.set(target.flow, resolution);\n if (!resolution.ok) errors.push(resolution.error);\n }\n if (!resolution?.ok) {\n skipped.push({\n path: at,\n check: 'entry:settings',\n reason: `flows.${target.flow} does not resolve, so its settings cannot be checked`,\n code: 'GATED_BY_ERRORS',\n });\n continue;\n }\n const outcome = await checkStepSettings(\n target,\n resolution.flow,\n 'entry:settings',\n context,\n );\n errors.push(...outcome.findings);\n skipped.push(...outcome.skipped);\n deferred.push(...outcome.deferred);\n if (outcome.checked) packages.push(outcome.checked);\n }\n\n return {\n valid: errors.length === 0,\n type: 'entry',\n errors,\n warnings: [],\n details: {\n scope: describeScope(resolved, ['entry:settings']),\n skipped,\n deferred,\n ...(packages.length > 0 ? { packages } : {}),\n },\n };\n}\n","// walkerOS/packages/cli/src/commands/validate/types.ts\n\nimport type {\n ValidateResult as CoreValidateResult,\n ValidationError as CoreValidationError,\n ValidationType,\n ValidationWarning as CoreValidationWarning,\n} from '@walkeros/core/dev';\n\n// Base result types are declared in @walkeros/core/dev beside\n// validateFlowStructure, so consumers can type its return without the CLI.\n// The CLI narrows them: every code comes from VALIDATE_CODES, and the\n// details carry the scope, skips and deferred values of the run.\nexport type { ValidateResultType, ValidationType } from '@walkeros/core/dev';\n\n/**\n * Every code an error, warning or skip of `validate` can carry. Agents and\n * tests branch on these, never on message text.\n */\nexport const VALIDATE_CODES = [\n // Input and options\n 'INPUT_ERROR',\n 'SERIALIZATION_ERROR',\n 'OPTION_NOT_APPLICABLE',\n // Scope (--flow, --path)\n 'FLOW_NOT_FOUND',\n 'ENTRY_NOT_FOUND',\n 'AMBIGUOUS_ENTRY',\n 'UNSUPPORTED_SECTION',\n 'INVALID_PATH',\n // Flow file\n 'SCHEMA_VALIDATION',\n 'CONFIG_WARNING',\n 'EMPTY_FLOWS',\n 'UNKNOWN_KEY',\n 'CONFLICT',\n 'MISSING_PACKAGE',\n 'OBSOLETE_CODE_STRING',\n 'INVALID_IMPORT',\n 'INVALID_CODE_SHAPE',\n 'PACKAGE_VERSION_MISSING',\n 'DEPRECATED_PACKAGE',\n 'UNKNOWN_ROUTE_TARGET',\n 'ROUTE_DEAD_CODE',\n 'ROUTE_EMPTY_MANY',\n 'ROUTE_SINGLE_MANY',\n 'ROUTE_FIRST_MATCH',\n 'INCOMPATIBLE_EXAMPLES',\n 'EXAMPLES_INCOMPLETE',\n 'CONTRACT_VIOLATION',\n 'MAPPING_DOT_KEY',\n 'FLOW_REF_WARNING',\n 'WEB_LOGGER_FORMAT',\n 'FLOW_CYCLE',\n 'VAR_CYCLE',\n // Structure (the bundler's own preflight, validateFlowStructure)\n 'INVALID_COMPONENT_NAME',\n 'INVALID_REFERENCE',\n 'STORE_REFERENCE_NOT_FOUND',\n 'STATE_STORE_UNKNOWN',\n 'STATE_STORE_FILE',\n 'MAPPING_NO_OP',\n 'MAPPING_VALUE_NO_OPERATOR',\n 'MAPPING_VALUE_PRODUCERS',\n 'BATCH_WITHOUT_PUSH_BATCH',\n 'MALFORMED_REFERENCE',\n // Resolution (getFlowSettings, as the bundler resolves the flow)\n 'UNRESOLVED_REFERENCE',\n 'MISSING_ENV',\n 'WEB_SECRET_REF',\n 'UNRESOLVED_CONTRACT',\n // Entry settings against the package schema\n 'ENTRY_SCHEMA',\n // Contract\n 'NO_CONTRACT_SECTION',\n 'INVALID_CONTRACT',\n 'FLAT_CONTRACT_SHAPE',\n 'INVALID_CONTRACT_ENTRY',\n 'UNKNOWN_CONTRACT_KEY',\n 'INVALID_TAGGING',\n 'INVALID_SCHEMA',\n 'INVALID_EXTENDS',\n 'INVALID_EVENTS',\n 'CIRCULAR_EXTENDS',\n 'INVALID_ENTITY_KEY',\n 'INVALID_ENTITY',\n 'INVALID_ACTION_KEY',\n 'INVALID_SCHEMA_ENTRY',\n // Event\n 'NOT_AN_OBJECT',\n 'MISSING_EVENT_NAME',\n 'EMPTY_EVENT_NAME',\n 'INVALID_EVENT_NAME',\n // Mapping\n 'INVALID_MAPPING_TYPE',\n 'INVALID_EVENT_PATTERN',\n 'INVALID_RULE_TYPE',\n 'CATCH_ALL_NOT_LAST',\n // Skips (details.skipped[].code)\n 'GATED_BY_ERRORS',\n 'CONTRACT_NOT_STATIC',\n 'NO_PACKAGE',\n 'NO_SETTINGS_SCHEMA',\n 'SCHEMA_UNAVAILABLE',\n] as const;\n\nexport type ValidateCode = (typeof VALIDATE_CODES)[number];\n\n/** Codes that mean validate could not run as asked (CLI exit 3). */\nexport const USAGE_CODES: readonly ValidateCode[] = ['OPTION_NOT_APPLICABLE'];\n\n/**\n * Named checks a run includes (`details.scope.checks`). `file:` checks run\n * on the whole file whatever `--flow` says; `flow:` checks run per flow in\n * scope; `entry:` checks run per entry `--path` addressed.\n */\nexport type ValidateCheck =\n | 'file:schema'\n | 'file:contract'\n | 'flow:schema'\n | 'flow:steps'\n | 'flow:structure'\n | 'flow:package-versions'\n | 'flow:routes'\n | 'flow:examples'\n | 'flow:contract-examples'\n | 'flow:mapping-keys'\n | 'flow:resolve'\n | 'flow:package-settings'\n | 'entry:settings'\n | 'contract'\n | 'event'\n | 'mapping';\n\nexport interface ValidateScopeEntry {\n /** Section of the addressed entry, when the path or the match names one. */\n section?: string;\n key: string;\n /** Flows that have the entry and were checked. */\n flows: string[];\n /** Flows searched for the entry. */\n searchedFlows: string[];\n}\n\n/** What a run covered (C6). */\nexport interface ValidateScope {\n flows: string[];\n entry?: ValidateScopeEntry;\n checks: ValidateCheck[];\n /** Set when `offline` removed the package settings check from `checks`. */\n offline?: true;\n}\n\n/** A check the scope includes that did not run (C5). */\nexport interface ValidateSkip {\n path: string;\n check: ValidateCheck;\n reason: string;\n code: ValidateCode;\n}\n\n/** A value known only at runtime, not schema-checked (info, not a skip). */\nexport interface ValidateDeferred {\n path: string;\n reference: string;\n}\n\nexport interface ValidateDetails {\n scope?: ValidateScope;\n skipped?: ValidateSkip[];\n deferred?: ValidateDeferred[];\n [key: string]: unknown;\n}\n\n// `code` stays core's `string` for consumers; validate emits only\n// ValidateCode values.\nexport interface ValidationError extends CoreValidationError {\n /** The JSON Schema keyword of an ENTRY_SCHEMA error. */\n keyword?: string;\n}\n\nexport interface ValidationWarning extends CoreValidationWarning {\n keyword?: string;\n}\n\nexport interface ValidateResult extends CoreValidateResult {\n errors: ValidationError[];\n warnings: ValidationWarning[];\n details: ValidateDetails;\n}\n\nexport interface ValidateCommandOptions {\n type: ValidationType;\n input?: string;\n output?: string;\n flow?: string; // Flow name for multi-flow configs\n path?: string; // Entry path for dot-notation validation (e.g., \"destinations.snowplow\")\n json?: boolean;\n verbose?: boolean;\n strict?: boolean;\n /** Skip every package settings check (no network); named in the scope. */\n offline?: boolean;\n silent?: boolean;\n}\n"],"mappings":"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","names":["fs","path","chalk","isObject","fs","fs","fs","fs","fs","z","z","z","z","z","schemas","fs","fs","isObject","validateEvent","checkWindowCollector","ENV_MARKER_PREFIX","isObject","SECRET_MARKER_PREFIX","schemas","validateFlowStructure","isObject","path","getFlowSettings","getErrorMessage","getErrorMessage","isObject","fs","path","isObject","isObject","getErrorMessage","validate","validateFlowConfig","schemas","isObject","path","validateFlowConfig","result","validateFlowStructure","ENV_MARKER_PREFIX","checkWindowCollector","SECRET_MARKER_PREFIX","isObject","schemas","path","isObject","validateEvent"]}