import * as Effect from 'effect/Effect'; import * as Option from 'effect/Option'; import * as Pipeable from 'effect/Pipeable'; import * as Atom from 'effect/reactivity/Atom'; import * as Registry from 'effect/reactivity/AtomRegistry'; import { type CleanupFn, Event } from '@dxos/async'; import { type MakeOptional } from '@dxos/util'; import * as Node from './AppGraphNode.ts'; /** * Identifier denoting a Graph. */ export declare const GraphTypeId: unique symbol; export type GraphTypeId = typeof GraphTypeId; /** * Identifier for the graph kind discriminator. */ export declare const GraphKind: unique symbol; export type GraphKind = typeof GraphKind; export type GraphTraversalOptions = { /** * A callback which is called for each node visited during traversal. * * If the callback returns `false`, traversal is stops recursing. */ visitor: (node: Node.Node, path: string[]) => boolean | void; /** * The node to start traversing from. * * @default ROOT_ID */ source?: string; /** The relation(s) to traverse graph edges. */ relation: Node.RelationInput | Node.RelationInput[]; }; export type GraphProps = { registry?: Registry.AtomRegistry; nodes?: MakeOptional[]; edges?: Record; onExpand?: (id: string, relation: Node.Relation) => void; onRemoveNode?: (id: string) => void; }; export type Edge = { source: string; target: string; relation: Node.RelationInput; }; export type Edges = Record; export type GraphKindType = 'readable' | 'expandable' | 'writable'; /** * While the graph is retained, a node's atom stays mounted as long as the node is in it. The other accessors are views that a * bare `registry.subscribe` never computes, so it never fires: pass `{ immediate: true }`. */ export interface BaseGraph extends Pipeable.Pipeable { readonly [GraphTypeId]: GraphTypeId; readonly [GraphKind]: GraphKindType; /** * Event emitted when a node is changed. */ readonly onNodeChanged: Event<{ id: string; node: Option.Option; }>; /** * Get the atom key for the JSON representation of the graph. */ json(id?: string): Atom.Atom; /** * Get the atom key for the node with the given id. */ node(id: string): Atom.Atom>; /** * Get the atom key for the connections of the node with the given id. */ connections(id: string, relation: Node.RelationInput): Atom.Atom; /** * Get the atom key for the actions of the node with the given id. */ actions(id: string): Atom.Atom<(Node.Action | Node.ActionGroup)[]>; /** * Get the atom key for the edges of the node with the given id. */ edges(id: string): Atom.Atom; } export type ReadableGraph = BaseGraph & { readonly [GraphKind]: 'readable' | 'expandable' | 'writable'; }; export type ExpandableGraph = BaseGraph & { readonly [GraphKind]: 'expandable' | 'writable'; }; export type WritableGraph = BaseGraph & { readonly [GraphKind]: 'writable'; }; /** * Graph interface. */ export type Graph = WritableGraph; export declare const relationKey: (relation: Node.RelationInput) => string; export declare const relationFromKey: (encoded: string) => Node.Relation; export declare const connectionKey: (id: string, relation: Node.RelationInput) => string; export declare const inverseRelation: (relation: Node.RelationInput) => Node.Relation; /** * Get the Graph a Node is currently associated with. */ export declare const getGraph: (node: Node.Node) => Graph; /** * Creates a new Graph instance. */ export declare const make: (params?: GraphProps) => Graph; /** * Pins the graph's nodes in its registry, including nodes added later, until the returned function * is called. A graph has one holder at a time. */ export declare const retain: (graph: BaseGraph) => CleanupFn; /** * Convert the graph to a JSON object. */ export declare const toJSON: (graph: BaseGraph, id?: string) => object; /** * Get the node with the given id from the graph's registry. */ export declare const getNode: (graph: BaseGraph, id: string) => Option.Option; /** * Get the node with the given id from the graph's registry. * * @throws {GraphNode.NotFoundError} If the graph has no node with the id. */ export declare const getNodeOrThrow: (graph: BaseGraph, id: string) => Node.Node; /** * Get the root node of the graph. * This is an alias for `getNodeOrThrow(graph, ROOT_ID)`. */ export declare function getRoot(graph: BaseGraph): Node.Node; /** * Get all nodes connected to the node with the given id by the given relation from the graph's registry. */ export declare const getConnections: (graph: BaseGraph, id: string, relation: Node.RelationInput) => Node.Node[]; /** * Get all actions connected to the node with the given id from the graph's registry. */ export declare const getActions: (graph: BaseGraph, id: string) => Node.Node[]; /** * Get the edges from the node with the given id from the graph's registry. */ export declare const getEdges: (graph: BaseGraph, id: string) => Edges; /** * Recursive depth-first traversal of the graph. */ export declare const traverse: (graph: BaseGraph, options: GraphTraversalOptions, path?: string[]) => void; /** * Get the path between two nodes in the graph. */ export declare const getPath: (graph: BaseGraph, params: { source?: string; target: string; }) => Option.Option; /** * Wait for the path between two nodes in the graph to be established. */ export declare const waitForPath: (graph: BaseGraph, params: { source?: string; target: string; }, options?: { timeout?: number; interval?: number; }) => Promise; /** * Resolves when the node exists in the graph; immediately if it already does. * * The graph is populated asynchronously — connectors expand a level at a time and the objects * behind them load out of band — and {@link expandSync} is fire-and-forget, so a consumer that needs * a specific node has no completion to await and would otherwise poll for it. * * Deliberately unbounded: whether a node is merely late or will never arrive is the caller's * question, so the deadline belongs at the call site (`Effect.timeout`). Interrupting is safe — * the subscription is released on interruption. */ export declare const waitFor: (graph: BaseGraph, id: string) => Effect.Effect; /** * Implementation helper for expandSync. * If the node does not exist yet, the expand is recorded as pending and applied when the node is added. * * Fires the `onExpand` callback to add connections to the node. The builder only marks the relation's * connectors dirty; they run, and their output reaches the graph, on its next flush. * * Expanding a node that is already expanded for the same relation is a no-op. */ export declare const expandSync: (graph: T, id: string, relation: Node.RelationInput) => T; /** * {@link expandSync} the `child` relation of every ancestor of a qualified id, and of the id itself. * A missing ancestor's expand is deferred until it arrives, so a single call populates the whole path. */ export declare const expandPath: (graph: T, qualifiedId: string) => T; /** * Expand a node in the graph, off the paint-critical path. * * Yields to the main thread before running {@link expandSync}, so a caller reacting to input does not * block the frame. Interrupting the effect cancels a still-pending expansion, which makes rescheduling * (e.g. superseding a hover with the next one) a matter of interrupting the previous fiber. */ export declare const expand: (graph: T, id: string, relation: Node.RelationInput) => Effect.Effect; /** * Sort the edges of the node with the given id. */ export declare const sortEdges: (graph: T, id: string, relation: Node.RelationInput, order: string[]) => T; /** * Applies `fn`'s writes as a single observable change: the model bumps its version once, so derived * views recompute and notify once for the whole group. */ export declare const batch: (graph: T, fn: () => A) => A; /** * Unloads the nodes: they leave the model outright, along with the expansion bookkeeping that would * otherwise keep the graph remembering ids it will never be asked about again. * * Unlike {@link removeNodes} this leaves no tombstone, so a subsequent read of a released relation * expands it afresh rather than resolving to an emptied node. */ export declare const release: (graph: T, ids: readonly string[]) => T; /** * Forgets that `relation` of `id` was expanded. Paired with the builder tearing down the matching * connector subscription, so a relation whose contents were released re-expands rather than * resolving to the emptied list it had before. */ export declare const releaseRelation: (graph: T, id: string, relation: string) => T; /** * Add nodes to the graph. */ export declare const addNodes: (graph: T, nodes: Node.NodeArg>[]) => T; /** * Add a node to the graph. */ export declare const addNode: (graph: T, nodeArg: Node.NodeArg>) => T; /** * Remove nodes from the graph. */ export declare const removeNodes: (graph: T, ids: string[], edges?: boolean) => T; /** * Remove a node from the graph. */ export declare const removeNode: (graph: T, id: string, edges?: boolean) => T; /** * Add edges to the graph. */ export declare const addEdges: (graph: T, edges: Edge[]) => T; /** * Add an edge to the graph. */ export declare const addEdge: (graph: T, edgeArg: Edge) => T; /** * Remove edges from the graph. */ export declare const removeEdges: (graph: T, edges: Edge[], removeOrphans?: boolean) => T; /** * Remove an edge from the graph. */ export declare const removeEdge: (graph: T, edgeArg: Edge, removeOrphans?: boolean) => T; //# sourceMappingURL=AppGraph.d.ts.map