UNPKG

aws-cdk-lib

Version:

Version 2 of the AWS Cloud Development Kit library

358 lines (357 loc) 13.4 kB
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 {};