import { ChildKey, Context, NodeDefinition, Params, StatelessGraphNode, StatelessNodeDefinition, StatelessNodeType } from '../../types/graph'; /** * An instance of the [[tree]] node. * See the [[tree]] documentation to find out more. */ export interface TreeNode extends StatelessGraphNode<'tree', TreeNodeProperties> { } /** * A definition of the [[tree]] node. * See the [[tree]] documentation to find out more. */ export interface TreeNodeDefinition extends StatelessNodeDefinition<'tree', TreeNodeProperties> { } export declare type KeyMatcher = (key: ChildKey) => boolean; export declare const MISSING_PARAM_NAME = "$$graph-missing"; export declare type BranchDefinition = { match: ChildKey | KeyMatcher; param: string | undefined; node: NodeDefinition; }; export interface TreeNodeProperties { branches: Array; } export interface SerializedTreeNodeProperties { branches: Array<{ match: string | undefined; param: string | undefined; node: T; }>; } /** * The implementation of the [[tree]] node. * See the [[tree]] documentation to learn more. */ export declare const TreeNodeType: StatelessNodeType<'tree', TreeNodeProperties, SerializedTreeNodeProperties>; /** * Creates a new instance of a [[tree]] node, which is a node defining a single tree level. * It implements the `NodeType.getChild` method which enables the use of paths when traversing a Muster graph. * * In most cases, trees are defined as an array of string matchers, with every tree having * a unique name matcher. See the "**Simple tree**" example. * * [[tree]] also allows for dynamic tree names that use the [[match]] helper to generate * a typed matcher. See the "**Tree matchers**" example for more information. * * This node is serializable. * * * @example **Simple trees** * ```js * import { tree, value } from '@dws/muster'; * * const myTree = tree({ * firstName: value('Bob'), * lastName: value('Builder'), * }); * ``` * In this example we have created a tree with two string-based tree matchers: * - `firstName` * - `lastName` * * The content of each tree can be any [[NodeDefinition]]. In this example, both of these trees * contain [[value]]s. * * To access the contents of the `firstName` tree we'd first have to place it in a muster graph. * ```js * import muster, { tree, value } from '@dws/muster'; * * const myTree = tree({ * firstName: value('Bob'), * lastName: value('Builder'), * }); * * const app = muster(myTree); * ``` * As it happens, we chose to place our tree in the root of the muster graph. * Then we can just make a query for first name: * ```js * import muster, { tree, ref, value } from '@dws/muster'; * * const myTree = tree({ * firstName: value('Bob'), * lastName: value('Builder'), * }); * * const app = muster(myTree); * * const firstNameValue = await app.resolve(ref('firstName')); * // firstNameValue === 'Bob' * ``` * * See the [[muster]] helper documentation for more information on how to create an instance of muster. * * * @example **Nested trees** * ```js * import muster, { tree, ref, value } from '@dws/muster'; * * const app = muster(tree({ * currentUser: tree({ * firstName: value('Bob'), * lastName: value('Builder'), * }), * })); * * const firstNameValue = await app.resolve(ref('currentUser', 'firstName')); * // firstNameValue === 'Bob' * ``` * As mentioned before, the content [[tree]]'s tree can be any [[NodeDefinition]]. This allows * for the creation of nested trees. * * In this example we have created a tree `currentUser` which contains a tree with two leaves: * `firstName` and `lastName`. * * To access `firstName` from the `currentUser` we can use a [[ref]] and just specify the full * path: `ref('currentUser', 'firstName')`. * * * @example **Computed trees** * ```js * import muster, { tree, computed, ref, value } from '@dws/muster'; * * const app = muster(tree({ * name: value('Bob'), * currentUser: computed([ref('name')], (name) => * tree({ * firstName: value(name), * }), * ), * })); * * const firstName = await app.resolve(ref('currentUser', 'firstName')); * // firstName === 'Bob' * ``` * Trees in muster do not have to be defined at the time of creation of the muster instance. * New trees can be created as a result of resolving different [[NodeDefinition]]s. In this example * we have created a dynamic tree from a [[computed]]. * * See the [[computed]] documentation for more information about computed nodes. * * * @example **Tree matchers** * ```js * import muster, { tree, match, param, ref, types } from '@dws/muster'; * * const app = muster(tree({ * [match(types.string, 'treeName')]: param('treeName'), * })); * * const hello = await app.resolve(ref('hello')); * // hello === 'hello' * * const world = await app.resolve(ref('world')); * world === 'world' * * const stringOfNumbers = await app.resolve(ref('123')); * // stringOfNumbers === '123' * * const numeric = await app.resolve(ref(123)); * // numeric === 'Invalid child name: 123' * ``` * So far we've defined trees with pre-defined, static names. [[tree]] enables one more * way of defining trees: using a tree matcher. See [[match]] for more information on * matcher syntax and [[types]] about supported muster type matchers. * * In this example we've created a tree with a matcher allowing for every `string` path. * The content of this tree is a [[param]]. See the [[param]] documentation for more * information about that node. * * Below the application definition we tried to retrieve four tree names: * - `string 'hello'` - returned `value('hello')` * - `string 'world'` - returned `value('world')` * - `string '123'` - returned `value('123')` * - `number 123` - returned an error as the numeric name was not matched by this type matcher * * Note the tree matcher has two arguments: * - `types.string` * - `'treeName'` * The second argument defines the name of the parameter to define on the resolution context when * the tree name gets matched by this matcher. * * This parameter can then be used in every node belonging to that tree. * * * @example **Shape matchers** * ```js * import muster, { match, param, ref, tree, types, value } from '@dws/muster'; * * const app = muster({ * [match(types.shape({ name: types.string }), 'obj')]: param('obj'), * }); * * const test = await app.resolve(ref(value({ name: 'test name' }))); * // test === { name: 'test name' } * * const testWithExtra = await app.resolve( * ref(value({ name: 'test name', extra: 'extra prop' })), * ); * // testWithExtra === { name: 'test name', extra: 'extra prop' } * * const notFoundNode = await app.resolve(ref(value({ extra: 'extra prop' }))); * // notFoundNode === 'Invalid child name: {extra: "extra prop"})' * ``` * Tree matchers need not operate only on primitive types. With the use of a [[shape]] matcher * we can define more complex matchers allowing more data to be squeezed into a single path entry. * See [[match]] documentation for more information. * * * @example **Using tree parameters** * ```js * import muster, { computed, match, param, ref, tree, types, value } from '@dws/muster'; * * const app = muster({ * users: tree({ * [match(types.number, 'userId')]: computed([param('userId')], (userId) => { * // Synchronously retrieve the user from some data source * // or in our case - return a test tree * return tree({ * id: value(userId), * firstName: value(`User ${userId}`), * }); * }), * }), * }); * * const userId = await app.resolve(ref('users', 1, 'id')); * const userFirstName = await app.resolve(ref('users', 1, 'firstName')); * // userId === 1 * // userFirstName === 'User 1' * * const invalidName = await app.resolve(ref('users', 'testId', 'firstName')); * // invalidName === 'Invalid child key: testId' * ``` * Tree matchers are a good way of creating gateways between muster and an external API. * * To handle asynchronous requests one could replace the [[computed]] with a [[fromPromise]] * and request the data asynchronously */ export declare function tree(branches: TreeNodeProperties['branches'] | { [id: string]: NodeDefinition; }): TreeNodeDefinition; export declare function isTreeNodeDefinition(value: NodeDefinition): value is TreeNodeDefinition; export declare function match(predicate: KeyMatcher, param?: string): string; export declare function isMatcherKey(name: string): boolean; export declare function hasMatcherKeyId(name: string): boolean; export declare function getMatcherKeyId(name: string): string | undefined; export declare function getParamContextId(param: string): string; export declare function isParamContextId(contextId: string | symbol): contextId is string; export declare function parseContextIdParamName(contextId: string | symbol): string | undefined; export declare function getParams(context: Context): Params; export declare function getBranchNames(tree: TreeNode | TreeNodeDefinition): Array; export declare function getBranchByName(tree: TreeNode | TreeNodeDefinition, name: string): NodeDefinition | undefined;