import type { IResolvable } from '../resolvable';
export type MakeReadonly<A> = A extends Set<infer E> ? ReadonlySet<E> : A extends Map<infer K, infer E> ? ReadonlyMap<K, E> : A extends Array<infer E> ? ReadonlyArray<E> : A extends object ? Readonly<A> : A;
/**
 * A read-only observable container that holds a value of type `A` and implements `IResolvable`.
 *
 * Readable boxes participate in CDK token resolution: they can be passed into
 * L1 construct properties via `Token.asString()`, `Token.asList()`, etc., and
 * their value will be resolved at synthesis time.
 *
 * Readable boxes also track stack traces of mutations (when `CDK_DEBUG` is enabled),
 * which are used to emit `aws:cdk:propertyAssignment` metadata linking CloudFormation
 * property values back to the user code that produced them.
 */
export interface IReadableBox<A> extends IResolvable {
    /**
     * Returns the current value held by this box.
     *
     * The returned value is wrapped in `MakeReadonly` so that consumers cannot
     * mutate it in place. All mutations must go through the box's own API
     * (e.g. `set`, `push`, `put`, `add`).
     */
    get(): MakeReadonly<A>;
    /**
     * Returns the current value held by this box as a mutable type.
     *
     * Unlike `get()`, the returned value is not wrapped in `MakeReadonly`,
     * allowing direct mutation of the underlying value.
     */
    getMutable(): A;
    /**
     * Creates a new read-only box whose value is derived by applying `fn` to this box's value.
     *
     * The derived box inherits the stack traces of its source, so mutations to
     * this box are correctly attributed to all CloudFormation properties that
     * consume the derived value.
     *
     * @param fn a pure transformation function.
     * @returns a new read-only box that recomputes on every `get()` / `resolve()`.
     */
    derive<B>(fn: (a: MakeReadonly<A>) => B): IReadableBox<B>;
    /**
     * Returns the stack traces captured by this box.
     *
     * Each entry corresponds to a mutation (`set`, `push`, or initial construction)
     * that occurred while stack trace collection was enabled (`CDK_DEBUG=1`).
     * Returns an empty array when debug mode is off or no mutations were recorded.
     */
    getStackTraces(): Array<StackTrace>;
}
/**
 * A mutable box that extends `IReadableBox` with the ability to replace its value.
 *
 * When `set` is called (and the new value differs from the current one), the box
 * replaces its stored stack traces with a single new trace captured at the call
 * site, so that the metadata points to the code that last changed the value.
 */
export interface IBox<A> extends IReadableBox<A> {
    /**
     * Replaces the value held by this box.
     *
     * If the new value is equal to the current value (by reference equality, or
     * by the custom `equals` function provided at construction), this is a no-op
     * and no stack trace is captured.
     *
     * @param a the new value.
     */
    set(a: A): void;
}
/**
 * A mutable box specialized for arrays, extending `Box<Array<A>>` with `push`.
 *
 * Unlike `set` (which replaces all stack traces), `push` *appends* a new stack
 * trace to the existing list. This means that each element addition is tracked
 * individually, and the resulting metadata will contain one entry per `push` call
 * (plus one for the initial construction or last `set`, if any).
 */
export interface IArrayBox<A> extends IBox<Array<A>>, Iterable<A> {
    /**
     * Returns the number of elements in the array.
     */
    readonly length: number;
    /**
     * Appends one or more elements to the array and captures a stack trace for this addition.
     *
     * @param items the elements to append.
     */
    push(...items: A[]): void;
    /**
     * Removes the last element from the array and captures a stack trace for this removal.
     *
     * @returns the removed element, or `undefined` if the array is empty.
     */
    pop(): A | undefined;
    /**
     * Returns the index of the first element that satisfies the predicate, or -1.
     *
     * Delegates to `Array.prototype.findIndex` on the underlying array.
     *
     * @param predicate a function called for each element.
     * @returns the index of the first matching element, or -1.
     */
    findIndex(predicate: (value: A, index: number, obj: Array<A>) => unknown): number;
    /**
     * Returns the first element that satisfies the predicate, or `undefined`.
     *
     * Delegates to `Array.prototype.find` on the underlying array.
     *
     * @param predicate a function called for each element.
     * @returns the first matching element, or `undefined`.
     */
    find(predicate: (value: A, index: number, obj: Array<A>) => unknown): A | undefined;
    /**
     * Removes elements from the array and optionally inserts new elements in their place.
     *
     * Delegates to `Array.prototype.splice` on the underlying array.
     *
     * @param start the zero-based index at which to start changing the array.
     * @param deleteCount the number of elements to remove.
     * @param items elements to insert at `start`.
     * @returns an array of the removed elements.
     */
    splice(start: number, deleteCount: number, ...items: A[]): A[];
    /**
     * Tests whether at least one element in the array passes the predicate.
     *
     * Delegates to `Array.prototype.some` on the underlying array.
     *
     * @param predicate a function called for each element.
     * @returns `true` if the predicate returns a truthy value for at least one element, otherwise `false`.
     */
    some(predicate: (value: A, index: number, obj: Array<A>) => unknown): boolean;
    /**
     * Creates a derived read-only box by applying `fn` to each element of the array.
     *
     * Shorthand for `this.derive(a => a.map(fn))`.
     *
     * @param fn a pure transformation applied to each element.
     * @returns a new read-only box holding the mapped array.
     */
    map<B>(fn: (a: A) => B): IReadableBox<Array<B>>;
}
/**
 * A mutable box specialized for maps, extending `Box<Map<K, V>>` with
 * map-mutation methods.
 *
 * Like `ArrayBox`, mutating methods (`put`, `delete`) *append* stack traces
 * rather than replacing them, so each mutation is tracked individually.
 */
export interface IMapBox<K, V> extends IBox<Map<K, V>>, Iterable<[K, V]> {
    /**
     * Returns the number of entries in the map.
     */
    readonly size: number;
    /**
     * Sets a key-value pair in the map and captures a stack trace for this mutation.
     *
     * @param key the key.
     * @param value the value.
     */
    put(key: K, value: V): void;
    /**
     * Removes a key from the map and captures a stack trace for this mutation.
     *
     * @param key the key to remove.
     * @returns `true` if the key existed and was removed, `false` otherwise.
     */
    delete(key: K): boolean;
    /**
     * Returns whether the map contains the given key.
     *
     * @param key the key to check.
     */
    has(key: K): boolean;
    /**
     * Returns the value associated with the given key, or `undefined`.
     *
     * @param key the key to look up.
     */
    getEntry(key: K): V | undefined;
    /**
     * Returns an iterable of the map's keys.
     */
    keys(): IterableIterator<K>;
    /**
     * Returns an iterable of the map's values.
     */
    values(): IterableIterator<V>;
    /**
     * Returns an iterable of the map's [key, value] pairs.
     */
    entries(): IterableIterator<[K, V]>;
    /**
     * Executes a callback for each entry in the map.
     *
     * @param callbackfn a function called for each entry.
     */
    forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void): void;
}
/**
 * A mutable box specialized for sets, extending `Box<Set<A>>` with
 * set-mutation methods.
 *
 * Like `ArrayBox`, mutating methods (`add`, `delete`) *append* stack traces
 * rather than replacing them, so each mutation is tracked individually.
 */
export interface ISetBox<A> extends IBox<Set<A>>, Iterable<A> {
    /**
     * Returns the number of elements in the set.
     */
    readonly size: number;
    /**
     * Adds a value to the set and captures a stack trace for this mutation.
     *
     * @param value the value to add.
     */
    add(value: A): void;
    /**
     * Removes a value from the set and captures a stack trace for this mutation.
     *
     * @param value the value to remove.
     * @returns `true` if the value existed and was removed, `false` otherwise.
     */
    delete(value: A): boolean;
    /**
     * Returns whether the set contains the given value.
     *
     * @param value the value to check.
     */
    has(value: A): boolean;
    /**
     * Returns an iterable of the set's values.
     */
    values(): IterableIterator<A>;
    /**
     * Returns an iterable of the set's [value, value] pairs (for compatibility with `Map`).
     */
    entries(): IterableIterator<[A, A]>;
    /**
     * Executes a callback for each value in the set.
     *
     * @param callbackfn a function called for each value.
     */
    forEach(callbackfn: (value: A, value2: A, set: Set<A>) => void): void;
}
type StackTrace = Array<string>;
/**
 * Factory for creating boxes.
 *
 * Boxes are mutable, observable containers that implement `IResolvable` and
 * capture stack traces when mutated (under `CDK_DEBUG`). They are intended as a
 * replacement for the `Lazy` API in L2 constructs: where `Lazy` captures a
 * stack trace only at creation time (typically inside the L2 constructor),
 * boxes capture traces at the point of mutation — which is usually in user code.
 *
 * ### Typical L2 usage
 *
 * ```ts
 * @noBoxStackTraces
 * class MyL2 extends Resource {
 *   private readonly items: ArrayBox<string>;
 *
 *   constructor(scope: Construct, id: string) {
 *     super(scope, id);
 *     this.items = Boxes.fromArray([]);
 *     new CfnResource(this, 'Resource', {
 *       items: Token.asList(this.items),
 *     });
 *   }
 *
 *   addItem(item: string) {
 *     this.items.push(item); // stack trace captured here
 *   }
 * }
 * ```
 */
export declare class Box {
    /**
     * Creates a mutable box holding a single value.
     *
     * @param value the initial value.
     * @param options.equals optional equality function used to skip no-op `set` calls.
     *   Defaults to reference equality (`===`).
     * @returns a new `Box<A>`.
     */
    static fromValue<A>(value: A, options?: {
        equals?: (a: A, b: A) => boolean;
    }): IBox<A>;
    /**
     * Creates a read-only box that combines multiple source boxes through a function.
     *
     * The resulting box collects stack traces from all source boxes, sorted by
     * the global mutation order. This is useful when a single CloudFormation
     * property depends on multiple pieces of L2 state.
     *
     * @param boxes a record of named source boxes.
     * @param fn a pure function that receives the unwrapped values and produces the result.
     * @returns a new read-only `IReadableBox<R>`.
     * @example
     *   const a = Boxes.fromValue(10);
     *   const b = Boxes.fromValue(20);
     *   const result = Boxes.combine({ a, b }, (x) => x.a + x.b).derive((x) => x * 2);
     *   result.get(); // 60
     */
    static combine<T extends Record<string, IReadableBox<any>>, R>(boxes: T, fn: (values: {
        [K in keyof T]: T[K] extends IReadableBox<infer U> ? MakeReadonly<U> : never;
    }) => R): IReadableBox<R>;
    /**
     * Type guard that checks whether a value is a box.
     *
     * Used internally by `PropertyAssignmentMetadataWriter` to decide whether a
     * resolved token should contribute `aws:cdk:propertyAssignment` metadata.
     */
    static isBox(x: any): x is IReadableBox<any>;
    /**
     * Creates a mutable array box.
     *
     * @param as the initial array contents.
     * @param options.omitEmpty if true (the default), the box resolves to `undefined` when the
     *   array is empty. This behavior propagates through `map`, `derive`, and
     *   `reduce`, so derived boxes also resolve to `undefined` when the source
     *   array is empty. Set to `false` to resolve to an empty array instead.
     * @returns a new `ArrayBox<A>`.
     */
    static fromArray<A>(as: Array<A>, options?: {
        omitEmpty?: boolean;
    }): IArrayBox<A>;
    /**
     * Creates a mutable map box.
     *
     * @param map the initial map contents.
     * @returns a new `MapBox<K, V>`.
     */
    static fromMap<K, V>(map: Map<K, V>): IMapBox<K, V>;
    /**
     * Creates a mutable set box.
     *
     * @param set the initial set contents.
     * @returns a new `SetBox<A>`.
     */
    static fromSet<A>(set: Set<A>): ISetBox<A>;
    /**
     * Globally disables stack trace collection for all box mutations.
     *
     * Called by the `@noBoxStackTraces` decorator before entering an L2
     * constructor, so that default/initial values set during construction
     * do not produce misleading stack traces.
     */
    static disableStackTraceCollection(): void;
    /**
     * Re-enables stack trace collection for all box mutations.
     *
     * Called by the `@noBoxStackTraces` decorator after the L2 constructor
     * returns, so that subsequent mutations (from user code) are tracked.
     */
    static enableStackTraceCollection(): void;
}
export {};
