{"version":3,"file":"chunk-AppGraph.mjs","names":[],"sources":["../../src/util.ts","../../src/AppGraph.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Atom from 'effect/reactivity/Atom';\n\nimport { shallowEqual } from '@dxos/util';\n\nimport * as Node from './AppGraphNode.ts';\n\n/**\n * `Atom.withLabel` captures and formats a stack trace on every call, and the graph labels an atom per node,\n * per connection key, and per extension, so one expansion costs hundreds of captures on the main thread —\n * hence opt-in, and only under the dev server.\n */\nconst ATOM_LABELS = Boolean(import.meta.env?.DEV) && import.meta.env?.VITE_ATOM_LABELS === 'true';\n\n/** {@link Atom.withLabel}, reduced to a pass-through wherever labels are not collected. */\nexport const withLabel: (name: string) => <A extends Atom.Atom<any>>(self: A) => A = ATOM_LABELS\n  ? Atom.withLabel\n  : () => (self) => self;\n\n// PRIMARY separates top-level components (e.g., node ID from relation) in compound string keys used within the app-graph package.\nconst PRIMARY = '\\u0001';\n\n// SECONDARY separates sub-components within an encoded value (e.g., relation kind from direction) in the same context.\nconst SECONDARY = '\\u0002';\n\n/** Join parts with the primary separator. */\nexport const primaryKey = (...parts: string[]): string => parts.join(PRIMARY);\n\n/** Split a key on the primary separator. */\nexport const primaryParts = (key: string): string[] => key.split(PRIMARY);\n\n/** Join parts with the secondary separator. */\nexport const secondaryKey = (...parts: string[]): string => parts.join(SECONDARY);\n\n/** Split a key on the secondary separator. */\nexport const secondaryParts = (key: string): string[] => key.split(SECONDARY);\n\n/**\n * Normalize a relation input to a full Relation object.\n */\nexport const normalizeRelation = (relation?: Node.RelationInput): Node.Relation =>\n  relation == null ? Node.child : typeof relation === 'string' ? Node.relation(relation) : relation;\n\n/**\n * Returns true if two NodeArg arrays are semantically identical (same id, type, data, properties per index).\n * Inline child nodes (the `nodes` field) are compared recursively.\n */\nexport const nodeArgsUnchanged = (prev: readonly Node.NodeArg<any>[], next: readonly Node.NodeArg<any>[]): boolean => {\n  if (prev.length !== next.length) {\n    return false;\n  }\n\n  return prev.every((prevNode, idx) => {\n    const nextNode = next[idx];\n    return (\n      prevNode.id === nextNode.id &&\n      prevNode.type === nextNode.type &&\n      shallowEqual(prevNode.data, nextNode.data) &&\n      shallowEqual(prevNode.properties, nextNode.properties) &&\n      nodeArgsUnchanged(prevNode.nodes ?? [], nextNode.nodes ?? []) &&\n      nodeArgsUnchanged(prevNode.actions ?? [], nextNode.actions ?? [])\n    );\n  });\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as Option from 'effect/Option';\nimport * as Pipeable from 'effect/Pipeable';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Registry from 'effect/reactivity/AtomRegistry';\n\nimport { type CleanupFn, Event, Trigger } from '@dxos/async';\nimport { todo } from '@dxos/debug';\nimport * as AtomEx from '@dxos/effect/AtomEx';\nimport * as GraphModel from '@dxos/graph/GraphModel';\nimport * as GraphNode from '@dxos/graph/GraphNode';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { type MakeOptional, shallowEqual } from '@dxos/util';\n\nimport { scheduleTask } from '#scheduler';\n\nimport * as Node from './AppGraphNode.ts';\nimport { normalizeRelation, primaryKey, primaryParts, secondaryKey, secondaryParts, withLabel } from './util.ts';\n\n//\n// The app graph: the vocabulary, the store that holds it, and the operations over it. One module\n// because the store stamps the brand symbols at class-definition time and the operations reach into\n// the store — split across files those are mutually recursive imports, and the class body runs\n// against an uninitialized binding.\n//\n\n//\n// Vocabulary.\n//\n\n/**\n * Identifier denoting a Graph.\n */\nexport const GraphTypeId: unique symbol = Symbol.for('@dxos/app-graph/Graph');\nexport type GraphTypeId = typeof GraphTypeId;\n\n/**\n * Identifier for the graph kind discriminator.\n */\nexport const GraphKind: unique symbol = Symbol.for('@dxos/app-graph/GraphKind');\nexport type GraphKind = typeof GraphKind;\n\nexport type GraphTraversalOptions = {\n  /**\n   * A callback which is called for each node visited during traversal.\n   *\n   * If the callback returns `false`, traversal is stops recursing.\n   */\n  visitor: (node: Node.Node, path: string[]) => boolean | void;\n\n  /**\n   * The node to start traversing from.\n   *\n   * @default ROOT_ID\n   */\n  source?: string;\n\n  /** The relation(s) to traverse graph edges. */\n  relation: Node.RelationInput | Node.RelationInput[];\n};\n\nexport type GraphProps = {\n  registry?: Registry.AtomRegistry;\n  nodes?: MakeOptional<Node.Node, 'data' | 'cacheable'>[];\n  edges?: Record<string, Edges>;\n  onExpand?: (id: string, relation: Node.Relation) => void;\n  onRemoveNode?: (id: string) => void;\n};\n\nexport type Edge = { source: string; target: string; relation: Node.RelationInput };\nexport type Edges = Record<string, string[]>;\n\nexport type GraphKindType = 'readable' | 'expandable' | 'writable';\n\n/**\n * 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\n * bare `registry.subscribe` never computes, so it never fires: pass `{ immediate: true }`.\n */\nexport interface BaseGraph extends Pipeable.Pipeable {\n  readonly [GraphTypeId]: GraphTypeId;\n  readonly [GraphKind]: GraphKindType;\n  /**\n   * Event emitted when a node is changed.\n   */\n  readonly onNodeChanged: Event<{ id: string; node: Option.Option<Node.Node> }>;\n  /**\n   * Get the atom key for the JSON representation of the graph.\n   */\n  json(id?: string): Atom.Atom<any>;\n  /**\n   * Get the atom key for the node with the given id.\n   */\n  node(id: string): Atom.Atom<Option.Option<Node.Node>>;\n  /**\n   * Get the atom key for the connections of the node with the given id.\n   */\n  connections(id: string, relation: Node.RelationInput): Atom.Atom<Node.Node[]>;\n  /**\n   * Get the atom key for the actions of the node with the given id.\n   */\n  actions(id: string): Atom.Atom<(Node.Action | Node.ActionGroup)[]>;\n  /**\n   * Get the atom key for the edges of the node with the given id.\n   */\n  edges(id: string): Atom.Atom<Edges>;\n  /**\n   * Upsert a node directly, bypassing expansion.\n   * @internal\n   */\n  _setNode(id: string, node: Option.Option<Node.Node>): void;\n  /**\n   * Materialize a node argument into a node without adding it to the graph.\n   * @internal\n   */\n  _constructNode(node: Node.NodeArg<any>): Option.Option<Node.Node>;\n}\n\nexport type ReadableGraph = BaseGraph & { readonly [GraphKind]: 'readable' | 'expandable' | 'writable' };\nexport type ExpandableGraph = BaseGraph & { readonly [GraphKind]: 'expandable' | 'writable' };\nexport type WritableGraph = BaseGraph & { readonly [GraphKind]: 'writable' };\n\n/**\n * Graph interface.\n */\nexport type Graph = WritableGraph;\n\n//\n// The store: the model handle, the derived atom families and the expansion bookkeeping.\n//\n\nconst graphSymbol = Symbol('graph');\n\ntype DeepWriteable<T> = {\n  -readonly [K in keyof T]: T[K] extends object ? DeepWriteable<T[K]> : T[K];\n};\n\ntype NodeInternal = DeepWriteable<Node.Node> & { [graphSymbol]: GraphImpl };\n\n//\n// Utilities\n//\n\nexport const relationKey = (relation: Node.RelationInput): string => {\n  const normalized = normalizeRelation(relation);\n  return secondaryKey(normalized.kind, normalized.direction);\n};\n\nexport const relationFromKey = (encoded: string): Node.Relation => {\n  const parts = secondaryParts(encoded);\n  invariant(parts.length === 2 && parts[0].length > 0 && parts[1].length > 0, `Invalid relation key: ${encoded}`);\n  const [kind, directionRaw] = parts;\n  invariant(directionRaw === 'outbound' || directionRaw === 'inbound', `Invalid relation direction: ${directionRaw}`);\n  return Node.relation(kind, directionRaw);\n};\n\nexport const connectionKey = (id: string, relation: Node.RelationInput): string =>\n  primaryKey(id, relationKey(relation));\n\nconst relationFromConnectionKey = (key: string): { id: string; relation: Node.Relation } => {\n  const [id, encodedRelation] = primaryParts(key);\n  invariant(id && encodedRelation, `Invalid connection key: ${key}`);\n  return { id, relation: relationFromKey(encodedRelation) };\n};\n\nexport const inverseRelation = (relation: Node.RelationInput): Node.Relation => {\n  const normalized = normalizeRelation(relation);\n  return Node.relation(normalized.kind, normalized.direction === 'outbound' ? 'inbound' : 'outbound');\n};\n\n/** Node payload; `data` is undefined for a placeholder the graph has not been given yet. */\ntype ModelNode = { id: string; data?: Node.Node };\n\n/** Relation lives in `type`; `order` carries the caller's sort position. */\ntype GraphEdge = { id: string; type: string; source: string; target: string };\n\n/**\n * Every edge is held in its outbound form, so an inbound relation is the same edge read backwards.\n * Without this a relation would have two storage encodings and reads would have to try both.\n */\nconst storedEdge = (source: string, target: string, relation: string): GraphEdge => {\n  const { kind, direction } = relationFromKey(relation);\n  const outbound = relationKey(Node.relation(kind, 'outbound'));\n  const [from, to] = direction === 'inbound' ? [target, source] : [source, target];\n  return { id: primaryKey(from, secondaryKey(outbound, to)), type: outbound, source: from, target: to };\n};\n\nconst inverseKey = (relation: string): string => relationKey(inverseRelation(relationFromKey(relation)));\n\nconst edgesEqual = (a: Edges, b: Edges): boolean => {\n  const keys = Object.keys(a);\n  return (\n    keys.length === Object.keys(b).length &&\n    keys.every((key) => {\n      const left = a[key];\n      const right = b[key];\n      return right !== undefined && left.length === right.length && left.every((id, index) => id === right[index]);\n    })\n  );\n};\n\n/**\n * Get the Graph a Node is currently associated with.\n */\nexport const getGraph = (node: Node.Node): Graph => {\n  const graph = (node as NodeInternal)[graphSymbol];\n  invariant(graph, 'Node is not associated with a graph.');\n  return graph as Graph;\n};\n\n/**\n * The Graph represents the user interface information architecture of the application constructed via plugins.\n * @internal\n */\nexport class GraphImpl implements WritableGraph {\n  readonly [GraphTypeId]: GraphTypeId = GraphTypeId;\n  readonly [GraphKind] = 'writable' as const;\n\n  pipe() {\n    // eslint-disable-next-line prefer-rest-params\n    return Pipeable.pipeArguments(this, arguments);\n  }\n\n  readonly onNodeChanged = new Event<{\n    id: string;\n    node: Option.Option<Node.Node>;\n  }>();\n\n  readonly _onExpand?: GraphProps['onExpand'];\n  readonly _onRemoveNode?: GraphProps['onRemoveNode'];\n\n  readonly _registry: Registry.AtomRegistry;\n\n  /**\n   * One mount per node while the graph is retained, so a node's atom keeps one identity and its\n   * dependents stay wired. Undefined while nothing retains the graph; see {@link retain}.\n   * @internal\n   */\n  _pins?: Map<string, CleanupFn>;\n  readonly _expanded = new Set<string>();\n  /** Relation keys a node has held, so an emptied relation still reports an empty list. */\n  readonly _relations = new Map<string, Set<string>>();\n  readonly _pendingExpands = new Set<string>();\n\n  /**\n   * Canonical store. Nodes an edge references before they are contributed sit in it as\n   * placeholders, and a removed node leaves one behind, so arrival order is free.\n   */\n  readonly _model: GraphModel.GraphModel<ModelNode, GraphEdge>;\n\n  /** @internal */\n  readonly _node = Atom.family<string, Atom.Atom<Option.Option<Node.Node>>>((id) => {\n    return Atom.make((get) => Option.fromUndefinedOr(get(this._model.nodeAtom(id))?.data)).pipe(\n      // Cut off on the payload: `Option.some` allocates a fresh wrapper on every recompute, so\n      // without this a version bump notifies subscribers of nodes it did not touch.\n      Atom.withEquality(\n        (a: Option.Option<Node.Node>, b: Option.Option<Node.Node>) =>\n          Option.getOrUndefined(a) === Option.getOrUndefined(b),\n      ),\n      withLabel(`graph:node:${id}`),\n    );\n  });\n\n  /**\n   * The node as the model holds it right now.\n   *\n   * A flush applies its writes inside {@link batch}, which bumps the version once at the end, so\n   * reading {@link Graph._node} from a write path yields the value from before the batch — and\n   * `addNode` merges onto what it reads, so that would silently undo an earlier write in the\n   * same flush.\n   * @internal\n   */\n  _currentNode(id: string): Option.Option<Node.Node> {\n    return Option.fromUndefinedOr(this._model.findNode(id)?.data);\n  }\n\n  /** The outgoing and inbound edges as the model holds them right now; see {@link Graph._currentNode}. @internal */\n  _currentEdges(id: string): Edges {\n    return this._computeEdges(id);\n  }\n\n  /** {@link Graph._currentEdges}; separate so the `_edges` atom body reads it without indirection. */\n  _computeEdges(id: string): Edges {\n    const edges: Edges = {};\n    for (const relation of this._relations.get(id) ?? []) {\n      edges[relation] = [];\n    }\n    for (const edge of this._model.outgoing(id)) {\n      (edges[edge.type] ??= []).push(edge.target);\n    }\n    for (const edge of this._model.incoming(id)) {\n      (edges[inverseKey(edge.type)] ??= []).push(edge.source);\n    }\n    return edges;\n  }\n\n  readonly _edges = Atom.family<string, Atom.Atom<Edges>>((id) => {\n    return Atom.make((get) => {\n      get(this._model.version);\n      return this._computeEdges(id);\n    }).pipe(Atom.withEquality(edgesEqual), withLabel(`graph:edges:${id}`));\n  });\n\n  // NOTE: Currently the argument to the family needs to be referentially stable for the atom to be referentially stable.\n  // TODO(wittjosiah): Atom feature request, support for something akin to `ComplexMap` to allow for complex arguments.\n  readonly _connections = Atom.family<string, Atom.Atom<Node.Node[]>>((key) => {\n    const parts = key ? primaryParts(key) : [];\n    // An empty id (e.g. `useConnections(graph, undefined, ...)`) yields a key with two parts but no\n    // id — treat that as no connections rather than throwing.\n    if (parts.length < 2 || !parts[0]) {\n      return Atom.make((): Node.Node[] => []);\n    }\n\n    const { id, relation } = relationFromConnectionKey(key);\n    return Atom.make((get) =>\n      (get(this._edges(id))[relationKey(relation)] ?? [])\n        .map((childId) => get(this._node(childId)))\n        .filter(Option.isSome)\n        .map((node) => node.value),\n    ).pipe(\n      // Depend on each child's own atom so a child's change invalidates this view, but cut off on the\n      // resolved list: without this a no-op re-add re-allocates the Options and notifies spuriously.\n      Atom.withEquality(\n        (a: Node.Node[], b: Node.Node[]) => a.length === b.length && a.every((node, index) => node === b[index]),\n      ),\n      withLabel(`graph:connections:${key}`),\n    );\n  });\n\n  readonly _actions = Atom.family<string, Atom.Atom<(Node.Action | Node.ActionGroup)[]>>((id) => {\n    return Atom.make((get) => {\n      if (!id) {\n        return [];\n      }\n      return get(this._connections(connectionKey(id, Node.action))) as (Node.Action | Node.ActionGroup)[];\n    }).pipe(withLabel(`graph:actions:${id}`));\n  });\n\n  readonly _json = Atom.family<string, Atom.Atom<any>>((id) => {\n    return Atom.make((get) => {\n      get(this._model.version);\n      return this._model.toTree(\n        id,\n        (node, children: any[]) => {\n          const data = node.data;\n          if (!data) {\n            return undefined;\n          }\n          return {\n            id: data.id,\n            type: data.type,\n            ...(data.properties.label ? { label: data.properties.label } : {}),\n            ...(children.length ? { nodes: children } : {}),\n          };\n        },\n        relationKey('child'),\n      );\n    }).pipe(withLabel(`graph:json:${id}`));\n  });\n\n  constructor({ registry, nodes, edges, onExpand, onRemoveNode }: GraphProps = {}) {\n    this._registry = registry ?? AtomEx.makeRegistry();\n    this._onExpand = onExpand;\n    this._onRemoveNode = onRemoveNode;\n    this._model = new GraphModel.GraphModel<ModelNode, GraphEdge>({ registry: this._registry });\n\n    this._model.batch(() => {\n      this._setNode(GraphNode.RootId, this._constructNode({ id: GraphNode.RootId, type: Node.RootType, data: null }));\n      nodes?.forEach((node) => this._setNode(node.id, this._constructNode(node)));\n      Object.entries(edges ?? {}).forEach(([source, relations]) => {\n        Object.entries(relations).forEach(([relation, targets]) => {\n          targets.forEach((target) => this._setEdge(source, target, relation));\n        });\n      });\n    });\n  }\n\n  json(id = GraphNode.RootId): Atom.Atom<any> {\n    return this._json(id);\n  }\n\n  node(id: string): Atom.Atom<Option.Option<Node.Node>> {\n    return this._node(id);\n  }\n\n  connections(id: string, relation: Node.RelationInput): Atom.Atom<Node.Node[]> {\n    return this._connections(connectionKey(id, relation));\n  }\n\n  actions(id: string): Atom.Atom<(Node.Action | Node.ActionGroup)[]> {\n    return this._actions(id);\n  }\n\n  edges(id: string): Atom.Atom<Edges> {\n    return this._edges(id);\n  }\n\n  /** @internal */\n  _constructNode(node: Node.NodeArg<any>): Option.Option<Node.Node> {\n    return Option.some({\n      [graphSymbol]: this,\n      data: null,\n      properties: {},\n      ...node,\n    });\n  }\n\n  /**\n   * Writes the node payload, materializing a placeholder slot when the id is new.\n   * @internal\n   */\n  _setNode(id: string, node: Option.Option<Node.Node>): void {\n    this._model.setNode({ id, data: Option.getOrUndefined(node) });\n    // After the write, so the atom is computed with the value rather than with `none`.\n    if (this._pins && !this._pins.has(id)) {\n      this._pins.set(id, this._registry.mount(this._node(id)));\n    }\n  }\n\n  /** @internal */\n  _retain(): CleanupFn {\n    invariant(!this._pins, 'Graph is already retained.');\n    const pins = new Map<string, CleanupFn>();\n    for (const { id } of this._model.nodes) {\n      pins.set(id, this._registry.mount(this._node(id)));\n    }\n    this._pins = pins;\n    return () => {\n      if (this._pins !== pins) {\n        return;\n      }\n      pins.forEach((unpin) => unpin());\n      this._pins = undefined;\n    };\n  }\n\n  /** @internal */\n  _setEdge(source: string, target: string, relation: string): void {\n    this._trackRelation(source, relation);\n    this._trackRelation(target, inverseKey(relation));\n    const stored = storedEdge(source, target, relation);\n    if (this._model.findEdge(stored.id)) {\n      return;\n    }\n\n    this._model.addEdge(stored);\n  }\n\n  /** @internal */\n  _removeEdge(source: string, target: string, relation: string): void {\n    // `detachEdge`, not `removeEdge`: the latter hands back the removed edge as a graph, and nothing\n    // here reads it.\n    this._model.detachEdge(storedEdge(source, target, relation).id);\n  }\n\n  /** @internal */\n  _trackRelation(id: string, relation: string): void {\n    const relations = this._relations.get(id) ?? new Set<string>();\n    relations.add(relation);\n    this._relations.set(id, relations);\n  }\n}\n\n/**\n * The implementation behind a graph handle. Exported for this package's tests, which assert against\n * the model and expansion bookkeeping; not part of the public surface.\n * @internal\n */\nexport const getInternal = (graph: BaseGraph): GraphImpl => {\n  return graph as unknown as GraphImpl;\n};\n\n/**\n * Creates a new Graph instance.\n */\nexport const make = (params?: GraphProps): Graph => {\n  return new GraphImpl(params);\n};\n\n/**\n * Pins the graph's nodes in its registry, including nodes added later, until the returned function\n * is called. A graph has one holder at a time.\n */\nexport const retain = (graph: BaseGraph): CleanupFn => getInternal(graph)._retain();\n\n/**\n * Convert the graph to a JSON object.\n */\nexport const toJSON = (graph: BaseGraph, id = GraphNode.RootId): object => {\n  const internal = getInternal(graph);\n  return internal._registry.get(internal._json(id));\n};\n\n/**\n * Get the node with the given id from the graph's registry.\n */\nexport const getNode = (graph: BaseGraph, id: string): Option.Option<Node.Node> => {\n  const internal = getInternal(graph);\n  return internal._registry.get(internal._node(id));\n};\n\n/**\n * Get the node with the given id from the graph's registry.\n *\n * @throws {GraphNode.NotFoundError} If the graph has no node with the id.\n */\nexport const getNodeOrThrow = (graph: BaseGraph, id: string): Node.Node => {\n  const internal = getInternal(graph);\n  return Option.getOrThrowWith(internal._registry.get(internal._node(id)), () => new GraphNode.NotFoundError(id));\n};\n\n/**\n * Get the root node of the graph.\n * This is an alias for `getNodeOrThrow(graph, ROOT_ID)`.\n */\nexport function getRoot(graph: BaseGraph): Node.Node {\n  return getNodeOrThrow(graph, GraphNode.RootId);\n}\n\n/**\n * Get all nodes connected to the node with the given id by the given relation from the graph's registry.\n */\nexport const getConnections = (graph: BaseGraph, id: string, relation: Node.RelationInput): Node.Node[] => {\n  const internal = getInternal(graph);\n  return internal._registry.get(internal._connections(connectionKey(id, relation)));\n};\n\n/**\n * Get all actions connected to the node with the given id from the graph's registry.\n */\nexport const getActions = (graph: BaseGraph, id: string): Node.Node[] => {\n  const internal = getInternal(graph);\n  return internal._registry.get(internal._actions(id));\n};\n\n/**\n * Get the edges from the node with the given id from the graph's registry.\n */\nexport const getEdges = (graph: BaseGraph, id: string): Edges => {\n  const internal = getInternal(graph);\n  return internal._registry.get(internal._edges(id));\n};\n\n/**\n * Recursive depth-first traversal of the graph.\n */\nexport const traverse = (graph: BaseGraph, options: GraphTraversalOptions, path: string[] = []): void => {\n  const { visitor, source = GraphNode.RootId, relation } = options;\n  // Break cycles.\n  if (path.includes(source)) {\n    return;\n  }\n\n  const node = getNodeOrThrow(graph, source);\n  const shouldContinue = visitor(node, [...path, source]);\n  if (shouldContinue === false) {\n    return;\n  }\n\n  const relations = Array.isArray(relation) ? relation : [relation];\n  const seen = new Set<string>();\n  for (const rel of relations) {\n    for (const connected of getConnections(graph, source, rel)) {\n      if (!seen.has(connected.id)) {\n        seen.add(connected.id);\n        traverse(graph, { source: connected.id, relation, visitor }, [...path, source]);\n      }\n    }\n  }\n};\n\n/**\n * Get the path between two nodes in the graph.\n */\nexport const getPath = (graph: BaseGraph, params: { source?: string; target: string }): Option.Option<string[]> => {\n  return Function.pipe(\n    getNode(graph, params.source ?? 'root'),\n    Option.flatMap((node) => {\n      let found: Option.Option<string[]> = Option.none();\n      traverse(graph, {\n        source: node.id,\n        relation: 'child',\n        visitor: (node, path) => {\n          if (Option.isSome(found)) {\n            return false;\n          }\n\n          if (node.id === params.target) {\n            found = Option.some(path);\n          }\n        },\n      });\n\n      return found;\n    }),\n  );\n};\n\n/**\n * Wait for the path between two nodes in the graph to be established.\n */\nexport const waitForPath = (\n  graph: BaseGraph,\n  params: { source?: string; target: string },\n  options?: { timeout?: number; interval?: number },\n): Promise<string[]> => {\n  const { timeout = 5_000, interval = 500 } = options ?? {};\n  const path = getPath(graph, params);\n  if (Option.isSome(path)) {\n    return Promise.resolve(path.value);\n  }\n\n  const trigger = new Trigger<string[]>();\n  const i = setInterval(() => {\n    const path = getPath(graph, params);\n    if (Option.isSome(path)) {\n      trigger.wake(path.value);\n    }\n  }, interval);\n\n  return trigger.wait({ timeout }).finally(() => clearInterval(i));\n};\n\n/**\n * Resolves when the node exists in the graph; immediately if it already does.\n *\n * The graph is populated asynchronously — connectors expand a level at a time and the objects\n * behind them load out of band — and {@link expandSync} is fire-and-forget, so a consumer that needs\n * a specific node has no completion to await and would otherwise poll for it.\n *\n * Deliberately unbounded: whether a node is merely late or will never arrive is the caller's\n * question, so the deadline belongs at the call site (`Effect.timeout`). Interrupting is safe —\n * the subscription is released on interruption.\n */\nexport const waitFor = (graph: BaseGraph, id: string): Effect.Effect<Node.Node> =>\n  Effect.suspend(() => {\n    const current = getNode(graph, id);\n    if (Option.isSome(current)) {\n      return Effect.succeed(current.value);\n    }\n\n    return Effect.callback<Node.Node>((resume) => {\n      const unsubscribe = graph.onNodeChanged.on(({ id: changed, node }) => {\n        if (changed === id && Option.isSome(node)) {\n          unsubscribe();\n          resume(Effect.succeed(node.value));\n        }\n      });\n\n      // Re-read after subscribing: a node added between the read above and the subscription\n      // emits nothing further, and the wait would hang on an event that already happened.\n      const raced = getNode(graph, id);\n      if (Option.isSome(raced)) {\n        unsubscribe();\n        resume(Effect.succeed(raced.value));\n      }\n\n      return Effect.sync(() => unsubscribe());\n    });\n  });\n\n/**\n * Implementation helper for expandSync.\n * If the node does not exist yet, the expand is recorded as pending and applied when the node is added.\n *\n * Fires the `onExpand` callback to add connections to the node. The builder only marks the relation's\n * connectors dirty; they run, and their output reaches the graph, on its next flush.\n *\n * Expanding a node that is already expanded for the same relation is a no-op.\n */\nexport const expandSync = <T extends ExpandableGraph | WritableGraph>(\n  graph: T,\n  id: string,\n  relation: Node.RelationInput,\n): T => {\n  const internal = getInternal(graph);\n  const normalizedRelation = normalizeRelation(relation);\n  const key = primaryKey(id, relationKey(normalizedRelation));\n  const nodeOpt = internal._currentNode(id);\n  if (Option.isNone(nodeOpt)) {\n    // Node not yet in graph: record expand to run when the node is added.\n    internal._pendingExpands.add(key);\n    log('expand', { key, deferred: true });\n    return graph;\n  }\n\n  const expanded = internal._expanded.has(key);\n  log('expand', { key, expanded });\n  if (!expanded) {\n    internal._expanded.add(key);\n    internal._onExpand?.(id, normalizedRelation);\n  }\n  return graph;\n};\n\n/**\n * {@link expandSync} the `child` relation of every ancestor of a qualified id, and of the id itself.\n * A missing ancestor's expand is deferred until it arrives, so a single call populates the whole path.\n */\nexport const expandPath = <T extends ExpandableGraph | WritableGraph>(graph: T, qualifiedId: string): T => {\n  const segments = qualifiedId.split('/');\n  for (let index = 1; index <= segments.length; index++) {\n    expandSync(graph, segments.slice(0, index).join('/'), 'child');\n  }\n  return graph;\n};\n\n/**\n * Expand a node in the graph, off the paint-critical path.\n *\n * Yields to the main thread before running {@link expandSync}, so a caller reacting to input does not\n * block the frame. Interrupting the effect cancels a still-pending expansion, which makes rescheduling\n * (e.g. superseding a hover with the next one) a matter of interrupting the previous fiber.\n */\nexport const expand = <T extends ExpandableGraph | WritableGraph>(\n  graph: T,\n  id: string,\n  relation: Node.RelationInput,\n): Effect.Effect<void> =>\n  Effect.promise((signal) =>\n    scheduleTask(\n      () => {\n        expandSync(graph, id, relation);\n      },\n      { strategy: 'idle', signal },\n    ),\n  );\n\n/**\n * Sort the edges of the node with the given id.\n */\nexport const sortEdges = <T extends ExpandableGraph | WritableGraph>(\n  graph: T,\n  id: string,\n  relation: Node.RelationInput,\n  order: string[],\n): T => {\n  const internal = getInternal(graph);\n  const edges = internal._currentEdges(id);\n  const relationId = relationKey(relation);\n  const current = edges[relationId] ?? [];\n  // Set membership, not `includes`: both filters run over the whole relation, and a connector\n  // sorting its output makes an array scan here quadratic in the number of siblings.\n  const ordered = new Set(order);\n  const existing = new Set(current);\n  const unsorted = current.filter((id) => !ordered.has(id));\n  const sorted = order.filter((id) => existing.has(id));\n  const newOrder = [...sorted, ...unsorted];\n  if (newOrder.length === current.length && newOrder.every((id, i) => id === current[i])) {\n    return graph;\n  }\n  // Insertion order carries the sort, so reordering means re-adding the relation's edges.\n  internal._model.batch(() => {\n    current.forEach((target) => internal._removeEdge(id, target, relationId));\n    newOrder.forEach((target) => internal._setEdge(id, target, relationId));\n  });\n  return graph;\n};\n\n/**\n * Applies `fn`'s writes as a single observable change: the model bumps its version once, so derived\n * views recompute and notify once for the whole group.\n */\nexport const batch = <T extends WritableGraph, A>(graph: T, fn: () => A): A => getInternal(graph)._model.batch(fn);\n\n/**\n * Unloads the nodes: they leave the model outright, along with the expansion bookkeeping that would\n * otherwise keep the graph remembering ids it will never be asked about again.\n *\n * Unlike {@link removeNodes} this leaves no tombstone, so a subsequent read of a released relation\n * expands it afresh rather than resolving to an emptied node.\n */\nexport const release = <T extends WritableGraph>(graph: T, ids: readonly string[]): T => {\n  const internal = getInternal(graph);\n  internal._model.batch(() => {\n    for (const id of ids) {\n      internal._pins?.get(id)?.();\n      internal._pins?.delete(id);\n      internal._relations.delete(id);\n    }\n\n    releaseExpansions(internal, new Set(ids));\n    internal._model.release(ids);\n  });\n\n  return graph;\n};\n\nconst releaseExpansions = (internal: GraphImpl, ids: ReadonlySet<string>): void => {\n  for (const set of [internal._expanded, internal._pendingExpands]) {\n    for (const key of set) {\n      if (ids.has(primaryParts(key)[0])) {\n        set.delete(key);\n      }\n    }\n  }\n};\n\n/**\n * Forgets that `relation` of `id` was expanded. Paired with the builder tearing down the matching\n * connector subscription, so a relation whose contents were released re-expands rather than\n * resolving to the emptied list it had before.\n */\nexport const releaseRelation = <T extends ExpandableGraph | WritableGraph>(\n  graph: T,\n  id: string,\n  relation: string,\n): T => {\n  const internal = getInternal(graph);\n  const key = primaryKey(id, relation);\n  internal._expanded.delete(key);\n  internal._pendingExpands.delete(key);\n  return graph;\n};\n\n/**\n * Add nodes to the graph.\n */\nexport const addNodes = <T extends WritableGraph>(graph: T, nodes: Node.NodeArg<any, Record<string, any>>[]): T => {\n  // The model's own depth counter, not `Atom.batch`: these calls nest (a node applies its inline\n  // children and edges), and a nested `Atom.batch` leaves the registry in its collect phase after\n  // the inner call returns, so invalidations raised afterwards are gathered and then discarded\n  // without ever being rebuilt. The version atom still bumps once for the whole group.\n  getInternal(graph)._model.batch(() => {\n    nodes.map((node) => addNode(graph, node));\n  });\n  return graph;\n};\n\n/**\n * Add a node to the graph.\n */\nexport const addNode = <T extends WritableGraph>(graph: T, nodeArg: Node.NodeArg<any, Record<string, any>>): T => {\n  const internal = getInternal(graph);\n  // Extract known NodeArg fields, preserve any extra fields (like _actionContext) in rest.\n  const {\n    nodes,\n    actions,\n    edges,\n    id,\n    type,\n    data = null,\n    properties = {},\n    ...rest\n  } = nodeArg as Node.NodeArg<any> & {\n    _actionContext?: Node.ActionContext;\n  };\n  const existingNode = internal._currentNode(id);\n  Option.match(existingNode, {\n    onSome: (existing) => {\n      const typeChanged = existing.type !== type;\n      const dataChanged = !shallowEqual(existing.data, data);\n      const propertiesChanged = Object.keys(properties).some((key) => existing.properties[key] !== properties[key]);\n      // `changed` is on the visit log because counting `existing node` lines alone measures how often a\n      // node was re-offered, not how often it actually changed — two very different costs.\n      const changed = typeChanged || dataChanged || propertiesChanged;\n      log('existing node', {\n        id,\n        changed,\n        typeChanged,\n        dataChanged,\n        propertiesChanged,\n      });\n      if (changed) {\n        log('updating node', { id, type, data, properties });\n        const newNode = Option.some({\n          ...existing,\n          ...rest,\n          type,\n          data,\n          // Merged, not replaced, so several extensions can each contribute properties to one node —\n          // which makes a key one generation sets and the next omits impossible to clear. A builder\n          // that varies a property must emit it on every generation, `false`/`undefined` included.\n          properties: { ...existing.properties, ...properties },\n        });\n        internal._setNode(id, newNode);\n        graph.onNodeChanged.emit({ id, node: newNode });\n      }\n    },\n    onNone: () => {\n      log('new node', { id, type, data, properties });\n      const newNode = internal._constructNode({ id, type, data, properties, ...rest });\n      internal._setNode(id, newNode);\n      graph.onNodeChanged.emit({ id, node: newNode });\n\n      // Apply any expands that were deferred because this node did not exist yet.\n      const toApply = [...internal._pendingExpands].filter((k) => primaryParts(k)[0] === id);\n      for (const pendingKey of toApply) {\n        internal._pendingExpands.delete(pendingKey);\n        const relation = relationFromKey(primaryParts(pendingKey)[1]);\n        internal._expanded.add(pendingKey);\n        internal._onExpand?.(id, relation);\n      }\n    },\n  });\n\n  if (nodes) {\n    addNodes(graph, nodes);\n    const _edges = nodes.map((node) => ({ source: id, target: node.id, relation: 'child' as const }));\n    addEdges(graph, _edges);\n    sortEdges(\n      graph,\n      id,\n      'child',\n      nodes.map((n) => n.id),\n    );\n  }\n\n  if (actions) {\n    addNodes(graph, actions);\n    const _edges = actions.map((node) => ({ source: id, target: node.id, relation: Node.action }));\n    addEdges(graph, _edges);\n    sortEdges(\n      graph,\n      id,\n      Node.action,\n      actions.map((node) => node.id),\n    );\n  }\n\n  if (edges) {\n    todo();\n  }\n  return graph;\n};\n\n/**\n * Remove nodes from the graph.\n */\nexport const removeNodes = <T extends WritableGraph>(graph: T, ids: string[], edges = false): T => {\n  getInternal(graph)._model.batch(() => {\n    ids.map((id) => removeNode(graph, id, edges));\n  });\n  return graph;\n};\n\n/**\n * Remove a node from the graph.\n */\nexport const removeNode = <T extends WritableGraph>(graph: T, id: string, edges = false): T => {\n  const internal = getInternal(graph);\n  internal._setNode(id, Option.none());\n  graph.onNodeChanged.emit({ id, node: Option.none() });\n  // TODO(wittjosiah): Reset expanded and initialized flags?\n\n  if (edges) {\n    const nodeEdges = internal._currentEdges(id);\n    const edgesToRemove: Edge[] = [];\n    for (const [relationKeyValue, relatedIds] of Object.entries(nodeEdges)) {\n      const relation = relationFromKey(relationKeyValue);\n      const isInboundRelation = relation.direction === 'inbound';\n      for (const relatedId of relatedIds) {\n        if (isInboundRelation) {\n          // Inbound edge lists store source node IDs; reconstruct the canonical outbound edge.\n          edgesToRemove.push({ source: relatedId, target: id, relation: inverseRelation(relation) });\n        } else {\n          edgesToRemove.push({ source: id, target: relatedId, relation });\n        }\n      }\n    }\n    removeEdges(graph, edgesToRemove);\n  }\n\n  internal._onRemoveNode?.(id);\n  return graph;\n};\n\n/**\n * Add edges to the graph.\n */\nexport const addEdges = <T extends WritableGraph>(graph: T, edges: Edge[]): T => {\n  getInternal(graph)._model.batch(() => {\n    edges.map((edge) => addEdge(graph, edge));\n  });\n  return graph;\n};\n\n/**\n * Add an edge to the graph.\n */\nexport const addEdge = <T extends WritableGraph>(graph: T, edgeArg: Edge): T => {\n  const relationId = relationKey(normalizeRelation(edgeArg.relation));\n  const internal = getInternal(graph);\n  // Deduping is `_setEdge`'s job and it does it by edge id in O(1); the membership check that used\n  // to guard this built the source's whole edge record per edge, which is quadratic on a wide fan-out.\n  // No log line here — it runs per edge of every flush, and building the entry costs more than the write.\n  internal._setEdge(edgeArg.source, edgeArg.target, relationId);\n  return graph;\n};\n\n/**\n * Remove edges from the graph.\n */\nexport const removeEdges = <T extends WritableGraph>(graph: T, edges: Edge[], removeOrphans = false): T => {\n  getInternal(graph)._model.batch(() => {\n    edges.map((edge) => removeEdge(graph, edge, removeOrphans));\n  });\n  return graph;\n};\n\n/**\n * Remove an edge from the graph.\n */\nexport const removeEdge = <T extends WritableGraph>(graph: T, edgeArg: Edge, removeOrphans = false): T => {\n  const relation = normalizeRelation(edgeArg.relation);\n  const relationId = relationKey(relation);\n  const inverse = inverseRelation(relation);\n  const inverseId = relationKey(inverse);\n  const internal = getInternal(graph);\n\n  internal._removeEdge(edgeArg.source, edgeArg.target, relationId);\n\n  if (removeOrphans) {\n    // Asked of the model directly rather than through `_edges`, whose adjacency read rebuilds an\n    // O(E) index on every version bump — quadratic when a connector drops all of its nodes at once.\n    for (const endpoint of [edgeArg.source, edgeArg.target]) {\n      if (endpoint !== GraphNode.RootId && !internal._model.hasEdges(endpoint)) {\n        removeNodes(graph, [endpoint]);\n      }\n    }\n  }\n  return graph;\n};\n"],"mappings":"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"}