import { DisposeCallback, GraphNode, NodeDefinition, NodeLike, StatelessGraphNode, StatelessNodeDefinition, StatelessNodeType } from '../../types/graph'; import { FieldSetDefinition } from './fields'; export declare function createEmptyDisposeEmitter(): () => () => void; /** * An instance of the [[legacyQuery]] node. * See the [[legacyQuery]] documentation to find out more. */ export interface LegacyQueryNode extends StatelessGraphNode<'legacyQuery', LegacyQueryNodeProperties> { } /** * A definition of the [[legacyQuery]] node. * See the [[legacyQuery]] documentation to find out more. */ export interface LegacyQueryNodeDefinition extends StatelessNodeDefinition<'legacyQuery', LegacyQueryNodeProperties> { } export interface LegacyQueryNodeProperties { keys: NodeDefinition; root: NodeDefinition; } /** * The implementation of the [[legacyQuery]] node. * See the [[legacyQuery]] documentation to learn more. */ export declare const LegacyQueryNodeType: StatelessNodeType<'legacyQuery', LegacyQueryNodeProperties>; /** * Creates an instance of a [[legacyQuery]] node, which is a node used to request values from multiple [[NodeDefinition]]s at a time. * This node is used internally by Muster-React when creating bindings between components and the graph. * See Muster-React for more information. * * By default, the [[legacyQuery]] waits for every field to resolve to a non-dynamic node. * This behaviour can be modified through the use of the [[isPending]] and the [[defer]]. * * A [[legacyQuery]] resolves into a [[tree]], an [[array]] or a [[value]] (depending on * the type of the legacyQuery made). These can be easily converted into plain JS objects * with the [[valueOf]] helper. This conversion is reversible through the use of * [[toNode]] helper. * * * @example **Basic legacyQuery** * ```js * import muster, { key, legacyQuery, root, valueOf } from '@dws/muster'; * * const app = muster({ * firstName: 'Rosalind', * lastName: 'Franklin', * dateOfBirth: 1948, * }); * * const user = await app.resolve(legacyQuery(root(), { * userFirstName: key('firstName'), * lastName: key('lastName'), * })); * // user = { * // userFirstName: 'Rosalind', * // lastName: 'Franklin', * // } * ``` * This example shows how to use a [[legacyQuery]] to request two [[NodeDefinition]] at the same * time. The `legacyQuery` call consists of: the first argument which defines the graph starting point from which * legacyQuery should begin the traversal; and the second argument, an object (implicitly cast to a [[fields]]) which * defines a map of fields to retrieve from the graph. The name of each property in that map * corresponds to the name in the output [[tree]]. Note that this name does not have to be the same * as the name in the [[key]] on the right of that property. Take the `firstName` graph node * and its corresponding `userFirstName` name in the legacyQuery. The ability to override the name of a * node is useful especially when a given [[NodeDefinition]] can be accessed in more than one way. * This behaviour is used extensively by the [[proxy]], and by extension the [[remote]], when * building a legacyQuery to a remote node. * * The [[key]] used in this legacyQuery defines the name of a given node in the graph. Additionally, the * [[key]] can define a map of child nodes to retrieve from that node. See the "**Getting values of * nested nodes**" example for more information. * * A [[legacyQuery]] resolves into a combination of [[tree]]s, [[array]]s and [[value]]s. * This means an output of one legacyQuery can be used as an input for another [[NodeDefinition]] and even * another [[legacyQuery]]. * * * @example **Getting async values** * ```js * import muster, { fromPromise, key, legacyQuery, root } from '@dws/muster'; * * let resolvePromise1; * const app = muster({ * name: 'sync name', * asyncName: fromPromise(() => * new Promise((res) => resolvePromise1 = res) * .then(() => 'async name'), * ), * }); * * console.log('Making the legacyQuery'); * app.resolve(legacyQuery(root(), { * name: key('name'), * asyncName: key('asyncName'), * })).subscribe((res) => { * console.log(res); * }); * * console.log('Resolving the promise'); * resolvePromise1(); * * // Console output: * // Making the legacyQuery * // Resolving the promise * // { * // name: 'sync name', * // asyncName: 'async name', * // } * ``` * The [[legacyQuery]] by default waits for every part of the legacyQuery to resolve to a non-pending and * non-dynamic value. This example demonstrates this behaviour with the help of [[fromPromise]]. * Note that the legacyQuery output gets logged only once - after the promise is resolved. Muster can * also mark certain parts of the legacyQuery with [[defer]]s. This instructs Muster * to return the legacyQuery result even if that part of the legacyQuery is loading. See the "**Defer part of the * legacyQuery**" example for more information. * * * @example **Getting values of nested nodes** * ```js * import muster, { key, legacyQuery, root } from '@dws/muster'; * * const app = muster({ * user: { * firstName: 'Rosalind', * lastName: 'Franklin', * }, * }); * * const user = await app.resolve(legacyQuery(root(), { * user: key('user', { * firstName: key('firstName'), * }), * })); * // user = { * // user: { * // firstName: 'Rosalind', * // }, * // } * ``` * The [[legacyQuery]] can extract values from nested [[NodeDefinition]]s. As shown * in the previous example, the [[legacyQuery]] factory function performs an implicit conversion to a * [[fields]]. This saves developers from having to explicitly write unnecessarily verbose code. * The same functionality is available in the [[key]]. When the factory is * called with a pure JS object, it will recursively cast it to a [[fields]]. * * Sometimes, the second argument to the [[key]] won't be a pure JS object. For * example, when a developer wants to get items from a collection. [[fields]] lets the * legacyQuery know that a target [[NodeDefinition]] is expected to be a container-like node. This assumption * breaks apart when requesting a collection. To request items from collections, one has to make a * legacyQuery with an [[entries]] instead of a [[fields]]. See the "**Getting atomic items from a * collection**" example to find out more. * * * @example **Getting atomic items from a collection** * ```js * import muster, { entries, key, legacyQuery, root } from '@dws/muster'; * * const app = muster({ * numbers: [1, 2, 3, 4], * }); * * const numbers = await app.resolve(legacyQuery(root(), { * numbers: key('numbers', entries()), * })); * // numbers = { * // numbers: [1, 2, 3, 4], * // } * ``` * An [[entries]] can be provided instead of a [[fields]] to instruct the [[legacyQuery]] to retrieve * all items from a given graph node. In this example, we had it easy: every item is an atomic * value. The fun with [[legacyQuery]]s and collections does not end here. See the "**Getting specific * fields from items**" example to learn more about selecting certain fields from items. * * * @example **Getting specific fields from items** * ```js * import muster, { entries, key, legacyQuery, ref } from '@dws/muster'; * * const app = muster({ * books: [ * { name: 'The Expeditionary Force', author: 'Craig Alanson' }, * { name: 'Fear The Sky', author: 'Stephen Moss' }, * { name: 'After It Happened', author: 'Devon Ford' }, * ], * }); * * const bookNames = await app.resolve(legacyQuery(ref('books'), entries({ * name: key('name'), * }))); * // bookNames = [ * // { name: 'The Expeditionary Force' }, * // { name: 'Fear The Sky' }, * // { name: 'After It Happened' }, * // ] * ``` * In this example, apart from getting just a selected field from each item in the collection, we * demonstrated another feature of the [[legacyQuery]]: changing its starting point. * In all previous examples, we've used a [[root]] as the starting point of the legacyQuery, but this time * to make the output shorter we've changed it to a `books` collection. * * * @example **Creating setters** * ```js * import muster, { * createSetter, * key, * legacyQuery, * root, * variable, * } from '@dws/muster'; * * const app = muster({ * name: variable('initial'), * }); * * console.log('Making a legacyQuery for `name`'); * app.resolve(legacyQuery(root(), { name: key('name') })).subscribe((result) => { * console.log(result); * }); * * console.log('Making a legacyQuery for setter function'); * const result = await app.resolve(legacyQuery(root(), { * setName: createSetter('name'), * })); * * console.log('Calling a setter'); * result.setName('updated'); * * // Console output: * // Making a legacyQuery for `name` * // initial * // Making a legacyQuery for setter function * // Calling a setter * // updated * ``` * This example shows how a [[legacyQuery]] can be used when there's a need for imperative call * to a set method. This setter function can be used, for example, * when integrating with view frameworks such as React, Vue etc. * Muster-React uses this way of creating setters extensively. Similarly, there's a * [[createCaller]] node which allows for imperative calls to actions. * * [[createSetter]]s and [[createCaller]]s should be used only when the output of the legacyQuery * is used outside the "Muster world". Inside Muster code, using [[call]]s * [[apply]]s and [[set]]s is recommended for these kinds of actions. * * * @example **Defer part of a legacyQuery** * ```js * import muster, { defer, fromPromise, key, legacyQuery, root } from '@dws/muster'; * * let resolvePromise2; * const app = muster({ * name: 'sync name', * asyncName: fromPromise(() => * new Promise((res) => resolvePromise2 = res) * .then(() => 'async name'), * ), * }); * * console.log('Requesting the legacyQuery'); * app.resolve(legacyQuery(root(), { * name: key('name'), * asyncName: defer('asyncName'), * })).subscribe((res) => { * console.log(res); * }); * * console.log('Resolving the promise'); * resolvePromise2(); * * // Console output: * // Requesting the legacyQuery * // { * // name: 'sync name', * // asyncName: null, * // } * // Resolving the promise * // { * // name: 'sync name', * // asyncName: 'async name', * // } * ``` * This example demonstrates how to instruct a [[legacyQuery]] to return the output of its legacyQuery * even if a given [[NodeDefinition]] is in a pending state. The legacyQuery is built with a * [[defer]]. This example uses a shorthand syntax for the `defer(...)` node. * Internally, the node converts the argument to a [[key]]: * ```js * import { defer, key } from '@dws/muster'; * * defer('asyncName'); * // is equivalent to * defer(key('name')); * ``` * The [[defer]] also enables the defer part of the legacyQuery to return a previously loaded * (stale) value of the target node. See the "**Defer with previous value**" example for more * information. * * * @example **Check if defer part of the legacyQuery is loading** * ```js * import muster, { * defer, * fromPromise, * isPending, * key, * legacyQuery, * root, * } from '@dws/muster'; * * let resolvePromise3; * const app = muster({ * name: 'sync name', * asyncName: fromPromise(() => * new Promise((res) => resolvePromise3 = res) * .then(() => 'async name'), * ), * }); * * console.log('Requesting the legacyQuery'); * app.resolve(legacyQuery(root(), { * name: key('name'), * asyncName: defer('asyncName'), * isLoadingAsyncName: isPending('asyncName'), * })).subscribe((res) => { * console.log(res); * }); * * console.log('Resolving the promise'); * resolvePromise3(); * * // Console output: * // Requesting the legacyQuery * // { * // name: 'sync name', * // asyncName: null, * // isLoadingAsyncName: true, * // } * // Resolving the promise * // { * // name: 'sync name', * // asyncName: 'async name', * // isLoadingAsyncName: false, * // } * ``` * This example presents the use of the [[isPending]]. When used in a legacyQuery, this node checks if * a part of the legacyQuery is currently loading. Similarly to the [[defer]], * the [[isPending]] factory function implicitly converts its argument to a [[key]]. * ```js * import { isPending, key } from '@dws/muster'; * * isPending('asyncName'); * // is equivalent to * isPending(key('asyncName')) * ``` */ export declare function legacyQuery(root: NodeLike, keys: NodeDefinition | FieldSetDefinition): LegacyQueryNodeDefinition; export declare function isLegacyQueryNodeDefinition(value: NodeDefinition): value is LegacyQueryNodeDefinition; export declare function resolveTransaction(node: GraphNode, disposeEmitter: (listener: () => void) => DisposeCallback): Promise;