import type { IResolvable, IResolveContext } from '../resolvable'; export type MakeReadonly = A extends Set ? ReadonlySet : A extends Map ? ReadonlyMap : A extends Array ? ReadonlyArray : A extends object ? Readonly : 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 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; /** * 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(fn: (a: MakeReadonly) => B): IReadableBox; /** * 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; } /** * 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 extends IReadableBox { /** * 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; /** * Applies a function to the current value and replaces it with the result. * * Equivalent to `box.set(fn(box.getMutable()))`. The same equality check * and stack trace capture rules as `set` apply. * * @param fn a function that receives the current value and returns the new value. */ update(fn: (a: A) => A): void; } /** * A mutable box specialized for arrays, extending `Box>` 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 extends IBox>, Iterable { /** * 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) => 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) => 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) => unknown): boolean; /** * Determines whether the array includes a certain element, returning true or false as appropriate. * * Delegates to `Array.prototype.includes` on the underlying array. * * @param searchElement the element to search for. * @returns `true` if the element is found, `false` otherwise. */ includes(searchElement: A): 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(fn: (a: A) => B): IReadableBox>; /** * Creates a derived read-only box containing only elements that satisfy the predicate. * * Shorthand for `this.derive(a => a.filter(fn))`. * * @param fn a predicate function called for each element. * @returns a new read-only box holding the filtered array. */ filter(fn: (a: A) => boolean): IReadableBox>; } /** * A mutable box specialized for maps, extending `Box>` 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 extends IBox>, 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; /** * Returns an iterable of the map's values. */ values(): IterableIterator; /** * 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) => void): void; } /** * A mutable box specialized for sets, extending `Box>` 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 extends IBox>, Iterable { /** * 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; /** * 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) => void): void; } type StackTrace = Array; type OrderedStackTrace = { trace: StackTrace; seq: number; }; /** * 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; * * 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`. */ static fromValue(value: A, options?: { equals?: (a: A, b: A) => boolean; }): IBox; /** * 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`. * @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>, R>(boxes: T, fn: (values: { [K in keyof T]: T[K] extends IReadableBox ? MakeReadonly : never; }) => R): IReadableBox; /** * 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; /** * 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`. */ static fromArray(as?: Array, options?: { omitEmpty?: boolean; }): IArrayBox; /** * Creates a mutable map box. * * @param map the initial map contents. * @returns a new `MapBox`. */ static fromMap(map?: Map): IMapBox; /** * Creates a mutable set box. * * @param set the initial set contents. * @returns a new `SetBox`. */ static fromSet(set?: Set): ISetBox; /** * 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; } declare abstract class BaseReadableBox implements IReadableBox { readonly creationStack: string[]; protected constructor(); derive(fn: (a: MakeReadonly) => B): IReadableBox; abstract get(): MakeReadonly; abstract getMutable(): A; abstract getStackTraces(): Array; resolve(_: IResolveContext): MakeReadonly | undefined; } export declare abstract class ReadonlyState extends BaseReadableBox { protected value: A; protected orderedTraces: Array; constructor(value: A); get(): MakeReadonly; getMutable(): A; getStackTraces(): Array; getOrderedStackTraces(): Array; } export {};