/**
 * Pure helpers behind `<ComponentBlockField>`. Extracted so vitest can
 * exercise them in node without spinning up jsdom — the React component
 * just composes these into UI.
 *
 * Kept structurally typed (no peer-dep import of types here would be
 * cleaner, but the `PropType` discriminated union is the contract we
 * promise consumers, so we lean on the package's types).
 */
import type { DiscriminatedUnion, DiscriminatedVariant } from '@actuate-media/component-blocks/discriminated-union';
import type { ComponentSpec, Manifest, PropType } from '@actuate-media/component-blocks';
import type { ComponentBlockValue } from './ComponentBlockField.js';
/**
 * Compute the list of components an editor can pick from given the
 * manifest + the optional allow-list. Returns the full list when no
 * filter is provided; returns `[]` when every entry is filtered out
 * (the caller renders an error in that case).
 */
export declare function getAllowedComponents(manifest: Manifest, allow: string[] | undefined): ComponentSpec[];
/**
 * JSON-encode any value so it round-trips cleanly through the
 * JsonFallback textarea. Previously the helper short-circuited for
 * strings (returning the raw string), which made the textarea
 * stateful: typing `"hello"` parsed to the JS string `hello`,
 * re-rendered as bare `hello`, then `JSON.parse('hello')` threw on
 * the next edit and the field "locked" as a raw string. Always
 * stringifying keeps the textarea visually consistent with the
 * underlying JSON value.
 *
 * Falls back to `String(v)` for values that can't be JSON-encoded
 * (e.g. circular refs) so the field remains editable.
 */
export declare function safeJsonStringify(v: unknown): string;
/**
 * Map an `<select>` change-event value to an enum-prop value.
 *
 * The placeholder `<option value="">Select…</option>` clears the field
 * to `undefined`; any other value is the stringified array index into
 * `options`. Extracted so we can test the clear semantics without
 * spinning up jsdom: previously the inline handler used `Number("")`,
 * which evaluates to `0` and silently picked the first option instead
 * of clearing.
 */
export declare function parseEnumSelection<T>(raw: string, options: readonly T[]): T | undefined;
/**
 * Produce a sensible empty value for a freshly-created array item or
 * required prop. The admin form generator never wants `undefined` in
 * an array; that breaks downstream PropInput defaults and confuses
 * validators.
 */
export declare function defaultForType(type: PropType): unknown;
/**
 * Seed an empty props object for a freshly-picked component using each
 * prop's explicit `defaultValue` when set, else the structural default
 * from {@link defaultForType} (only for required props — optional
 * unset props stay absent so they render as empty inputs).
 */
export declare function seedPropsForComponent(spec: ComponentSpec): Record<string, unknown>;
/**
 * Compute the next value for a discriminated-union field when the
 * editor switches variants. Pure helper so the React component stays
 * thin and the seeding rules can be unit-tested without rendering.
 *
 * Seeding rules:
 *   1. The discriminator field is always written to the variant's
 *      tag value.
 *   2. Optional fields shared by both variants survive the switch
 *      (preserves "edited the same label" intent across, e.g.,
 *      Link → Modal where both carry `label`).
 *   3. Required fields exclusive to the new variant are seeded by
 *      priority: explicit `field.defaultValue` first (matches
 *      `seedPropsForComponent`), else the structural default from
 *      `defaultForType` — but only when that default is non-null.
 *      A `null` from `defaultForType` (reference / unknown / non-
 *      discriminated union types) is treated as "no sensible default"
 *      and the field is left absent so the validator's
 *      "missing required prop" message and the data agree.
 *   4. Fields exclusive to the previous variant are dropped so the
 *      value stays structurally valid against the chosen variant.
 */
export declare function switchUnionVariant(current: unknown, union: DiscriminatedUnion, variant: DiscriminatedVariant): Record<string, unknown>;
/**
 * Build a minimal structural validator that mirrors the rules the
 * cms-core `validateComponentBlockValue` runs server-side, so the form
 * can show feedback without a round-trip. Kept in sync with cms-core
 * by inspection; the canonical implementation lives there.
 */
export declare function buildClientValidator(manifest: Manifest, allow: string[] | undefined): (value: ComponentBlockValue) => string | true;
/**
 * The validator returns a single error string. The form wants to show
 * the message under the specific input that triggered it — so we parse
 * the message back into a sparse `{ propPath → message }` map. Pure
 * function so it's trivial to unit-test.
 */
export declare function parsePerPropErrors(message: string | null): Record<string, string>;
//# sourceMappingURL=component-block-helpers.d.ts.map