@coldsmirk/abacus-core 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/engine/errors.ts","../src/internal/predicates.ts","../src/engine/loader.ts","../src/internal/env.ts","../src/condition/compile.ts","../src/condition/types.ts","../src/engine/evaluate.ts","../src/engine/messages.ts","../src/engine/intellisense.ts"],"sourcesContent":["/**\n * Error raised when the ZEN engine fails to load or an expression cannot be\n * evaluated. The original failure is preserved on {@link cause} and the\n * offending expression (if any) on {@link expression}.\n */\nexport class ExpressionError extends Error {\n readonly expression?: string;\n\n constructor(message: string, expression?: string, cause?: unknown) {\n super(message, { cause });\n this.name = \"ExpressionError\";\n this.expression = expression;\n }\n}\n\n/**\n * Raised by the synchronous evaluation helpers when the engine has not finished\n * initializing yet. Await {@link loadEngine} (or render under\n * `<ExpressionEngineProvider>`) before evaluating synchronously.\n */\nexport class ExpressionNotReadyError extends ExpressionError {\n constructor(\n message = \"Expression engine is not initialized. Await loadEngine() or render under <ExpressionEngineProvider>.\"\n ) {\n super(message);\n this.name = \"ExpressionNotReadyError\";\n }\n}\n","/**\n * Minimal internal type predicates. Inlined so the engine stays dependency-free\n * (no shared-utility package), keeping `@coldsmirk/abacus-core` framework- and\n * ecosystem-agnostic.\n */\n\nexport function isUndefined(value: unknown): value is undefined {\n return value === undefined;\n}\n\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nexport function isArray(value: unknown): value is unknown[] {\n return Array.isArray(value);\n}\n\nexport function isNullish(value: unknown): value is null | undefined {\n return value === null || value === undefined;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import type { InitInput, VariableType, VariableTypeJson } from \"@gorules/zen-engine-wasm\";\n\nimport type { ExpressionAnalysis, ExpressionType, ExpressionTypeSpan } from \"./intellisense\";\n\nimport { isUndefined } from \"../internal/predicates\";\nimport { ExpressionError, ExpressionNotReadyError } from \"./errors\";\n\n/**\n * The data an expression reads from. Property paths in the expression (e.g.\n * `customer.name`) resolve against this object.\n */\nexport type ExpressionContext = Record<string, unknown>;\n\nexport interface LoadEngineOptions {\n /**\n * Override how the wasm binary is located. By default the engine resolves the\n * co-located `.wasm` through the GoRules package's own `import.meta.url`,\n * which the host bundler (Vite / webpack) rewrites to a served asset URL — no\n * option is needed in normal browser app setups. Supply a URL / Response /\n * bytes for exotic hosting (CDN, embedded buffer) or for a non-browser host\n * (Node / SSR), where `import.meta.url` asset resolution does not work and the\n * input must be provided explicitly. Aliased to the wasm initializer's own\n * `InitInput` so it never drifts from the dependency.\n */\n wasmInput?: InitInput;\n}\n\n/**\n * The initialized ZEN expression engine. Every method is synchronous — obtain\n * an instance via {@link loadEngine} (async, loads the wasm) before calling.\n */\nexport interface ExpressionEngine {\n /**\n * Evaluate a standard ZEN expression, returning its computed value.\n */\n evaluate: <T = unknown>(expression: string, context?: ExpressionContext) => T;\n /**\n * Evaluate a ZEN unary (test) expression, returning a boolean.\n */\n evaluateUnary: (expression: string, context?: ExpressionContext) => boolean;\n /**\n * Validate a standard expression; returns ZEN's diagnostic payload (`null`\n * when the expression parses, an error object otherwise).\n */\n validate: (expression: string) => unknown;\n /**\n * Validate a unary expression; returns ZEN's diagnostic payload.\n */\n validateUnary: (expression: string) => unknown;\n /**\n * Return ZEN's completion metadata, for building an expression editor.\n */\n getCompletions: () => unknown;\n /**\n * Type-check `source` against a `variables` context, returning the root context\n * type and the inferred type of every span (`unary` selects test-expression\n * checking). Powers an editor's type-aware completion / hover / diagnostics.\n */\n analyze: (variables: ExpressionType, source: string, unary: boolean) => ExpressionAnalysis;\n /**\n * Whether `actual` satisfies (is assignable to) `expected`. Powers\n * expected-return-type validation in an editor.\n */\n satisfies: (actual: ExpressionType, expected: ExpressionType) => boolean;\n /**\n * Whether the underlying wasm module reports itself ready.\n */\n isReady: () => boolean;\n}\n\ninterface TypeContext {\n variables: ExpressionType;\n handle: VariableType;\n rootKind: ExpressionType;\n}\n\nlet enginePromise: Promise<ExpressionEngine> | null = null;\nlet engineSync: ExpressionEngine | null = null;\nlet engineError: ExpressionError | null = null;\nlet configuredInput: InitInput | undefined;\n// The compiled variable context, cached by `variables` reference (see analyze).\nlet typeContextCache: TypeContext | null = null;\n\nfunction evaluateSafely<T>(expression: string, run: () => T): T {\n try {\n return run();\n } catch (error) {\n throw new ExpressionError(`Failed to evaluate expression: ${expression}`, expression, error);\n }\n}\n\nfunction loadFailureMessage(): string {\n const base = \"Failed to load the ZEN expression engine\";\n\n // typeof, not a property read: the standard environment probe that survives\n // both hosts without a DOM and lint rewrites of global-object aliases.\n if (typeof window === \"undefined\") {\n return `${base}. In a non-browser or non-DOM host (Node, SSR, Web Worker) the wasm cannot be auto-resolved; call configureEngine({ wasmInput }) with the wasm bytes or URL before loading.`;\n }\n\n return base;\n}\n\n/**\n * Configure how the wasm binary is located, before the engine loads. Must be\n * called before the first {@link loadEngine} (or any evaluation), so that the\n * configured input is the one actually used — calling it after the engine has\n * started loading throws, rather than silently taking no effect.\n */\nexport function configureEngine(options: LoadEngineOptions): void {\n if (enginePromise || engineSync) {\n throw new ExpressionError(\"configureEngine() must be called before the engine loads.\");\n }\n\n configuredInput = options.wasmInput;\n}\n\n/**\n * Load and initialize the ZEN expression engine exactly once. Concurrent and\n * subsequent calls share the same in-flight promise / resolved instance. Set a\n * custom wasm source up front with {@link configureEngine}.\n *\n * The GoRules wasm dependency is reached via a dynamic `import()` on purpose: it\n * keeps this module usable from the package's CommonJS build (the dep is\n * ESM-only, so a static `require` would throw) and defers the multi-megabyte\n * wasm download until the engine is actually needed. Do NOT convert this to a\n * static import.\n *\n * A failed load is not cached: the rejected promise is dropped (the error is\n * latched on {@link getEngineError} for inspection), so calling `loadEngine()`\n * again retries from scratch. This is the clear-and-reload retry primitive after\n * a transient failure — an error boundary should call it before resetting.\n */\nexport function loadEngine(): Promise<ExpressionEngine> {\n if (enginePromise) {\n return enginePromise;\n }\n\n engineError = null;\n enginePromise = (async () => {\n const zen = await import(\"@gorules/zen-engine-wasm\");\n\n await zen.default(isUndefined(configuredInput) ? undefined : { module_or_path: configuredInput });\n\n // The wasm `VariableTypeJson` omits \"Date\" — a kind the ZEN engine itself\n // emits and accepts — so ExpressionType is a deliberate superset. This is the\n // single wasm-boundary cast that bridges the two.\n const toVariableType = (type: ExpressionType): VariableType => zen.VariableType.fromJson(type as VariableTypeJson);\n\n // Cache the compiled VariableType and its rootKind by `variables` reference:\n // both are document-independent, so per-keystroke type-checking reuses them\n // instead of rebuilding the wasm handle on every edit (the editor targets 100s\n // of fields). A changed reference frees the previous handle before rebuilding;\n // `typeContextCache` is nulled first so a throw never leaves a freed handle\n // cached, and a `toJson()` throw frees the freshly built handle it would\n // otherwise strand (built but neither cached nor freed).\n const ensureTypeContext = (variables: ExpressionType): TypeContext => {\n if (typeContextCache && typeContextCache.variables === variables) {\n return typeContextCache;\n }\n\n const stale = typeContextCache;\n typeContextCache = null;\n stale?.handle.free();\n const handle = toVariableType(variables);\n\n try {\n typeContextCache = {\n variables,\n handle,\n rootKind: handle.toJson()\n };\n } catch (error) {\n handle.free();\n throw error;\n }\n\n return typeContextCache;\n };\n\n const engine: ExpressionEngine = Object.freeze({\n evaluate: <T = unknown>(expression: string, context: ExpressionContext = {}): T => evaluateSafely(expression, () => zen.evaluateExpression(expression, context) as T),\n evaluateUnary: (expression: string, context: ExpressionContext = {}): boolean => evaluateSafely(expression, () => zen.evaluateUnaryExpression(expression, context)),\n validate: (expression: string): unknown => zen.validateExpression(expression),\n validateUnary: (expression: string): unknown => zen.validateUnaryExpression(expression),\n getCompletions: (): unknown => zen.getCompletions(),\n analyze: (variables: ExpressionType, source: string, unary: boolean): ExpressionAnalysis => {\n const context = ensureTypeContext(variables);\n // `typeCheck` is typed `any` by the wasm bindings; gate it to `unknown`, then\n // assert the element shape only once confirmed an array. The shape is fixed by\n // the pinned zen-engine-wasm version, so one boundary assertion is sound.\n const rawSpans: unknown = unary ? context.handle.typeCheckUnary(source) : context.handle.typeCheck(source);\n\n return {\n rootKind: context.rootKind,\n spans: Array.isArray(rawSpans) ? rawSpans as ExpressionTypeSpan[] : []\n };\n },\n // Acquire both handles leak-safely: the second acquisition lives inside the\n // first's try so a throw from it still frees the first (wasm handles are not\n // GC-managed). A portable equivalent of `using` without relying on Node 22's\n // explicit-resource-management runtime support.\n satisfies: (actual: ExpressionType, expected: ExpressionType): boolean => {\n const actualType = toVariableType(actual);\n\n try {\n const expectedType = toVariableType(expected);\n\n try {\n return actualType.satisfies(expectedType);\n } finally {\n expectedType.free();\n }\n } finally {\n actualType.free();\n }\n },\n isReady: (): boolean => zen.isReady()\n });\n\n engineSync = engine;\n return engine;\n })().catch((error: unknown) => {\n // Drop the rejected promise so a later imperative call can retry the load,\n // but remember the failure so the React provider can surface it to an error\n // boundary instead of re-suspending on a fresh load forever.\n enginePromise = null;\n engineError = new ExpressionError(loadFailureMessage(), undefined, error);\n throw engineError;\n });\n\n return enginePromise;\n}\n\n/**\n * Whether the engine has finished initializing and is ready for sync use.\n */\nexport function isEngineReady(): boolean {\n return engineSync?.isReady() ?? false;\n}\n\n/**\n * The error from the last failed {@link loadEngine} attempt, or `null`. Used by\n * the React provider to surface a wasm-load failure to an error boundary rather\n * than suspending forever, and by imperative pollers to tell \"failed\" apart from\n * \"still loading\". Cleared when a new load starts or {@link resetEngine}.\n */\nexport function getEngineError(): ExpressionError | null {\n return engineError;\n}\n\n/**\n * Return the already-initialized engine synchronously, or throw\n * {@link ExpressionNotReadyError} if {@link loadEngine} has not resolved yet.\n */\nexport function getEngineSync(): ExpressionEngine {\n if (!engineSync) {\n throw new ExpressionNotReadyError();\n }\n\n return engineSync;\n}\n\n/**\n * Reset the engine singleton — drops the loaded engine, the cached type context,\n * the latched error, and the configured wasm input. Mainly for tests, but also the\n * way to re-run {@link configureEngine} after a load (configure throws once the\n * engine has started loading).\n */\nexport function resetEngine(): void {\n typeContextCache?.handle.free();\n typeContextCache = null;\n enginePromise = null;\n engineSync = null;\n engineError = null;\n configuredInput = undefined;\n}\n","/**\n * Best-effort development-mode flag for diagnostic-only code paths.\n *\n * Detected from `process.env.NODE_ENV` (defined by Node, test runners, webpack,\n * and Vite's SSR/`define`). The `typeof` guard is load-bearing: in a plain\n * browser bundle `process` is not declared at all, and any bare reference would\n * throw a ReferenceError while this module is being imported. Hosts without a\n * `process` default to \"production\" — the safe choice, since `isDev` only gates\n * extra developer-facing warnings.\n */\nexport const isDev: boolean = detectDev();\n\nfunction detectDev(): boolean {\n if (typeof process === \"undefined\") {\n return false;\n }\n\n return process.env ? process.env.NODE_ENV !== \"production\" : false;\n}\n","import type { ExpressionContext, ExpressionEngine } from \"../engine/loader\";\nimport type {\n BranchSelection,\n ConditionBranchInput,\n ConditionGroupInput,\n ConditionInput,\n ConditionOperator\n} from \"./types\";\n\nimport { ExpressionError } from \"../engine/errors\";\nimport { loadEngine } from \"../engine/loader\";\nimport { isDev } from \"../internal/env\";\nimport { isArray, isNullish, isString } from \"../internal/predicates\";\n\n/**\n * A subject must be a plain identifier path (`amount`, `user.age`, `items[0]`).\n * It is emitted verbatim into ZEN source, so anything else is rejected to keep\n * the condition compiler from being an expression-injection sink.\n */\nconst SUBJECT_PATTERN = /^[A-Z_$][\\w$]*(?:\\.[A-Z_$][\\w$]*|\\[\\d+\\])*$/i;\n\n// ZEN's operator and literal keywords. The path pattern alone would accept them\n// as identifiers, compiling well-formed nonsense like `true == 5` or `not > 1`\n// that evaluates without error and silently never (or always) matches; a path\n// segment hitting one degrades the condition to null like any invalid subject.\nconst ZEN_RESERVED_WORDS = new Set([\"and\", \"or\", \"not\", \"in\", \"true\", \"false\", \"null\"]);\n\nfunction isIdentifierPath(subject: string): boolean {\n return SUBJECT_PATTERN.test(subject)\n && (subject.match(/[A-Z_$][\\w$]*/gi) ?? []).every(segment => !ZEN_RESERVED_WORDS.has(segment));\n}\n\n/**\n * Serialize a JavaScript value into a ZEN literal. Nullish becomes `null`;\n * numbers / booleans / bigints are emitted verbatim; strings are quoted via\n * {@link encodeZenString}; arrays become `[a, b, ...]`.\n *\n * Throws {@link ExpressionError} for a value with no faithful ZEN\n * representation — an object, symbol, or function, or a string containing both\n * quote styles. Callers that need a sentinel instead of a throw go through\n * {@link compileCondition}, which degrades such a value to a non-compiling\n * (null) condition.\n */\nexport function toZenLiteral(value: unknown): string {\n if (isNullish(value)) {\n return \"null\";\n }\n\n if (typeof value === \"number\") {\n // NaN / Infinity stringify to bare identifiers (\"NaN\", \"Infinity\") that ZEN\n // resolves to null, silently corrupting comparisons — reject them so the value\n // degrades to a null (non-compiling) condition like other unrepresentable ones.\n if (!Number.isFinite(value)) {\n throw new ExpressionError(`Number ${String(value)} has no ZEN literal representation`);\n }\n\n return String(value);\n }\n\n if (typeof value === \"boolean\" || typeof value === \"bigint\") {\n return String(value);\n }\n\n if (isString(value)) {\n return encodeZenString(value);\n }\n\n if (isArray(value)) {\n return `[${value.map(item => toZenLiteral(item)).join(\", \")}]`;\n }\n\n throw new ExpressionError(`Value of type \"${typeof value}\" has no ZEN literal representation`);\n}\n\n/**\n * Encode a string as a ZEN literal. ZEN string literals are **raw** between\n * matching quotes and honor no backslash escapes (`'a\\nb'` is the four\n * characters `a \\ n b`), so the encoder must not escape — it picks a delimiter\n * the value does not contain. A value containing both quote styles cannot be\n * represented as a raw ZEN literal and throws.\n */\nfunction encodeZenString(value: string): string {\n const hasSingle = value.includes(\"'\");\n const hasDouble = value.includes(\"\\\"\");\n\n if (hasSingle && hasDouble) {\n throw new ExpressionError(\"String contains both single and double quotes and has no ZEN literal representation\");\n }\n\n return hasSingle ? `\"${value}\"` : `'${value}'`;\n}\n\nfunction toArrayLiteral(value: unknown): string {\n return isArray(value) ? toZenLiteral(value) : `[${toZenLiteral(value)}]`;\n}\n\n/**\n * Emit the ZEN emptiness test matching the backend field evaluator's\n * `isEmptyValue`: null, blank (whitespace-only) text, and empty arrays are\n * empty; numbers and booleans never are. ZEN's `or` short-circuits, so the\n * type-guarded branches never evaluate against a null subject. The backend\n * additionally treats an empty map as empty for totality, but form values —\n * the only subjects this compiler targets — are never objects, and ZEN's\n * `len()` does not accept one.\n */\nfunction zenIsEmpty(subject: string): string {\n return `(${subject} == null`\n + ` or (type(${subject}) == 'string' and len(trim(${subject})) == 0)`\n + ` or (type(${subject}) == 'array' and len(${subject}) == 0))`;\n}\n\nfunction compileFieldCondition(subject: string, operator: ConditionOperator, value: unknown): string {\n switch (operator) {\n case \"eq\": {\n return `${subject} == ${toZenLiteral(value)}`;\n }\n\n case \"ne\": {\n return `${subject} != ${toZenLiteral(value)}`;\n }\n\n case \"gt\": {\n return `${subject} > ${toZenLiteral(value)}`;\n }\n\n case \"gte\": {\n return `${subject} >= ${toZenLiteral(value)}`;\n }\n\n case \"lt\": {\n return `${subject} < ${toZenLiteral(value)}`;\n }\n\n case \"lte\": {\n return `${subject} <= ${toZenLiteral(value)}`;\n }\n\n case \"contains\": {\n return `contains(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"not_contains\": {\n return `not contains(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"starts_with\": {\n return `startsWith(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"ends_with\": {\n return `endsWith(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"in\": {\n return `${subject} in ${toArrayLiteral(value)}`;\n }\n\n case \"not_in\": {\n return `not (${subject} in ${toArrayLiteral(value)})`;\n }\n\n case \"is_empty\": {\n return zenIsEmpty(subject);\n }\n\n case \"is_not_empty\": {\n return `not ${zenIsEmpty(subject)}`;\n }\n\n default: {\n // Exhaustiveness guard: adding a ConditionOperator without a case here is a\n // compile error. The throw only covers runtime-invalid data forced past the\n // type with a cast, and is caught by compileCondition.\n operator satisfies never;\n throw new ExpressionError(`Unsupported operator: ${String(operator)}`);\n }\n }\n}\n\n/**\n * Compile a single condition into a ZEN boolean expression. Field conditions map\n * their operator to ZEN; expression conditions are wrapped in parentheses to\n * preserve their grouping. Returns `null` when the condition is empty, its\n * subject is not an identifier path, or its value has no ZEN representation.\n */\nexport function compileCondition(condition: ConditionInput): string | null {\n if (condition.kind === \"expression\") {\n const expression = condition.expression.trim();\n // Parenthesize: a raw expression with a top-level `or` / ternary would be\n // regrouped by ZEN precedence once a group joins parts with ` and ` (which\n // binds tighter than `or`), silently selecting the wrong branch.\n return expression === \"\" ? null : `(${expression})`;\n }\n\n const subject = condition.subject.trim();\n\n if (!isIdentifierPath(subject)) {\n return null;\n }\n\n try {\n return compileFieldCondition(subject, condition.operator, condition.value);\n } catch {\n // A value with no ZEN representation (object value, both-quote string) or a\n // cast-in invalid operator makes the condition uncompilable; degrade it to\n // null like an invalid subject so the group simply drops it.\n return null;\n }\n}\n\n/**\n * Compile a condition group (its conditions joined with AND). Returns `null`\n * when the group has no compilable conditions.\n */\nexport function compileGroup(group: ConditionGroupInput): string | null {\n const parts = group.conditions\n .map(condition => compileCondition(condition))\n .filter(part => part !== null);\n\n return parts.length === 0 ? null : parts.join(\" and \");\n}\n\n/**\n * Compile a branch's condition groups into a single ZEN expression (groups\n * joined with OR). Returns `null` when the branch has no compilable groups\n * (e.g. a default branch).\n */\nexport function compileBranch(branch: ConditionBranchInput): string | null {\n const groups = (branch.conditionGroups ?? [])\n .map(group => compileGroup(group))\n .filter(group => group !== null);\n\n return groups.length === 0 ? null : groups.map(group => `(${group})`).join(\" or \");\n}\n\n/**\n * Pick the matching branch for the given context using a pre-loaded engine.\n * Non-default branches are tested in ascending `priority` order; the first whose\n * compiled expression evaluates to `true` wins. Falls back to the default\n * branch, or a `null` id when neither matches.\n *\n * A branch whose expression throws for the given context (e.g. ZEN's `>`\n * throws when the subject is missing or non-numeric) is treated as not matching\n * rather than propagating — so a missing field degrades to the default branch\n * instead of crashing the caller.\n */\nexport function selectBranchWith(\n branches: readonly ConditionBranchInput[],\n context: ExpressionContext,\n engine: Pick<ExpressionEngine, \"evaluate\" | \"validate\">\n): BranchSelection {\n const ordered = branches.toSorted((a, b) => a.priority - b.priority);\n\n for (const branch of ordered) {\n if (branch.isDefault) {\n continue;\n }\n\n const expression = compileBranch(branch);\n\n if (expression === null) {\n // A non-default branch that compiles to nothing can never match — almost\n // always a configuration bug (mistyped subject, unrepresentable value)\n // that would otherwise surface only as \"always falls through to default\".\n if (isDev) {\n console.warn(`[expression] branch \"${branch.id}\" has no compilable condition and can never match`);\n }\n\n continue;\n }\n\n if (evaluatesTrue(engine, expression, context)) {\n return { branchId: branch.id, matched: true };\n }\n }\n\n const fallback = ordered.find(branch => branch.isDefault);\n return { branchId: fallback?.id ?? null, matched: false };\n}\n\nfunction evaluatesTrue(\n engine: Pick<ExpressionEngine, \"evaluate\" | \"validate\">,\n expression: string,\n context: ExpressionContext\n): boolean {\n try {\n return engine.evaluate(expression, context) === true;\n } catch (error) {\n if (isDev) {\n reportEvaluationFailure(engine, expression, error);\n }\n\n return false;\n }\n}\n\nfunction reportEvaluationFailure(\n engine: Pick<ExpressionEngine, \"validate\">,\n expression: string,\n error: unknown\n): void {\n // A compiled branch expression that fails to PARSE signals a bug in this\n // package's own emitter, not a runtime type mismatch (ZEN's `>` on a missing\n // field, the intended degrade-to-default path). validate() returns null for a\n // parsable expression and a diagnostic object otherwise, so surface only the\n // former — a real authoring/emitter bug should not hide behind the fallback.\n let diagnostic: unknown;\n\n try {\n diagnostic = engine.validate(expression);\n } catch {\n return;\n }\n\n if (!isNullish(diagnostic)) {\n console.warn(`[expression] compiled branch expression failed to parse: ${expression}`, diagnostic, error);\n }\n}\n\n/**\n * Pick the matching branch for the given context, loading the ZEN engine on\n * first use. See {@link selectBranchWith} for the selection semantics.\n */\nexport async function selectBranch(\n branches: readonly ConditionBranchInput[],\n context: ExpressionContext\n): Promise<BranchSelection> {\n const engine = await loadEngine();\n return selectBranchWith(branches, context, engine);\n}\n","/**\n * Structural shapes for the visual condition model compiled to ZEN. They mirror\n * the condition types used in the form and flow editors so a host can map those\n * definitions to {@link compileCondition} / {@link selectBranch} without this\n * package depending on either editor. The editors keep a flat working model for\n * form ergonomics (a row can hold both a half-typed field triple and an\n * expression while the author toggles between them); this discriminated input is\n * the narrowed, compiler-facing shape where each kind carries only what it uses.\n */\n\n/**\n * The closed operator vocabulary understood by {@link compileCondition}, as a\n * runtime constant so validators can build allow-lists from it instead of\n * re-declaring the set. The {@link ConditionOperator} type derives from this\n * array — one definition site for both the type and the runtime list.\n */\nexport const CONDITION_OPERATORS = [\n \"eq\",\n \"ne\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"contains\",\n \"not_contains\",\n \"starts_with\",\n \"ends_with\",\n \"in\",\n \"not_in\",\n \"is_empty\",\n \"is_not_empty\"\n] as const;\n\n/**\n * Operators understood by {@link compileCondition}. The compiler maps each to a\n * ZEN expression; this closed set is the single source of truth for the operator\n * vocabulary (the flow editor shares it instead of re-declaring its own).\n */\nexport type ConditionOperator = typeof CONDITION_OPERATORS[number];\n\n/**\n * A field/operator/value condition.\n */\nexport interface FieldConditionInput {\n kind: \"field\";\n /**\n * The field path the operator tests. Emitted **verbatim** into the compiled\n * ZEN source as a path expression (guarded by an identifier-path pattern that\n * also rejects ZEN reserved words such as `true` or `not` as segments), unlike\n * `value`, which is serialized to a ZEN literal — so callers must supply a\n * valid identifier path, not arbitrary user text.\n */\n subject: string;\n operator: ConditionOperator;\n value: unknown;\n}\n\n/**\n * A raw ZEN expression condition, passed through to the engine verbatim.\n */\nexport interface ExpressionConditionInput {\n kind: \"expression\";\n expression: string;\n}\n\n/**\n * A single condition: either a field/operator/value triple or a raw expression.\n */\nexport type ConditionInput = FieldConditionInput | ExpressionConditionInput;\n\n/**\n * A group of conditions, combined with AND.\n */\nexport interface ConditionGroupInput {\n conditions: readonly ConditionInput[];\n}\n\n/**\n * A branch guarded by one or more condition groups (combined with OR).\n */\nexport interface ConditionBranchInput {\n id: string;\n priority: number;\n isDefault?: boolean;\n conditionGroups?: readonly ConditionGroupInput[];\n}\n\n/**\n * The branch chosen by {@link selectBranch}. `matched` is true only when a\n * non-default branch's expression evaluated true (then `branchId` is that\n * branch's id). On the default-branch fallback `matched` is false while\n * `branchId` is the default's id; `branchId` is null only when nothing matched\n * and there is no default branch.\n */\nexport type BranchSelection\n = | { matched: true; branchId: string }\n | { matched: false; branchId: string | null };\n","import type { ExpressionContext } from \"./loader\";\n\nimport { getEngineSync, loadEngine } from \"./loader\";\n\n/**\n * Evaluate a standard ZEN expression, loading the engine on first use.\n *\n * `T` is an **unchecked** assertion — the value is returned as `T` with no\n * runtime validation, and ZEN's result type depends on the expression and\n * context. Prefer the `unknown` default and narrow at the call site.\n */\nexport async function evaluate<T = unknown>(expression: string, context?: ExpressionContext): Promise<T> {\n await loadEngine();\n return evaluateSync<T>(expression, context);\n}\n\n/**\n * Evaluate a ZEN unary (test) expression, loading the engine on first use.\n */\nexport async function evaluateUnary(expression: string, context?: ExpressionContext): Promise<boolean> {\n await loadEngine();\n return evaluateUnarySync(expression, context);\n}\n\n/**\n * Evaluate a standard ZEN expression synchronously. Throws\n * {@link ExpressionNotReadyError} when the engine has not loaded yet — use this\n * only behind a readiness gate (e.g. under `<ExpressionEngineProvider>`).\n */\nexport function evaluateSync<T = unknown>(expression: string, context?: ExpressionContext): T {\n return getEngineSync().evaluate<T>(expression, context);\n}\n\n/**\n * Evaluate a ZEN unary (test) expression synchronously. Throws\n * {@link ExpressionNotReadyError} when the engine has not loaded yet.\n */\nexport function evaluateUnarySync(expression: string, context?: ExpressionContext): boolean {\n return getEngineSync().evaluateUnary(expression, context);\n}\n","/**\n * The locales the library ships with out of the box.\n */\nexport type BuiltInExpressionLocale = \"en-US\" | \"zh-CN\";\n\n/**\n * A locale key for editor-produced text (built-in descriptions, type-check prose,\n * and diagnostic origin labels — the wasm engine's own error bodies, identifiers,\n * and type signatures are never translated). The built-in locales surface in\n * autocomplete, while any string is accepted so a host can select a locale\n * registered through {@link registerExpressionLocale}.\n */\nexport type ExpressionLocale = BuiltInExpressionLocale | (string & {});\n\n/**\n * The catalog of editor-produced messages for one locale. All localized text the\n * editor surfaces flows through this interface, so a host can switch language or\n * override individual strings in one typed place — and add a language the library\n * does not ship by passing a full `messages` object to\n * {@link configureExpressionMessages}.\n */\nexport interface ExpressionMessages {\n /**\n * The diagnostic origin label for a wasm error `type` (`\"parserError\"` →\n * `\"Parser error\"`), falling back to a generic label for an unknown type.\n */\n sourceLabel: (type?: string) => string;\n /**\n * Translate a built-in's English `info` description to this locale. Returns the\n * input unchanged for English or when no translation exists.\n */\n completionInfo: (englishInfo: string) => string;\n /**\n * The `source` label shown on type-check (as opposed to syntax) diagnostics.\n */\n typeCheckSource: string;\n /**\n * Warning shown when a unary (test) expression does not evaluate to a boolean.\n */\n expectedBoolean: (actualType: string) => string;\n /**\n * Warning shown when a standard expression's result type does not satisfy the\n * configured expected type.\n */\n expectedType: (expectedType: string, actualType: string) => string;\n}\n\n// Built-in descriptions, keyed by the engine's English `info` text rather than by\n// label. The builtin catalog is finite and the descriptions are stable, while\n// labels collide (the function `year` and the Date method `year` share a name but\n// carry different descriptions) — keying on the description sidesteps that and\n// degrades gracefully (an unmapped/renamed builtin falls through to English).\nconst COMPLETION_INFO_ZH: Record<string, string> = {\n // String / array / object functions\n \"Returns the length of variable\": \"返回变量的长度\",\n \"Checks if variable contains a needle\": \"检查变量是否包含指定元素\",\n \"Flattens an array\": \"将数组扁平化\",\n \"Merges multiple objects into one.\": \"将多个对象合并为一个。\",\n \"Deeply merges multiple objects into one.\": \"将多个对象深度合并为一个。\",\n \"Converts all characters in a string to uppercase\": \"将字符串中所有字符转换为大写\",\n \"Converts all characters in a string to lowercase\": \"将字符串中所有字符转换为小写\",\n \"Returns the string with leading and trailing whitespace removed\": \"返回去除首尾空白后的字符串\",\n \"Returns true if the string starts with the specified prefix\": \"若字符串以指定前缀开头则返回 true\",\n \"Returns true if the string ends with the specified suffix\": \"若字符串以指定后缀结尾则返回 true\",\n \"Returns true if the string matches the specified pattern\": \"若字符串匹配指定模式则返回 true\",\n \"Extracts matching substrings according to a pattern\": \"按模式提取匹配的子串\",\n \"Performs a fuzzy search of the needle in the haystack, and returns the match score(s).\": \"在目标中对关键字进行模糊搜索,并返回匹配得分。\",\n \"Splits a string into an array of substrings using the specified delimiter.\": \"使用指定分隔符将字符串拆分为子串数组。\",\n\n // Math / aggregation functions\n \"Returns the absolute value of a number\": \"返回数字的绝对值\",\n \"Returns the sum of all elements in the input array.\": \"返回输入数组中所有元素之和。\",\n \"Calculates the average of all elements in the input array.\": \"计算输入数组中所有元素的平均值。\",\n \"Returns the smallest of the elements in the input array.\": \"返回输入数组中的最小元素。\",\n \"Returns the largest of the elements in the input array.\": \"返回输入数组中的最大元素。\",\n \"Generates a random number between 0 (inclusive) and max (inclusive).\": \"生成 0 到 max(均包含)之间的随机数。\",\n \"Calculates the median value of all elements in the input array.\": \"计算输入数组中所有元素的中位数。\",\n \"Finds the mode(s) of the input array, which are the most frequent element(s).\": \"求输入数组的众数,即出现最频繁的元素。\",\n \"Rounds a number down to the nearest integer.\": \"向下取整到最接近的整数。\",\n \"Rounds a number up to the nearest integer.\": \"向上取整到最接近的整数。\",\n \"Rounds a number to a specified number of decimal places.\": \"将数字四舍五入到指定的小数位数。\",\n \"Truncates a number to a specified number of decimal places.\": \"将数字截断到指定的小数位数。\",\n\n // Type / conversion functions\n \"Checks if the given value is of a numeric type.\": \"检查给定值是否为数值类型。\",\n \"Converts the given value to a string.\": \"将给定值转换为字符串。\",\n \"Converts the given value to a number.\": \"将给定值转换为数字。\",\n \"Converts the given value to a boolean.\": \"将给定值转换为布尔值。\",\n \"Returns a string representing the data type of the value.\": \"返回表示该值数据类型的字符串。\",\n \"Returns an array of a given object's own enumerable property names.\": \"返回由给定对象自身可枚举属性名组成的数组。\",\n \"Returns an array of a given object's own enumerable property values.\": \"返回由给定对象自身可枚举属性值组成的数组。\",\n\n // Date / time functions\n \"Returns a new date time instance.\": \"返回一个新的日期时间实例。\",\n \"Converts a numeric timestamp to a unix timestamp.\": \"将数值时间戳转换为 Unix 时间戳。\",\n \"Extracts the time from a numeric timestamp and returns it as a seconds from beginning of day.\": \"从数值时间戳中提取时间,以当天起始的秒数返回。\",\n \"e.g. 1h30min\": \"例如 1h30min\",\n \"Extracts the year from a given timestamp.\": \"从给定时间戳中提取年份。\",\n \"Gets the day of the week from a given timestamp, where Sunday might be 0.\": \"获取给定时间戳的星期几(周日可能为 0)。\",\n \"Extracts the day of the month from a given timestamp.\": \"从给定时间戳中提取当月的日期。\",\n \"Gets the day of the year from a given timestamp.\": \"获取给定时间戳在一年中的第几天。\",\n \"Calculates the week of the year from a given timestamp.\": \"计算给定时间戳在一年中的第几周。\",\n \"Extracts the month from a given timestamp, typically with January as 1.\": \"从给定时间戳中提取月份(通常 1 月为 1)。\",\n \"Converts the month from a given timestamp into its string representation (e.g., 'Jan').\": \"将给定时间戳的月份转换为字符串表示(例如 'Jan')。\",\n \"Converts a timestamp to a human-readable date string.\": \"将时间戳转换为人类可读的日期字符串。\",\n \"Converts the day of the week from a given timestamp into its string representation (e.g., 'Mon').\": \"将给定时间戳的星期几转换为字符串表示(例如 'Mon')。\",\n \"Returns the timestamp representing the start of a specified unit (e.g., day, month, year) based on a given timestamp.\": \"返回给定时间戳在指定单位(如日、月、年)起始处的时间戳。\",\n \"Returns the timestamp representing the end of a specified unit (e.g., day, month, year) based on a given timestamp.\": \"返回给定时间戳在指定单位(如日、月、年)结束处的时间戳。\",\n\n // Higher-order array functions\n \"Checks if all elements in the array satisfy the condition defined in the callback.\": \"检查数组中所有元素是否都满足回调中定义的条件。\",\n \"Checks if no elements in the array satisfy the condition defined in the callback.\": \"检查数组中是否没有任何元素满足回调中定义的条件。\",\n \"Checks if at least one element in the array satisfies the condition defined in the callback.\": \"检查数组中是否至少有一个元素满足回调中定义的条件。\",\n \"Checks if exactly one element in the array satisfies the condition defined in the callback.\": \"检查数组中是否恰好有一个元素满足回调中定义的条件。\",\n \"Creates a new array with all elements that satisfy the condition defined in the callback.\": \"创建一个仅包含满足回调中定义条件的元素的新数组。\",\n \"Creates a new array populated with the results of calling the provided function on every element in the calling array.\": \"创建一个新数组,其元素为对原数组每个元素调用所提供函数的结果。\",\n \"First maps each element using a mapping function, then flattens the result into a new array.\": \"先用映射函数处理每个元素,再将结果扁平化为新数组。\",\n \"Counts the number of elements in the array that satisfy the condition defined in the callback.\": \"统计数组中满足回调中定义条件的元素个数。\",\n\n // Date methods\n \"Adds time to a date\": \"为日期增加时间\",\n \"Subtracts time from a date\": \"从日期中减去时间\",\n \"Sets a specific unit of time on a date\": \"设置日期的某个时间单位\",\n \"Formats a date into a string representation\": \"将日期格式化为字符串\",\n \"Returns the start of a specified time unit for a date\": \"返回日期在指定时间单位上的起始\",\n \"Returns the end of a specified time unit for a date\": \"返回日期在指定时间单位上的结束\",\n \"Calculates the difference between two dates\": \"计算两个日期之间的差值\",\n \"Converts a date to a different timezone\": \"将日期转换到不同的时区\",\n \"Checks if two dates are the same\": \"检查两个日期是否相同\",\n \"Checks if a date is before another date\": \"检查日期是否早于另一个日期\",\n \"Checks if a date is after another date\": \"检查日期是否晚于另一个日期\",\n \"Checks if a date is the same as or before another date\": \"检查日期是否等于或早于另一个日期\",\n \"Checks if a date is the same as or after another date\": \"检查日期是否等于或晚于另一个日期\",\n \"Gets the seconds of a date\": \"获取日期的秒\",\n \"Gets the minutes of a date\": \"获取日期的分钟\",\n \"Gets the hours of a date\": \"获取日期的小时\",\n \"Gets the day of the month for a date\": \"获取日期在当月的第几天\",\n \"Gets the day of the year for a date\": \"获取日期在当年的第几天\",\n \"Gets the week of the year for a date\": \"获取日期在当年的第几周\",\n \"Gets the day of the week for a date\": \"获取日期的星期几\",\n \"Gets the month for a date\": \"获取日期的月份\",\n \"Gets the quarter for a date\": \"获取日期所在的季度\",\n \"Gets the year for a date\": \"获取日期的年份\",\n \"Gets the Unix timestamp for a date\": \"获取日期的 Unix 时间戳\",\n \"Gets the timezone offset name for a date\": \"获取日期的时区偏移名称\",\n \"Checks if a date is valid\": \"检查日期是否有效\",\n \"Checks if a date is yesterday\": \"检查日期是否为昨天\",\n \"Checks if a date is today\": \"检查日期是否为今天\",\n \"Checks if a date is tomorrow\": \"检查日期是否为明天\",\n \"Checks if the year of a date is a leap year\": \"检查日期所在年份是否为闰年\"\n};\n\nconst EN_SOURCE_LABELS: Record<string, string> = {\n lexerError: \"Lexer error\",\n parserError: \"Parser error\",\n compilerError: \"Compiler error\",\n vmError: \"VM error\"\n};\n\nconst ZH_SOURCE_LABELS: Record<string, string> = {\n lexerError: \"词法错误\",\n parserError: \"语法错误\",\n compilerError: \"编译错误\",\n vmError: \"运行时错误\"\n};\n\nfunction sourceLabelFrom(table: Record<string, string>, fallback: string, type: string | undefined): string {\n return (type === undefined ? undefined : table[type]) ?? fallback;\n}\n\n/**\n * Built-in English message catalog (the default).\n */\nexport const enMessages: ExpressionMessages = {\n sourceLabel: type => sourceLabelFrom(EN_SOURCE_LABELS, \"Error\", type),\n completionInfo: info => info,\n typeCheckSource: \"Type check\",\n expectedBoolean: actualType => `Expected a boolean test expression, received \\`${actualType}\\`.`,\n expectedType: (expectedType, actualType) => `Expected \\`${expectedType}\\`, received \\`${actualType}\\`.`\n};\n\n/**\n * Built-in Simplified Chinese message catalog.\n */\nexport const zhCNMessages: ExpressionMessages = {\n sourceLabel: type => sourceLabelFrom(ZH_SOURCE_LABELS, \"错误\", type),\n completionInfo: info => info === \"\" ? \"\" : COMPLETION_INFO_ZH[info] ?? info,\n typeCheckSource: \"类型检查\",\n expectedBoolean: actualType => `期望布尔测试表达式,实际类型为 \\`${actualType}\\`。`,\n expectedType: (expectedType, actualType) => `期望 \\`${expectedType}\\`,实际为 \\`${actualType}\\`。`\n};\n\n// The locale registry. Built-ins are pre-registered; a host adds its own through\n// registerExpressionLocale, so a new language is a first-class, selectable locale\n// rather than a special case — the design stays open as more languages are added.\nconst localeRegistry = new Map<string, ExpressionMessages>([\n [\"en-US\", enMessages],\n [\"zh-CN\", zhCNMessages]\n]);\n\n// Module-global active catalog. The sync editor accessors (completion / lint) read\n// it without a React context, mirroring the engine singleton itself.\nlet activeMessages: ExpressionMessages = enMessages;\n\n/**\n * Register (or replace) the message catalog for a locale key, making it selectable\n * via {@link configureExpressionMessages}. This is how a host adds a language the\n * library does not ship — the built-in locales are registered the same way, so\n * there is no privileged path.\n */\nexport function registerExpressionLocale(locale: string, messages: ExpressionMessages): void {\n localeRegistry.set(locale, messages);\n}\n\nexport interface ConfigureMessagesOptions {\n /**\n * A registered locale to use as the base — built-in or registered through\n * {@link registerExpressionLocale}. An unknown key keeps the current base; omit\n * to keep the current base.\n */\n locale?: ExpressionLocale;\n /**\n * Per-message overrides merged over the base — a quick way to tweak a few strings\n * without registering a whole locale.\n */\n messages?: Partial<ExpressionMessages>;\n}\n\n/**\n * Configure the active message catalog. Pass a `locale`, a partial `messages`\n * override, or both (overrides win). Idempotent and module-global, so a host\n * configures it once (e.g. through `ExpressionConfigProvider`).\n */\nexport function configureExpressionMessages({ locale, messages }: ConfigureMessagesOptions): void {\n const base = locale === undefined ? activeMessages : localeRegistry.get(locale) ?? activeMessages;\n activeMessages = messages === undefined ? base : { ...base, ...messages };\n}\n\n/**\n * The active {@link ExpressionMessages} catalog (English by default).\n */\nexport function getExpressionMessages(): ExpressionMessages {\n return activeMessages;\n}\n","import { isRecord } from \"../internal/predicates\";\nimport { getEngineSync, loadEngine } from \"./loader\";\nimport { getExpressionMessages } from \"./messages\";\n\n/**\n * Whether an expression is a standard value expression or a unary (test) expression.\n */\nexport type ExpressionMode = \"standard\" | \"unary\";\n\n/**\n * A ZEN type descriptor — the shape the engine understands for type-aware\n * completion and validation: a primitive kind name or one of the structural\n * variants `{ Const }`, `{ Enum }`, `{ Array }`, `{ Object }`.\n *\n * Defined here rather than aliasing the wasm `VariableTypeJson` because the\n * engine also emits and accepts `\"Date\"` — its sole method-receiver kind — which\n * that type omits. Aliasing it would make a Date-typed variable unrepresentable\n * and break `kind === \"Date\"` narrowing; the structural variants mirror the wasm\n * shape exactly so a value crosses the boundary unchanged.\n */\nexport type ExpressionType\n = | \"Any\"\n | \"Null\"\n | \"Bool\"\n | \"String\"\n | \"Number\"\n | \"Date\"\n | { Const: string }\n | { Enum: [string | undefined, string[]] }\n | { Array: ExpressionType }\n | { Object: Record<string, ExpressionType> };\n\n/**\n * A single syntax / type diagnostic, positioned by character offset.\n */\nexport interface ExpressionDiagnostic {\n /**\n * Start offset in the source.\n */\n from: number;\n /**\n * End offset in the source.\n */\n to: number;\n /**\n * Human-readable error message.\n */\n message: string;\n /**\n * Diagnostic origin label (e.g. `\"Parser error\"`).\n */\n source: string;\n}\n\n/**\n * An autocomplete suggestion for a ZEN built-in (function / method / variable).\n */\nexport interface ExpressionCompletion {\n type: \"function\" | \"method\" | \"variable\";\n label: string;\n detail: string;\n info: string;\n boost: number | null;\n /**\n * For methods, the type-kind they attach to (e.g. `\"Date\"`); otherwise `null`.\n */\n methodFor: string | null;\n}\n\n/**\n * The inferred type of one span of the source, produced by type-checking.\n */\nexport interface ExpressionTypeSpan {\n error: string | null;\n kind: ExpressionType;\n nodeKind: string;\n span: [number, number];\n}\n\n/**\n * The result of type-checking an expression against a variable context.\n */\nexport interface ExpressionAnalysis {\n /**\n * The root context type (the variables object).\n */\n rootKind: ExpressionType;\n /**\n * Per-span inferred types; `spans[0]` is the whole-expression result type.\n */\n spans: ExpressionTypeSpan[];\n}\n\n// Strict offset parsing: Number() rejects partial-numeric text (\"12:30\") that\n// parseInt would silently truncate to 12, and the empty-string guard covers\n// Number(\"\") being 0 rather than NaN.\nfunction parseOffset(text: string | undefined): number {\n const trimmed = text?.trim();\n return trimmed ? Number(trimmed) : NaN;\n}\n\n/**\n * Parse a trailing `... at (from, to)` / `... at pos` position out of a ZEN error\n * message. Returns a `[from, to]` range — a single offset collapses to\n * `[pos, pos]` — or `null` when no position is present.\n */\nexport function extractPosition(message: string): [number, number] | null {\n const segments = message.split(\" at \");\n const last = segments.length <= 1 ? undefined : segments.at(-1);\n\n if (last === undefined) {\n return null;\n }\n\n const [left, right] = last.replace(\"(\", \"\").replace(\")\", \"\").split(\", \");\n const from = parseOffset(left);\n\n if (Number.isNaN(from)) {\n return null;\n }\n\n const to = parseOffset(right);\n return [from, Number.isNaN(to) ? from : to];\n}\n\n/**\n * Normalize the wasm validate payload (`null` or `{ type, source }`) into a\n * positioned {@link ExpressionDiagnostic}, or `null` when the expression is valid.\n */\nexport function normalizeDiagnostic(raw: unknown, source: string): ExpressionDiagnostic | null {\n if (raw === null || raw === undefined) {\n return null;\n }\n\n const errorType = isRecord(raw) && typeof raw.type === \"string\" ? raw.type : undefined;\n const message = isRecord(raw) && typeof raw.source === \"string\" ? raw.source : String(raw);\n const [from, to] = extractPosition(message) ?? [0, source.length];\n\n return {\n from,\n to,\n message,\n source: getExpressionMessages().sourceLabel(errorType)\n };\n}\n\n/**\n * Normalize the wasm `getCompletions` payload into a typed list, dropping\n * malformed entries and stripping the backtick markers ZEN wraps type names in.\n */\nexport function normalizeCompletions(raw: unknown): ExpressionCompletion[] {\n if (!Array.isArray(raw)) {\n return [];\n }\n\n const messages = getExpressionMessages();\n\n return raw.flatMap((entry): ExpressionCompletion[] => {\n if (!isRecord(entry) || typeof entry.label !== \"string\" || entry.label === \"\") {\n return [];\n }\n\n return [\n {\n type: entry.type === \"method\" || entry.type === \"variable\" ? entry.type : \"function\",\n label: entry.label,\n detail: typeof entry.detail === \"string\" ? entry.detail.replaceAll(\"`\", \"\") : \"\",\n info: messages.completionInfo(typeof entry.info === \"string\" ? entry.info : \"\"),\n boost: typeof entry.boost === \"number\" ? entry.boost : null,\n methodFor: typeof entry.methodFor === \"string\" ? entry.methodFor : null\n }\n ];\n });\n}\n\n/**\n * Validate an expression, loading the engine on first use. Resolves to `null`\n * when the expression is valid, or a positioned diagnostic otherwise.\n */\nexport async function getDiagnostics(expression: string, mode: ExpressionMode): Promise<ExpressionDiagnostic | null> {\n await loadEngine();\n return getDiagnosticsSync(expression, mode);\n}\n\n/**\n * Synchronous {@link getDiagnostics}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet — use only behind a readiness gate.\n */\nexport function getDiagnosticsSync(expression: string, mode: ExpressionMode): ExpressionDiagnostic | null {\n const engine = getEngineSync();\n return normalizeDiagnostic(mode === \"unary\" ? engine.validateUnary(expression) : engine.validate(expression), expression);\n}\n\n/**\n * Return the ZEN built-in completion list, loading the engine on first use.\n */\nexport async function getCompletionItems(): Promise<ExpressionCompletion[]> {\n await loadEngine();\n return getCompletionItemsSync();\n}\n\n/**\n * Synchronous {@link getCompletionItems}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet.\n */\nexport function getCompletionItemsSync(): ExpressionCompletion[] {\n return normalizeCompletions(getEngineSync().getCompletions());\n}\n\n/**\n * Type-check an expression against a `variables` context, loading the engine on\n * first use. See {@link ExpressionAnalysis}.\n */\nexport async function analyzeTypes(\n variables: ExpressionType,\n source: string,\n mode: ExpressionMode\n): Promise<ExpressionAnalysis> {\n await loadEngine();\n return analyzeTypesSync(variables, source, mode);\n}\n\n/**\n * Synchronous {@link analyzeTypes}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet.\n */\nexport function analyzeTypesSync(variables: ExpressionType, source: string, mode: ExpressionMode): ExpressionAnalysis {\n return getEngineSync().analyze(variables, source, mode === \"unary\");\n}\n\n/**\n * Whether `actual` satisfies (is assignable to) `expected`, loading the engine on\n * first use. Used for expected-return-type validation.\n */\nexport async function satisfiesType(actual: ExpressionType, expected: ExpressionType): Promise<boolean> {\n await loadEngine();\n return satisfiesTypeSync(actual, expected);\n}\n\n/**\n * Synchronous {@link satisfiesType}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet.\n */\nexport function satisfiesTypeSync(actual: ExpressionType, expected: ExpressionType): boolean {\n return getEngineSync().satisfies(actual, expected);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/engine/errors.ts","../src/internal/predicates.ts","../src/engine/loader.ts","../src/internal/env.ts","../src/condition/subject.ts","../src/condition/compile.ts","../src/condition/types.ts","../src/condition/compile-tree.ts","../src/condition/lift-tree.ts","../src/condition/tree-types.ts","../src/engine/evaluate.ts","../src/engine/messages.ts","../src/engine/intellisense.ts","../src/engine/template.ts"],"sourcesContent":["/**\n * Error raised when the ZEN engine fails to load or an expression cannot be\n * evaluated. The original failure is preserved on {@link cause} and the\n * offending expression (if any) on {@link expression}.\n */\nexport class ExpressionError extends Error {\n readonly expression?: string;\n\n constructor(message: string, expression?: string, cause?: unknown) {\n super(message, { cause });\n this.name = \"ExpressionError\";\n this.expression = expression;\n }\n}\n\n/**\n * Raised by the synchronous evaluation helpers when the engine has not finished\n * initializing yet. Await {@link loadEngine} (or render under\n * `<ExpressionEngineProvider>`) before evaluating synchronously.\n */\nexport class ExpressionNotReadyError extends ExpressionError {\n constructor(\n message = \"Expression engine is not initialized. Await loadEngine() or render under <ExpressionEngineProvider>.\"\n ) {\n super(message);\n this.name = \"ExpressionNotReadyError\";\n }\n}\n","/**\n * Minimal internal type predicates. Inlined so the engine stays dependency-free\n * (no shared-utility package), keeping `@coldsmirk/abacus-core` framework- and\n * ecosystem-agnostic.\n */\n\nexport function isUndefined(value: unknown): value is undefined {\n return value === undefined;\n}\n\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nexport function isArray(value: unknown): value is unknown[] {\n return Array.isArray(value);\n}\n\nexport function isNullish(value: unknown): value is null | undefined {\n return value === null || value === undefined;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import type { InitInput, VariableType, VariableTypeJson } from \"@gorules/zen-engine-wasm\";\n\nimport type { ExpressionAnalysis, ExpressionType, ExpressionTypeSpan } from \"./intellisense\";\n\nimport { isUndefined } from \"../internal/predicates\";\nimport { ExpressionError, ExpressionNotReadyError } from \"./errors\";\n\n/**\n * The data an expression reads from. Property paths in the expression (e.g.\n * `customer.name`) resolve against this object.\n */\nexport type ExpressionContext = Record<string, unknown>;\n\nexport interface LoadEngineOptions {\n /**\n * Override how the wasm binary is located. By default the engine resolves the\n * co-located `.wasm` through the GoRules package's own `import.meta.url`,\n * which the host bundler (Vite / webpack) rewrites to a served asset URL — no\n * option is needed in normal browser app setups. Supply a URL / Response /\n * bytes for exotic hosting (CDN, embedded buffer) or for a non-browser host\n * (Node / SSR), where `import.meta.url` asset resolution does not work and the\n * input must be provided explicitly. Aliased to the wasm initializer's own\n * `InitInput` so it never drifts from the dependency.\n */\n wasmInput?: InitInput;\n}\n\n/**\n * The initialized ZEN expression engine. Every method is synchronous — obtain\n * an instance via {@link loadEngine} (async, loads the wasm) before calling.\n */\nexport interface ExpressionEngine {\n /**\n * Evaluate a standard ZEN expression, returning its computed value.\n */\n evaluate: <T = unknown>(expression: string, context?: ExpressionContext) => T;\n /**\n * Evaluate a ZEN unary (test) expression, returning a boolean.\n */\n evaluateUnary: (expression: string, context?: ExpressionContext) => boolean;\n /**\n * Validate a standard expression; returns ZEN's diagnostic payload (`null`\n * when the expression parses, an error object otherwise).\n */\n validate: (expression: string) => unknown;\n /**\n * Validate a unary expression; returns ZEN's diagnostic payload.\n */\n validateUnary: (expression: string) => unknown;\n /**\n * Return ZEN's completion metadata, for building an expression editor.\n */\n getCompletions: () => unknown;\n /**\n * Type-check `source` against a `variables` context, returning the root context\n * type and the inferred type of every span (`unary` selects test-expression\n * checking). Powers an editor's type-aware completion / hover / diagnostics.\n */\n analyze: (variables: ExpressionType, source: string, unary: boolean) => ExpressionAnalysis;\n /**\n * Whether `actual` satisfies (is assignable to) `expected`. Powers\n * expected-return-type validation in an editor.\n */\n satisfies: (actual: ExpressionType, expected: ExpressionType) => boolean;\n /**\n * Whether the underlying wasm module reports itself ready.\n */\n isReady: () => boolean;\n}\n\ninterface TypeContext {\n variables: ExpressionType;\n handle: VariableType;\n rootKind: ExpressionType;\n}\n\nlet enginePromise: Promise<ExpressionEngine> | null = null;\nlet engineSync: ExpressionEngine | null = null;\nlet engineError: ExpressionError | null = null;\nlet configuredInput: InitInput | undefined;\n// The compiled variable context, cached by `variables` reference (see analyze).\nlet typeContextCache: TypeContext | null = null;\n\nfunction evaluateSafely<T>(expression: string, run: () => T): T {\n try {\n return run();\n } catch (error) {\n throw new ExpressionError(`Failed to evaluate expression: ${expression}`, expression, error);\n }\n}\n\nfunction loadFailureMessage(): string {\n const base = \"Failed to load the ZEN expression engine\";\n\n // typeof, not a property read: the standard environment probe that survives\n // both hosts without a DOM and lint rewrites of global-object aliases.\n if (typeof window === \"undefined\") {\n return `${base}. In a non-browser or non-DOM host (Node, SSR, Web Worker) the wasm cannot be auto-resolved; call configureEngine({ wasmInput }) with the wasm bytes or URL before loading.`;\n }\n\n return base;\n}\n\n/**\n * Configure how the wasm binary is located, before the engine loads. Must be\n * called before the first {@link loadEngine} (or any evaluation), so that the\n * configured input is the one actually used — calling it after the engine has\n * started loading throws, rather than silently taking no effect.\n */\nexport function configureEngine(options: LoadEngineOptions): void {\n if (enginePromise || engineSync) {\n throw new ExpressionError(\"configureEngine() must be called before the engine loads.\");\n }\n\n configuredInput = options.wasmInput;\n}\n\n/**\n * Load and initialize the ZEN expression engine exactly once. Concurrent and\n * subsequent calls share the same in-flight promise / resolved instance. Set a\n * custom wasm source up front with {@link configureEngine}.\n *\n * The GoRules wasm dependency is reached via a dynamic `import()` on purpose: it\n * keeps this module usable from the package's CommonJS build (the dep is\n * ESM-only, so a static `require` would throw) and defers the multi-megabyte\n * wasm download until the engine is actually needed. Do NOT convert this to a\n * static import.\n *\n * A failed load is not cached: the rejected promise is dropped (the error is\n * latched on {@link getEngineError} for inspection), so calling `loadEngine()`\n * again retries from scratch. This is the clear-and-reload retry primitive after\n * a transient failure — an error boundary should call it before resetting.\n */\nexport function loadEngine(): Promise<ExpressionEngine> {\n if (enginePromise) {\n return enginePromise;\n }\n\n engineError = null;\n enginePromise = (async () => {\n const zen = await import(\"@gorules/zen-engine-wasm\");\n\n await zen.default(isUndefined(configuredInput) ? undefined : { module_or_path: configuredInput });\n\n // The wasm `VariableTypeJson` omits \"Date\" — a kind the ZEN engine itself\n // emits and accepts — so ExpressionType is a deliberate superset. This is the\n // single wasm-boundary cast that bridges the two.\n const toVariableType = (type: ExpressionType): VariableType => zen.VariableType.fromJson(type as VariableTypeJson);\n\n // Cache the compiled VariableType and its rootKind by `variables` reference:\n // both are document-independent, so per-keystroke type-checking reuses them\n // instead of rebuilding the wasm handle on every edit (the editor targets 100s\n // of fields). A changed reference frees the previous handle before rebuilding;\n // `typeContextCache` is nulled first so a throw never leaves a freed handle\n // cached, and a `toJson()` throw frees the freshly built handle it would\n // otherwise strand (built but neither cached nor freed).\n const ensureTypeContext = (variables: ExpressionType): TypeContext => {\n if (typeContextCache && typeContextCache.variables === variables) {\n return typeContextCache;\n }\n\n const stale = typeContextCache;\n typeContextCache = null;\n stale?.handle.free();\n const handle = toVariableType(variables);\n\n try {\n typeContextCache = {\n variables,\n handle,\n rootKind: handle.toJson()\n };\n } catch (error) {\n handle.free();\n throw error;\n }\n\n return typeContextCache;\n };\n\n const engine: ExpressionEngine = Object.freeze({\n evaluate: <T = unknown>(expression: string, context: ExpressionContext = {}): T => evaluateSafely(expression, () => zen.evaluateExpression(expression, context) as T),\n evaluateUnary: (expression: string, context: ExpressionContext = {}): boolean => evaluateSafely(expression, () => zen.evaluateUnaryExpression(expression, context)),\n validate: (expression: string): unknown => zen.validateExpression(expression),\n validateUnary: (expression: string): unknown => zen.validateUnaryExpression(expression),\n getCompletions: (): unknown => zen.getCompletions(),\n analyze: (variables: ExpressionType, source: string, unary: boolean): ExpressionAnalysis => {\n const context = ensureTypeContext(variables);\n // `typeCheck` is typed `any` by the wasm bindings; gate it to `unknown`, then\n // assert the element shape only once confirmed an array. The shape is fixed by\n // the pinned zen-engine-wasm version, so one boundary assertion is sound.\n const rawSpans: unknown = unary ? context.handle.typeCheckUnary(source) : context.handle.typeCheck(source);\n\n return {\n rootKind: context.rootKind,\n spans: Array.isArray(rawSpans) ? rawSpans as ExpressionTypeSpan[] : []\n };\n },\n // Acquire both handles leak-safely: the second acquisition lives inside the\n // first's try so a throw from it still frees the first (wasm handles are not\n // GC-managed). A portable equivalent of `using` without relying on Node 22's\n // explicit-resource-management runtime support.\n satisfies: (actual: ExpressionType, expected: ExpressionType): boolean => {\n const actualType = toVariableType(actual);\n\n try {\n const expectedType = toVariableType(expected);\n\n try {\n return actualType.satisfies(expectedType);\n } finally {\n expectedType.free();\n }\n } finally {\n actualType.free();\n }\n },\n isReady: (): boolean => zen.isReady()\n });\n\n engineSync = engine;\n return engine;\n })().catch((error: unknown) => {\n // Drop the rejected promise so a later imperative call can retry the load,\n // but remember the failure so the React provider can surface it to an error\n // boundary instead of re-suspending on a fresh load forever.\n enginePromise = null;\n engineError = new ExpressionError(loadFailureMessage(), undefined, error);\n throw engineError;\n });\n\n return enginePromise;\n}\n\n/**\n * Whether the engine has finished initializing and is ready for sync use.\n */\nexport function isEngineReady(): boolean {\n return engineSync?.isReady() ?? false;\n}\n\n/**\n * The error from the last failed {@link loadEngine} attempt, or `null`. Used by\n * the React provider to surface a wasm-load failure to an error boundary rather\n * than suspending forever, and by imperative pollers to tell \"failed\" apart from\n * \"still loading\". Cleared when a new load starts or {@link resetEngine}.\n */\nexport function getEngineError(): ExpressionError | null {\n return engineError;\n}\n\n/**\n * Return the already-initialized engine synchronously, or throw\n * {@link ExpressionNotReadyError} if {@link loadEngine} has not resolved yet.\n */\nexport function getEngineSync(): ExpressionEngine {\n if (!engineSync) {\n throw new ExpressionNotReadyError();\n }\n\n return engineSync;\n}\n\n/**\n * Reset the engine singleton — drops the loaded engine, the cached type context,\n * the latched error, and the configured wasm input. Mainly for tests, but also the\n * way to re-run {@link configureEngine} after a load (configure throws once the\n * engine has started loading).\n */\nexport function resetEngine(): void {\n typeContextCache?.handle.free();\n typeContextCache = null;\n enginePromise = null;\n engineSync = null;\n engineError = null;\n configuredInput = undefined;\n}\n","/**\n * Best-effort development-mode flag for diagnostic-only code paths.\n *\n * Detected from `process.env.NODE_ENV` (defined by Node, test runners, webpack,\n * and Vite's SSR/`define`). The `typeof` guard is load-bearing: in a plain\n * browser bundle `process` is not declared at all, and any bare reference would\n * throw a ReferenceError while this module is being imported. Hosts without a\n * `process` default to \"production\" — the safe choice, since `isDev` only gates\n * extra developer-facing warnings.\n */\nexport const isDev: boolean = detectDev();\n\nfunction detectDev(): boolean {\n if (typeof process === \"undefined\") {\n return false;\n }\n\n return process.env ? process.env.NODE_ENV !== \"production\" : false;\n}\n","/**\n * Shared subject-path guard for the condition compilers. A subject / left-hand\n * path is emitted **verbatim** into ZEN source, so both {@link compileCondition}\n * (which writes that source) and {@link liftConditionTree} (which reads it back)\n * gate paths through this one predicate — a single definition of \"what is a safe\n * field path\" that cannot drift between the writer and the reader, which matters\n * because the guard is also the compiler's injection defense.\n */\n\n// A subject must be a plain identifier path (`amount`, `user.age`, `items[0]`).\n// Anything else is rejected to keep the condition compiler from being an\n// expression-injection sink.\nconst SUBJECT_PATTERN = /^[A-Z_$][\\w$]*(?:\\.[A-Z_$][\\w$]*|\\[\\d+\\])*$/i;\n\n// ZEN's operator and literal keywords. The path pattern alone would accept them\n// as identifiers, compiling well-formed nonsense like `true == 5` or `not > 1`\n// that evaluates without error and silently never (or always) matches; a path\n// segment hitting one makes the whole path invalid.\nconst ZEN_RESERVED_WORDS = new Set([\"and\", \"or\", \"not\", \"in\", \"true\", \"false\", \"null\"]);\n\n/**\n * Whether `subject` is a plain identifier path safe to emit verbatim into ZEN\n * source: dotted/indexed segments only, none of which is a ZEN reserved word.\n */\nexport function isIdentifierPath(subject: string): boolean {\n return SUBJECT_PATTERN.test(subject)\n && (subject.match(/[A-Z_$][\\w$]*/gi) ?? []).every(segment => !ZEN_RESERVED_WORDS.has(segment));\n}\n","import type { ExpressionContext, ExpressionEngine } from \"../engine/loader\";\nimport type {\n BranchSelection,\n ConditionBranchInput,\n ConditionGroupInput,\n ConditionInput,\n ConditionOperator\n} from \"./types\";\n\nimport { ExpressionError } from \"../engine/errors\";\nimport { loadEngine } from \"../engine/loader\";\nimport { isDev } from \"../internal/env\";\nimport { isArray, isNullish, isString } from \"../internal/predicates\";\nimport { isIdentifierPath } from \"./subject\";\n\n/**\n * Serialize a JavaScript value into a ZEN literal. Nullish becomes `null`;\n * numbers / booleans / bigints are emitted verbatim; strings are quoted via\n * {@link encodeZenString}; arrays become `[a, b, ...]`.\n *\n * Throws {@link ExpressionError} for a value with no faithful ZEN\n * representation — an object, symbol, or function, or a string containing both\n * quote styles. Callers that need a sentinel instead of a throw go through\n * {@link compileCondition}, which degrades such a value to a non-compiling\n * (null) condition.\n */\nexport function toZenLiteral(value: unknown): string {\n if (isNullish(value)) {\n return \"null\";\n }\n\n if (typeof value === \"number\") {\n // NaN / Infinity stringify to bare identifiers (\"NaN\", \"Infinity\") that ZEN\n // resolves to null, silently corrupting comparisons — reject them so the value\n // degrades to a null (non-compiling) condition like other unrepresentable ones.\n if (!Number.isFinite(value)) {\n throw new ExpressionError(`Number ${String(value)} has no ZEN literal representation`);\n }\n\n return String(value);\n }\n\n if (typeof value === \"boolean\" || typeof value === \"bigint\") {\n return String(value);\n }\n\n if (isString(value)) {\n return encodeZenString(value);\n }\n\n if (isArray(value)) {\n return `[${value.map(item => toZenLiteral(item)).join(\", \")}]`;\n }\n\n throw new ExpressionError(`Value of type \"${typeof value}\" has no ZEN literal representation`);\n}\n\n/**\n * Encode a string as a ZEN literal. ZEN string literals are **raw** between\n * matching quotes and honor no backslash escapes (`'a\\nb'` is the four\n * characters `a \\ n b`), so the encoder must not escape — it picks a delimiter\n * the value does not contain. A value containing both quote styles cannot be\n * represented as a raw ZEN literal and throws.\n */\nfunction encodeZenString(value: string): string {\n const hasSingle = value.includes(\"'\");\n const hasDouble = value.includes(\"\\\"\");\n\n if (hasSingle && hasDouble) {\n throw new ExpressionError(\"String contains both single and double quotes and has no ZEN literal representation\");\n }\n\n return hasSingle ? `\"${value}\"` : `'${value}'`;\n}\n\nfunction toArrayLiteral(value: unknown): string {\n return isArray(value) ? toZenLiteral(value) : `[${toZenLiteral(value)}]`;\n}\n\n/**\n * Emit the ZEN emptiness test matching the backend field evaluator's\n * `isEmptyValue`: null, blank (whitespace-only) text, and empty arrays are\n * empty; numbers and booleans never are. ZEN's `or` short-circuits, so the\n * type-guarded branches never evaluate against a null subject. The backend\n * additionally treats an empty map as empty for totality, but form values —\n * the only subjects this compiler targets — are never objects, and ZEN's\n * `len()` does not accept one.\n */\nfunction zenIsEmpty(subject: string): string {\n return `(${subject} == null`\n + ` or (type(${subject}) == 'string' and len(trim(${subject})) == 0)`\n + ` or (type(${subject}) == 'array' and len(${subject}) == 0))`;\n}\n\nfunction compileFieldCondition(subject: string, operator: ConditionOperator, value: unknown): string {\n switch (operator) {\n case \"eq\": {\n return `${subject} == ${toZenLiteral(value)}`;\n }\n\n case \"ne\": {\n return `${subject} != ${toZenLiteral(value)}`;\n }\n\n case \"gt\": {\n return `${subject} > ${toZenLiteral(value)}`;\n }\n\n case \"gte\": {\n return `${subject} >= ${toZenLiteral(value)}`;\n }\n\n case \"lt\": {\n return `${subject} < ${toZenLiteral(value)}`;\n }\n\n case \"lte\": {\n return `${subject} <= ${toZenLiteral(value)}`;\n }\n\n case \"contains\": {\n return `contains(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"not_contains\": {\n return `not contains(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"starts_with\": {\n return `startsWith(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"ends_with\": {\n return `endsWith(${subject}, ${toZenLiteral(value)})`;\n }\n\n case \"in\": {\n return `${subject} in ${toArrayLiteral(value)}`;\n }\n\n case \"not_in\": {\n return `not (${subject} in ${toArrayLiteral(value)})`;\n }\n\n case \"is_empty\": {\n return zenIsEmpty(subject);\n }\n\n case \"is_not_empty\": {\n return `not ${zenIsEmpty(subject)}`;\n }\n\n default: {\n // Exhaustiveness guard: adding a ConditionOperator without a case here is a\n // compile error. The throw only covers runtime-invalid data forced past the\n // type with a cast, and is caught by compileCondition.\n operator satisfies never;\n throw new ExpressionError(`Unsupported operator: ${String(operator)}`);\n }\n }\n}\n\n/**\n * Compile a single condition into a ZEN boolean expression. Field conditions map\n * their operator to ZEN; expression conditions are wrapped in parentheses to\n * preserve their grouping. Returns `null` when the condition is empty, its\n * subject is not an identifier path, or its value has no ZEN representation.\n */\nexport function compileCondition(condition: ConditionInput): string | null {\n if (condition.kind === \"expression\") {\n const expression = condition.expression.trim();\n // Parenthesize: a raw expression with a top-level `or` / ternary would be\n // regrouped by ZEN precedence once a group joins parts with ` and ` (which\n // binds tighter than `or`), silently selecting the wrong branch.\n return expression === \"\" ? null : `(${expression})`;\n }\n\n const subject = condition.subject.trim();\n\n if (!isIdentifierPath(subject)) {\n return null;\n }\n\n try {\n return compileFieldCondition(subject, condition.operator, condition.value);\n } catch {\n // A value with no ZEN representation (object value, both-quote string) or a\n // cast-in invalid operator makes the condition uncompilable; degrade it to\n // null like an invalid subject so the group simply drops it.\n return null;\n }\n}\n\n/**\n * Compile a condition group (its conditions joined with AND). Returns `null`\n * when the group has no compilable conditions.\n */\nexport function compileGroup(group: ConditionGroupInput): string | null {\n const parts = group.conditions\n .map(condition => compileCondition(condition))\n .filter(part => part !== null);\n\n return parts.length === 0 ? null : parts.join(\" and \");\n}\n\n/**\n * Compile a branch's condition groups into a single ZEN expression (groups\n * joined with OR). Returns `null` when the branch has no compilable groups\n * (e.g. a default branch).\n */\nexport function compileBranch(branch: ConditionBranchInput): string | null {\n const groups = (branch.conditionGroups ?? [])\n .map(group => compileGroup(group))\n .filter(group => group !== null);\n\n return groups.length === 0 ? null : groups.map(group => `(${group})`).join(\" or \");\n}\n\n/**\n * Pick the matching branch for the given context using a pre-loaded engine.\n * Non-default branches are tested in ascending `priority` order; the first whose\n * compiled expression evaluates to `true` wins. Falls back to the default\n * branch, or a `null` id when neither matches.\n *\n * A branch whose expression throws for the given context (e.g. ZEN's `>`\n * throws when the subject is missing or non-numeric) is treated as not matching\n * rather than propagating — so a missing field degrades to the default branch\n * instead of crashing the caller.\n */\nexport function selectBranchWith(\n branches: readonly ConditionBranchInput[],\n context: ExpressionContext,\n engine: Pick<ExpressionEngine, \"evaluate\" | \"validate\">\n): BranchSelection {\n const ordered = branches.toSorted((a, b) => a.priority - b.priority);\n\n for (const branch of ordered) {\n if (branch.isDefault) {\n continue;\n }\n\n const expression = compileBranch(branch);\n\n if (expression === null) {\n // A non-default branch that compiles to nothing can never match — almost\n // always a configuration bug (mistyped subject, unrepresentable value)\n // that would otherwise surface only as \"always falls through to default\".\n if (isDev) {\n console.warn(`[expression] branch \"${branch.id}\" has no compilable condition and can never match`);\n }\n\n continue;\n }\n\n if (evaluatesTrue(engine, expression, context)) {\n return { branchId: branch.id, matched: true };\n }\n }\n\n const fallback = ordered.find(branch => branch.isDefault);\n return { branchId: fallback?.id ?? null, matched: false };\n}\n\nfunction evaluatesTrue(\n engine: Pick<ExpressionEngine, \"evaluate\" | \"validate\">,\n expression: string,\n context: ExpressionContext\n): boolean {\n try {\n return engine.evaluate(expression, context) === true;\n } catch (error) {\n if (isDev) {\n reportEvaluationFailure(engine, expression, error);\n }\n\n return false;\n }\n}\n\nfunction reportEvaluationFailure(\n engine: Pick<ExpressionEngine, \"validate\">,\n expression: string,\n error: unknown\n): void {\n // A compiled branch expression that fails to PARSE signals a bug in this\n // package's own emitter, not a runtime type mismatch (ZEN's `>` on a missing\n // field, the intended degrade-to-default path). validate() returns null for a\n // parsable expression and a diagnostic object otherwise, so surface only the\n // former — a real authoring/emitter bug should not hide behind the fallback.\n let diagnostic: unknown;\n\n try {\n diagnostic = engine.validate(expression);\n } catch {\n return;\n }\n\n if (!isNullish(diagnostic)) {\n console.warn(`[expression] compiled branch expression failed to parse: ${expression}`, diagnostic, error);\n }\n}\n\n/**\n * Pick the matching branch for the given context, loading the ZEN engine on\n * first use. See {@link selectBranchWith} for the selection semantics.\n */\nexport async function selectBranch(\n branches: readonly ConditionBranchInput[],\n context: ExpressionContext\n): Promise<BranchSelection> {\n const engine = await loadEngine();\n return selectBranchWith(branches, context, engine);\n}\n","/**\n * Structural shapes for the visual condition model compiled to ZEN. They mirror\n * the condition types used in the form and flow editors so a host can map those\n * definitions to {@link compileCondition} / {@link selectBranch} without this\n * package depending on either editor. The editors keep a flat working model for\n * form ergonomics (a row can hold both a half-typed field triple and an\n * expression while the author toggles between them); this discriminated input is\n * the narrowed, compiler-facing shape where each kind carries only what it uses.\n */\n\n/**\n * The closed operator vocabulary understood by {@link compileCondition}, as a\n * runtime constant so validators can build allow-lists from it instead of\n * re-declaring the set. The {@link ConditionOperator} type derives from this\n * array — one definition site for both the type and the runtime list.\n */\nexport const CONDITION_OPERATORS = [\n \"eq\",\n \"ne\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"contains\",\n \"not_contains\",\n \"starts_with\",\n \"ends_with\",\n \"in\",\n \"not_in\",\n \"is_empty\",\n \"is_not_empty\"\n] as const;\n\n/**\n * Operators understood by {@link compileCondition}. The compiler maps each to a\n * ZEN expression; this closed set is the single source of truth for the operator\n * vocabulary (the flow editor shares it instead of re-declaring its own).\n */\nexport type ConditionOperator = typeof CONDITION_OPERATORS[number];\n\n/**\n * The shape of an operator's right-hand operand:\n *\n * - `scalar` — a single comparison value (`eq`, `gt`, `contains`, …);\n * - `array` — a list of membership values (`in` / `not_in`);\n * - `none` — no operand at all (`is_empty` / `is_not_empty`).\n */\nexport type ConditionOperatorArity = \"scalar\" | \"array\" | \"none\";\n\n// Exhaustive by construction: adding a ConditionOperator without classifying its\n// arity here is a compile error, the same discipline the compiler's operator\n// switch enforces.\nconst CONDITION_OPERATOR_ARITIES: Record<ConditionOperator, ConditionOperatorArity> = {\n eq: \"scalar\",\n ne: \"scalar\",\n gt: \"scalar\",\n gte: \"scalar\",\n lt: \"scalar\",\n lte: \"scalar\",\n contains: \"scalar\",\n not_contains: \"scalar\",\n starts_with: \"scalar\",\n ends_with: \"scalar\",\n in: \"array\",\n not_in: \"array\",\n is_empty: \"none\",\n is_not_empty: \"none\"\n};\n\n/**\n * The arity of `operator`'s right-hand operand (see\n * {@link ConditionOperatorArity}). One definition site next to\n * {@link CONDITION_OPERATORS}, so the tree compiler's arity enforcement and a\n * condition editor's operand controls classify operators identically instead of\n * each keeping a drift-prone copy.\n */\nexport function conditionOperatorArity(operator: ConditionOperator): ConditionOperatorArity {\n return CONDITION_OPERATOR_ARITIES[operator];\n}\n\n/**\n * A field/operator/value condition.\n */\nexport interface FieldConditionInput {\n kind: \"field\";\n /**\n * The field path the operator tests. Emitted **verbatim** into the compiled\n * ZEN source as a path expression (guarded by an identifier-path pattern that\n * also rejects ZEN reserved words such as `true` or `not` as segments), unlike\n * `value`, which is serialized to a ZEN literal — so callers must supply a\n * valid identifier path, not arbitrary user text.\n */\n subject: string;\n operator: ConditionOperator;\n value: unknown;\n}\n\n/**\n * A raw ZEN expression condition, passed through to the engine verbatim.\n */\nexport interface ExpressionConditionInput {\n kind: \"expression\";\n expression: string;\n}\n\n/**\n * A single condition: either a field/operator/value triple or a raw expression.\n */\nexport type ConditionInput = FieldConditionInput | ExpressionConditionInput;\n\n/**\n * A group of conditions, combined with AND.\n */\nexport interface ConditionGroupInput {\n conditions: readonly ConditionInput[];\n}\n\n/**\n * A branch guarded by one or more condition groups (combined with OR).\n */\nexport interface ConditionBranchInput {\n id: string;\n priority: number;\n isDefault?: boolean;\n conditionGroups?: readonly ConditionGroupInput[];\n}\n\n/**\n * The branch chosen by {@link selectBranch}. `matched` is true only when a\n * non-default branch's expression evaluated true (then `branchId` is that\n * branch's id). On the default-branch fallback `matched` is false while\n * `branchId` is the default's id; `branchId` is null only when nothing matched\n * and there is no default branch.\n */\nexport type BranchSelection\n = | { matched: true; branchId: string }\n | { matched: false; branchId: string | null };\n","/**\n * Serialize a {@link ConditionTreeGroup} to a single canonical ZEN boolean\n * expression. Canonical means structure-preserving and stable: nested groups are\n * always explicitly parenthesized (never relying on ZEN's `and`/`or` precedence),\n * single-item groups collapse to their item, and a group's join is driven solely by\n * its `op`. That canonical form is what {@link liftConditionTree} recognizes, so\n * `lift(compile(tree))` reconstructs an equal tree for every tree this function can\n * emit (up to the lifter's nesting budget — see `liftConditionTree`).\n *\n * Leaves are lowered through {@link compileCondition}, the one owner of the operator\n * mapping, literal encoding, and identifier-path guard — this module never touches\n * ZEN syntax itself. A rule this module cannot represent **canonically** is dropped:\n * a non-path `left`, a `right` that does not match the operator's arity (see\n * {@link compileRule}), or a `right` value with no ZEN literal. A group keeps only\n * its compilable items, an all-dropped group vanishes, and a whole tree that\n * compiles to nothing yields `\"\"`.\n */\n\nimport type { ConditionScalar, ConditionTreeGroup, ConditionTreeNode, ConditionTreeRule, ConditionTreeValue } from \"./tree-types\";\n\nimport { isArray } from \"../internal/predicates\";\nimport { compileCondition } from \"./compile\";\nimport { conditionOperatorArity } from \"./types\";\n\n/**\n * Compile a condition tree to a canonical ZEN expression, or `\"\"` when no rule in\n * the tree is compilable (see the module note for the drop semantics).\n */\nexport function compileConditionTree(tree: ConditionTreeGroup): string {\n const normalized = normalizeNode(tree);\n return normalized === null ? \"\" : emitNode(normalized, true);\n}\n\n/**\n * Prune a node to its compilable core: drop rules {@link compileRule} rejects, drop\n * emptied groups, and collapse a single-surviving-item group to that item (so the\n * emitted shape carries no redundant parentheses). Returns `null` when nothing in\n * the node survives.\n */\nfunction normalizeNode(node: ConditionTreeNode): ConditionTreeNode | null {\n if (node.kind === \"rule\") {\n return compileRule(node) === null ? null : node;\n }\n\n const items = node.items\n .map(item => normalizeNode(item))\n .filter((item): item is ConditionTreeNode => item !== null);\n\n if (items.length === 0) {\n return null;\n }\n\n if (items.length === 1) {\n return items[0]!;\n }\n\n return {\n kind: \"group\",\n op: node.op,\n items\n };\n}\n\n/**\n * Render a normalized node. A rule emits its lowered ZEN; a group joins its items\n * with ` and ` / ` or ` and, unless it is the top-level group, wraps them in\n * parentheses so the tree structure survives ZEN's operator precedence on lift.\n */\nfunction emitNode(node: ConditionTreeNode, topLevel: boolean): string {\n if (node.kind === \"rule\") {\n return compileRule(node)!;\n }\n\n const joined = node.items\n .map(item => emitNode(item, false))\n .join(node.op === \"and\" ? \" and \" : \" or \");\n\n return topLevel ? joined : `(${joined})`;\n}\n\n/**\n * Lower a leaf rule to ZEN via {@link compileCondition}, or `null` for a rule the\n * compiler cannot represent. The rule's `right` must match its operator's arity —\n * a single scalar for the comparison / string operators, an array of scalars for\n * `in` / `not_in`, absent for the emptiness operators (the contract\n * {@link ConditionTreeValue} documents); an off-arity rule is non-compilable and\n * drops. Enforcing arity here, not just relying on `compileCondition`, is what\n * keeps every emitted expression liftable: the flat compiler's lax value handling\n * would happily serialize e.g. a missing `right` as a `null` literal, which is not\n * part of the canonical grammar.\n */\nfunction compileRule(rule: ConditionTreeRule): string | null {\n if (!matchesOperatorArity(rule)) {\n return null;\n }\n\n return compileCondition({\n kind: \"field\",\n subject: rule.left,\n operator: rule.operator,\n value: rule.right\n });\n}\n\nfunction matchesOperatorArity(rule: ConditionTreeRule): boolean {\n switch (conditionOperatorArity(rule.operator)) {\n case \"scalar\": {\n return isConditionScalar(rule.right);\n }\n\n case \"array\": {\n return isScalarArray(rule.right);\n }\n\n case \"none\": {\n return rule.right === undefined;\n }\n }\n}\n\nfunction isConditionScalar(value: unknown): value is ConditionScalar {\n return typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\";\n}\n\n// Every element must itself be a scalar: `null` slips through the type as JSON\n// data, and toZenLiteral would serialize it to a `null` literal outside the\n// canonical grammar rather than throwing.\nfunction isScalarArray(value: ConditionTreeValue | undefined): value is readonly ConditionScalar[] {\n return isArray(value) && value.every(item => isConditionScalar(item));\n}\n","/**\n * Reconstruct a {@link ConditionTreeGroup} from a ZEN expression — the inverse of\n * {@link compileConditionTree}. It is a **hand-written recursive-descent parser**\n * (no lezer / codemirror dependency) over the canonical subset of ZEN that\n * {@link compileConditionTree} emits: identifier-path comparisons, `contains` /\n * `startsWith` / `endsWith` calls, `in` / `not (… in …)` membership, the emptiness\n * blob, and and/or groups with explicit parentheses. Parsing is whitespace-tolerant\n * (it works on a token stream) but shape-strict: anything outside that canonical\n * subset — a raw expression, a hand-authored variant, mixed and/or without parens,\n * groups nested beyond the depth budget — makes it return `null`, the consumer's\n * signal to fall back to raw-expression mode rather than silently mis-lift.\n *\n * Two design choices keep the reader honest against the writer:\n *\n * - Leaf paths are gated through {@link isIdentifierPath}, the same guard\n * {@link compileCondition} writes through, so \"what is a field path\" cannot drift\n * between the two directions.\n * - The emptiness operators (`is_empty` / `is_not_empty`) are recognized by an\n * **oracle**: rather than re-encoding ZEN's multi-clause emptiness blob here, the\n * parser asks {@link compileCondition} to emit the canonical blob for the candidate\n * path and matches the input tokens against it. The blob's exact shape therefore\n * has one owner (the compiler), and this parser recognizes precisely what that\n * owner produces.\n */\n\nimport type { ConditionScalar, ConditionTreeGroup, ConditionTreeNode, ConditionTreeOperator, ConditionTreeRule } from \"./tree-types\";\n\nimport { compileCondition } from \"./compile\";\nimport { isIdentifierPath } from \"./subject\";\n\ntype TokenKind = \"ident\" | \"number\" | \"punct\" | \"string\";\n\ninterface Token {\n kind: TokenKind;\n value: string;\n}\n\ninterface PathRead {\n path: string;\n next: number;\n}\n\n// Punctuation the tokenizer recognizes. Two-char operators are matched before the\n// one-char set so `<=` is never read as `<` then `=`.\nconst TWO_CHAR_PUNCTUATION = new Set([\"==\", \"!=\", \"<=\", \">=\"]);\nconst ONE_CHAR_PUNCTUATION = new Set([\"(\", \")\", \"[\", \"]\", \",\", \".\", \"<\", \">\", \"-\"]);\n\nconst IDENT_START = /[a-z_$]/i;\nconst IDENT_PART = /[\\w$]/;\nconst DIGIT = /\\d/;\nconst NUMBER_PATTERN = /^\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?/i;\nconst INTEGER_PATTERN = /^\\d+$/;\n\n// Punct → operator for the infix comparisons, and function name → operator for the\n// positive string calls (`not_contains` is handled via the `not` prefix, so it is\n// absent here). Membership (`in`) is recognized by keyword, not through these.\nconst COMPARISON_OPERATORS: Record<string, ConditionTreeOperator> = {\n \"==\": \"eq\",\n \"!=\": \"ne\",\n \">\": \"gt\",\n \">=\": \"gte\",\n \"<\": \"lt\",\n \"<=\": \"lte\"\n};\n\nconst CALL_OPERATORS: Record<string, ConditionTreeOperator> = {\n contains: \"contains\",\n startsWith: \"starts_with\",\n endsWith: \"ends_with\"\n};\n\n// Nesting budget for parenthesized groups. The parser recurses per group, and its\n// input is an arbitrary stored string — without a budget a hostile ten-thousand-\n// paren expression overflows the call stack and throws instead of returning null.\n// Builder-authored trees are shallow (the UI indents every level), so the budget is\n// far beyond any canonical tree while keeping the recursion trivially safe.\nconst MAX_GROUP_DEPTH = 64;\n\n/**\n * Lift a ZEN expression to a condition tree, or `null` when it is not in the\n * canonical form {@link compileConditionTree} produces (the consumer then keeps the\n * raw expression). Groups nested deeper than 64 parenthesized levels are refused as\n * non-canonical rather than risking parser-stack overflow on adversarial input. The\n * returned root is always a group.\n */\nexport function liftConditionTree(expression: string): ConditionTreeGroup | null {\n const tokens = tokenize(expression);\n\n if (tokens === null || tokens.length === 0) {\n return null;\n }\n\n let pos = 0;\n\n function peek(offset = 0): Token | undefined {\n return tokens![pos + offset];\n }\n\n function consumePunct(value: string): boolean {\n const token = peek();\n\n if (token !== undefined && token.kind === \"punct\" && token.value === value) {\n pos += 1;\n return true;\n }\n\n return false;\n }\n\n function consumeIdent(value: string): boolean {\n const token = peek();\n\n if (token !== undefined && token.kind === \"ident\" && token.value === value) {\n pos += 1;\n return true;\n }\n\n return false;\n }\n\n function tokensMatchAt(from: number, expected: readonly Token[]): boolean {\n for (const [index, element] of expected.entries()) {\n const actual = tokens![from + index];\n const want = element!;\n\n if (actual === undefined || actual.kind !== want.kind || actual.value !== want.value) {\n return false;\n }\n }\n\n return true;\n }\n\n function parsePath(): string | null {\n const read = readPath(tokens!, pos);\n\n if (read === null) {\n return null;\n }\n\n pos = read.next;\n return read.path;\n }\n\n function parseLiteral(): ConditionScalar | null {\n const token = peek();\n\n if (token === undefined) {\n return null;\n }\n\n if (token.kind === \"string\") {\n pos += 1;\n return token.value;\n }\n\n if (token.kind === \"number\") {\n pos += 1;\n return Number(token.value);\n }\n\n if (token.kind === \"ident\") {\n if (token.value === \"true\") {\n pos += 1;\n return true;\n }\n\n if (token.value === \"false\") {\n pos += 1;\n return false;\n }\n\n return null;\n }\n\n if (token.kind === \"punct\" && token.value === \"-\") {\n const digits = peek(1);\n\n if (digits === undefined || digits.kind !== \"number\") {\n return null;\n }\n\n pos += 2;\n return -Number(digits.value);\n }\n\n return null;\n }\n\n function parseArray(): ConditionScalar[] | null {\n if (!consumePunct(\"[\")) {\n return null;\n }\n\n if (isPunct(peek(), \"]\")) {\n pos += 1;\n return [];\n }\n\n const first = parseLiteral();\n\n if (first === null) {\n return null;\n }\n\n const items: ConditionScalar[] = [first];\n\n while (isPunct(peek(), \",\")) {\n pos += 1;\n const next = parseLiteral();\n\n if (next === null) {\n return null;\n }\n\n items.push(next);\n }\n\n return consumePunct(\"]\") ? items : null;\n }\n\n // `fn(path, literal)` for a string call whose fn name has already been consumed\n // (pos sits at the opening paren).\n function parseCall(operator: ConditionTreeOperator): ConditionTreeRule | null {\n if (!consumePunct(\"(\")) {\n return null;\n }\n\n const left = parsePath();\n\n if (left === null || !consumePunct(\",\")) {\n return null;\n }\n\n const right = parseLiteral();\n\n if (right === null || !consumePunct(\")\")) {\n return null;\n }\n\n return {\n kind: \"rule\",\n left,\n operator,\n right\n };\n }\n\n // `not (path in [array])` — the `not (` has already been consumed.\n function parseNotIn(): ConditionTreeRule | null {\n const left = parsePath();\n\n if (left === null || !consumeIdent(\"in\")) {\n return null;\n }\n\n const right = parseArray();\n\n if (right === null || !consumePunct(\")\")) {\n return null;\n }\n\n return {\n kind: \"rule\",\n left,\n operator: \"not_in\",\n right\n };\n }\n\n // Recognize an emptiness blob by asking the compiler for the canonical shape and\n // matching tokens; consumes nothing unless the whole blob matches.\n function tryEmptiness(): ConditionTreeRule | null {\n const first = peek();\n\n if (first === undefined) {\n return null;\n }\n\n let operator: ConditionTreeOperator;\n let pathIndex: number;\n\n if (first.kind === \"ident\" && first.value === \"not\" && isPunct(peek(1), \"(\")) {\n operator = \"is_not_empty\";\n pathIndex = pos + 2;\n } else if (first.kind === \"punct\" && first.value === \"(\") {\n operator = \"is_empty\";\n pathIndex = pos + 1;\n } else {\n return null;\n }\n\n const read = readPath(tokens!, pathIndex);\n\n if (read === null) {\n return null;\n }\n\n const canonical = compileCondition({\n kind: \"field\",\n subject: read.path,\n operator,\n value: undefined\n });\n\n if (canonical === null) {\n return null;\n }\n\n const expected = tokenize(canonical);\n\n if (expected === null || !tokensMatchAt(pos, expected)) {\n return null;\n }\n\n pos += expected.length;\n return {\n kind: \"rule\",\n left: read.path,\n operator\n };\n }\n\n function parseRule(): ConditionTreeRule | null {\n const token = peek();\n\n if (token === undefined || token.kind !== \"ident\") {\n return null;\n }\n\n if (token.value === \"not\") {\n const next = peek(1);\n\n if (next === undefined) {\n return null;\n }\n\n if (next.kind === \"ident\" && next.value === \"contains\" && isPunct(peek(2), \"(\")) {\n pos += 2;\n return parseCall(\"not_contains\");\n }\n\n if (next.kind === \"punct\" && next.value === \"(\") {\n pos += 2;\n return parseNotIn();\n }\n\n return null;\n }\n\n if (isPunct(peek(1), \"(\")) {\n const operator = CALL_OPERATORS[token.value];\n\n if (operator === undefined) {\n return null;\n }\n\n pos += 1;\n return parseCall(operator);\n }\n\n const left = parsePath();\n\n if (left === null) {\n return null;\n }\n\n const operatorToken = peek();\n\n if (operatorToken === undefined) {\n return null;\n }\n\n if (operatorToken.kind === \"ident\" && operatorToken.value === \"in\") {\n pos += 1;\n const right = parseArray();\n return right === null\n ? null\n : {\n kind: \"rule\",\n left,\n operator: \"in\",\n right\n };\n }\n\n if (operatorToken.kind === \"punct\") {\n const operator = COMPARISON_OPERATORS[operatorToken.value];\n\n if (operator === undefined) {\n return null;\n }\n\n pos += 1;\n const right = parseLiteral();\n return right === null\n ? null\n : {\n kind: \"rule\",\n left,\n operator,\n right\n };\n }\n\n return null;\n }\n\n function parseItem(depth: number): ConditionTreeNode | null {\n const emptiness = tryEmptiness();\n\n if (emptiness !== null) {\n return emptiness;\n }\n\n if (isPunct(peek(), \"(\")) {\n if (depth >= MAX_GROUP_DEPTH) {\n return null;\n }\n\n pos += 1;\n const inner = parseGroup(depth + 1);\n\n if (inner === null || !consumePunct(\")\")) {\n return null;\n }\n\n return asGroup(inner);\n }\n\n return parseRule();\n }\n\n function peekJoin(): \"and\" | \"or\" | null {\n const token = peek();\n\n if (token !== undefined && token.kind === \"ident\" && (token.value === \"and\" || token.value === \"or\")) {\n return token.value;\n }\n\n return null;\n }\n\n function parseGroup(depth: number): ConditionTreeNode | null {\n const first = parseItem(depth);\n\n if (first === null) {\n return null;\n }\n\n const items: ConditionTreeNode[] = [first];\n let op: \"and\" | \"or\" | null = null;\n let join = peekJoin();\n\n while (join !== null) {\n // A canonical group joins its items with one operator; mixed and/or at the\n // same paren level is not a shape the compiler emits, so refuse it.\n if (op === null) {\n op = join;\n } else if (op !== join) {\n return null;\n }\n\n pos += 1;\n const next = parseItem(depth);\n\n if (next === null) {\n return null;\n }\n\n items.push(next);\n join = peekJoin();\n }\n\n return items.length === 1\n ? items[0]!\n : {\n kind: \"group\",\n op: op ?? \"and\",\n items\n };\n }\n\n const parsed = parseGroup(0);\n\n if (parsed === null || pos !== tokens.length) {\n return null;\n }\n\n return asGroup(parsed);\n}\n\n/**\n * Read a dotted/indexed identifier path starting at `from`, validated through\n * {@link isIdentifierPath}. Pure over the token array (no cursor) so it can probe a\n * candidate path — the emptiness oracle reads the blob's subject without committing\n * the cursor. Returns the path and the index just past it, or `null`.\n */\nfunction readPath(tokens: readonly Token[], from: number): PathRead | null {\n const head = tokens[from];\n\n if (head === undefined || head.kind !== \"ident\") {\n return null;\n }\n\n let path = head.value;\n let index = from + 1;\n let segment = tokens[index];\n\n while (segment !== undefined && segment.kind === \"punct\" && (segment.value === \".\" || segment.value === \"[\")) {\n if (segment.value === \".\") {\n const name = tokens[index + 1];\n\n if (name === undefined || name.kind !== \"ident\") {\n return null;\n }\n\n path += `.${name.value}`;\n index += 2;\n } else {\n const inner = tokens[index + 1];\n\n if (inner === undefined || inner.kind !== \"number\" || !INTEGER_PATTERN.test(inner.value)) {\n return null;\n }\n\n const close = tokens[index + 2];\n\n if (close === undefined || close.kind !== \"punct\" || close.value !== \"]\") {\n return null;\n }\n\n path += `[${inner.value}]`;\n index += 3;\n }\n\n segment = tokens[index];\n }\n\n return isIdentifierPath(path) ? { path, next: index } : null;\n}\n\nfunction asGroup(node: ConditionTreeNode): ConditionTreeGroup {\n return node.kind === \"group\"\n ? node\n : {\n kind: \"group\",\n op: \"and\",\n items: [node]\n };\n}\n\nfunction isPunct(token: Token | undefined, value: string): boolean {\n return token !== undefined && token.kind === \"punct\" && token.value === value;\n}\n\n/**\n * Split a ZEN expression into tokens, or `null` on an unexpected character or an\n * unterminated string. Whitespace is dropped, so the parser (and the emptiness\n * oracle's token match) is insensitive to spacing. String literals are read raw\n * between matching quotes — ZEN honors no backslash escapes, mirroring the encoder\n * in {@link compileCondition}.\n */\nfunction tokenize(input: string): Token[] | null {\n const tokens: Token[] = [];\n let index = 0;\n\n while (index < input.length) {\n const char = input[index]!;\n\n if (char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\") {\n index += 1;\n continue;\n }\n\n if (char === \"'\" || char === \"\\\"\") {\n const end = input.indexOf(char, index + 1);\n\n if (end === -1) {\n return null;\n }\n\n tokens.push({ kind: \"string\", value: input.slice(index + 1, end) });\n index = end + 1;\n continue;\n }\n\n const pair = input.slice(index, index + 2);\n\n if (TWO_CHAR_PUNCTUATION.has(pair)) {\n tokens.push({ kind: \"punct\", value: pair });\n index += 2;\n continue;\n }\n\n if (ONE_CHAR_PUNCTUATION.has(char)) {\n tokens.push({ kind: \"punct\", value: char });\n index += 1;\n continue;\n }\n\n if (DIGIT.test(char)) {\n const match = NUMBER_PATTERN.exec(input.slice(index));\n\n if (match === null) {\n return null;\n }\n\n tokens.push({ kind: \"number\", value: match[0] });\n index += match[0].length;\n continue;\n }\n\n if (IDENT_START.test(char)) {\n let end = index + 1;\n\n while (end < input.length && IDENT_PART.test(input[end]!)) {\n end += 1;\n }\n\n tokens.push({ kind: \"ident\", value: input.slice(index, end) });\n index = end;\n continue;\n }\n\n return null;\n }\n\n return tokens;\n}\n","/**\n * The visual condition **tree** model: an arbitrarily nested and/or tree of typed\n * comparison rules that {@link compileConditionTree} serializes to a single ZEN\n * boolean expression and {@link liftConditionTree} reconstructs from one. It is a\n * distinct, self-contained shape from the compiler's flat {@link ConditionInput} —\n * the tree is what a structured builder UI edits, whereas `ConditionInput` is the\n * narrowed per-condition shape the compiler consumes. The two meet only at the\n * leaf: a {@link ConditionTreeRule} lowers to a field {@link ConditionInput} so the\n * operator vocabulary, literal encoding, and injection guard have one owner.\n */\n\nimport { CONDITION_OPERATORS } from \"./types\";\n\n/**\n * A scalar operand: the value types ZEN compares against and that survive the\n * compile/lift round-trip (strings, numbers, booleans). Objects and null are not\n * representable as a rule operand.\n */\nexport type ConditionScalar = string | number | boolean;\n\n/**\n * A rule's right-hand operand, whose shape follows the operator's arity:\n *\n * - comparison / string operators (`eq`, `gt`, `contains`, …) take a single\n * {@link ConditionScalar};\n * - membership operators (`in`, `not_in`) take an array of scalars;\n * - the emptiness operators (`is_empty`, `is_not_empty`) take no operand and omit\n * `right` entirely.\n *\n * The builder keeps a rule's `right` consistent with its operator's arity as the\n * author switches operators.\n */\nexport type ConditionTreeValue = ConditionScalar | readonly ConditionScalar[];\n\n/**\n * The tree's operator vocabulary as a runtime list — the compiler's full\n * {@link CONDITION_OPERATORS} set under the tree name. An alias, not a second\n * hand-maintained list, so the tree vocabulary can never drift from the compiler's;\n * it gives tree code and the builder UI one tree-named import for the operators they\n * support.\n */\nexport const CONDITION_TREE_OPERATORS = CONDITION_OPERATORS;\n\n/**\n * The tree's operator type: every operator {@link compileCondition} can emit (all\n * 14). The builder deliberately constructs the whole set, so there is no \"the library\n * compiles it but the builder cannot express it\" gap. Derived from\n * {@link CONDITION_TREE_OPERATORS} so the type and the runtime list share one\n * definition; identical to the compiler's `ConditionOperator`.\n */\nexport type ConditionTreeOperator = typeof CONDITION_TREE_OPERATORS[number];\n\n/**\n * A leaf comparison. `left` is a field path emitted **verbatim** into ZEN (guarded\n * by the same identifier-path predicate as {@link compileCondition}); `right`\n * carries the operand for the operator's arity (see {@link ConditionTreeValue}) and\n * is absent for the emptiness operators.\n */\nexport interface ConditionTreeRule {\n kind: \"rule\";\n left: string;\n operator: ConditionTreeOperator;\n right?: ConditionTreeValue;\n}\n\n/**\n * A boolean group joining its `items` with `op`. Items are leaf rules or nested\n * groups, making the model an arbitrarily deep and/or tree. The root of a lifted\n * condition is always a group, so callers can treat {@link ConditionTreeGroup} as\n * the tree's entry type.\n */\nexport interface ConditionTreeGroup {\n kind: \"group\";\n op: \"and\" | \"or\";\n items: readonly ConditionTreeNode[];\n}\n\n/**\n * A node in the condition tree: a nested group or a leaf rule.\n */\nexport type ConditionTreeNode = ConditionTreeGroup | ConditionTreeRule;\n","import type { ExpressionContext } from \"./loader\";\n\nimport { getEngineSync, loadEngine } from \"./loader\";\n\n/**\n * Evaluate a standard ZEN expression, loading the engine on first use.\n *\n * `T` is an **unchecked** assertion — the value is returned as `T` with no\n * runtime validation, and ZEN's result type depends on the expression and\n * context. Prefer the `unknown` default and narrow at the call site.\n */\nexport async function evaluate<T = unknown>(expression: string, context?: ExpressionContext): Promise<T> {\n await loadEngine();\n return evaluateSync<T>(expression, context);\n}\n\n/**\n * Evaluate a ZEN unary (test) expression, loading the engine on first use.\n */\nexport async function evaluateUnary(expression: string, context?: ExpressionContext): Promise<boolean> {\n await loadEngine();\n return evaluateUnarySync(expression, context);\n}\n\n/**\n * Evaluate a standard ZEN expression synchronously. Throws\n * {@link ExpressionNotReadyError} when the engine has not loaded yet — use this\n * only behind a readiness gate (e.g. under `<ExpressionEngineProvider>`).\n */\nexport function evaluateSync<T = unknown>(expression: string, context?: ExpressionContext): T {\n return getEngineSync().evaluate<T>(expression, context);\n}\n\n/**\n * Evaluate a ZEN unary (test) expression synchronously. Throws\n * {@link ExpressionNotReadyError} when the engine has not loaded yet.\n */\nexport function evaluateUnarySync(expression: string, context?: ExpressionContext): boolean {\n return getEngineSync().evaluateUnary(expression, context);\n}\n","/**\n * The locales the library ships with out of the box.\n */\nexport type BuiltInExpressionLocale = \"en-US\" | \"zh-CN\";\n\n/**\n * A locale key for editor-produced text (built-in descriptions, type-check prose,\n * and diagnostic origin labels — the wasm engine's own error bodies, identifiers,\n * and type signatures are never translated). The built-in locales surface in\n * autocomplete, while any string is accepted so a host can select a locale\n * registered through {@link registerExpressionLocale}.\n */\nexport type ExpressionLocale = BuiltInExpressionLocale | (string & {});\n\n/**\n * The catalog of editor-produced messages for one locale. All localized text the\n * editor surfaces flows through this interface, so a host can switch language or\n * override individual strings in one typed place — and add a language the library\n * does not ship by passing a full `messages` object to\n * {@link configureExpressionMessages}.\n */\nexport interface ExpressionMessages {\n /**\n * The diagnostic origin label for a wasm error `type` (`\"parserError\"` →\n * `\"Parser error\"`), falling back to a generic label for an unknown type.\n */\n sourceLabel: (type?: string) => string;\n /**\n * Translate a built-in's English `info` description to this locale. Returns the\n * input unchanged for English or when no translation exists.\n */\n completionInfo: (englishInfo: string) => string;\n /**\n * The `source` label shown on type-check (as opposed to syntax) diagnostics.\n */\n typeCheckSource: string;\n /**\n * Warning shown when a unary (test) expression does not evaluate to a boolean.\n */\n expectedBoolean: (actualType: string) => string;\n /**\n * Warning shown when a standard expression's result type does not satisfy the\n * configured expected type.\n */\n expectedType: (expectedType: string, actualType: string) => string;\n}\n\n// Built-in descriptions, keyed by the engine's English `info` text rather than by\n// label. The builtin catalog is finite and the descriptions are stable, while\n// labels collide (the function `year` and the Date method `year` share a name but\n// carry different descriptions) — keying on the description sidesteps that and\n// degrades gracefully (an unmapped/renamed builtin falls through to English).\nconst COMPLETION_INFO_ZH: Record<string, string> = {\n // String / array / object functions\n \"Returns the length of variable\": \"返回变量的长度\",\n \"Checks if variable contains a needle\": \"检查变量是否包含指定元素\",\n \"Flattens an array\": \"将数组扁平化\",\n \"Merges multiple objects into one.\": \"将多个对象合并为一个。\",\n \"Deeply merges multiple objects into one.\": \"将多个对象深度合并为一个。\",\n \"Converts all characters in a string to uppercase\": \"将字符串中所有字符转换为大写\",\n \"Converts all characters in a string to lowercase\": \"将字符串中所有字符转换为小写\",\n \"Returns the string with leading and trailing whitespace removed\": \"返回去除首尾空白后的字符串\",\n \"Returns true if the string starts with the specified prefix\": \"若字符串以指定前缀开头则返回 true\",\n \"Returns true if the string ends with the specified suffix\": \"若字符串以指定后缀结尾则返回 true\",\n \"Returns true if the string matches the specified pattern\": \"若字符串匹配指定模式则返回 true\",\n \"Extracts matching substrings according to a pattern\": \"按模式提取匹配的子串\",\n \"Performs a fuzzy search of the needle in the haystack, and returns the match score(s).\": \"在目标中对关键字进行模糊搜索,并返回匹配得分。\",\n \"Splits a string into an array of substrings using the specified delimiter.\": \"使用指定分隔符将字符串拆分为子串数组。\",\n\n // Math / aggregation functions\n \"Returns the absolute value of a number\": \"返回数字的绝对值\",\n \"Returns the sum of all elements in the input array.\": \"返回输入数组中所有元素之和。\",\n \"Calculates the average of all elements in the input array.\": \"计算输入数组中所有元素的平均值。\",\n \"Returns the smallest of the elements in the input array.\": \"返回输入数组中的最小元素。\",\n \"Returns the largest of the elements in the input array.\": \"返回输入数组中的最大元素。\",\n \"Generates a random number between 0 (inclusive) and max (inclusive).\": \"生成 0 到 max(均包含)之间的随机数。\",\n \"Calculates the median value of all elements in the input array.\": \"计算输入数组中所有元素的中位数。\",\n \"Finds the mode(s) of the input array, which are the most frequent element(s).\": \"求输入数组的众数,即出现最频繁的元素。\",\n \"Rounds a number down to the nearest integer.\": \"向下取整到最接近的整数。\",\n \"Rounds a number up to the nearest integer.\": \"向上取整到最接近的整数。\",\n \"Rounds a number to a specified number of decimal places.\": \"将数字四舍五入到指定的小数位数。\",\n \"Truncates a number to a specified number of decimal places.\": \"将数字截断到指定的小数位数。\",\n\n // Type / conversion functions\n \"Checks if the given value is of a numeric type.\": \"检查给定值是否为数值类型。\",\n \"Converts the given value to a string.\": \"将给定值转换为字符串。\",\n \"Converts the given value to a number.\": \"将给定值转换为数字。\",\n \"Converts the given value to a boolean.\": \"将给定值转换为布尔值。\",\n \"Returns a string representing the data type of the value.\": \"返回表示该值数据类型的字符串。\",\n \"Returns an array of a given object's own enumerable property names.\": \"返回由给定对象自身可枚举属性名组成的数组。\",\n \"Returns an array of a given object's own enumerable property values.\": \"返回由给定对象自身可枚举属性值组成的数组。\",\n\n // Date / time functions\n \"Returns a new date time instance.\": \"返回一个新的日期时间实例。\",\n \"Converts a numeric timestamp to a unix timestamp.\": \"将数值时间戳转换为 Unix 时间戳。\",\n \"Extracts the time from a numeric timestamp and returns it as a seconds from beginning of day.\": \"从数值时间戳中提取时间,以当天起始的秒数返回。\",\n \"e.g. 1h30min\": \"例如 1h30min\",\n \"Extracts the year from a given timestamp.\": \"从给定时间戳中提取年份。\",\n \"Gets the day of the week from a given timestamp, where Sunday might be 0.\": \"获取给定时间戳的星期几(周日可能为 0)。\",\n \"Extracts the day of the month from a given timestamp.\": \"从给定时间戳中提取当月的日期。\",\n \"Gets the day of the year from a given timestamp.\": \"获取给定时间戳在一年中的第几天。\",\n \"Calculates the week of the year from a given timestamp.\": \"计算给定时间戳在一年中的第几周。\",\n \"Extracts the month from a given timestamp, typically with January as 1.\": \"从给定时间戳中提取月份(通常 1 月为 1)。\",\n \"Converts the month from a given timestamp into its string representation (e.g., 'Jan').\": \"将给定时间戳的月份转换为字符串表示(例如 'Jan')。\",\n \"Converts a timestamp to a human-readable date string.\": \"将时间戳转换为人类可读的日期字符串。\",\n \"Converts the day of the week from a given timestamp into its string representation (e.g., 'Mon').\": \"将给定时间戳的星期几转换为字符串表示(例如 'Mon')。\",\n \"Returns the timestamp representing the start of a specified unit (e.g., day, month, year) based on a given timestamp.\": \"返回给定时间戳在指定单位(如日、月、年)起始处的时间戳。\",\n \"Returns the timestamp representing the end of a specified unit (e.g., day, month, year) based on a given timestamp.\": \"返回给定时间戳在指定单位(如日、月、年)结束处的时间戳。\",\n\n // Higher-order array functions\n \"Checks if all elements in the array satisfy the condition defined in the callback.\": \"检查数组中所有元素是否都满足回调中定义的条件。\",\n \"Checks if no elements in the array satisfy the condition defined in the callback.\": \"检查数组中是否没有任何元素满足回调中定义的条件。\",\n \"Checks if at least one element in the array satisfies the condition defined in the callback.\": \"检查数组中是否至少有一个元素满足回调中定义的条件。\",\n \"Checks if exactly one element in the array satisfies the condition defined in the callback.\": \"检查数组中是否恰好有一个元素满足回调中定义的条件。\",\n \"Creates a new array with all elements that satisfy the condition defined in the callback.\": \"创建一个仅包含满足回调中定义条件的元素的新数组。\",\n \"Creates a new array populated with the results of calling the provided function on every element in the calling array.\": \"创建一个新数组,其元素为对原数组每个元素调用所提供函数的结果。\",\n \"First maps each element using a mapping function, then flattens the result into a new array.\": \"先用映射函数处理每个元素,再将结果扁平化为新数组。\",\n \"Counts the number of elements in the array that satisfy the condition defined in the callback.\": \"统计数组中满足回调中定义条件的元素个数。\",\n\n // Date methods\n \"Adds time to a date\": \"为日期增加时间\",\n \"Subtracts time from a date\": \"从日期中减去时间\",\n \"Sets a specific unit of time on a date\": \"设置日期的某个时间单位\",\n \"Formats a date into a string representation\": \"将日期格式化为字符串\",\n \"Returns the start of a specified time unit for a date\": \"返回日期在指定时间单位上的起始\",\n \"Returns the end of a specified time unit for a date\": \"返回日期在指定时间单位上的结束\",\n \"Calculates the difference between two dates\": \"计算两个日期之间的差值\",\n \"Converts a date to a different timezone\": \"将日期转换到不同的时区\",\n \"Checks if two dates are the same\": \"检查两个日期是否相同\",\n \"Checks if a date is before another date\": \"检查日期是否早于另一个日期\",\n \"Checks if a date is after another date\": \"检查日期是否晚于另一个日期\",\n \"Checks if a date is the same as or before another date\": \"检查日期是否等于或早于另一个日期\",\n \"Checks if a date is the same as or after another date\": \"检查日期是否等于或晚于另一个日期\",\n \"Gets the seconds of a date\": \"获取日期的秒\",\n \"Gets the minutes of a date\": \"获取日期的分钟\",\n \"Gets the hours of a date\": \"获取日期的小时\",\n \"Gets the day of the month for a date\": \"获取日期在当月的第几天\",\n \"Gets the day of the year for a date\": \"获取日期在当年的第几天\",\n \"Gets the week of the year for a date\": \"获取日期在当年的第几周\",\n \"Gets the day of the week for a date\": \"获取日期的星期几\",\n \"Gets the month for a date\": \"获取日期的月份\",\n \"Gets the quarter for a date\": \"获取日期所在的季度\",\n \"Gets the year for a date\": \"获取日期的年份\",\n \"Gets the Unix timestamp for a date\": \"获取日期的 Unix 时间戳\",\n \"Gets the timezone offset name for a date\": \"获取日期的时区偏移名称\",\n \"Checks if a date is valid\": \"检查日期是否有效\",\n \"Checks if a date is yesterday\": \"检查日期是否为昨天\",\n \"Checks if a date is today\": \"检查日期是否为今天\",\n \"Checks if a date is tomorrow\": \"检查日期是否为明天\",\n \"Checks if the year of a date is a leap year\": \"检查日期所在年份是否为闰年\"\n};\n\nconst EN_SOURCE_LABELS: Record<string, string> = {\n lexerError: \"Lexer error\",\n parserError: \"Parser error\",\n compilerError: \"Compiler error\",\n vmError: \"VM error\"\n};\n\nconst ZH_SOURCE_LABELS: Record<string, string> = {\n lexerError: \"词法错误\",\n parserError: \"语法错误\",\n compilerError: \"编译错误\",\n vmError: \"运行时错误\"\n};\n\nfunction sourceLabelFrom(table: Record<string, string>, fallback: string, type: string | undefined): string {\n return (type === undefined ? undefined : table[type]) ?? fallback;\n}\n\n/**\n * Built-in English message catalog (the default).\n */\nexport const enMessages: ExpressionMessages = {\n sourceLabel: type => sourceLabelFrom(EN_SOURCE_LABELS, \"Error\", type),\n completionInfo: info => info,\n typeCheckSource: \"Type check\",\n expectedBoolean: actualType => `Expected a boolean test expression, received \\`${actualType}\\`.`,\n expectedType: (expectedType, actualType) => `Expected \\`${expectedType}\\`, received \\`${actualType}\\`.`\n};\n\n/**\n * Built-in Simplified Chinese message catalog.\n */\nexport const zhCNMessages: ExpressionMessages = {\n sourceLabel: type => sourceLabelFrom(ZH_SOURCE_LABELS, \"错误\", type),\n completionInfo: info => info === \"\" ? \"\" : COMPLETION_INFO_ZH[info] ?? info,\n typeCheckSource: \"类型检查\",\n expectedBoolean: actualType => `期望布尔测试表达式,实际类型为 \\`${actualType}\\`。`,\n expectedType: (expectedType, actualType) => `期望 \\`${expectedType}\\`,实际为 \\`${actualType}\\`。`\n};\n\n// The locale registry. Built-ins are pre-registered; a host adds its own through\n// registerExpressionLocale, so a new language is a first-class, selectable locale\n// rather than a special case — the design stays open as more languages are added.\nconst localeRegistry = new Map<string, ExpressionMessages>([\n [\"en-US\", enMessages],\n [\"zh-CN\", zhCNMessages]\n]);\n\n// Module-global active catalog. The sync editor accessors (completion / lint) read\n// it without a React context, mirroring the engine singleton itself.\nlet activeMessages: ExpressionMessages = enMessages;\n\n/**\n * Register (or replace) the message catalog for a locale key, making it selectable\n * via {@link configureExpressionMessages}. This is how a host adds a language the\n * library does not ship — the built-in locales are registered the same way, so\n * there is no privileged path.\n */\nexport function registerExpressionLocale(locale: string, messages: ExpressionMessages): void {\n localeRegistry.set(locale, messages);\n}\n\nexport interface ConfigureMessagesOptions {\n /**\n * A registered locale to use as the base — built-in or registered through\n * {@link registerExpressionLocale}. An unknown key keeps the current base; omit\n * to keep the current base.\n */\n locale?: ExpressionLocale;\n /**\n * Per-message overrides merged over the base — a quick way to tweak a few strings\n * without registering a whole locale.\n */\n messages?: Partial<ExpressionMessages>;\n}\n\n/**\n * Configure the active message catalog. Pass a `locale`, a partial `messages`\n * override, or both (overrides win). Idempotent and module-global, so a host\n * configures it once (e.g. through `ExpressionConfigProvider`).\n */\nexport function configureExpressionMessages({ locale, messages }: ConfigureMessagesOptions): void {\n const base = locale === undefined ? activeMessages : localeRegistry.get(locale) ?? activeMessages;\n activeMessages = messages === undefined ? base : { ...base, ...messages };\n}\n\n/**\n * The active {@link ExpressionMessages} catalog (English by default).\n */\nexport function getExpressionMessages(): ExpressionMessages {\n return activeMessages;\n}\n","import { isRecord } from \"../internal/predicates\";\nimport { getEngineSync, loadEngine } from \"./loader\";\nimport { getExpressionMessages } from \"./messages\";\n\n/**\n * How an expression editor interprets its document:\n *\n * - `\"standard\"` — a single ZEN value expression.\n * - `\"unary\"` — a single ZEN unary (test) expression, evaluating to a boolean.\n * - `\"template\"` — literal text with `{{ expression }}` holes; each hole is a\n * standard value expression, the surrounding text is inert.\n *\n * The single-expression intellisense functions here ({@link getDiagnosticsSync},\n * {@link analyzeTypesSync}) treat a `\"template\"` argument as `\"standard\"` — they\n * analyze one expression. Whole template documents are handled hole-by-hole by\n * the template functions ({@link analyzeTemplateSync},\n * {@link getTemplateDiagnosticsSync}).\n */\nexport type ExpressionMode = \"standard\" | \"unary\" | \"template\";\n\n/**\n * A ZEN type descriptor — the shape the engine understands for type-aware\n * completion and validation: a primitive kind name or one of the structural\n * variants `{ Const }`, `{ Enum }`, `{ Array }`, `{ Object }`.\n *\n * Defined here rather than aliasing the wasm `VariableTypeJson` because the\n * engine also emits and accepts `\"Date\"` — its sole method-receiver kind — which\n * that type omits. Aliasing it would make a Date-typed variable unrepresentable\n * and break `kind === \"Date\"` narrowing; the structural variants mirror the wasm\n * shape exactly so a value crosses the boundary unchanged.\n */\nexport type ExpressionType\n = | \"Any\"\n | \"Null\"\n | \"Bool\"\n | \"String\"\n | \"Number\"\n | \"Date\"\n | { Const: string }\n | { Enum: [string | undefined, string[]] }\n | { Array: ExpressionType }\n | { Object: Record<string, ExpressionType> };\n\n/**\n * A single syntax / type diagnostic, positioned by character offset.\n */\nexport interface ExpressionDiagnostic {\n /**\n * Start offset in the source.\n */\n from: number;\n /**\n * End offset in the source.\n */\n to: number;\n /**\n * Human-readable error message.\n */\n message: string;\n /**\n * Diagnostic origin label (e.g. `\"Parser error\"`).\n */\n source: string;\n}\n\n/**\n * An autocomplete suggestion for a ZEN built-in (function / method / variable).\n */\nexport interface ExpressionCompletion {\n type: \"function\" | \"method\" | \"variable\";\n label: string;\n detail: string;\n info: string;\n boost: number | null;\n /**\n * For methods, the type-kind they attach to (e.g. `\"Date\"`); otherwise `null`.\n */\n methodFor: string | null;\n}\n\n/**\n * The inferred type of one span of the source, produced by type-checking.\n */\nexport interface ExpressionTypeSpan {\n error: string | null;\n kind: ExpressionType;\n nodeKind: string;\n span: [number, number];\n}\n\n/**\n * The result of type-checking an expression against a variable context.\n */\nexport interface ExpressionAnalysis {\n /**\n * The root context type (the variables object).\n */\n rootKind: ExpressionType;\n /**\n * Per-span inferred types; `spans[0]` is the whole-expression result type.\n */\n spans: ExpressionTypeSpan[];\n}\n\n// Strict offset parsing: Number() rejects partial-numeric text (\"12:30\") that\n// parseInt would silently truncate to 12, and the empty-string guard covers\n// Number(\"\") being 0 rather than NaN.\nfunction parseOffset(text: string | undefined): number {\n const trimmed = text?.trim();\n return trimmed ? Number(trimmed) : NaN;\n}\n\n/**\n * Parse a trailing `... at (from, to)` / `... at pos` position out of a ZEN error\n * message. Returns a `[from, to]` range — a single offset collapses to\n * `[pos, pos]` — or `null` when no position is present.\n */\nexport function extractPosition(message: string): [number, number] | null {\n const segments = message.split(\" at \");\n const last = segments.length <= 1 ? undefined : segments.at(-1);\n\n if (last === undefined) {\n return null;\n }\n\n const [left, right] = last.replace(\"(\", \"\").replace(\")\", \"\").split(\", \");\n const from = parseOffset(left);\n\n if (Number.isNaN(from)) {\n return null;\n }\n\n const to = parseOffset(right);\n return [from, Number.isNaN(to) ? from : to];\n}\n\n/**\n * Normalize the wasm validate payload (`null` or `{ type, source }`) into a\n * positioned {@link ExpressionDiagnostic}, or `null` when the expression is valid.\n */\nexport function normalizeDiagnostic(raw: unknown, source: string): ExpressionDiagnostic | null {\n if (raw === null || raw === undefined) {\n return null;\n }\n\n const errorType = isRecord(raw) && typeof raw.type === \"string\" ? raw.type : undefined;\n const message = isRecord(raw) && typeof raw.source === \"string\" ? raw.source : String(raw);\n const [from, to] = extractPosition(message) ?? [0, source.length];\n\n return {\n from,\n to,\n message,\n source: getExpressionMessages().sourceLabel(errorType)\n };\n}\n\n/**\n * Normalize the wasm `getCompletions` payload into a typed list, dropping\n * malformed entries and stripping the backtick markers ZEN wraps type names in.\n */\nexport function normalizeCompletions(raw: unknown): ExpressionCompletion[] {\n if (!Array.isArray(raw)) {\n return [];\n }\n\n const messages = getExpressionMessages();\n\n return raw.flatMap((entry): ExpressionCompletion[] => {\n if (!isRecord(entry) || typeof entry.label !== \"string\" || entry.label === \"\") {\n return [];\n }\n\n return [\n {\n type: entry.type === \"method\" || entry.type === \"variable\" ? entry.type : \"function\",\n label: entry.label,\n detail: typeof entry.detail === \"string\" ? entry.detail.replaceAll(\"`\", \"\") : \"\",\n info: messages.completionInfo(typeof entry.info === \"string\" ? entry.info : \"\"),\n boost: typeof entry.boost === \"number\" ? entry.boost : null,\n methodFor: typeof entry.methodFor === \"string\" ? entry.methodFor : null\n }\n ];\n });\n}\n\n/**\n * Validate an expression, loading the engine on first use. Resolves to `null`\n * when the expression is valid, or a positioned diagnostic otherwise.\n */\nexport async function getDiagnostics(expression: string, mode: ExpressionMode): Promise<ExpressionDiagnostic | null> {\n await loadEngine();\n return getDiagnosticsSync(expression, mode);\n}\n\n/**\n * Synchronous {@link getDiagnostics}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet — use only behind a readiness gate.\n */\nexport function getDiagnosticsSync(expression: string, mode: ExpressionMode): ExpressionDiagnostic | null {\n const engine = getEngineSync();\n return normalizeDiagnostic(mode === \"unary\" ? engine.validateUnary(expression) : engine.validate(expression), expression);\n}\n\n/**\n * Return the ZEN built-in completion list, loading the engine on first use.\n */\nexport async function getCompletionItems(): Promise<ExpressionCompletion[]> {\n await loadEngine();\n return getCompletionItemsSync();\n}\n\n/**\n * Synchronous {@link getCompletionItems}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet.\n */\nexport function getCompletionItemsSync(): ExpressionCompletion[] {\n return normalizeCompletions(getEngineSync().getCompletions());\n}\n\n/**\n * Type-check an expression against a `variables` context, loading the engine on\n * first use. See {@link ExpressionAnalysis}.\n */\nexport async function analyzeTypes(\n variables: ExpressionType,\n source: string,\n mode: ExpressionMode\n): Promise<ExpressionAnalysis> {\n await loadEngine();\n return analyzeTypesSync(variables, source, mode);\n}\n\n/**\n * Synchronous {@link analyzeTypes}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet.\n */\nexport function analyzeTypesSync(variables: ExpressionType, source: string, mode: ExpressionMode): ExpressionAnalysis {\n return getEngineSync().analyze(variables, source, mode === \"unary\");\n}\n\n/**\n * Whether `actual` satisfies (is assignable to) `expected`, loading the engine on\n * first use. Used for expected-return-type validation.\n */\nexport async function satisfiesType(actual: ExpressionType, expected: ExpressionType): Promise<boolean> {\n await loadEngine();\n return satisfiesTypeSync(actual, expected);\n}\n\n/**\n * Synchronous {@link satisfiesType}. Throws `ExpressionNotReadyError` if the\n * engine has not loaded yet.\n */\nexport function satisfiesTypeSync(actual: ExpressionType, expected: ExpressionType): boolean {\n return getEngineSync().satisfies(actual, expected);\n}\n","import type { ExpressionAnalysis, ExpressionDiagnostic, ExpressionType } from \"./intellisense\";\n\nimport { ExpressionNotReadyError } from \"./errors\";\nimport { analyzeTypesSync, getDiagnosticsSync } from \"./intellisense\";\nimport { isEngineReady, loadEngine } from \"./loader\";\n\n/**\n * One `{{ expression }}` hole located in a template document. `from` / `to` are\n * the character offsets of the inner expression itself — the text between the\n * `{{` and `}}` delimiters, delimiters excluded — so a hole's diagnostics and\n * inferred-type spans map straight back onto the template with no delimiter math.\n */\nexport interface TemplateHole {\n /**\n * Offset of the inner expression start (immediately after `{{`).\n */\n from: number;\n /**\n * Offset of the inner expression end (immediately before `}}`).\n */\n to: number;\n /**\n * The inner expression text, i.e. `source.slice(from, to)`.\n */\n expression: string;\n}\n\n// A hole is `{{`, then any run of non-brace characters, then `}}` — the exact\n// shape the `@gorules/lezer-zen-template` grammar recognizes (its `anyChar` token\n// is `![{}]+`), so string-level hole extraction and the editor's mixed parse\n// agree on where holes are. Braces cannot appear inside a hole; an unterminated\n// `{{` is not a hole. Kept module-level: a global-flag regex is stateful\n// (`lastIndex`), so each call resets it before scanning.\nconst HOLE_PATTERN = /\\{\\{(?<expression>[^{}]*)\\}\\}/g;\n\n/**\n * Extract every `{{ expression }}` hole from a template document, in source\n * order. Literal text outside holes is ignored. Pure string scan — no engine\n * required — matching the `@gorules/lezer-zen-template` hole grammar, so it is\n * consistent with the editor's highlighting and completion gating.\n */\nexport function parseTemplateHoles(source: string): TemplateHole[] {\n const holes: TemplateHole[] = [];\n\n HOLE_PATTERN.lastIndex = 0;\n\n for (let match = HOLE_PATTERN.exec(source); match !== null; match = HOLE_PATTERN.exec(source)) {\n const inner = match.groups?.expression ?? \"\";\n const from = match.index + 2;\n holes.push({\n from,\n to: from + inner.length,\n expression: inner\n });\n }\n\n return holes;\n}\n\n/**\n * The template hole whose inner expression range contains `pos` (boundaries\n * included, so a caret sitting right after `{{` or right before `}}` counts as\n * inside), or `null` when `pos` is in literal text. Drives the editor's\n * hole-scoped completion and hover: intelligence fires inside a hole, nothing in\n * the surrounding literal text.\n */\nexport function templateHoleAt(source: string, pos: number): TemplateHole | null {\n return parseTemplateHoles(source).find(hole => pos >= hole.from && pos <= hole.to) ?? null;\n}\n\n// Whether a hole carries an expression worth analyzing: an empty or\n// whitespace-only hole (`{{}}`, `{{ }}`) is an incomplete edit, not an error, so\n// it gets neither diagnostics nor type spans.\nfunction hasExpression(hole: TemplateHole): boolean {\n return hole.expression.trim().length > 0;\n}\n\n/**\n * Type-check every hole of a template against a `variables` context and merge the\n * results into one {@link ExpressionAnalysis} whose spans are offset onto the\n * template document. Each hole is a standard ZEN value expression; literal text\n * contributes nothing. `rootKind` is the context itself (shared by every hole),\n * so top-level completion works even in an empty hole.\n *\n * Best-effort: a hole that fails to analyze (e.g. mid-edit syntax) is skipped\n * rather than discarding the spans of its siblings. Throws\n * {@link ExpressionNotReadyError} if the engine has not loaded — use only behind\n * a readiness gate.\n */\nexport function analyzeTemplateSync(variables: ExpressionType, source: string): ExpressionAnalysis {\n if (!isEngineReady()) {\n throw new ExpressionNotReadyError();\n }\n\n const spans: ExpressionAnalysis[\"spans\"] = [];\n\n for (const hole of parseTemplateHoles(source)) {\n if (!hasExpression(hole)) {\n continue;\n }\n\n try {\n for (const span of analyzeTypesSync(variables, hole.expression, \"standard\").spans) {\n spans.push({\n ...span,\n span: [span.span[0] + hole.from, span.span[1] + hole.from]\n });\n }\n } catch {\n // Skip this hole; siblings keep their inferred types.\n }\n }\n\n return { rootKind: variables, spans };\n}\n\n/**\n * Async {@link analyzeTemplateSync}, loading the engine on first use.\n */\nexport async function analyzeTemplate(variables: ExpressionType, source: string): Promise<ExpressionAnalysis> {\n await loadEngine();\n return analyzeTemplateSync(variables, source);\n}\n\n/**\n * Validate every hole of a template and return their syntax diagnostics, each\n * offset onto the template document (empty holes and literal text produce none).\n * The single-expression {@link getDiagnosticsSync} yields at most one diagnostic\n * per hole, so a template with several broken holes surfaces each one.\n *\n * Throws {@link ExpressionNotReadyError} if the engine has not loaded — use only\n * behind a readiness gate.\n */\nexport function getTemplateDiagnosticsSync(source: string): ExpressionDiagnostic[] {\n if (!isEngineReady()) {\n throw new ExpressionNotReadyError();\n }\n\n const diagnostics: ExpressionDiagnostic[] = [];\n\n for (const hole of parseTemplateHoles(source)) {\n if (!hasExpression(hole)) {\n continue;\n }\n\n const diagnostic = getDiagnosticsSync(hole.expression, \"standard\");\n\n if (diagnostic) {\n diagnostics.push({\n ...diagnostic,\n from: diagnostic.from + hole.from,\n to: diagnostic.to + hole.from\n });\n }\n }\n\n return diagnostics;\n}\n\n/**\n * Async {@link getTemplateDiagnosticsSync}, loading the engine on first use.\n */\nexport async function getTemplateDiagnostics(source: string): Promise<ExpressionDiagnostic[]> {\n await loadEngine();\n return getTemplateDiagnosticsSync(source);\n}\n"],"mappings":";;;;;;;AAKA,IAAa,kBAAb,cAAqC,MAAM;CACzC;CAEA,YAAY,SAAiB,YAAqB,OAAiB;EACjE,MAAM,SAAS,EAAE,MAAM,CAAC;EACxB,KAAK,OAAO;EACZ,KAAK,aAAa;CACpB;AACF;;;;;;AAOA,IAAa,0BAAb,cAA6C,gBAAgB;CAC3D,YACE,UAAU,wGACV;EACA,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;;;;;ACrBA,SAAgB,YAAY,OAAoC;CAC9D,OAAO,UAAU,KAAA;AACnB;AAEA,SAAgB,SAAS,OAAiC;CACxD,OAAO,OAAO,UAAU;AAC1B;AAEA,SAAgB,QAAQ,OAAoC;CAC1D,OAAO,MAAM,QAAQ,KAAK;AAC5B;AAEA,SAAgB,UAAU,OAA2C;CACnE,OAAO,UAAU,QAAQ,UAAU,KAAA;AACrC;AAEA,SAAgB,SAAS,OAAkD;CACzE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;ACoDA,IAAI,gBAAkD;AACtD,IAAI,aAAsC;AAC1C,IAAI,cAAsC;AAC1C,IAAI;AAEJ,IAAI,mBAAuC;AAE3C,SAAS,eAAkB,YAAoB,KAAiB;CAC9D,IAAI;EACF,OAAO,IAAI;CACb,SAAS,OAAO;EACd,MAAM,IAAI,gBAAgB,kCAAkC,cAAc,YAAY,KAAK;CAC7F;AACF;AAEA,SAAS,qBAA6B;CACpC,MAAM,OAAO;CAIb,IAAI,OAAO,WAAW,aACpB,OAAO,GAAG,KAAK;CAGjB,OAAO;AACT;;;;;;;AAQA,SAAgB,gBAAgB,SAAkC;CAChE,IAAI,iBAAiB,YACnB,MAAM,IAAI,gBAAgB,2DAA2D;CAGvF,kBAAkB,QAAQ;AAC5B;;;;;;;;;;;;;;;;;AAkBA,SAAgB,aAAwC;CACtD,IAAI,eACF,OAAO;CAGT,cAAc;CACd,iBAAiB,YAAY;EAC3B,MAAM,MAAM,MAAM,OAAO;EAEzB,MAAM,IAAI,QAAQ,YAAY,eAAe,IAAI,KAAA,IAAY,EAAE,gBAAgB,gBAAgB,CAAC;EAKhG,MAAM,kBAAkB,SAAuC,IAAI,aAAa,SAAS,IAAwB;EASjH,MAAM,qBAAqB,cAA2C;GACpE,IAAI,oBAAoB,iBAAiB,cAAc,WACrD,OAAO;GAGT,MAAM,QAAQ;GACd,mBAAmB;GACnB,OAAO,OAAO,KAAK;GACnB,MAAM,SAAS,eAAe,SAAS;GAEvC,IAAI;IACF,mBAAmB;KACjB;KACA;KACA,UAAU,OAAO,OAAO;IAC1B;GACF,SAAS,OAAO;IACd,OAAO,KAAK;IACZ,MAAM;GACR;GAEA,OAAO;EACT;EAEA,MAAM,SAA2B,OAAO,OAAO;GAC7C,WAAwB,YAAoB,UAA6B,CAAC,MAAS,eAAe,kBAAkB,IAAI,mBAAmB,YAAY,OAAO,CAAM;GACpK,gBAAgB,YAAoB,UAA6B,CAAC,MAAe,eAAe,kBAAkB,IAAI,wBAAwB,YAAY,OAAO,CAAC;GAClK,WAAW,eAAgC,IAAI,mBAAmB,UAAU;GAC5E,gBAAgB,eAAgC,IAAI,wBAAwB,UAAU;GACtF,sBAA+B,IAAI,eAAe;GAClD,UAAU,WAA2B,QAAgB,UAAuC;IAC1F,MAAM,UAAU,kBAAkB,SAAS;IAI3C,MAAM,WAAoB,QAAQ,QAAQ,OAAO,eAAe,MAAM,IAAI,QAAQ,OAAO,UAAU,MAAM;IAEzG,OAAO;KACL,UAAU,QAAQ;KAClB,OAAO,MAAM,QAAQ,QAAQ,IAAI,WAAmC,CAAC;IACvE;GACF;GAKA,YAAY,QAAwB,aAAsC;IACxE,MAAM,aAAa,eAAe,MAAM;IAExC,IAAI;KACF,MAAM,eAAe,eAAe,QAAQ;KAE5C,IAAI;MACF,OAAO,WAAW,UAAU,YAAY;KAC1C,UAAU;MACR,aAAa,KAAK;KACpB;IACF,UAAU;KACR,WAAW,KAAK;IAClB;GACF;GACA,eAAwB,IAAI,QAAQ;EACtC,CAAC;EAED,aAAa;EACb,OAAO;CACT,EAAA,CAAG,CAAC,CAAC,OAAO,UAAmB;EAI7B,gBAAgB;EAChB,cAAc,IAAI,gBAAgB,mBAAmB,GAAG,KAAA,GAAW,KAAK;EACxE,MAAM;CACR,CAAC;CAED,OAAO;AACT;;;;AAKA,SAAgB,gBAAyB;CACvC,OAAO,YAAY,QAAQ,KAAK;AAClC;;;;;;;AAQA,SAAgB,iBAAyC;CACvD,OAAO;AACT;;;;;AAMA,SAAgB,gBAAkC;CAChD,IAAI,CAAC,YACH,MAAM,IAAI,wBAAwB;CAGpC,OAAO;AACT;;;;;;;AAQA,SAAgB,cAAoB;CAClC,kBAAkB,OAAO,KAAK;CAC9B,mBAAmB;CACnB,gBAAgB;CAChB,aAAa;CACb,cAAc;CACd,kBAAkB,KAAA;AACpB;;;;;;;;;;;;;AC1QA,MAAa,QAAiB,UAAU;AAExC,SAAS,YAAqB;CAC5B,IAAI,OAAO,YAAY,aACrB,OAAO;CAGT,OAAO,QAAQ,MAAM,QAAQ,IAAI,aAAa,eAAe;AAC/D;;;;;;;;;;;ACNA,MAAM,kBAAkB;AAMxB,MAAM,qBAAqB,IAAI,IAAI;CAAC;CAAO;CAAM;CAAO;CAAM;CAAQ;CAAS;AAAM,CAAC;;;;;AAMtF,SAAgB,iBAAiB,SAA0B;CACzD,OAAO,gBAAgB,KAAK,OAAO,MAC7B,QAAQ,MAAM,iBAAiB,KAAK,CAAC,EAAA,CAAG,OAAM,YAAW,CAAC,mBAAmB,IAAI,OAAO,CAAC;AACjG;;;;;;;;;;;;;;ACDA,SAAgB,aAAa,OAAwB;CACnD,IAAI,UAAU,KAAK,GACjB,OAAO;CAGT,IAAI,OAAO,UAAU,UAAU;EAI7B,IAAI,CAAC,OAAO,SAAS,KAAK,GACxB,MAAM,IAAI,gBAAgB,UAAU,OAAO,KAAK,EAAE,mCAAmC;EAGvF,OAAO,OAAO,KAAK;CACrB;CAEA,IAAI,OAAO,UAAU,aAAa,OAAO,UAAU,UACjD,OAAO,OAAO,KAAK;CAGrB,IAAI,SAAS,KAAK,GAChB,OAAO,gBAAgB,KAAK;CAG9B,IAAI,QAAQ,KAAK,GACf,OAAO,IAAI,MAAM,KAAI,SAAQ,aAAa,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;CAG9D,MAAM,IAAI,gBAAgB,kBAAkB,OAAO,MAAM,oCAAoC;AAC/F;;;;;;;;AASA,SAAS,gBAAgB,OAAuB;CAC9C,MAAM,YAAY,MAAM,SAAS,GAAG;CACpC,MAAM,YAAY,MAAM,SAAS,IAAI;CAErC,IAAI,aAAa,WACf,MAAM,IAAI,gBAAgB,qFAAqF;CAGjH,OAAO,YAAY,IAAI,MAAM,KAAK,IAAI,MAAM;AAC9C;AAEA,SAAS,eAAe,OAAwB;CAC9C,OAAO,QAAQ,KAAK,IAAI,aAAa,KAAK,IAAI,IAAI,aAAa,KAAK,EAAE;AACxE;;;;;;;;;;AAWA,SAAS,WAAW,SAAyB;CAC3C,OAAO,IAAI,QAAQ,oBACF,QAAQ,6BAA6B,QAAQ,oBAC7C,QAAQ,uBAAuB,QAAQ;AAC1D;AAEA,SAAS,sBAAsB,SAAiB,UAA6B,OAAwB;CACnG,QAAQ,UAAR;EACE,KAAK,MACH,OAAO,GAAG,QAAQ,MAAM,aAAa,KAAK;EAG5C,KAAK,MACH,OAAO,GAAG,QAAQ,MAAM,aAAa,KAAK;EAG5C,KAAK,MACH,OAAO,GAAG,QAAQ,KAAK,aAAa,KAAK;EAG3C,KAAK,OACH,OAAO,GAAG,QAAQ,MAAM,aAAa,KAAK;EAG5C,KAAK,MACH,OAAO,GAAG,QAAQ,KAAK,aAAa,KAAK;EAG3C,KAAK,OACH,OAAO,GAAG,QAAQ,MAAM,aAAa,KAAK;EAG5C,KAAK,YACH,OAAO,YAAY,QAAQ,IAAI,aAAa,KAAK,EAAE;EAGrD,KAAK,gBACH,OAAO,gBAAgB,QAAQ,IAAI,aAAa,KAAK,EAAE;EAGzD,KAAK,eACH,OAAO,cAAc,QAAQ,IAAI,aAAa,KAAK,EAAE;EAGvD,KAAK,aACH,OAAO,YAAY,QAAQ,IAAI,aAAa,KAAK,EAAE;EAGrD,KAAK,MACH,OAAO,GAAG,QAAQ,MAAM,eAAe,KAAK;EAG9C,KAAK,UACH,OAAO,QAAQ,QAAQ,MAAM,eAAe,KAAK,EAAE;EAGrD,KAAK,YACH,OAAO,WAAW,OAAO;EAG3B,KAAK,gBACH,OAAO,OAAO,WAAW,OAAO;EAGlC,SAKE,MAAM,IAAI,gBAAgB,yBAAyB,OAAO,QAAQ,GAAG;CAEzE;AACF;;;;;;;AAQA,SAAgB,iBAAiB,WAA0C;CACzE,IAAI,UAAU,SAAS,cAAc;EACnC,MAAM,aAAa,UAAU,WAAW,KAAK;EAI7C,OAAO,eAAe,KAAK,OAAO,IAAI,WAAW;CACnD;CAEA,MAAM,UAAU,UAAU,QAAQ,KAAK;CAEvC,IAAI,CAAC,iBAAiB,OAAO,GAC3B,OAAO;CAGT,IAAI;EACF,OAAO,sBAAsB,SAAS,UAAU,UAAU,UAAU,KAAK;CAC3E,QAAQ;EAIN,OAAO;CACT;AACF;;;;;AAMA,SAAgB,aAAa,OAA2C;CACtE,MAAM,QAAQ,MAAM,WACjB,KAAI,cAAa,iBAAiB,SAAS,CAAC,CAAC,CAC7C,QAAO,SAAQ,SAAS,IAAI;CAE/B,OAAO,MAAM,WAAW,IAAI,OAAO,MAAM,KAAK,OAAO;AACvD;;;;;;AAOA,SAAgB,cAAc,QAA6C;CACzE,MAAM,UAAU,OAAO,mBAAmB,CAAC,EAAA,CACxC,KAAI,UAAS,aAAa,KAAK,CAAC,CAAC,CACjC,QAAO,UAAS,UAAU,IAAI;CAEjC,OAAO,OAAO,WAAW,IAAI,OAAO,OAAO,KAAI,UAAS,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,MAAM;AACnF;;;;;;;;;;;;AAaA,SAAgB,iBACd,UACA,SACA,QACiB;CACjB,MAAM,UAAU,SAAS,UAAU,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;CAEnE,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,WACT;EAGF,MAAM,aAAa,cAAc,MAAM;EAEvC,IAAI,eAAe,MAAM;GAIvB,IAAI,OACF,QAAQ,KAAK,wBAAwB,OAAO,GAAG,kDAAkD;GAGnG;EACF;EAEA,IAAI,cAAc,QAAQ,YAAY,OAAO,GAC3C,OAAO;GAAE,UAAU,OAAO;GAAI,SAAS;EAAK;CAEhD;CAGA,OAAO;EAAE,UADQ,QAAQ,MAAK,WAAU,OAAO,SACrB,CAAC,EAAE,MAAM;EAAM,SAAS;CAAM;AAC1D;AAEA,SAAS,cACP,QACA,YACA,SACS;CACT,IAAI;EACF,OAAO,OAAO,SAAS,YAAY,OAAO,MAAM;CAClD,SAAS,OAAO;EACd,IAAI,OACF,wBAAwB,QAAQ,YAAY,KAAK;EAGnD,OAAO;CACT;AACF;AAEA,SAAS,wBACP,QACA,YACA,OACM;CAMN,IAAI;CAEJ,IAAI;EACF,aAAa,OAAO,SAAS,UAAU;CACzC,QAAQ;EACN;CACF;CAEA,IAAI,CAAC,UAAU,UAAU,GACvB,QAAQ,KAAK,4DAA4D,cAAc,YAAY,KAAK;AAE5G;;;;;AAMA,eAAsB,aACpB,UACA,SAC0B;CAE1B,OAAO,iBAAiB,UAAU,SAAS,MADtB,WAAW,CACiB;AACnD;;;;;;;;;;;;;;;;;;ACxSA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAqBA,MAAM,6BAAgF;CACpF,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,UAAU;CACV,cAAc;CACd,aAAa;CACb,WAAW;CACX,IAAI;CACJ,QAAQ;CACR,UAAU;CACV,cAAc;AAChB;;;;;;;;AASA,SAAgB,uBAAuB,UAAqD;CAC1F,OAAO,2BAA2B;AACpC;;;;;;;AClDA,SAAgB,qBAAqB,MAAkC;CACrE,MAAM,aAAa,cAAc,IAAI;CACrC,OAAO,eAAe,OAAO,KAAK,SAAS,YAAY,IAAI;AAC7D;;;;;;;AAQA,SAAS,cAAc,MAAmD;CACxE,IAAI,KAAK,SAAS,QAChB,OAAO,YAAY,IAAI,MAAM,OAAO,OAAO;CAG7C,MAAM,QAAQ,KAAK,MAChB,KAAI,SAAQ,cAAc,IAAI,CAAC,CAAC,CAChC,QAAQ,SAAoC,SAAS,IAAI;CAE5D,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,IAAI,MAAM,WAAW,GACnB,OAAO,MAAM;CAGf,OAAO;EACL,MAAM;EACN,IAAI,KAAK;EACT;CACF;AACF;;;;;;AAOA,SAAS,SAAS,MAAyB,UAA2B;CACpE,IAAI,KAAK,SAAS,QAChB,OAAO,YAAY,IAAI;CAGzB,MAAM,SAAS,KAAK,MACjB,KAAI,SAAQ,SAAS,MAAM,KAAK,CAAC,CAAC,CAClC,KAAK,KAAK,OAAO,QAAQ,UAAU,MAAM;CAE5C,OAAO,WAAW,SAAS,IAAI,OAAO;AACxC;;;;;;;;;;;;AAaA,SAAS,YAAY,MAAwC;CAC3D,IAAI,CAAC,qBAAqB,IAAI,GAC5B,OAAO;CAGT,OAAO,iBAAiB;EACtB,MAAM;EACN,SAAS,KAAK;EACd,UAAU,KAAK;EACf,OAAO,KAAK;CACd,CAAC;AACH;AAEA,SAAS,qBAAqB,MAAkC;CAC9D,QAAQ,uBAAuB,KAAK,QAAQ,GAA5C;EACE,KAAK,UACH,OAAO,kBAAkB,KAAK,KAAK;EAGrC,KAAK,SACH,OAAO,cAAc,KAAK,KAAK;EAGjC,KAAK,QACH,OAAO,KAAK,UAAU,KAAA;CAE1B;AACF;AAEA,SAAS,kBAAkB,OAA0C;CACnE,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU;AACpF;AAKA,SAAS,cAAc,OAA4E;CACjG,OAAO,QAAQ,KAAK,KAAK,MAAM,OAAM,SAAQ,kBAAkB,IAAI,CAAC;AACtE;;;ACrFA,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAM;CAAM;CAAM;AAAI,CAAC;AAC7D,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG,CAAC;AAElF,MAAM,cAAc;AACpB,MAAM,aAAa;AACnB,MAAM,QAAQ;AACd,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAKxB,MAAM,uBAA8D;CAClE,MAAM;CACN,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;AACR;AAEA,MAAM,iBAAwD;CAC5D,UAAU;CACV,YAAY;CACZ,UAAU;AACZ;AAOA,MAAM,kBAAkB;;;;;;;;AASxB,SAAgB,kBAAkB,YAA+C;CAC/E,MAAM,SAAS,SAAS,UAAU;CAElC,IAAI,WAAW,QAAQ,OAAO,WAAW,GACvC,OAAO;CAGT,IAAI,MAAM;CAEV,SAAS,KAAK,SAAS,GAAsB;EAC3C,OAAO,OAAQ,MAAM;CACvB;CAEA,SAAS,aAAa,OAAwB;EAC5C,MAAM,QAAQ,KAAK;EAEnB,IAAI,UAAU,KAAA,KAAa,MAAM,SAAS,WAAW,MAAM,UAAU,OAAO;GAC1E,OAAO;GACP,OAAO;EACT;EAEA,OAAO;CACT;CAEA,SAAS,aAAa,OAAwB;EAC5C,MAAM,QAAQ,KAAK;EAEnB,IAAI,UAAU,KAAA,KAAa,MAAM,SAAS,WAAW,MAAM,UAAU,OAAO;GAC1E,OAAO;GACP,OAAO;EACT;EAEA,OAAO;CACT;CAEA,SAAS,cAAc,MAAc,UAAqC;EACxE,KAAK,MAAM,CAAC,OAAO,YAAY,SAAS,QAAQ,GAAG;GACjD,MAAM,SAAS,OAAQ,OAAO;GAC9B,MAAM,OAAO;GAEb,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,KAAK,QAAQ,OAAO,UAAU,KAAK,OAC7E,OAAO;EAEX;EAEA,OAAO;CACT;CAEA,SAAS,YAA2B;EAClC,MAAM,OAAO,SAAS,QAAS,GAAG;EAElC,IAAI,SAAS,MACX,OAAO;EAGT,MAAM,KAAK;EACX,OAAO,KAAK;CACd;CAEA,SAAS,eAAuC;EAC9C,MAAM,QAAQ,KAAK;EAEnB,IAAI,UAAU,KAAA,GACZ,OAAO;EAGT,IAAI,MAAM,SAAS,UAAU;GAC3B,OAAO;GACP,OAAO,MAAM;EACf;EAEA,IAAI,MAAM,SAAS,UAAU;GAC3B,OAAO;GACP,OAAO,OAAO,MAAM,KAAK;EAC3B;EAEA,IAAI,MAAM,SAAS,SAAS;GAC1B,IAAI,MAAM,UAAU,QAAQ;IAC1B,OAAO;IACP,OAAO;GACT;GAEA,IAAI,MAAM,UAAU,SAAS;IAC3B,OAAO;IACP,OAAO;GACT;GAEA,OAAO;EACT;EAEA,IAAI,MAAM,SAAS,WAAW,MAAM,UAAU,KAAK;GACjD,MAAM,SAAS,KAAK,CAAC;GAErB,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,UAC1C,OAAO;GAGT,OAAO;GACP,OAAO,CAAC,OAAO,OAAO,KAAK;EAC7B;EAEA,OAAO;CACT;CAEA,SAAS,aAAuC;EAC9C,IAAI,CAAC,aAAa,GAAG,GACnB,OAAO;EAGT,IAAI,QAAQ,KAAK,GAAG,GAAG,GAAG;GACxB,OAAO;GACP,OAAO,CAAC;EACV;EAEA,MAAM,QAAQ,aAAa;EAE3B,IAAI,UAAU,MACZ,OAAO;EAGT,MAAM,QAA2B,CAAC,KAAK;EAEvC,OAAO,QAAQ,KAAK,GAAG,GAAG,GAAG;GAC3B,OAAO;GACP,MAAM,OAAO,aAAa;GAE1B,IAAI,SAAS,MACX,OAAO;GAGT,MAAM,KAAK,IAAI;EACjB;EAEA,OAAO,aAAa,GAAG,IAAI,QAAQ;CACrC;CAIA,SAAS,UAAU,UAA2D;EAC5E,IAAI,CAAC,aAAa,GAAG,GACnB,OAAO;EAGT,MAAM,OAAO,UAAU;EAEvB,IAAI,SAAS,QAAQ,CAAC,aAAa,GAAG,GACpC,OAAO;EAGT,MAAM,QAAQ,aAAa;EAE3B,IAAI,UAAU,QAAQ,CAAC,aAAa,GAAG,GACrC,OAAO;EAGT,OAAO;GACL,MAAM;GACN;GACA;GACA;EACF;CACF;CAGA,SAAS,aAAuC;EAC9C,MAAM,OAAO,UAAU;EAEvB,IAAI,SAAS,QAAQ,CAAC,aAAa,IAAI,GACrC,OAAO;EAGT,MAAM,QAAQ,WAAW;EAEzB,IAAI,UAAU,QAAQ,CAAC,aAAa,GAAG,GACrC,OAAO;EAGT,OAAO;GACL,MAAM;GACN;GACA,UAAU;GACV;EACF;CACF;CAIA,SAAS,eAAyC;EAChD,MAAM,QAAQ,KAAK;EAEnB,IAAI,UAAU,KAAA,GACZ,OAAO;EAGT,IAAI;EACJ,IAAI;EAEJ,IAAI,MAAM,SAAS,WAAW,MAAM,UAAU,SAAS,QAAQ,KAAK,CAAC,GAAG,GAAG,GAAG;GAC5E,WAAW;GACX,YAAY,MAAM;EACpB,OAAO,IAAI,MAAM,SAAS,WAAW,MAAM,UAAU,KAAK;GACxD,WAAW;GACX,YAAY,MAAM;EACpB,OACE,OAAO;EAGT,MAAM,OAAO,SAAS,QAAS,SAAS;EAExC,IAAI,SAAS,MACX,OAAO;EAGT,MAAM,YAAY,iBAAiB;GACjC,MAAM;GACN,SAAS,KAAK;GACd;GACA,OAAO,KAAA;EACT,CAAC;EAED,IAAI,cAAc,MAChB,OAAO;EAGT,MAAM,WAAW,SAAS,SAAS;EAEnC,IAAI,aAAa,QAAQ,CAAC,cAAc,KAAK,QAAQ,GACnD,OAAO;EAGT,OAAO,SAAS;EAChB,OAAO;GACL,MAAM;GACN,MAAM,KAAK;GACX;EACF;CACF;CAEA,SAAS,YAAsC;EAC7C,MAAM,QAAQ,KAAK;EAEnB,IAAI,UAAU,KAAA,KAAa,MAAM,SAAS,SACxC,OAAO;EAGT,IAAI,MAAM,UAAU,OAAO;GACzB,MAAM,OAAO,KAAK,CAAC;GAEnB,IAAI,SAAS,KAAA,GACX,OAAO;GAGT,IAAI,KAAK,SAAS,WAAW,KAAK,UAAU,cAAc,QAAQ,KAAK,CAAC,GAAG,GAAG,GAAG;IAC/E,OAAO;IACP,OAAO,UAAU,cAAc;GACjC;GAEA,IAAI,KAAK,SAAS,WAAW,KAAK,UAAU,KAAK;IAC/C,OAAO;IACP,OAAO,WAAW;GACpB;GAEA,OAAO;EACT;EAEA,IAAI,QAAQ,KAAK,CAAC,GAAG,GAAG,GAAG;GACzB,MAAM,WAAW,eAAe,MAAM;GAEtC,IAAI,aAAa,KAAA,GACf,OAAO;GAGT,OAAO;GACP,OAAO,UAAU,QAAQ;EAC3B;EAEA,MAAM,OAAO,UAAU;EAEvB,IAAI,SAAS,MACX,OAAO;EAGT,MAAM,gBAAgB,KAAK;EAE3B,IAAI,kBAAkB,KAAA,GACpB,OAAO;EAGT,IAAI,cAAc,SAAS,WAAW,cAAc,UAAU,MAAM;GAClE,OAAO;GACP,MAAM,QAAQ,WAAW;GACzB,OAAO,UAAU,OACb,OACA;IACE,MAAM;IACN;IACA,UAAU;IACV;GACF;EACN;EAEA,IAAI,cAAc,SAAS,SAAS;GAClC,MAAM,WAAW,qBAAqB,cAAc;GAEpD,IAAI,aAAa,KAAA,GACf,OAAO;GAGT,OAAO;GACP,MAAM,QAAQ,aAAa;GAC3B,OAAO,UAAU,OACb,OACA;IACE,MAAM;IACN;IACA;IACA;GACF;EACN;EAEA,OAAO;CACT;CAEA,SAAS,UAAU,OAAyC;EAC1D,MAAM,YAAY,aAAa;EAE/B,IAAI,cAAc,MAChB,OAAO;EAGT,IAAI,QAAQ,KAAK,GAAG,GAAG,GAAG;GACxB,IAAI,SAAS,iBACX,OAAO;GAGT,OAAO;GACP,MAAM,QAAQ,WAAW,QAAQ,CAAC;GAElC,IAAI,UAAU,QAAQ,CAAC,aAAa,GAAG,GACrC,OAAO;GAGT,OAAO,QAAQ,KAAK;EACtB;EAEA,OAAO,UAAU;CACnB;CAEA,SAAS,WAAgC;EACvC,MAAM,QAAQ,KAAK;EAEnB,IAAI,UAAU,KAAA,KAAa,MAAM,SAAS,YAAY,MAAM,UAAU,SAAS,MAAM,UAAU,OAC7F,OAAO,MAAM;EAGf,OAAO;CACT;CAEA,SAAS,WAAW,OAAyC;EAC3D,MAAM,QAAQ,UAAU,KAAK;EAE7B,IAAI,UAAU,MACZ,OAAO;EAGT,MAAM,QAA6B,CAAC,KAAK;EACzC,IAAI,KAA0B;EAC9B,IAAI,OAAO,SAAS;EAEpB,OAAO,SAAS,MAAM;GAGpB,IAAI,OAAO,MACT,KAAK;QACA,IAAI,OAAO,MAChB,OAAO;GAGT,OAAO;GACP,MAAM,OAAO,UAAU,KAAK;GAE5B,IAAI,SAAS,MACX,OAAO;GAGT,MAAM,KAAK,IAAI;GACf,OAAO,SAAS;EAClB;EAEA,OAAO,MAAM,WAAW,IACpB,MAAM,KACN;GACE,MAAM;GACN,IAAI,MAAM;GACV;EACF;CACN;CAEA,MAAM,SAAS,WAAW,CAAC;CAE3B,IAAI,WAAW,QAAQ,QAAQ,OAAO,QACpC,OAAO;CAGT,OAAO,QAAQ,MAAM;AACvB;;;;;;;AAQA,SAAS,SAAS,QAA0B,MAA+B;CACzE,MAAM,OAAO,OAAO;CAEpB,IAAI,SAAS,KAAA,KAAa,KAAK,SAAS,SACtC,OAAO;CAGT,IAAI,OAAO,KAAK;CAChB,IAAI,QAAQ,OAAO;CACnB,IAAI,UAAU,OAAO;CAErB,OAAO,YAAY,KAAA,KAAa,QAAQ,SAAS,YAAY,QAAQ,UAAU,OAAO,QAAQ,UAAU,MAAM;EAC5G,IAAI,QAAQ,UAAU,KAAK;GACzB,MAAM,OAAO,OAAO,QAAQ;GAE5B,IAAI,SAAS,KAAA,KAAa,KAAK,SAAS,SACtC,OAAO;GAGT,QAAQ,IAAI,KAAK;GACjB,SAAS;EACX,OAAO;GACL,MAAM,QAAQ,OAAO,QAAQ;GAE7B,IAAI,UAAU,KAAA,KAAa,MAAM,SAAS,YAAY,CAAC,gBAAgB,KAAK,MAAM,KAAK,GACrF,OAAO;GAGT,MAAM,QAAQ,OAAO,QAAQ;GAE7B,IAAI,UAAU,KAAA,KAAa,MAAM,SAAS,WAAW,MAAM,UAAU,KACnE,OAAO;GAGT,QAAQ,IAAI,MAAM,MAAM;GACxB,SAAS;EACX;EAEA,UAAU,OAAO;CACnB;CAEA,OAAO,iBAAiB,IAAI,IAAI;EAAE;EAAM,MAAM;CAAM,IAAI;AAC1D;AAEA,SAAS,QAAQ,MAA6C;CAC5D,OAAO,KAAK,SAAS,UACjB,OACA;EACE,MAAM;EACN,IAAI;EACJ,OAAO,CAAC,IAAI;CACd;AACN;AAEA,SAAS,QAAQ,OAA0B,OAAwB;CACjE,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,WAAW,MAAM,UAAU;AAC1E;;;;;;;;AASA,SAAS,SAAS,OAA+B;CAC/C,MAAM,SAAkB,CAAC;CACzB,IAAI,QAAQ;CAEZ,OAAO,QAAQ,MAAM,QAAQ;EAC3B,MAAM,OAAO,MAAM;EAEnB,IAAI,SAAS,OAAO,SAAS,OAAQ,SAAS,QAAQ,SAAS,MAAM;GACnE,SAAS;GACT;EACF;EAEA,IAAI,SAAS,OAAO,SAAS,MAAM;GACjC,MAAM,MAAM,MAAM,QAAQ,MAAM,QAAQ,CAAC;GAEzC,IAAI,QAAQ,IACV,OAAO;GAGT,OAAO,KAAK;IAAE,MAAM;IAAU,OAAO,MAAM,MAAM,QAAQ,GAAG,GAAG;GAAE,CAAC;GAClE,QAAQ,MAAM;GACd;EACF;EAEA,MAAM,OAAO,MAAM,MAAM,OAAO,QAAQ,CAAC;EAEzC,IAAI,qBAAqB,IAAI,IAAI,GAAG;GAClC,OAAO,KAAK;IAAE,MAAM;IAAS,OAAO;GAAK,CAAC;GAC1C,SAAS;GACT;EACF;EAEA,IAAI,qBAAqB,IAAI,IAAI,GAAG;GAClC,OAAO,KAAK;IAAE,MAAM;IAAS,OAAO;GAAK,CAAC;GAC1C,SAAS;GACT;EACF;EAEA,IAAI,MAAM,KAAK,IAAI,GAAG;GACpB,MAAM,QAAQ,eAAe,KAAK,MAAM,MAAM,KAAK,CAAC;GAEpD,IAAI,UAAU,MACZ,OAAO;GAGT,OAAO,KAAK;IAAE,MAAM;IAAU,OAAO,MAAM;GAAG,CAAC;GAC/C,SAAS,MAAM,EAAE,CAAC;GAClB;EACF;EAEA,IAAI,YAAY,KAAK,IAAI,GAAG;GAC1B,IAAI,MAAM,QAAQ;GAElB,OAAO,MAAM,MAAM,UAAU,WAAW,KAAK,MAAM,IAAK,GACtD,OAAO;GAGT,OAAO,KAAK;IAAE,MAAM;IAAS,OAAO,MAAM,MAAM,OAAO,GAAG;GAAE,CAAC;GAC7D,QAAQ;GACR;EACF;EAEA,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;AC5kBA,MAAa,2BAA2B;;;;;;;;;;AC9BxC,eAAsB,SAAsB,YAAoB,SAAyC;CACvG,MAAM,WAAW;CACjB,OAAO,aAAgB,YAAY,OAAO;AAC5C;;;;AAKA,eAAsB,cAAc,YAAoB,SAA+C;CACrG,MAAM,WAAW;CACjB,OAAO,kBAAkB,YAAY,OAAO;AAC9C;;;;;;AAOA,SAAgB,aAA0B,YAAoB,SAAgC;CAC5F,OAAO,cAAc,CAAC,CAAC,SAAY,YAAY,OAAO;AACxD;;;;;AAMA,SAAgB,kBAAkB,YAAoB,SAAsC;CAC1F,OAAO,cAAc,CAAC,CAAC,cAAc,YAAY,OAAO;AAC1D;;;ACaA,MAAM,qBAA6C;CAEjD,kCAAkC;CAClC,wCAAwC;CACxC,qBAAqB;CACrB,qCAAqC;CACrC,4CAA4C;CAC5C,oDAAoD;CACpD,oDAAoD;CACpD,mEAAmE;CACnE,+DAA+D;CAC/D,6DAA6D;CAC7D,4DAA4D;CAC5D,uDAAuD;CACvD,0FAA0F;CAC1F,8EAA8E;CAG9E,0CAA0C;CAC1C,uDAAuD;CACvD,8DAA8D;CAC9D,4DAA4D;CAC5D,2DAA2D;CAC3D,wEAAwE;CACxE,mEAAmE;CACnE,iFAAiF;CACjF,gDAAgD;CAChD,8CAA8C;CAC9C,4DAA4D;CAC5D,+DAA+D;CAG/D,mDAAmD;CACnD,yCAAyC;CACzC,yCAAyC;CACzC,0CAA0C;CAC1C,6DAA6D;CAC7D,uEAAuE;CACvE,wEAAwE;CAGxE,qCAAqC;CACrC,qDAAqD;CACrD,iGAAiG;CACjG,gBAAgB;CAChB,6CAA6C;CAC7C,6EAA6E;CAC7E,yDAAyD;CACzD,oDAAoD;CACpD,2DAA2D;CAC3D,2EAA2E;CAC3E,2FAA2F;CAC3F,yDAAyD;CACzD,qGAAqG;CACrG,yHAAyH;CACzH,uHAAuH;CAGvH,sFAAsF;CACtF,qFAAqF;CACrF,gGAAgG;CAChG,+FAA+F;CAC/F,6FAA6F;CAC7F,0HAA0H;CAC1H,gGAAgG;CAChG,kGAAkG;CAGlG,uBAAuB;CACvB,8BAA8B;CAC9B,0CAA0C;CAC1C,+CAA+C;CAC/C,yDAAyD;CACzD,uDAAuD;CACvD,+CAA+C;CAC/C,2CAA2C;CAC3C,oCAAoC;CACpC,2CAA2C;CAC3C,0CAA0C;CAC1C,0DAA0D;CAC1D,yDAAyD;CACzD,8BAA8B;CAC9B,8BAA8B;CAC9B,4BAA4B;CAC5B,wCAAwC;CACxC,uCAAuC;CACvC,wCAAwC;CACxC,uCAAuC;CACvC,6BAA6B;CAC7B,+BAA+B;CAC/B,4BAA4B;CAC5B,sCAAsC;CACtC,4CAA4C;CAC5C,6BAA6B;CAC7B,iCAAiC;CACjC,6BAA6B;CAC7B,gCAAgC;CAChC,+CAA+C;AACjD;AAEA,MAAM,mBAA2C;CAC/C,YAAY;CACZ,aAAa;CACb,eAAe;CACf,SAAS;AACX;AAEA,MAAM,mBAA2C;CAC/C,YAAY;CACZ,aAAa;CACb,eAAe;CACf,SAAS;AACX;AAEA,SAAS,gBAAgB,OAA+B,UAAkB,MAAkC;CAC1G,QAAQ,SAAS,KAAA,IAAY,KAAA,IAAY,MAAM,UAAU;AAC3D;;;;AAKA,MAAa,aAAiC;CAC5C,cAAa,SAAQ,gBAAgB,kBAAkB,SAAS,IAAI;CACpE,iBAAgB,SAAQ;CACxB,iBAAiB;CACjB,kBAAiB,eAAc,kDAAkD,WAAW;CAC5F,eAAe,cAAc,eAAe,cAAc,aAAa,iBAAiB,WAAW;AACrG;;;;AAKA,MAAa,eAAmC;CAC9C,cAAa,SAAQ,gBAAgB,kBAAkB,MAAM,IAAI;CACjE,iBAAgB,SAAQ,SAAS,KAAK,KAAK,mBAAmB,SAAS;CACvE,iBAAiB;CACjB,kBAAiB,eAAc,qBAAqB,WAAW;CAC/D,eAAe,cAAc,eAAe,QAAQ,aAAa,WAAW,WAAW;AACzF;AAKA,MAAM,iBAAiB,IAAI,IAAgC,CACzD,CAAC,SAAS,UAAU,GACpB,CAAC,SAAS,YAAY,CACxB,CAAC;AAID,IAAI,iBAAqC;;;;;;;AAQzC,SAAgB,yBAAyB,QAAgB,UAAoC;CAC3F,eAAe,IAAI,QAAQ,QAAQ;AACrC;;;;;;AAqBA,SAAgB,4BAA4B,EAAE,QAAQ,YAA4C;CAChG,MAAM,OAAO,WAAW,KAAA,IAAY,iBAAiB,eAAe,IAAI,MAAM,KAAK;CACnF,iBAAiB,aAAa,KAAA,IAAY,OAAO;EAAE,GAAG;EAAM,GAAG;CAAS;AAC1E;;;;AAKA,SAAgB,wBAA4C;CAC1D,OAAO;AACT;;;ACxIA,SAAS,YAAY,MAAkC;CACrD,MAAM,UAAU,MAAM,KAAK;CAC3B,OAAO,UAAU,OAAO,OAAO,IAAI;AACrC;;;;;;AAOA,SAAgB,gBAAgB,SAA0C;CACxE,MAAM,WAAW,QAAQ,MAAM,MAAM;CACrC,MAAM,OAAO,SAAS,UAAU,IAAI,KAAA,IAAY,SAAS,GAAG,EAAE;CAE9D,IAAI,SAAS,KAAA,GACX,OAAO;CAGT,MAAM,CAAC,MAAM,SAAS,KAAK,QAAQ,KAAK,EAAE,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,MAAM,IAAI;CACvE,MAAM,OAAO,YAAY,IAAI;CAE7B,IAAI,OAAO,MAAM,IAAI,GACnB,OAAO;CAGT,MAAM,KAAK,YAAY,KAAK;CAC5B,OAAO,CAAC,MAAM,OAAO,MAAM,EAAE,IAAI,OAAO,EAAE;AAC5C;;;;;AAMA,SAAgB,oBAAoB,KAAc,QAA6C;CAC7F,IAAI,QAAQ,QAAQ,QAAQ,KAAA,GAC1B,OAAO;CAGT,MAAM,YAAY,SAAS,GAAG,KAAK,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO,KAAA;CAC7E,MAAM,UAAU,SAAS,GAAG,KAAK,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,OAAO,GAAG;CACzF,MAAM,CAAC,MAAM,MAAM,gBAAgB,OAAO,KAAK,CAAC,GAAG,OAAO,MAAM;CAEhE,OAAO;EACL;EACA;EACA;EACA,QAAQ,sBAAsB,CAAC,CAAC,YAAY,SAAS;CACvD;AACF;;;;;AAMA,SAAgB,qBAAqB,KAAsC;CACzE,IAAI,CAAC,MAAM,QAAQ,GAAG,GACpB,OAAO,CAAC;CAGV,MAAM,WAAW,sBAAsB;CAEvC,OAAO,IAAI,SAAS,UAAkC;EACpD,IAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,UAAU,YAAY,MAAM,UAAU,IACzE,OAAO,CAAC;EAGV,OAAO,CACL;GACE,MAAM,MAAM,SAAS,YAAY,MAAM,SAAS,aAAa,MAAM,OAAO;GAC1E,OAAO,MAAM;GACb,QAAQ,OAAO,MAAM,WAAW,WAAW,MAAM,OAAO,WAAW,KAAK,EAAE,IAAI;GAC9E,MAAM,SAAS,eAAe,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,EAAE;GAC9E,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;GACvD,WAAW,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;EACrE,CACF;CACF,CAAC;AACH;;;;;AAMA,eAAsB,eAAe,YAAoB,MAA4D;CACnH,MAAM,WAAW;CACjB,OAAO,mBAAmB,YAAY,IAAI;AAC5C;;;;;AAMA,SAAgB,mBAAmB,YAAoB,MAAmD;CACxG,MAAM,SAAS,cAAc;CAC7B,OAAO,oBAAoB,SAAS,UAAU,OAAO,cAAc,UAAU,IAAI,OAAO,SAAS,UAAU,GAAG,UAAU;AAC1H;;;;AAKA,eAAsB,qBAAsD;CAC1E,MAAM,WAAW;CACjB,OAAO,uBAAuB;AAChC;;;;;AAMA,SAAgB,yBAAiD;CAC/D,OAAO,qBAAqB,cAAc,CAAC,CAAC,eAAe,CAAC;AAC9D;;;;;AAMA,eAAsB,aACpB,WACA,QACA,MAC6B;CAC7B,MAAM,WAAW;CACjB,OAAO,iBAAiB,WAAW,QAAQ,IAAI;AACjD;;;;;AAMA,SAAgB,iBAAiB,WAA2B,QAAgB,MAA0C;CACpH,OAAO,cAAc,CAAC,CAAC,QAAQ,WAAW,QAAQ,SAAS,OAAO;AACpE;;;;;AAMA,eAAsB,cAAc,QAAwB,UAA4C;CACtG,MAAM,WAAW;CACjB,OAAO,kBAAkB,QAAQ,QAAQ;AAC3C;;;;;AAMA,SAAgB,kBAAkB,QAAwB,UAAmC;CAC3F,OAAO,cAAc,CAAC,CAAC,UAAU,QAAQ,QAAQ;AACnD;;;AC/NA,MAAM,eAAe;;;;;;;AAQrB,SAAgB,mBAAmB,QAAgC;CACjE,MAAM,QAAwB,CAAC;CAE/B,aAAa,YAAY;CAEzB,KAAK,IAAI,QAAQ,aAAa,KAAK,MAAM,GAAG,UAAU,MAAM,QAAQ,aAAa,KAAK,MAAM,GAAG;EAC7F,MAAM,QAAQ,MAAM,QAAQ,cAAc;EAC1C,MAAM,OAAO,MAAM,QAAQ;EAC3B,MAAM,KAAK;GACT;GACA,IAAI,OAAO,MAAM;GACjB,YAAY;EACd,CAAC;CACH;CAEA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,QAAgB,KAAkC;CAC/E,OAAO,mBAAmB,MAAM,CAAC,CAAC,MAAK,SAAQ,OAAO,KAAK,QAAQ,OAAO,KAAK,EAAE,KAAK;AACxF;AAKA,SAAS,cAAc,MAA6B;CAClD,OAAO,KAAK,WAAW,KAAK,CAAC,CAAC,SAAS;AACzC;;;;;;;;;;;;;AAcA,SAAgB,oBAAoB,WAA2B,QAAoC;CACjG,IAAI,CAAC,cAAc,GACjB,MAAM,IAAI,wBAAwB;CAGpC,MAAM,QAAqC,CAAC;CAE5C,KAAK,MAAM,QAAQ,mBAAmB,MAAM,GAAG;EAC7C,IAAI,CAAC,cAAc,IAAI,GACrB;EAGF,IAAI;GACF,KAAK,MAAM,QAAQ,iBAAiB,WAAW,KAAK,YAAY,UAAU,CAAC,CAAC,OAC1E,MAAM,KAAK;IACT,GAAG;IACH,MAAM,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK,IAAI;GAC3D,CAAC;EAEL,QAAQ,CAER;CACF;CAEA,OAAO;EAAE,UAAU;EAAW;CAAM;AACtC;;;;AAKA,eAAsB,gBAAgB,WAA2B,QAA6C;CAC5G,MAAM,WAAW;CACjB,OAAO,oBAAoB,WAAW,MAAM;AAC9C;;;;;;;;;;AAWA,SAAgB,2BAA2B,QAAwC;CACjF,IAAI,CAAC,cAAc,GACjB,MAAM,IAAI,wBAAwB;CAGpC,MAAM,cAAsC,CAAC;CAE7C,KAAK,MAAM,QAAQ,mBAAmB,MAAM,GAAG;EAC7C,IAAI,CAAC,cAAc,IAAI,GACrB;EAGF,MAAM,aAAa,mBAAmB,KAAK,YAAY,UAAU;EAEjE,IAAI,YACF,YAAY,KAAK;GACf,GAAG;GACH,MAAM,WAAW,OAAO,KAAK;GAC7B,IAAI,WAAW,KAAK,KAAK;EAC3B,CAAC;CAEL;CAEA,OAAO;AACT;;;;AAKA,eAAsB,uBAAuB,QAAiD;CAC5F,MAAM,WAAW;CACjB,OAAO,2BAA2B,MAAM;AAC1C"}