{"version":3,"file":"index.cjs","names":[],"sources":["../src/routing/routingContext.ts","../src/routing/useSmartEdgePath.ts","../src/SmartEdge/noProviderWarning.ts","../src/SmartEdge/smartEdgeSelection.ts","../src/routing/obstacleIndex.ts","../src/functions/alignEndpoints.ts","../src/pathfinding/flatGrid.ts","../src/functions/guaranteeWalkablePath.ts","../src/functions/pointConversion.ts","../src/functions/utils.ts","../src/functions/createGrid.ts","../src/functions/edgeHops.ts","../src/functions/drawSvgPath.ts","../src/functions/drawHopPath.ts","../src/pathfinding/binaryHeap.ts","../src/pathfinding/searchScratch.ts","../src/pathfinding/flatAStar.ts","../src/pathfinding/flatJumpPoint.ts","../src/functions/generatePath.ts","../src/functions/getBoundingBoxes.ts","../src/functions/getFloatingEdgeParams.ts","../src/functions/subflow.ts","../src/SmartEdge/smartEdgeHops.ts","../src/SmartEdge/controlPointGeometry.ts","../src/SmartEdge/ControlPoint.tsx","../src/SmartEdge/smartEdgeData.ts","../src/SmartEdge/smartEdgeRouting.ts","../src/SmartEdge/renderDecision.tsx","../src/SmartEdge/index.tsx","../src/smartEdgePresets.ts","../src/createSmartEdge/index.tsx","../src/SmartBezierEdge/index.tsx","../src/SmartStraightEdge/index.tsx","../src/SmartStepEdge/index.tsx","../src/SmartSmoothStepEdge/index.tsx","../src/SmartSimpleBezierEdge/index.tsx","../src/SmartFloatingEdge/index.tsx","../src/SmartEditableEdge/index.tsx","../src/SmartCheckpointEdge/index.tsx","../src/routing/corridor.ts","../src/getSmartEdge/index.ts","../src/SmartFloatingConnectionLine/index.tsx","../src/getSmartEdge/getSmartEdgeWaypoints.ts","../src/routing/providerStore.ts","../src/routing/routeCache.ts","../src/routing/invalidation.ts","../src/routing/invalidationCorridor.ts","../src/routing/schedulerFlush.ts","../src/routing/scheduler.ts","../src/routing/routingRegistry.ts","../src/routing/routeBatch.ts","../src/routing/workerProtocol.ts","../src/routing/workerDispatch.ts","../src/routing/SmartEdgeProvider.tsx"],"sourcesContent":["import { createContext } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport type { Node } from \"@xyflow/react\";\nimport type { SmartEdgePreset } from \"../smartEdgePresets\";\nimport type { SmartEdgeStore } from \"./providerStore\";\nimport type {\n  RegisteredSmartEdge,\n  ResolvedProviderOptions as SchedulerOptions,\n} from \"./scheduler\";\n\n/**\n * Every option a `SmartEdgeProvider` resolves from its props: the scheduler's\n * routing-decision fields (inherited from `SchedulerOptions`, declared once in\n * `scheduler.ts`) plus the presentation fields the provider itself owns\n * (preset, corner radius, drag placeholder style). Extending rather than\n * redeclaring the shared fields keeps them in exactly one place while this\n * type still satisfies `SchedulerDeps[\"options\"]` structurally, with no cast.\n */\nexport interface ResolvedProviderOptions extends SchedulerOptions {\n  preset: SmartEdgePreset;\n  /** Corner radius for the `smoothstep` preset. */\n  borderRadius?: number;\n  /** Extra style merged onto a smart edge's native path while the route is\n   * pending or an endpoint is being dragged. Drag uses a\n   * `react-flow__edge animated` wrapper; pending uses a static dash. */\n  dragFallbackStyle: CSSProperties;\n}\n\n/** Provider-facing options: every field of `ResolvedProviderOptions` is\n * optional, defaulted by `resolveProviderOptions`. */\nexport type SmartEdgeProviderOptions = Partial<ResolvedProviderOptions>;\n\n/** What `SmartEdgeProvider` exposes to descendants through React context. */\nexport interface SmartEdgeContextValue {\n  store: SmartEdgeStore;\n  /** An immutable snapshot of the resolved provider options. The provider\n   * mutates its live options object in place (the scheduler closes over\n   * that reference) and publishes a fresh snapshot on every applied change,\n   * so the identity of this object tracks option value changes exactly. */\n  options: ResolvedProviderOptions;\n  /** Registers one edge's routing geometry, assigning it the next\n   * registration order. Returns an unregister function. */\n  registerEdge: (edge: Omit<RegisteredSmartEdge, \"order\">) => () => void;\n  /** Every currently registered edge, in registration (paint) order — used\n   * to place hop arcs where edges cross (a later task). */\n  getRegistrationsInOrder: () => RegisteredSmartEdge[];\n  /** The latest `nodes` prop, resolved to absolute (subflow-aware)\n   * coordinates — used by floating edges (a later task). */\n  getNodesSnapshot: () => Node[];\n}\n\n/**\n * Holds the active `SmartEdgeProvider` context. `null` when no provider is\n * mounted, in which case consumers should render their non-smart fallback.\n */\nexport const SmartEdgeRoutingContext =\n  createContext<SmartEdgeContextValue | null>(null);\n\nconst DEFAULT_DRAG_FALLBACK_STYLE: CSSProperties = {};\n\n/** Applies every `ResolvedProviderOptions` default to a provider's (partial)\n * `options` prop. */\nexport const resolveProviderOptions = (\n  options: SmartEdgeProviderOptions = {},\n): ResolvedProviderOptions => ({\n  preset: options.preset ?? \"bezier\",\n  gridRatio: options.gridRatio ?? 10,\n  nodePadding: options.nodePadding ?? 10,\n  avoidAreas: options.avoidAreas ?? [],\n  borderRadius: options.borderRadius,\n  routeOnlyWhenBlocked: options.routeOnlyWhenBlocked ?? true,\n  routeWhileDragging: options.routeWhileDragging ?? false,\n  dragFallbackStyle: options.dragFallbackStyle ?? DEFAULT_DRAG_FALLBACK_STYLE,\n  debounceMs: options.debounceMs ?? 16,\n  cacheSize: options.cacheSize ?? 500,\n});\n\nexport interface ProviderOptionsDiff {\n  any: boolean;\n  routing: boolean;\n}\n\nconst routingSnapshot = (options: ResolvedProviderOptions): string =>\n  JSON.stringify({\n    gridRatio: options.gridRatio,\n    nodePadding: options.nodePadding,\n    avoidAreas: options.avoidAreas,\n    routeOnlyWhenBlocked: options.routeOnlyWhenBlocked,\n    routeWhileDragging: options.routeWhileDragging,\n    cacheSize: options.cacheSize,\n    borderRadius: options.borderRadius,\n    preset: options.preset,\n  });\n\nconst presentationSnapshot = (options: ResolvedProviderOptions): string =>\n  JSON.stringify({\n    debounceMs: options.debounceMs,\n    dragFallbackStyle: options.dragFallbackStyle,\n  });\n\nexport const diffResolvedProviderOptions = (\n  previous: ResolvedProviderOptions,\n  next: ResolvedProviderOptions,\n): ProviderOptionsDiff => {\n  const routing = routingSnapshot(previous) !== routingSnapshot(next);\n  const presentation =\n    presentationSnapshot(previous) !== presentationSnapshot(next);\n\n  return { any: routing || presentation, routing };\n};\n","import {\n  useCallback,\n  useContext,\n  useEffect,\n  useRef,\n  useSyncExternalStore,\n} from \"react\";\nimport { SmartEdgeRoutingContext } from \"./routingContext\";\nimport type { SmartEdgeRouteResult } from \"./providerStore\";\nimport type { RegisteredSmartEdge } from \"./scheduler\";\nimport type { SmartEdgeBatchItem } from \"./routeBatch\";\nimport type { SmartEdgePreset } from \"../smartEdgePresets\";\nimport type { Position, XYPosition } from \"@xyflow/react\";\n\n/** Everything an edge component needs to hand `useSmartEdgePath` to register\n * its routing geometry and read back its route. Mirrors `SmartEdgeBatchItem`\n * (Task 10) minus `preset`, which is optional here and defaults to the\n * nearest `SmartEdgeProvider`'s configured preset. */\nexport interface UseSmartEdgePathInput {\n  id: string;\n  source: string;\n  target: string;\n  sourceX: number;\n  sourceY: number;\n  targetX: number;\n  targetY: number;\n  sourcePosition: Position;\n  targetPosition: Position;\n  /** Defaults to the nearest `SmartEdgeProvider`'s `options.preset`. */\n  preset?: SmartEdgePreset;\n  /** Per-edge routing overrides (grid ratio, padding, avoid areas, corner\n   * radius). */\n  options?: NonNullable<SmartEdgeBatchItem[\"options\"]>;\n  waypoints?: XYPosition[];\n}\n\nexport interface UseSmartEdgePathResult {\n  /** `null` while the route is still pending (or when there is no\n   * provider). */\n  route: SmartEdgeRouteResult | null;\n  /** True when this edge's source or target node is being dragged. */\n  isDragging: boolean;\n  /** False outside a `SmartEdgeProvider`; every other field is then inert. */\n  hasProvider: boolean;\n}\n\n/** What a store subscription returns when there is no provider to\n * subscribe to. */\nconst inertUnsubscribe = (): void => {\n  // No provider in context: nothing to unsubscribe from.\n};\n\n/* v8 ignore next 3 -- useSyncExternalStore server snapshot; unused in browser */\nconst getNullRoute = (): null => null;\n\n/* v8 ignore next 3 -- useSyncExternalStore server snapshot; unused in browser */\nconst getIdleDragging = (): boolean => false;\n\n/**\n * Registers one edge's routing geometry with the nearest `SmartEdgeProvider`\n * and subscribes to that edge's routed path and drag state.\n *\n * Mechanics mirror the 4.x `useSmartEdgeRoute`: building the registration\n * object is cheap, so it happens on every render, but a JSON signature of\n * that object gates the registration effect, so `registerEdge` only runs\n * again when the edge's routable geometry actually changed — not on every\n * unrelated re-render. The latest registration is kept in a ref (written in\n * its own effect, never during render) so the signature-gated effect can\n * read the current value without needing the object itself — which is\n * a fresh reference every render — in its dependency array.\n *\n * Every hook runs unconditionally, provider or not: outside a\n * `SmartEdgeProvider` this resolves to inert subscriptions and\n * `{ route: null, isDragging: false, hasProvider: false }`, which is also\n * exactly what a server render (no context, no DOM) produces.\n */\nexport const useSmartEdgePath = (\n  input: UseSmartEdgePathInput,\n): UseSmartEdgePathResult => {\n  const context = useContext(SmartEdgeRoutingContext);\n  const {\n    id,\n    source,\n    target,\n    sourceX,\n    sourceY,\n    targetX,\n    targetY,\n    sourcePosition,\n    targetPosition,\n    preset,\n    options,\n    waypoints,\n  } = input;\n\n  const registration: Omit<RegisteredSmartEdge, \"order\"> | null = context\n    ? {\n        id,\n        source,\n        target,\n        sourceX,\n        sourceY,\n        targetX,\n        targetY,\n        sourcePosition,\n        targetPosition,\n        preset: preset ?? context.options.preset,\n        options,\n        waypoints,\n      }\n    : null;\n\n  const signature = registration ? JSON.stringify(registration) : \"\";\n\n  const registrationRef = useRef(registration);\n  useEffect(() => {\n    registrationRef.current = registration;\n  });\n\n  const registerEdge = context?.registerEdge;\n  useEffect(() => {\n    const current = registrationRef.current;\n    if (!registerEdge || !current) return undefined;\n    return registerEdge(current);\n  }, [registerEdge, signature]);\n\n  const store = context?.store;\n\n  const subscribeEdge = useCallback(\n    (onRouteChange: () => void): (() => void) => {\n      if (!store) return inertUnsubscribe;\n      return store.subscribeEdge(id, onRouteChange);\n    },\n    [store, id],\n  );\n\n  const route = useSyncExternalStore(\n    subscribeEdge,\n    () => store?.getRoute(id) ?? null,\n    getNullRoute,\n  );\n\n  const subscribeNodeState = useCallback(\n    (onNodeStateChange: () => void): (() => void) => {\n      if (!store) return inertUnsubscribe;\n      return store.subscribeNodeState(onNodeStateChange);\n    },\n    [store],\n  );\n\n  const isDragging = useSyncExternalStore(\n    subscribeNodeState,\n    () => {\n      if (!store) return false;\n      const draggingIds = store.getDraggingNodeIds();\n      return draggingIds.has(source) || draggingIds.has(target);\n    },\n    getIdleDragging,\n  );\n\n  return {\n    route,\n    isDragging,\n    hasProvider: context !== null,\n  };\n};\n","const MIGRATION_GUIDE_URL =\n  \"https://tisoap.github.io/react-flow-smart-edge/docs/migration/v5\";\n\nconst NO_PROVIDER_MESSAGE =\n  \"[@tisoap/react-flow-smart-edge] A smart edge rendered without a \" +\n  \"<SmartEdgeProvider> ancestor, so routing is disabled and the edge falls \" +\n  \"back to its non-routed variant. Wrap your flow in <SmartEdgeProvider> to \" +\n  `enable routing. Migration guide: ${MIGRATION_GUIDE_URL}`;\n\nlet hasWarned = false;\n\n/** Narrows an unknown value to a plain, indexable object shape. */\nconst isRecord = (value: unknown): value is Record<PropertyKey, unknown> =>\n  typeof value === \"object\" && value !== null;\n\n/**\n * True when running in a production bundle, where the developer warning must\n * stay silent. Reads the ambient `process` global (if any) through\n * `Reflect.get` rather than referencing the bare `process` identifier, so\n * this file type-checks without a dependency on `@types/node` (the app\n * tsconfig deliberately has no Node types) and so browser ESM builds without\n * a `process` global do not throw.\n */\nconst isProduction = (): boolean => {\n  const maybeProcess: unknown = Reflect.get(globalThis, \"process\");\n  const env: unknown = isRecord(maybeProcess) ? maybeProcess[\"env\"] : undefined;\n  return isRecord(env) && env[\"NODE_ENV\"] === \"production\";\n};\n\n/**\n * Warns exactly once per module lifetime that a smart edge rendered outside a\n * `SmartEdgeProvider`. Silent in production. Repeated calls (e.g. one per\n * unprovided edge) are no-ops after the first warning.\n */\nexport const warnOnceNoProvider = (): void => {\n  if (hasWarned || isProduction()) return;\n  hasWarned = true;\n  console.warn(NO_PROVIDER_MESSAGE);\n};\n\n/**\n * Test hook: clears the module-level \"already warned\" flag so a test can\n * observe the first-warning behavior again.\n */\nexport const __resetNoProviderWarning = (): void => {\n  hasWarned = false;\n};\n","import { useCallback, useSyncExternalStore } from \"react\";\nimport type { SmartEdgeStore } from \"../routing/providerStore\";\n\n/** Inert unsubscribe used when there is no provider store to subscribe to. */\nconst inertUnsubscribe = (): void => {\n  // No store in context: nothing to unsubscribe from.\n};\n\n/* v8 ignore next 3 -- useSyncExternalStore server snapshot; unused in browser */\nconst getIdleSelected = (): boolean => false;\n\n/**\n * Subscribes to the provider store's selected-node set and reports whether\n * this edge's source or target node is currently selected. Used to decide\n * when an editable edge should reveal its draggable control points, without\n * reaching into React Flow's own store. Runs unconditionally; resolves to\n * `false` when there is no provider store.\n */\nexport const useEndpointNodesSelected = (\n  store: SmartEdgeStore | undefined,\n  source: string,\n  target: string,\n): boolean => {\n  const subscribe = useCallback(\n    (onChange: () => void): (() => void) =>\n      store ? store.subscribeNodeState(onChange) : inertUnsubscribe,\n    [store],\n  );\n\n  return useSyncExternalStore(\n    subscribe,\n    () => {\n      if (!store) return false;\n      const selected = store.getSelectedNodeIds();\n      return selected.has(source) || selected.has(target);\n    },\n    getIdleSelected,\n  );\n};\n","import type { Node, Rect, XYPosition, Position } from \"@xyflow/react\";\n\n/** A `Position`-shaped string union, kept local so this module never needs a\n * runtime import of `@xyflow/react` (it must stay reachable from the worker,\n * see `guaranteeWalkablePath.ts` for the same convention). */\ntype Direction = \"top\" | \"bottom\" | \"left\" | \"right\";\n\n/** A padded, axis-aligned obstacle rectangle in graph coordinates. */\nexport interface ObstacleBox {\n  id: string;\n  xMin: number;\n  yMin: number;\n  xMax: number;\n  yMax: number;\n}\n\nconst buildNodeBox = (node: Node, nodePadding: number): ObstacleBox => {\n  const width = Math.max(node.measured?.width ?? 0, 1);\n  const height = Math.max(node.measured?.height ?? 0, 1);\n\n  return {\n    id: node.id,\n    xMin: node.position.x - nodePadding,\n    yMin: node.position.y - nodePadding,\n    xMax: node.position.x + width + nodePadding,\n    yMax: node.position.y + height + nodePadding,\n  };\n};\n\nconst buildAreaBox = (\n  area: Rect,\n  index: number,\n  nodePadding: number,\n): ObstacleBox => {\n  const width = Math.max(area.width, 1);\n  const height = Math.max(area.height, 1);\n\n  return {\n    id: `avoid-${String(index)}`,\n    xMin: area.x - nodePadding,\n    yMin: area.y - nodePadding,\n    xMax: area.x + width + nodePadding,\n    yMax: area.y + height + nodePadding,\n  };\n};\n\n/**\n * Build padded obstacle boxes for every node (and optional `avoidAreas`),\n * using the same measured-size floor as `getBoundingBoxes` (missing\n * dimensions collapse to 1px). Unlike `getBoundingBoxes`, these are exact\n * rectangles — not rounded to a path-finding grid.\n *\n * Positions are expected to be absolute graph coordinates; callers resolve\n * subflow offsets before calling this function.\n */\nexport const buildObstacleBoxes = (\n  nodes: Node[],\n  nodePadding: number,\n  avoidAreas: Rect[] = [],\n): ObstacleBox[] => [\n  ...nodes.map((node) => buildNodeBox(node, nodePadding)),\n  ...avoidAreas.map((area, index) => buildAreaBox(area, index, nodePadding)),\n];\n\n/** Axis-aligned rect vs. obstacle-box overlap test (exclusive bounds — a\n * rect that only touches a box's edge does not count as intersecting). */\nexport const rectIntersectsBox = (\n  xMin: number,\n  yMin: number,\n  xMax: number,\n  yMax: number,\n  box: ObstacleBox,\n): boolean =>\n  xMin < box.xMax && xMax > box.xMin && yMin < box.yMax && yMax > box.yMin;\n\n/**\n * Liang-Barsky slab test for whether the segment from (startX, startY) to\n * (endX, endY) intersects the obstacle box (inclusive bounds — a segment\n * running exactly along a box edge counts as intersecting).\n */\nexport const segmentIntersectsBox = (\n  startX: number,\n  startY: number,\n  endX: number,\n  endY: number,\n  box: ObstacleBox,\n): boolean => {\n  const deltaX = endX - startX;\n  const deltaY = endY - startY;\n  let tMin = 0;\n  let tMax = 1;\n\n  const clip = (direction: number, distance: number): boolean => {\n    if (direction === 0) return distance >= 0;\n    const ratio = distance / direction;\n    if (direction < 0) {\n      if (ratio > tMax) return false;\n      if (ratio > tMin) tMin = ratio;\n    } else {\n      if (ratio < tMin) return false;\n      if (ratio < tMax) tMax = ratio;\n    }\n    return true;\n  };\n\n  return (\n    clip(-deltaX, startX - box.xMin) &&\n    clip(deltaX, box.xMax - startX) &&\n    clip(-deltaY, startY - box.yMin) &&\n    clip(deltaY, box.yMax - startY)\n  );\n};\n\n/**\n * Whether any leg of the polyline intersects any of the given boxes.\n * `excludeIds` skips boxes that should not count as obstacles for this\n * check (typically the polyline's own endpoint nodes).\n */\nexport const isPolylineBlocked = (\n  points: XYPosition[],\n  boxes: ObstacleBox[],\n  excludeIds?: ReadonlySet<string>,\n): boolean => {\n  for (let index = 0; index < points.length - 1; index += 1) {\n    const start = points[index];\n    const end = points[index + 1];\n\n    for (const box of boxes) {\n      if (excludeIds?.has(box.id)) continue;\n      if (segmentIntersectsBox(start.x, start.y, end.x, end.y, box)) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n};\n\nconst isHorizontal = (position: Direction): boolean =>\n  position === \"left\" || position === \"right\";\n\n/**\n * The Z/L skeleton approximating xyflow's built-in step path: exits\n * perpendicular to `sourcePosition`, splits at the midpoint of the axis the\n * handles don't share, and enters perpendicular to `targetPosition`.\n */\nexport const nativeStepPolyline = (\n  sourceX: number,\n  sourceY: number,\n  sourcePosition: Position,\n  targetX: number,\n  targetY: number,\n  targetPosition: Position,\n): XYPosition[] => {\n  const source: XYPosition = { x: sourceX, y: sourceY };\n  const target: XYPosition = { x: targetX, y: targetY };\n  const sourceHorizontal = isHorizontal(sourcePosition);\n  const targetHorizontal = isHorizontal(targetPosition);\n\n  if (sourceHorizontal && targetHorizontal) {\n    const midX = (sourceX + targetX) / 2;\n    return [source, { x: midX, y: sourceY }, { x: midX, y: targetY }, target];\n  }\n\n  if (!sourceHorizontal && !targetHorizontal) {\n    const midY = (sourceY + targetY) / 2;\n    return [source, { x: sourceX, y: midY }, { x: targetX, y: midY }, target];\n  }\n\n  const corner: XYPosition = sourceHorizontal\n    ? { x: targetX, y: sourceY }\n    : { x: sourceX, y: targetY };\n\n  return [source, corner, target];\n};\n\n/** Options for `isDirectPathBlocked`. */\nexport interface DirectPathOptions {\n  nodePadding?: number;\n  avoidAreas?: Rect[];\n  excludeNodeIds?: string[];\n}\n\n/**\n * Public utility: does a straight line between `source` and `target` cross\n * any padded node (or `avoidAreas`)? Useful for skipping path-finding\n * entirely when a direct line is already clear. `excludeNodeIds` should\n * typically include the edge's own source/target node ids, since a handle\n * point normally sits inside its own node's padded box.\n */\nexport const isDirectPathBlocked = (\n  source: XYPosition,\n  target: XYPosition,\n  nodes: Node[],\n  options?: DirectPathOptions,\n): boolean => {\n  const nodePadding = options?.nodePadding ?? 10;\n  const avoidAreas = options?.avoidAreas ?? [];\n  const excludeIds = options?.excludeNodeIds\n    ? new Set(options.excludeNodeIds)\n    : undefined;\n  const boxes = buildObstacleBoxes(nodes, nodePadding, avoidAreas);\n\n  return isPolylineBlocked([source, target], boxes, excludeIds);\n};\n","import type { XYPosition } from \"@xyflow/react\";\n\ntype Direction = \"top\" | \"bottom\" | \"left\" | \"right\";\n\n/**\n * Source/target points carry both their graph coordinates and the side of the\n * node they are attached to (top/bottom/left/right).\n */\nexport interface EndpointInfo extends XYPosition {\n  position: Direction;\n}\n\n/**\n * Returns the index of the path tuple that varies for a given handle position.\n * Vertical handles (top/bottom) leave/enter on the X axis; horizontal handles\n * (left/right) leave/enter on the Y axis.\n */\nconst axisFor = (position: Direction): 0 | 1 =>\n  position === \"top\" || position === \"bottom\" ? 0 : 1;\n\n/**\n * Aligns the leading and trailing runs of the grid-snapped graph path with the\n * actual handle coordinates so the rendered edge enters and leaves each node\n * perpendicular to its handle, with no diagonal or L-shaped jog near the\n * endpoints.\n *\n * The grid path's first cells will all share the same X (for top/bottom\n * handles) or the same Y (for left/right handles) until the path turns. We\n * rewrite that perpendicular axis on each leading cell to match the handle's\n * actual coordinate. The first turn in the path is therefore purely\n * orthogonal: the segment from the last leading cell to the next path cell\n * shares the parallel axis, so it remains a straight horizontal/vertical line\n * even after the shift.\n *\n * The same operation is applied symmetrically to the trailing run for the\n * target. The trailing scan is bounded so it cannot reach into cells already\n * claimed by the leading shift, and the trailing column/row is detected from\n * the original (un-shifted) path so a same-axis leading shift cannot corrupt\n * detection in the common case where source and target share an axis.\n */\nexport const alignEndpoints = (\n  source: EndpointInfo,\n  target: EndpointInfo,\n  graphPath: number[][],\n): number[][] => {\n  if (graphPath.length === 0) return graphPath;\n\n  const result = graphPath.map(([posX, posY]) => [posX, posY]);\n\n  const sourceAxis = axisFor(source.position);\n  const sourceCoord = sourceAxis === 0 ? source.x : source.y;\n  const sourceLeadingValue = result[0][sourceAxis];\n\n  let leadEnd = 0;\n  while (\n    leadEnd < result.length &&\n    result[leadEnd][sourceAxis] === sourceLeadingValue\n  ) {\n    leadEnd++;\n  }\n\n  for (let index = 0; index < leadEnd; index++) {\n    result[index][sourceAxis] = sourceCoord;\n  }\n\n  const targetAxis = axisFor(target.position);\n  const targetCoord = targetAxis === 0 ? target.x : target.y;\n  const targetTrailingValue = graphPath[graphPath.length - 1][targetAxis];\n\n  let trailStart = result.length;\n  while (\n    trailStart > leadEnd &&\n    graphPath[trailStart - 1][targetAxis] === targetTrailingValue\n  ) {\n    trailStart--;\n  }\n\n  for (let index = trailStart; index < result.length; index++) {\n    result[index][targetAxis] = targetCoord;\n  }\n\n  return result;\n};\n","/**\n * Flat typed-array obstacle grid: the v5 replacement for the object-based\n * PathFinding.js grid. One byte per cell, indexed `y * width + x`, so building\n * and cloning are single allocations and pathfinding touches no heap objects.\n */\nexport interface FlatGrid {\n  width: number;\n  height: number;\n  /** `1` = blocked, `0` = walkable; index = `y * width + x`. */\n  blocked: Uint8Array;\n}\n\nexport const createFlatGrid = (width: number, height: number): FlatGrid => ({\n  width,\n  height,\n  blocked: new Uint8Array(width * height),\n});\n\nexport const cloneFlatGrid = (grid: FlatGrid): FlatGrid => ({\n  width: grid.width,\n  height: grid.height,\n  blocked: grid.blocked.slice(),\n});\n\nexport const isInside = (\n  grid: FlatGrid,\n  column: number,\n  row: number,\n): boolean =>\n  column >= 0 && column < grid.width && row >= 0 && row < grid.height;\n\nexport const isWalkable = (\n  grid: FlatGrid,\n  column: number,\n  row: number,\n): boolean =>\n  isInside(grid, column, row) && grid.blocked[row * grid.width + column] === 0;\n\n/** Out-of-bounds writes are silently ignored, like the old `setWalkableAt`. */\nexport const setBlocked = (\n  grid: FlatGrid,\n  column: number,\n  row: number,\n  blocked: boolean,\n): void => {\n  if (!isInside(grid, column, row)) return;\n  grid.blocked[row * grid.width + column] = blocked ? 1 : 0;\n};\n\n/** Blocks every cell in `[columnStart, columnEnd) × [rowStart, rowEnd)`, clamped to bounds. */\nexport const blockCellRange = (\n  grid: FlatGrid,\n  columnStart: number,\n  rowStart: number,\n  columnEnd: number,\n  rowEnd: number,\n): void => {\n  const minColumn = Math.max(0, columnStart);\n  const minRow = Math.max(0, rowStart);\n  const maxColumn = Math.min(grid.width, columnEnd);\n  const maxRow = Math.min(grid.height, rowEnd);\n  for (let row = minRow; row < maxRow; row++) {\n    const rowOffset = row * grid.width;\n    grid.blocked.fill(1, rowOffset + minColumn, rowOffset + maxColumn);\n  }\n};\n","import { isWalkable, setBlocked, isInside } from \"../pathfinding/flatGrid\";\nimport type { FlatGrid } from \"../pathfinding/flatGrid\";\nimport type { XYPosition } from \"@xyflow/react\";\n\n/**\n * A `Position`-shaped string union. `Position`'s enum values are exactly\n * these strings, so working in plain strings here (and letting real\n * `Position` values widen into this type at call sites) avoids importing the\n * `@xyflow/react` runtime — this module must stay reachable from the routing\n * Web Worker. Exported so `createGrid.ts`'s `PointInfo` can reuse it.\n */\nexport type Direction = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport const getNextPointFromPosition = (\n  point: XYPosition,\n  position: Direction,\n): XYPosition => {\n  switch (position) {\n    case \"top\":\n      return { x: point.x, y: point.y - 1 };\n    case \"bottom\":\n      return { x: point.x, y: point.y + 1 };\n    case \"left\":\n      return { x: point.x - 1, y: point.y };\n    case \"right\":\n      return { x: point.x + 1, y: point.y };\n  }\n};\n\n/**\n * Guarantee that the path is walkable, even if the point is inside a non\n * walkable area, by carving a walkable lane in the direction of the point's\n * Position. Stops at the grid border.\n */\nexport const guaranteeWalkablePath = (\n  grid: FlatGrid,\n  point: XYPosition,\n  position: Direction,\n): void => {\n  let current = { x: point.x, y: point.y };\n  while (\n    isInside(grid, current.x, current.y) &&\n    !isWalkable(grid, current.x, current.y)\n  ) {\n    setBlocked(grid, current.x, current.y, false);\n    current = getNextPointFromPosition(current, position);\n  }\n};\n","import type { XYPosition } from \"@xyflow/react\";\n\n/**\n * Each bounding box is a collection of X/Y points in a graph, and we\n * need to convert them to \"occupied\" cells in a 2D grid representation.\n *\n * The top most position of the grid (grid[0][0]) needs to be equivalent\n * to the top most point in the graph (the graph.topLeft point).\n *\n * Since the top most point can have X/Y values different than zero,\n * and each cell in a grid represents a 10x10 pixel area in the grid (or a\n * gridRatio area), there's need to be a conversion between a point in a graph\n * to a point in the grid.\n *\n * We do this conversion by dividing a graph point X/Y values by the grid ratio,\n * and \"shifting\" their values up or down, depending on the values of the top\n * most point in the graph. The top most point in the graph will have the\n * smallest values for X and Y.\n *\n * We avoid setting nodes in the border of the grid (x=0 or y=0), so there's\n * always a \"walkable\" area around the grid.\n */\nexport const graphToGridPoint = (\n  graphPoint: XYPosition,\n  smallestX: number,\n  smallestY: number,\n  gridRatio: number,\n): XYPosition => {\n  // Affine transform: translate by top-left, scale by grid size, then offset border (1 cell)\n  const posX = (graphPoint.x - smallestX) / gridRatio + 1;\n  const posY = (graphPoint.y - smallestY) / gridRatio + 1;\n  return { x: posX, y: posY };\n};\n\n/**\n * Converts a grid point back to a graph point, using the reverse logic of\n * graphToGridPoint.\n */\nexport const gridToGraphPoint = (\n  gridPoint: XYPosition,\n  smallestX: number,\n  smallestY: number,\n  gridRatio: number,\n): XYPosition => {\n  // Inverse affine transform: remove border, scale by grid size, then translate by top-left\n  const posX = (gridPoint.x - 1) * gridRatio + smallestX;\n  const posY = (gridPoint.y - 1) * gridRatio + smallestY;\n  return { x: posX, y: posY };\n};\n","export const round = (value: number, multiple = 10) =>\n  Math.round(value / multiple) * multiple;\n\nexport const roundDown = (value: number, multiple = 10) =>\n  Math.floor(value / multiple) * multiple;\n\nexport const roundUp = (value: number, multiple = 10) =>\n  Math.ceil(value / multiple) * multiple;\n\nexport const toInteger = (value: number, min = 0) => {\n  let result = Math.max(Math.round(value), min);\n  result = Number.isInteger(result) ? result : min;\n  result = result >= min ? result : min;\n  return result;\n};\n","import {\n  createFlatGrid,\n  cloneFlatGrid,\n  setBlocked,\n} from \"../pathfinding/flatGrid\";\nimport type { FlatGrid } from \"../pathfinding/flatGrid\";\nimport {\n  guaranteeWalkablePath,\n  getNextPointFromPosition,\n} from \"./guaranteeWalkablePath\";\nimport type { Direction } from \"./guaranteeWalkablePath\";\nimport { graphToGridPoint } from \"./pointConversion\";\nimport { round, roundUp } from \"./utils\";\nimport type { NodeBoundingBox, GraphBoundingBox } from \"./getBoundingBoxes\";\n\nexport interface PointInfo {\n  x: number;\n  y: number;\n  position: Direction;\n}\n\n/** Grid dimensions (in cells) derived from the graph box and grid ratio. */\nconst getGridDimensions = (graph: GraphBoundingBox, gridRatio: number) => ({\n  mapColumns: roundUp(graph.width, gridRatio) / gridRatio + 1,\n  mapRows: roundUp(graph.height, gridRatio) / gridRatio + 1,\n});\n\n/**\n * Visit every grid cell occupied by a node bounding box. Shared by the live\n * grid marking and the cacheable base grid so both stay in sync.\n */\nconst forEachNodeCell = (\n  nodes: NodeBoundingBox[],\n  xMin: number,\n  yMin: number,\n  gridRatio: number,\n  visit: (column: number, row: number) => void,\n) => {\n  nodes.forEach((node) => {\n    const nodeStart = graphToGridPoint(node.topLeft, xMin, yMin, gridRatio);\n    const nodeEnd = graphToGridPoint(node.bottomRight, xMin, yMin, gridRatio);\n\n    for (let column = nodeStart.x; column < nodeEnd.x; column++) {\n      for (let row = nodeStart.y; row < nodeEnd.y; row++) {\n        visit(column, row);\n      }\n    }\n  });\n};\n\n/**\n * Build the base obstacle grid for a graph box: a flat `mapColumns x mapRows`\n * grid where every cell covered by a node is blocked and every other cell is\n * walkable. This is the endpoint-independent part of {@link createGrid}, so it\n * can be computed once per frame and reused across every edge that shares the\n * same nodes and options (see `getSmartEdge`'s shared grid cache).\n */\nexport const buildBaseGrid = (\n  graph: GraphBoundingBox,\n  nodes: NodeBoundingBox[],\n  gridRatio: number,\n): FlatGrid => {\n  const { xMin, yMin } = graph;\n  const { mapColumns, mapRows } = getGridDimensions(graph, gridRatio);\n\n  const grid = createFlatGrid(mapColumns, mapRows);\n\n  // `setBlocked` silently ignores out-of-bounds cells, matching the old\n  // `Grid.setWalkableAt` semantics.\n  forEachNodeCell(nodes, xMin, yMin, gridRatio, (column, row) => {\n    setBlocked(grid, column, row, true);\n  });\n\n  return grid;\n};\n\nexport const createGrid = (\n  graph: GraphBoundingBox,\n  nodes: NodeBoundingBox[],\n  source: PointInfo,\n  target: PointInfo,\n  gridRatio = 2,\n  baseGrid?: FlatGrid,\n) => {\n  const { xMin, yMin } = graph;\n\n  // Create a grid representation of the graph box, where each cell is\n  // equivalent to 10x10 pixels (or the grid ratio) on the graph. We'll use\n  // this simplified grid to do pathfinding. When a precomputed base grid is\n  // supplied (shared across edges in the same frame) we clone it instead of\n  // re-marking every node; otherwise we build it from the node boxes here.\n  const grid = baseGrid\n    ? cloneFlatGrid(baseGrid)\n    : buildBaseGrid(graph, nodes, gridRatio);\n\n  // Convert the starting and ending graph points to grid points\n  const startGrid = graphToGridPoint(\n    {\n      x: round(source.x, gridRatio),\n      y: round(source.y, gridRatio),\n    },\n    xMin,\n    yMin,\n    gridRatio,\n  );\n\n  const endGrid = graphToGridPoint(\n    {\n      x: round(target.x, gridRatio),\n      y: round(target.y, gridRatio),\n    },\n    xMin,\n    yMin,\n    gridRatio,\n  );\n\n  // Guarantee a walkable path between the start and end points, even if the\n  // source or target where covered by another node or by padding\n  guaranteeWalkablePath(grid, startGrid, source.position);\n  guaranteeWalkablePath(grid, endGrid, target.position);\n\n  // Use the next closest points as the start and end points, so\n  // pathfinding does not start too close to the nodes\n  const start = getNextPointFromPosition(startGrid, source.position);\n  const end = getNextPointFromPosition(endGrid, target.position);\n\n  return { grid, start, end };\n};\n","import type { XYPosition } from \"@xyflow/react\";\n\n/**\n * A bridge \"hop\" to draw on a routed orthogonal polyline where it crosses\n * another edge. `segmentIndex` is the polyline segment the crossing lies on and\n * `at` is the (positive) distance from that segment's start point to the\n * crossing.\n */\nexport interface Hop {\n  segmentIndex: number;\n  at: number;\n}\n\n/** Default axis-alignment / crossing tolerance in pixels. */\nexport const DEFAULT_HOP_EPSILON = 0.5;\n\n/**\n * Builds the orthogonal polyline an edge is drawn along, from its source/target\n * handle coordinates and the routed interior points returned by `getSmartEdge`.\n *\n * Consecutive duplicate points are dropped, and collinear midpoints are merged\n * so each straight run becomes a single maximal segment. Merging matters for\n * hop detection: routed paths contain many collinear grid points, so a crossing\n * frequently lands exactly on one of them — which strict-interior detection\n * would otherwise miss because it sits on a segment boundary.\n */\nexport const toPolyline = (\n  source: XYPosition,\n  target: XYPosition,\n  path: number[][],\n): XYPosition[] => {\n  const raw: XYPosition[] = [\n    { x: source.x, y: source.y },\n    ...path.map(([posX, posY]) => ({ x: posX, y: posY })),\n    { x: target.x, y: target.y },\n  ];\n\n  const deduped = raw.filter(\n    (point, index) =>\n      index === 0 ||\n      point.x !== raw[index - 1].x ||\n      point.y !== raw[index - 1].y,\n  );\n\n  const result: XYPosition[] = [];\n  for (let index = 0; index < deduped.length; index++) {\n    const current = deduped[index];\n    const hasNeighbors = index > 0 && index < deduped.length - 1;\n\n    if (hasNeighbors) {\n      const previous = result[result.length - 1];\n      const next = deduped[index + 1];\n      const collinearX = previous.x === current.x && current.x === next.x;\n      const collinearY = previous.y === current.y && current.y === next.y;\n      if (collinearX || collinearY) continue;\n    }\n\n    result.push(current);\n  }\n\n  return result;\n};\n\n/** Whether `value` lies strictly inside the open interval (bound1, bound2). */\nconst strictlyBetween = (\n  value: number,\n  bound1: number,\n  bound2: number,\n  epsilon: number,\n): boolean => {\n  const min = Math.min(bound1, bound2);\n  const max = Math.max(bound1, bound2);\n  return value > min + epsilon && value < max - epsilon;\n};\n\n/**\n * Crossing test for a horizontal segment of \"my\" polyline against a vertical\n * segment of another polyline.\n */\nconst horizontalCrossing = (\n  segmentStart: XYPosition,\n  segmentEnd: XYPosition,\n  otherStart: XYPosition,\n  otherEnd: XYPosition,\n  epsilon: number,\n  segmentIndex: number,\n): Hop | null => {\n  const crossX = otherStart.x;\n  const crossY = segmentStart.y;\n  if (\n    strictlyBetween(crossX, segmentStart.x, segmentEnd.x, epsilon) &&\n    strictlyBetween(crossY, otherStart.y, otherEnd.y, epsilon)\n  ) {\n    return { segmentIndex, at: Math.abs(crossX - segmentStart.x) };\n  }\n  return null;\n};\n\n/**\n * Crossing test for a vertical segment of \"my\" polyline against a horizontal\n * segment of another polyline.\n */\nconst verticalCrossing = (\n  segmentStart: XYPosition,\n  segmentEnd: XYPosition,\n  otherStart: XYPosition,\n  otherEnd: XYPosition,\n  epsilon: number,\n  segmentIndex: number,\n): Hop | null => {\n  const crossY = otherStart.y;\n  const crossX = segmentStart.x;\n  if (\n    strictlyBetween(crossY, segmentStart.y, segmentEnd.y, epsilon) &&\n    strictlyBetween(crossX, otherStart.x, otherEnd.x, epsilon)\n  ) {\n    return { segmentIndex, at: Math.abs(crossY - segmentStart.y) };\n  }\n  return null;\n};\n\n/** A single axis-aligned segment of \"my\" polyline being tested for crossings. */\ninterface MySegment {\n  start: XYPosition;\n  end: XYPosition;\n  index: number;\n  isHorizontal: boolean;\n  isVertical: boolean;\n}\n\n/**\n * Returns the hop where `segment` crosses a single other segment, or `null` for\n * any non-perpendicular pairing.\n */\nconst segmentPairHop = (\n  segment: MySegment,\n  otherStart: XYPosition,\n  otherEnd: XYPosition,\n  epsilon: number,\n): Hop | null => {\n  const otherHorizontal = Math.abs(otherStart.y - otherEnd.y) < epsilon;\n  const otherVertical = Math.abs(otherStart.x - otherEnd.x) < epsilon;\n\n  if (segment.isHorizontal && otherVertical && !otherHorizontal) {\n    return horizontalCrossing(\n      segment.start,\n      segment.end,\n      otherStart,\n      otherEnd,\n      epsilon,\n      segment.index,\n    );\n  }\n  if (segment.isVertical && otherHorizontal && !otherVertical) {\n    return verticalCrossing(\n      segment.start,\n      segment.end,\n      otherStart,\n      otherEnd,\n      epsilon,\n      segment.index,\n    );\n  }\n  return null;\n};\n\n/** Collects every hop where `segment` crosses one other polyline. */\nconst crossingsAgainst = (\n  segment: MySegment,\n  other: XYPosition[],\n  epsilon: number,\n): Hop[] => {\n  const hops: Hop[] = [];\n  for (let index = 0; index < other.length - 1; index++) {\n    const hop = segmentPairHop(\n      segment,\n      other[index],\n      other[index + 1],\n      epsilon,\n    );\n    if (hop) hops.push(hop);\n  }\n  return hops;\n};\n\n/**\n * Detects where an orthogonal polyline crosses other orthogonal polylines and\n * returns the hops to draw on it. Only strictly-interior perpendicular\n * crossings (a horizontal segment meeting a vertical one) produce a hop;\n * collinear overlaps, T-junctions at shared endpoints, and diagonal segments\n * are ignored.\n */\nexport const computeEdgeHops = (\n  polyline: XYPosition[],\n  otherPolylines: XYPosition[][],\n  epsilon: number = DEFAULT_HOP_EPSILON,\n): Hop[] => {\n  const hops: Hop[] = [];\n\n  for (let index = 0; index < polyline.length - 1; index++) {\n    const start = polyline[index];\n    const end = polyline[index + 1];\n    const segment: MySegment = {\n      start,\n      end,\n      index,\n      isHorizontal: Math.abs(start.y - end.y) < epsilon,\n      isVertical: Math.abs(start.x - end.x) < epsilon,\n    };\n    if (!segment.isHorizontal && !segment.isVertical) continue;\n\n    for (const other of otherPolylines) {\n      hops.push(...crossingsAgainst(segment, other, epsilon));\n    }\n  }\n\n  return hops;\n};\n","import type { Position, XYPosition } from \"@xyflow/react\";\nimport type { EndpointInfo } from \"./alignEndpoints\";\n\n/**\n * The four handle sides as plain string literals. `Position`'s enum values are\n * exactly these strings, so comparing against them avoids importing the\n * `@xyflow/react` runtime (and pulling React) into the Web Worker bundle.\n */\ntype HandleSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\n/**\n * Takes source and target `{x, y}` points, together with an array of number\n * tuples `[x, y]` representing the points along the path, and returns a string\n * to be used as the SVG path.\n */\nexport type SVGDrawFunction = (\n  source: XYPosition,\n  target: XYPosition,\n  path: number[][],\n) => string;\n\n/**\n * Like {@link SVGDrawFunction}, but requires handle positions on the endpoints\n * so cubic control points can mirror React Flow's `SimpleBezierEdge`.\n */\nexport type SVGSimpleBezierDrawFunction = (\n  source: EndpointInfo,\n  target: EndpointInfo,\n  path: number[][],\n) => string;\n\n/** Values accepted by {@link GetSmartEdgeOptions.drawEdge}. */\nexport type DrawEdgeFunction = SVGDrawFunction | SVGSimpleBezierDrawFunction;\n\ninterface SimpleBezierPoint extends XYPosition {\n  position: Position | HandleSide;\n}\n\nconst isHorizontalSide = (position: Position | HandleSide): boolean =>\n  position === \"left\" || position === \"right\";\n\n/**\n * Control point for a cubic segment, ported from xyflow's SimpleBezierEdge.\n * @see https://github.com/xyflow/xyflow/blob/main/packages/react/src/components/Edges/SimpleBezierEdge.tsx\n */\nconst getSimpleBezierControl = (\n  pos: Position | HandleSide,\n  fromX: number,\n  fromY: number,\n  toX: number,\n  toY: number,\n): [number, number] => {\n  if (isHorizontalSide(pos)) {\n    return [0.5 * (fromX + toX), fromY];\n  }\n\n  return [fromX, 0.5 * (fromY + toY)];\n};\n\n/** Infers a handle side from the dominant travel direction between two points. */\nconst inferHandlePosition = (\n  from: XYPosition,\n  toward: XYPosition,\n): HandleSide => {\n  const deltaX = toward.x - from.x;\n  const deltaY = toward.y - from.y;\n\n  if (Math.abs(deltaX) >= Math.abs(deltaY)) {\n    return deltaX >= 0 ? \"right\" : \"left\";\n  }\n\n  return deltaY >= 0 ? \"bottom\" : \"top\";\n};\n\nconst toSimpleBezierPoint = (\n  point: XYPosition,\n  prev: XYPosition,\n  next: XYPosition,\n): SimpleBezierPoint => ({\n  x: point.x,\n  y: point.y,\n  position: inferHandlePosition(prev, next),\n});\n\n/**\n * Draws an SVG path using chained cubic bezier segments with handle-position\n * controls, mirroring React Flow's SimpleBezierEdge.\n */\nexport const svgDrawSimpleBezierLinePath: SVGSimpleBezierDrawFunction = (\n  source,\n  target,\n  path,\n) => {\n  const waypoints = path.map(([posX, posY]) => ({ x: posX, y: posY }));\n  const allPoints: XYPosition[] = [source, ...waypoints, target];\n\n  const points: SimpleBezierPoint[] = allPoints.map((point, index) => {\n    if (index === 0) {\n      return source;\n    }\n    if (index === allPoints.length - 1) {\n      return target;\n    }\n\n    return toSimpleBezierPoint(\n      point,\n      allPoints[index - 1],\n      allPoints[index + 1],\n    );\n  });\n\n  let svgPath = `M${String(points[0].x)},${String(points[0].y)}`;\n\n  for (let index = 0; index < points.length - 1; index++) {\n    const from = points[index];\n    const targetPoint = points[index + 1];\n    const [sourceControlX, sourceControlY] = getSimpleBezierControl(\n      from.position,\n      from.x,\n      from.y,\n      targetPoint.x,\n      targetPoint.y,\n    );\n    const [targetControlX, targetControlY] = getSimpleBezierControl(\n      targetPoint.position,\n      targetPoint.x,\n      targetPoint.y,\n      from.x,\n      from.y,\n    );\n\n    svgPath += ` C${String(sourceControlX)},${String(sourceControlY)} ${String(targetControlX)},${String(targetControlY)} ${String(targetPoint.x)},${String(targetPoint.y)}`;\n  }\n\n  return svgPath;\n};\n\n/**\n * Draws a SVG path from a list of points, using straight lines.\n */\nexport const svgDrawStraightLinePath: SVGDrawFunction = (\n  source,\n  target,\n  path,\n) => {\n  let svgPathString = `M ${String(source.x)}, ${String(source.y)} `;\n\n  path.forEach((point) => {\n    const [posX, posY] = point;\n    svgPathString += `L ${String(posX)}, ${String(posY)} `;\n  });\n\n  svgPathString += `L ${String(target.x)}, ${String(target.y)} `;\n\n  return svgPathString;\n};\n\n/**\n * Draws a SVG path from a list of points, using rounded lines.\n */\nexport const svgDrawSmoothLinePath: SVGDrawFunction = (\n  source,\n  target,\n  path,\n) => {\n  const points = [[source.x, source.y], ...path, [target.x, target.y]];\n  return quadraticBezierCurve(points);\n};\n\nconst quadraticBezierCurve = (points: number[][]) => {\n  const axisX = 0;\n  const axisY = 1;\n  let point = points[0];\n\n  const first = points[0];\n  let svgPath = `M${String(first[axisX])},${String(first[axisY])}M`;\n\n  for (const next of points) {\n    const midPoint = getMidPoint(\n      point[axisX],\n      point[axisY],\n      next[axisX],\n      next[axisY],\n    );\n\n    svgPath += ` ${String(midPoint[axisX])},${String(midPoint[axisY])}`;\n    svgPath += `Q${String(next[axisX])},${String(next[axisY])}`;\n    point = next;\n  }\n\n  const last = points[points.length - 1];\n  svgPath += ` ${String(last[0])},${String(last[1])}`;\n\n  return svgPath;\n};\n\nconst getMidPoint = (\n  pointAx: number,\n  pointAy: number,\n  pointBx: number,\n  pointBy: number,\n) => {\n  const midX = (pointAx - pointBx) / 2 + pointBx;\n  const midY = (pointAy - pointBy) / 2 + pointBy;\n  return [midX, midY];\n};\n\nexport interface SmoothStepOptions {\n  borderRadius?: number;\n}\n\n/**\n * Returns a {@link SVGDrawFunction} that draws an orthogonal (\"step\") path with\n * rounded corners, mirroring React Flow's smooth step edge. The `borderRadius`\n * controls how much each corner is rounded (default `5`, matching React Flow);\n * it is clamped per-corner to half of the adjacent segment lengths so tight\n * grid corners never overshoot.\n *\n * Adapted from https://gist.github.com/holgergp/b95396f8e81abb17add1809c404b163c\n */\nexport const svgDrawSmoothStepLinePath = (\n  options: SmoothStepOptions = {},\n): SVGDrawFunction => {\n  const { borderRadius = 5 } = options;\n\n  return (source, target, path) => {\n    const points: XYPosition[] = dedupePoints([\n      { x: source.x, y: source.y },\n      ...path.map(([posX, posY]) => ({ x: posX, y: posY })),\n      { x: target.x, y: target.y },\n    ]);\n\n    return points.reduce((svgPath, point, index) => {\n      const isInteriorPoint = index > 0 && index < points.length - 1;\n\n      if (isInteriorPoint) {\n        return (\n          svgPath +\n          getBend(points[index - 1], point, points[index + 1], borderRadius)\n        );\n      }\n\n      const command = index === 0 ? \"M\" : \"L\";\n      return svgPath + `${command} ${String(point.x)},${String(point.y)} `;\n    }, \"\");\n  };\n};\n\nconst distance = (first: XYPosition, second: XYPosition) =>\n  Math.sqrt(Math.pow(second.x - first.x, 2) + Math.pow(second.y - first.y, 2));\n\n/**\n * Drops consecutive duplicate points so corner rounding doesn't produce\n * degenerate (zero-length) bends. The endpoint alignment step can repeat the\n * source/target coordinate as the first/last path point.\n */\nconst dedupePoints = (points: XYPosition[]) =>\n  points.filter(\n    (point, index) =>\n      index === 0 ||\n      point.x !== points[index - 1].x ||\n      point.y !== points[index - 1].y,\n  );\n\nconst getBend = (\n  pointA: XYPosition,\n  pointB: XYPosition,\n  pointC: XYPosition,\n  size: number,\n): string => {\n  const bendSize = Math.min(\n    distance(pointA, pointB) / 2,\n    distance(pointB, pointC) / 2,\n    size,\n  );\n  const { x: cornerX, y: cornerY } = pointB;\n\n  // Collinear points: no corner to round.\n  if (\n    (pointA.x === cornerX && cornerX === pointC.x) ||\n    (pointA.y === cornerY && cornerY === pointC.y)\n  ) {\n    return `L ${String(cornerX)},${String(cornerY)} `;\n  }\n\n  // First segment is horizontal.\n  if (pointA.y === cornerY) {\n    const xDir = pointA.x < pointC.x ? -1 : 1;\n    const yDir = pointA.y < pointC.y ? 1 : -1;\n    return `L ${String(cornerX + bendSize * xDir)},${String(cornerY)}Q ${String(cornerX)},${String(cornerY)} ${String(cornerX)},${String(cornerY + bendSize * yDir)} `;\n  }\n\n  // First segment is vertical.\n  const xDir = pointA.x < pointC.x ? 1 : -1;\n  const yDir = pointA.y < pointC.y ? -1 : 1;\n  return `L ${String(cornerX)},${String(cornerY + bendSize * yDir)}Q ${String(cornerX)},${String(cornerY)} ${String(cornerX + bendSize * xDir)},${String(cornerY)} `;\n};\n","import type { XYPosition } from \"@xyflow/react\";\nimport type { Hop } from \"./edgeHops\";\n\n/** Options for {@link drawOrthogonalHopPath}. */\nexport interface OrthogonalHopOptions {\n  /** Radius of the bridge arc drawn at each crossing. Default `6`. */\n  hopRadius?: number;\n  /**\n   * Corner rounding radius. `0` (default) yields sharp step corners; a positive\n   * value rounds corners like React Flow's smooth-step edge.\n   */\n  borderRadius?: number;\n}\n\nconst DEFAULT_HOP_RADIUS = 6;\n\nconst pointDistance = (first: XYPosition, second: XYPosition): number =>\n  Math.sqrt(Math.pow(second.x - first.x, 2) + Math.pow(second.y - first.y, 2));\n\n/**\n * The rounded-corner trim at a vertex, clamped to half of each adjacent\n * segment so corners never overshoot. Returns `0` for collinear points (no\n * corner to round). Mirrors the bend sizing used by the smooth-step drawer.\n */\nconst cornerBendSize = (\n  previous: XYPosition,\n  corner: XYPosition,\n  next: XYPosition,\n  radius: number,\n): number => {\n  const collinear =\n    (previous.x === corner.x && corner.x === next.x) ||\n    (previous.y === corner.y && corner.y === next.y);\n  if (collinear) return 0;\n  return Math.min(\n    pointDistance(previous, corner) / 2,\n    pointDistance(corner, next) / 2,\n    radius,\n  );\n};\n\n/**\n * Returns the point `amount` px from `origin` toward `toward`. Only called with\n * a positive corner bend size, which is itself clamped to half the segment\n * length, so the two points are always distinct.\n */\nconst movePointToward = (\n  origin: XYPosition,\n  toward: XYPosition,\n  amount: number,\n): XYPosition => {\n  const ratio = amount / pointDistance(origin, toward);\n  return {\n    x: origin.x + (toward.x - origin.x) * ratio,\n    y: origin.y + (toward.y - origin.y) * ratio,\n  };\n};\n\n/** SVG arc sweep flag keeping every bump on the same side of the wire. */\nconst sweepFor = (\n  orientation: \"horizontal\" | \"vertical\",\n  direction: 1 | -1,\n): 0 | 1 => {\n  if (orientation === \"horizontal\") return direction === 1 ? 1 : 0;\n  return direction === 1 ? 0 : 1;\n};\n\ninterface AxisSegment {\n  /** The constant coordinate of the segment (y for horizontal, x for vertical). */\n  fixed: number;\n  /** The varying coordinate at the segment's start. */\n  start: number;\n  /** The varying coordinate at the segment's end. */\n  end: number;\n}\n\n/**\n * Draws one axis-aligned segment (already trimmed for rounded corners),\n * inserting a small arc \"bridge\" at each crossing. Hops too close to a trimmed\n * corner or the segment ends are skipped rather than drawn over the rounding.\n */\nconst drawAxisSegment = (\n  line: AxisSegment,\n  segmentHops: Hop[],\n  hopRadius: number,\n  bendStart: number,\n  bendEnd: number,\n  orientation: \"horizontal\" | \"vertical\",\n): string => {\n  const direction: 1 | -1 = line.end >= line.start ? 1 : -1;\n  const drawFrom = line.start + direction * bendStart;\n  const drawTo = line.end - direction * bendEnd;\n\n  const pointAt = (along: number): string =>\n    orientation === \"horizontal\"\n      ? `${String(along)},${String(line.fixed)}`\n      : `${String(line.fixed)},${String(along)}`;\n\n  let svg = \"\";\n  for (const hop of segmentHops) {\n    const crossing = line.start + direction * hop.at;\n    const before = crossing - direction * hopRadius;\n    const after = crossing + direction * hopRadius;\n    const clearsCorners =\n      direction * (before - drawFrom) > 0 && direction * (drawTo - after) > 0;\n    if (clearsCorners) {\n      svg += `L ${pointAt(before)} `;\n      svg += `A ${String(hopRadius)} ${String(hopRadius)} 0 0 ${String(sweepFor(orientation, direction))} ${pointAt(after)} `;\n    }\n  }\n  svg += `L ${pointAt(drawTo)} `;\n  return svg;\n};\n\n/**\n * Draws an orthogonal (\"step\") SVG path through `points`, optionally rounding\n * corners (`borderRadius`) and drawing a small bridge arc at every crossing in\n * `hops`. With an empty `hops` list and `borderRadius` of `0` this is\n * equivalent to a plain step path; with `borderRadius > 0` it matches the\n * smooth-step look while still bridging crossings.\n *\n * `points` must be the full polyline including the source and target endpoints,\n * and each `hop`'s segment index must refer to a segment of that same polyline\n * (see {@link toPolyline} and {@link computeEdgeHops}).\n */\nexport const drawOrthogonalHopPath = (\n  points: XYPosition[],\n  hops: Hop[],\n  options: OrthogonalHopOptions = {},\n): string => {\n  const { hopRadius = DEFAULT_HOP_RADIUS, borderRadius = 0 } = options;\n  if (points.length === 0) return \"\";\n\n  let svgPath = `M ${String(points[0].x)},${String(points[0].y)} `;\n\n  for (let index = 0; index < points.length - 1; index++) {\n    const start = points[index];\n    const end = points[index + 1];\n\n    const bendStart =\n      borderRadius > 0 && index > 0\n        ? cornerBendSize(points[index - 1], start, end, borderRadius)\n        : 0;\n    const bendEnd =\n      borderRadius > 0 && index < points.length - 2\n        ? cornerBendSize(start, end, points[index + 2], borderRadius)\n        : 0;\n\n    const segmentHops = hops\n      .filter((hop) => hop.segmentIndex === index)\n      .sort((first, second) => first.at - second.at);\n\n    const isHorizontal = Math.abs(end.y - start.y) <= Math.abs(end.x - start.x);\n    const line: AxisSegment = isHorizontal\n      ? { fixed: start.y, start: start.x, end: end.x }\n      : { fixed: start.x, start: start.y, end: end.y };\n\n    svgPath += drawAxisSegment(\n      line,\n      segmentHops,\n      hopRadius,\n      bendStart,\n      bendEnd,\n      isHorizontal ? \"horizontal\" : \"vertical\",\n    );\n\n    if (bendEnd > 0) {\n      const exit = movePointToward(end, points[index + 2], bendEnd);\n      svgPath += `Q ${String(end.x)},${String(end.y)} ${String(exit.x)},${String(exit.y)} `;\n    }\n  }\n\n  return svgPath;\n};\n","/**\n * Array-backed binary min-heap of integer ids scored by numbers. Used as the\n * A-star/JPS open list. Decrease-key is handled lazily: callers push a duplicate\n * entry with the better score and skip stale pops via their closed check.\n */\nexport interface MinHeap {\n  push(id: number, score: number): void;\n  /** The id with the lowest score, or `-1` when empty. */\n  pop(): number;\n  readonly size: number;\n}\n\nexport const createMinHeap = (): MinHeap => {\n  const ids: number[] = [];\n  const scores: number[] = [];\n\n  const swap = (indexA: number, indexB: number): void => {\n    const itemId = ids[indexA];\n    ids[indexA] = ids[indexB];\n    ids[indexB] = itemId;\n    const score = scores[indexA];\n    scores[indexA] = scores[indexB];\n    scores[indexB] = score;\n  };\n\n  const siftUp = (start: number): void => {\n    let index = start;\n    while (index > 0) {\n      const parent = (index - 1) >> 1;\n      if (scores[parent] <= scores[index]) break;\n      swap(parent, index);\n      index = parent;\n    }\n  };\n\n  const siftDown = (start: number): void => {\n    let index = start;\n    for (;;) {\n      const left = index * 2 + 1;\n      const right = left + 1;\n      let smallest = index;\n      if (left < ids.length && scores[left] < scores[smallest]) smallest = left;\n      if (right < ids.length && scores[right] < scores[smallest])\n        smallest = right;\n      if (smallest === index) break;\n      swap(smallest, index);\n      index = smallest;\n    }\n  };\n\n  return {\n    push: (itemId, score) => {\n      ids.push(itemId);\n      scores.push(score);\n      siftUp(ids.length - 1);\n    },\n    pop: () => {\n      if (ids.length === 0) return -1;\n      const top = ids[0];\n      const lastItemId = ids[ids.length - 1];\n      const lastItemScore = scores[ids.length - 1];\n      ids.length -= 1;\n      scores.length -= 1;\n      if (ids.length > 0) {\n        ids[0] = lastItemId;\n        scores[0] = lastItemScore;\n        siftDown(0);\n      }\n      return top;\n    },\n    get size() {\n      return ids.length;\n    },\n  };\n};\n","/**\n * Reusable search buffers shared by the flat A* and JPS finders. Buffers are\n * module-level and grown on demand; a per-search `generation` stamp in `mark`\n * makes stale data invisible without clearing, so repeated searches allocate\n * nothing.\n */\nexport interface SearchScratch {\n  /** Cost from start (valid when `mark[i] === generation`). */\n  g: Float64Array;\n  /** Parent cell index, `-1` for the start (valid when marked). */\n  parent: Int32Array;\n  /** 0 = untouched, STATE_OPEN, STATE_CLOSED (valid when marked). */\n  state: Uint8Array;\n  /** Generation stamp per cell. */\n  mark: Int32Array;\n  generation: number;\n}\n\nexport const STATE_OPEN = 1;\nexport const STATE_CLOSED = 2;\n\nlet buffers: SearchScratch = {\n  g: new Float64Array(0),\n  parent: new Int32Array(0),\n  state: new Uint8Array(0),\n  mark: new Int32Array(0),\n  generation: 0,\n};\n\nexport const acquireScratch = (size: number): SearchScratch => {\n  if (buffers.g.length < size) {\n    buffers = {\n      g: new Float64Array(size),\n      parent: new Int32Array(size),\n      state: new Uint8Array(size),\n      // New Int32Array is zero-filled; starting generation at 1 keeps every\n      // cell \"unmarked\" without an explicit fill.\n      mark: new Int32Array(size),\n      generation: buffers.generation,\n    };\n  }\n  buffers.generation += 1;\n  return buffers;\n};\n\n/** Lazily initializes a cell for the current generation. */\nexport const touch = (scratch: SearchScratch, index: number): void => {\n  if (scratch.mark[index] === scratch.generation) return;\n  scratch.mark[index] = scratch.generation;\n  scratch.g[index] = Number.POSITIVE_INFINITY;\n  scratch.parent[index] = -1;\n  scratch.state[index] = 0;\n};\n\n/** Walks `parent` links from the end index back to the start. */\nexport const reconstructPath = (\n  scratch: SearchScratch,\n  endIndex: number,\n  width: number,\n): number[][] => {\n  const path: number[][] = [];\n  let index = endIndex;\n  while (index !== -1) {\n    path.push([index % width, Math.floor(index / width)]);\n    index = scratch.parent[index];\n  }\n  return path.reverse();\n};\n","import { createMinHeap } from \"./binaryHeap\";\nimport { isWalkable } from \"./flatGrid\";\nimport type { FlatGrid } from \"./flatGrid\";\nimport {\n  acquireScratch,\n  reconstructPath,\n  touch,\n  STATE_CLOSED,\n  STATE_OPEN,\n  type SearchScratch,\n} from \"./searchScratch\";\n\nconst manhattan = (deltaX: number, deltaY: number): number => deltaX + deltaY;\n\nconst octile = (deltaX: number, deltaY: number): number => {\n  const diagonalFactor = Math.SQRT2 - 1;\n  return deltaX < deltaY\n    ? diagonalFactor * deltaX + deltaY\n    : diagonalFactor * deltaY + deltaX;\n};\n\n/** Orthogonal neighbors first, then diagonals — same order as the old grid. */\nconst NEIGHBOR_STEPS: readonly (readonly [number, number, number])[] = [\n  [0, -1, 1],\n  [1, 0, 1],\n  [0, 1, 1],\n  [-1, 0, 1],\n  [1, -1, Math.SQRT2],\n  [1, 1, Math.SQRT2],\n  [-1, 1, Math.SQRT2],\n  [-1, -1, Math.SQRT2],\n];\n\ninterface NeighborProcessContext {\n  scratch: SearchScratch;\n  grid: FlatGrid;\n  endX: number;\n  endY: number;\n  width: number;\n  heuristic: (deltaX: number, deltaY: number) => number;\n  open: ReturnType<typeof createMinHeap>;\n}\n\n/** Process a neighbor cell, updating scratch and open list if the path is better. */\nconst processNeighbor = (\n  context: NeighborProcessContext,\n  currentIndex: number,\n  neighborStepIndex: number,\n  currentX: number,\n  currentY: number,\n): void => {\n  const { scratch, grid, endX, endY, width, heuristic, open } = context;\n  const [stepX, stepY, cost] = NEIGHBOR_STEPS[neighborStepIndex];\n  const neighborColumn = currentX + stepX;\n  const neighborRow = currentY + stepY;\n\n  if (!isWalkable(grid, neighborColumn, neighborRow)) return;\n\n  const neighbor = neighborRow * width + neighborColumn;\n  touch(scratch, neighbor);\n\n  if (scratch.state[neighbor] === STATE_CLOSED) return;\n\n  const tentativeG = scratch.g[currentIndex] + cost;\n  if (tentativeG >= scratch.g[neighbor]) return;\n\n  scratch.g[neighbor] = tentativeG;\n  scratch.parent[neighbor] = currentIndex;\n  scratch.state[neighbor] = STATE_OPEN;\n  const estimated =\n    tentativeG +\n    heuristic(Math.abs(neighborColumn - endX), Math.abs(neighborRow - endY));\n  open.push(neighbor, estimated);\n};\n\n/**\n * A* on a flat grid. Returns `[x, y]` pairs from start to end inclusive, or an\n * empty array when unreachable. Heuristics and step costs match the old\n * object-grid finder so routed paths keep their v4 shapes.\n */\nexport const findPathAStar = (\n  grid: FlatGrid,\n  startX: number,\n  startY: number,\n  endX: number,\n  endY: number,\n  diagonal: boolean,\n): number[][] => {\n  if (!isWalkable(grid, startX, startY) || !isWalkable(grid, endX, endY)) {\n    return [];\n  }\n\n  const { width } = grid;\n  const scratch = acquireScratch(width * grid.height);\n  const heuristic = diagonal ? octile : manhattan;\n  const neighborCount = diagonal ? 8 : 4;\n\n  const startIndex = startY * width + startX;\n  const endIndex = endY * width + endX;\n\n  const open = createMinHeap();\n  touch(scratch, startIndex);\n  scratch.g[startIndex] = 0;\n  scratch.state[startIndex] = STATE_OPEN;\n  open.push(startIndex, 0);\n\n  const context: NeighborProcessContext = {\n    scratch,\n    grid,\n    endX,\n    endY,\n    width,\n    heuristic,\n    open,\n  };\n\n  while (open.size > 0) {\n    const current = open.pop();\n    if (scratch.state[current] === STATE_CLOSED) continue; // stale duplicate\n    scratch.state[current] = STATE_CLOSED;\n\n    if (current === endIndex) {\n      return reconstructPath(scratch, endIndex, width);\n    }\n\n    const currentX = current % width;\n    const currentY = Math.floor(current / width);\n\n    for (let idx = 0; idx < neighborCount; idx++) {\n      processNeighbor(context, current, idx, currentX, currentY);\n    }\n  }\n\n  return [];\n};\n","// Orthogonal Jump Point Search on a flat grid.\n// Algorithm based on PathFinding.js (MIT) — https://github.com/qiao/PathFinding.js\n\nimport { createMinHeap } from \"./binaryHeap\";\nimport { isWalkable } from \"./flatGrid\";\nimport type { FlatGrid } from \"./flatGrid\";\nimport {\n  acquireScratch,\n  reconstructPath,\n  touch,\n  STATE_CLOSED,\n  STATE_OPEN,\n} from \"./searchScratch\";\nimport type { SearchScratch } from \"./searchScratch\";\n\nconst manhattan = (deltaX: number, deltaY: number): number => deltaX + deltaY;\n\nconst pushNeighbor = (\n  grid: FlatGrid,\n  out: number[][],\n  column: number,\n  row: number,\n): void => {\n  if (isWalkable(grid, column, row)) out.push([column, row]);\n};\n\n/**\n * Pruned neighbors given the direction of travel from the parent.\n * Returns only walkable cells that are part of the search direction.\n */\nconst findNeighbors = (\n  grid: FlatGrid,\n  scratch: SearchScratch,\n  index: number,\n): number[][] => {\n  const width = grid.width;\n  const column = index % width;\n  const row = Math.floor(index / width);\n  const parent = scratch.parent[index];\n  const neighbors: number[][] = [];\n\n  if (parent === -1) {\n    pushNeighbor(grid, neighbors, column, row - 1);\n    pushNeighbor(grid, neighbors, column + 1, row);\n    pushNeighbor(grid, neighbors, column, row + 1);\n    pushNeighbor(grid, neighbors, column - 1, row);\n    return neighbors;\n  }\n\n  const parentColumn = parent % width;\n  const parentRow = Math.floor(parent / width);\n  const deltaX =\n    (column - parentColumn) / Math.max(Math.abs(column - parentColumn), 1);\n  const deltaY = (row - parentRow) / Math.max(Math.abs(row - parentRow), 1);\n\n  // Movement is orthogonal-only, so a node can only ever differ from its\n  // parent along a single axis: deltaX !== 0 XOR deltaY !== 0. The vertical\n  // case is therefore the unconditional fallback rather than an `else if`.\n  if (deltaX !== 0) {\n    pushNeighbor(grid, neighbors, column, row - 1);\n    pushNeighbor(grid, neighbors, column, row + 1);\n    pushNeighbor(grid, neighbors, column + deltaX, row);\n  } else {\n    pushNeighbor(grid, neighbors, column - 1, row);\n    pushNeighbor(grid, neighbors, column + 1, row);\n    pushNeighbor(grid, neighbors, column, row + deltaY);\n  }\n\n  return neighbors;\n};\n\nconst hasForcedNeighborHorizontal = (\n  grid: FlatGrid,\n  column: number,\n  row: number,\n  deltaX: number,\n): boolean =>\n  (isWalkable(grid, column, row - 1) &&\n    !isWalkable(grid, column - deltaX, row - 1)) ||\n  (isWalkable(grid, column, row + 1) &&\n    !isWalkable(grid, column - deltaX, row + 1));\n\nconst hasForcedNeighborVertical = (\n  grid: FlatGrid,\n  column: number,\n  row: number,\n  deltaY: number,\n): boolean =>\n  (isWalkable(grid, column - 1, row) &&\n    !isWalkable(grid, column - 1, row - deltaY)) ||\n  (isWalkable(grid, column + 1, row) &&\n    !isWalkable(grid, column + 1, row - deltaY));\n\nconst createJump = (grid: FlatGrid, endColumn: number, endRow: number) => {\n  const jump = (\n    column: number,\n    row: number,\n    parentColumn: number,\n    parentRow: number,\n  ): number[] | null => {\n    if (!isWalkable(grid, column, row)) return null;\n    if (column === endColumn && row === endRow) return [column, row];\n\n    const deltaX = column - parentColumn;\n    const deltaY = row - parentRow;\n\n    if (\n      deltaX !== 0 &&\n      hasForcedNeighborHorizontal(grid, column, row, deltaX)\n    ) {\n      return [column, row];\n    }\n\n    if (deltaY !== 0) {\n      if (hasForcedNeighborVertical(grid, column, row, deltaY)) {\n        return [column, row];\n      }\n      if (\n        jump(column + 1, row, column, row) ??\n        jump(column - 1, row, column, row)\n      ) {\n        return [column, row];\n      }\n    }\n\n    return jump(column + deltaX, row + deltaY, column, row);\n  };\n\n  return jump;\n};\n\ninterface ProcessJumpContext {\n  scratch: SearchScratch;\n  endColumn: number;\n  endRow: number;\n  width: number;\n  jump: (\n    col: number,\n    row: number,\n    parentCol: number,\n    parentRow: number,\n  ) => number[] | null;\n  open: ReturnType<typeof createMinHeap>;\n}\n\nconst processJumpNeighbor = (\n  context: ProcessJumpContext,\n  currentIndex: number,\n  neighborColumn: number,\n  neighborRow: number,\n  currentColumn: number,\n  currentRow: number,\n): void => {\n  const { scratch, endColumn, endRow, width, jump, open } = context;\n\n  const jumpPoint = jump(\n    neighborColumn,\n    neighborRow,\n    currentColumn,\n    currentRow,\n  );\n  if (!jumpPoint) return;\n\n  const [jumpColumn, jumpRow] = jumpPoint;\n  const jumpIndex = jumpRow * width + jumpColumn;\n  touch(scratch, jumpIndex);\n  if (scratch.state[jumpIndex] === STATE_CLOSED) return;\n\n  const stepCost = manhattan(\n    Math.abs(jumpColumn - currentColumn),\n    Math.abs(jumpRow - currentRow),\n  );\n  const tentativeG = scratch.g[currentIndex] + stepCost;\n  if (tentativeG >= scratch.g[jumpIndex]) return;\n\n  scratch.g[jumpIndex] = tentativeG;\n  scratch.parent[jumpIndex] = currentIndex;\n  scratch.state[jumpIndex] = STATE_OPEN;\n  open.push(\n    jumpIndex,\n    tentativeG +\n      manhattan(Math.abs(jumpColumn - endColumn), Math.abs(jumpRow - endRow)),\n  );\n};\n\n/**\n * Orthogonal Jump Point Search on a flat grid. Returns jump points only\n * (expanded to full cell sequence downstream by alignEndpoints/draw functions).\n * Empty array when unreachable.\n */\nexport const findPathJumpPoint = (\n  grid: FlatGrid,\n  startColumn: number,\n  startRow: number,\n  endColumn: number,\n  endRow: number,\n): number[][] => {\n  if (\n    !isWalkable(grid, startColumn, startRow) ||\n    !isWalkable(grid, endColumn, endRow)\n  ) {\n    return [];\n  }\n\n  const width = grid.width;\n  const scratch = acquireScratch(width * grid.height);\n  const startIndex = startRow * width + startColumn;\n  const endIndex = endRow * width + endColumn;\n  const open = createMinHeap();\n  const jump = createJump(grid, endColumn, endRow);\n\n  touch(scratch, startIndex);\n  scratch.g[startIndex] = 0;\n  scratch.state[startIndex] = STATE_OPEN;\n  open.push(startIndex, 0);\n\n  const context: ProcessJumpContext = {\n    scratch,\n    endColumn,\n    endRow,\n    width,\n    jump,\n    open,\n  };\n\n  while (open.size > 0) {\n    const current = open.pop();\n    if (scratch.state[current] === STATE_CLOSED) continue;\n    scratch.state[current] = STATE_CLOSED;\n\n    if (current === endIndex) {\n      return reconstructPath(scratch, endIndex, width);\n    }\n\n    const currentColumn = current % width;\n    const currentRow = Math.floor(current / width);\n\n    for (const [neighborColumn, neighborRow] of findNeighbors(\n      grid,\n      scratch,\n      current,\n    )) {\n      processJumpNeighbor(\n        context,\n        current,\n        neighborColumn,\n        neighborRow,\n        currentColumn,\n        currentRow,\n      );\n    }\n  }\n\n  return [];\n};\n","import { findPathAStar } from \"../pathfinding/flatAStar\";\nimport { findPathJumpPoint } from \"../pathfinding/flatJumpPoint\";\nimport type { FlatGrid } from \"../pathfinding/flatGrid\";\nimport type { XYPosition } from \"@xyflow/react\";\n\n/**\n * Takes source and target {x, y} points, together with a flat grid\n * representation of the graph, and returns an array of number tuples [x, y],\n * representing the full path from source to target.\n */\nexport type PathFindingFunction = (\n  grid: FlatGrid,\n  start: XYPosition,\n  end: XYPosition,\n) => number[][];\n\nexport const NO_PATH_FOUND_ERROR = \"No path found\";\n\nexport const pathfindingAStarDiagonal: PathFindingFunction = (\n  grid,\n  start,\n  end,\n) => {\n  const fullPath = findPathAStar(grid, start.x, start.y, end.x, end.y, true);\n  if (fullPath.length === 0) {\n    throw new Error(NO_PATH_FOUND_ERROR);\n  }\n  return fullPath;\n};\n\nexport const pathfindingAStarNoDiagonal: PathFindingFunction = (\n  grid,\n  start,\n  end,\n) => {\n  const fullPath = findPathAStar(grid, start.x, start.y, end.x, end.y, false);\n  if (fullPath.length === 0) {\n    throw new Error(NO_PATH_FOUND_ERROR);\n  }\n  return fullPath;\n};\n\nexport const pathfindingJumpPointNoDiagonal: PathFindingFunction = (\n  grid,\n  start,\n  end,\n) => {\n  const jumpPointPath = findPathJumpPoint(grid, start.x, start.y, end.x, end.y);\n  if (jumpPointPath.length === 0) {\n    throw new Error(NO_PATH_FOUND_ERROR);\n  }\n  return jumpPointPath;\n};\n","import { roundUp, roundDown } from \"./utils\";\nimport type { Node, Rect, XYPosition } from \"@xyflow/react\";\n\nexport interface NodeBoundingBox {\n  id: string;\n  width: number;\n  height: number;\n  topLeft: XYPosition;\n  bottomLeft: XYPosition;\n  topRight: XYPosition;\n  bottomRight: XYPosition;\n}\n\nexport interface GraphBoundingBox {\n  width: number;\n  height: number;\n  topLeft: XYPosition;\n  bottomLeft: XYPosition;\n  topRight: XYPosition;\n  bottomRight: XYPosition;\n  xMax: number;\n  yMax: number;\n  xMin: number;\n  yMin: number;\n}\n\n/**\n * Build the padded corner points of a rectangle, applying the same rounding\n * rules used for both nodes and avoid areas so they create consistent\n * obstacles on the path-finding grid.\n */\nconst buildBox = (\n  originX: number,\n  originY: number,\n  width: number,\n  height: number,\n  nodePadding: number,\n  roundTo: number,\n) => {\n  const topLeft: XYPosition = {\n    x: originX - nodePadding,\n    y: originY - nodePadding,\n  };\n  const bottomLeft: XYPosition = {\n    x: originX - nodePadding,\n    y: originY + height + nodePadding,\n  };\n  const topRight: XYPosition = {\n    x: originX + width + nodePadding,\n    y: originY - nodePadding,\n  };\n  const bottomRight: XYPosition = {\n    x: originX + width + nodePadding,\n    y: originY + height + nodePadding,\n  };\n\n  if (roundTo > 0) {\n    topLeft.x = roundDown(topLeft.x, roundTo);\n    topLeft.y = roundDown(topLeft.y, roundTo);\n    bottomLeft.x = roundDown(bottomLeft.x, roundTo);\n    bottomLeft.y = roundUp(bottomLeft.y, roundTo);\n    topRight.x = roundUp(topRight.x, roundTo);\n    topRight.y = roundDown(topRight.y, roundTo);\n    bottomRight.x = roundUp(bottomRight.x, roundTo);\n    bottomRight.y = roundUp(bottomRight.y, roundTo);\n  }\n\n  return { topLeft, bottomLeft, topRight, bottomRight };\n};\n\n/**\n * Get the bounding box of all nodes and the graph itself, as X/Y coordinates\n * of all corner points. Consumer-provided `avoidAreas` are treated as extra\n * obstacles with the same `nodePadding` clearance as nodes.\n *\n * `extraPoints` are coordinates (e.g. the edge's source/target or any\n * waypoints) that must fall inside the graph box even when they sit beyond the\n * nodes, so the path-finding grid always covers them.\n */\nexport const getBoundingBoxes = (\n  nodes: Node[],\n  nodePadding = 2,\n  roundTo = 2,\n  avoidAreas: Rect[] = [],\n  extraPoints: XYPosition[] = [],\n) => {\n  let xMax = Number.MIN_SAFE_INTEGER;\n  let yMax = Number.MIN_SAFE_INTEGER;\n  let xMin = Number.MAX_SAFE_INTEGER;\n  let yMin = Number.MAX_SAFE_INTEGER;\n\n  const expandBounds = (topLeft: XYPosition, bottomRight: XYPosition) => {\n    if (topLeft.y < yMin) yMin = topLeft.y;\n    if (topLeft.x < xMin) xMin = topLeft.x;\n    if (bottomRight.y > yMax) yMax = bottomRight.y;\n    if (bottomRight.x > xMax) xMax = bottomRight.x;\n  };\n\n  const nodeBoxes: NodeBoundingBox[] = nodes.map((node) => {\n    const width = Math.max(node.measured?.width ?? 0, 1);\n    const height = Math.max(node.measured?.height ?? 0, 1);\n\n    const { topLeft, bottomLeft, topRight, bottomRight } = buildBox(\n      node.position.x,\n      node.position.y,\n      width,\n      height,\n      nodePadding,\n      roundTo,\n    );\n\n    expandBounds(topLeft, bottomRight);\n\n    return {\n      id: node.id,\n      width,\n      height,\n      topLeft,\n      bottomLeft,\n      topRight,\n      bottomRight,\n    };\n  });\n\n  const avoidBoxes: NodeBoundingBox[] = avoidAreas.map((area, index) => {\n    const width = Math.max(area.width, 1);\n    const height = Math.max(area.height, 1);\n\n    const { topLeft, bottomLeft, topRight, bottomRight } = buildBox(\n      area.x,\n      area.y,\n      width,\n      height,\n      nodePadding,\n      roundTo,\n    );\n\n    expandBounds(topLeft, bottomRight);\n\n    return {\n      id: `avoid-${String(index)}`,\n      width,\n      height,\n      topLeft,\n      bottomLeft,\n      topRight,\n      bottomRight,\n    };\n  });\n\n  // Make sure the graph box also contains any explicit points (edge endpoints\n  // and waypoints), even when they sit outside every node.\n  extraPoints.forEach((point) => {\n    expandBounds(point, point);\n  });\n\n  const graphPadding = nodePadding * 2;\n\n  xMax = roundUp(xMax + graphPadding, roundTo);\n  yMax = roundUp(yMax + graphPadding, roundTo);\n  xMin = roundDown(xMin - graphPadding, roundTo);\n  yMin = roundDown(yMin - graphPadding, roundTo);\n\n  const topLeft: XYPosition = {\n    x: xMin,\n    y: yMin,\n  };\n\n  const bottomLeft: XYPosition = {\n    x: xMin,\n    y: yMax,\n  };\n\n  const topRight: XYPosition = {\n    x: xMax,\n    y: yMin,\n  };\n\n  const bottomRight: XYPosition = {\n    x: xMax,\n    y: yMax,\n  };\n\n  const width = Math.abs(topLeft.x - topRight.x);\n  const height = Math.abs(topLeft.y - bottomLeft.y);\n\n  const graphBox: GraphBoundingBox = {\n    topLeft,\n    bottomLeft,\n    topRight,\n    bottomRight,\n    width,\n    height,\n    xMax,\n    yMax,\n    xMin,\n    yMin,\n  };\n\n  return { nodeBoxes, graphBox, avoidBoxes };\n};\n","import { Position } from \"@xyflow/react\";\nimport type { Node, XYPosition } from \"@xyflow/react\";\n\n/**\n * The dynamic connection parameters for a floating edge: the source/target\n * intersection coordinates on each node's border, plus the side (position) of\n * the node those points sit on.\n */\nexport interface FloatingEdgeParams {\n  sx: number;\n  sy: number;\n  tx: number;\n  ty: number;\n  sourcePos: Position;\n  targetPos: Position;\n}\n\nconst getNodeRect = (node: Node) => {\n  const width = Math.max(node.measured?.width ?? 0, 1);\n  const height = Math.max(node.measured?.height ?? 0, 1);\n  return { x: node.position.x, y: node.position.y, width, height };\n};\n\n/**\n * Returns the point where the straight line between the centers of\n * `intersectionNode` and `otherNode` crosses the border of `intersectionNode`.\n *\n * Ported from React Flow's Floating Edges example\n * (https://reactflow.dev/examples/edges/floating-edges), adapted to this\n * library's `Node` shape (`measured` dimensions + absolute `position`).\n */\nexport const getNodeIntersection = (\n  intersectionNode: Node,\n  otherNode: Node,\n): XYPosition => {\n  const intersection = getNodeRect(intersectionNode);\n  const other = getNodeRect(otherNode);\n\n  const halfWidth = intersection.width / 2;\n  const halfHeight = intersection.height / 2;\n\n  const intersectionRight = intersection.x + halfWidth;\n  const intersectionBottom = intersection.y + halfHeight;\n  const otherCenterX = other.x + other.width / 2;\n  const otherCenterY = other.y + other.height / 2;\n\n  const normalizedX =\n    (otherCenterX - intersectionRight) / (2 * halfWidth) -\n    (otherCenterY - intersectionBottom) / (2 * halfHeight);\n  const normalizedY =\n    (otherCenterX - intersectionRight) / (2 * halfWidth) +\n    (otherCenterY - intersectionBottom) / (2 * halfHeight);\n  const scale = 1 / (Math.abs(normalizedX) + Math.abs(normalizedY) || 1);\n  const scaledX = scale * normalizedX;\n  const scaledY = scale * normalizedY;\n  const intersectionX = halfWidth * (scaledX + scaledY) + intersectionRight;\n  const intersectionY = halfHeight * (-scaledX + scaledY) + intersectionBottom;\n\n  return { x: intersectionX, y: intersectionY };\n};\n\n/**\n * Returns the side of `node` (top/right/bottom/left) that `intersectionPoint`\n * sits on, used as the handle position fed into the pathfinding pipeline.\n */\nexport const getEdgePosition = (\n  node: Node,\n  intersectionPoint: XYPosition,\n): Position => {\n  const { x, y, width, height } = getNodeRect(node);\n  const nodeX = Math.round(x);\n  const nodeY = Math.round(y);\n  const pointX = Math.round(intersectionPoint.x);\n  const pointY = Math.round(intersectionPoint.y);\n\n  if (pointX <= nodeX + 1) {\n    return Position.Left;\n  }\n  if (pointX >= nodeX + width - 1) {\n    return Position.Right;\n  }\n  if (pointY <= nodeY + 1) {\n    return Position.Top;\n  }\n  if (pointY >= nodeY + height - 1) {\n    return Position.Bottom;\n  }\n\n  return Position.Top;\n};\n\n/**\n * Computes the floating connection parameters between two nodes: the points\n * where the edge meets each node's border and the corresponding sides. Feed the\n * result into {@link getSmartEdge} to route a floating edge around obstacles.\n *\n * Both nodes are expected to carry absolute `position` coordinates (resolve any\n * subflow offsets with `getAbsoluteNodes` first) and `measured` dimensions.\n */\nexport const getFloatingEdgeParams = (\n  sourceNode: Node,\n  targetNode: Node,\n): FloatingEdgeParams => {\n  const sourceIntersectionPoint = getNodeIntersection(sourceNode, targetNode);\n  const targetIntersectionPoint = getNodeIntersection(targetNode, sourceNode);\n\n  const sourcePos = getEdgePosition(sourceNode, sourceIntersectionPoint);\n  const targetPos = getEdgePosition(targetNode, targetIntersectionPoint);\n\n  return {\n    sx: sourceIntersectionPoint.x,\n    sy: sourceIntersectionPoint.y,\n    tx: targetIntersectionPoint.x,\n    ty: targetIntersectionPoint.y,\n    sourcePos,\n    targetPos,\n  };\n};\n","import type { Node, XYPosition } from \"@xyflow/react\";\n\n/**\n * Build a lookup of nodes by id for parent-chain traversal.\n */\nconst toNodeMap = (nodes: Node[]): Map<string, Node> => {\n  const map = new Map<string, Node>();\n  for (const node of nodes) {\n    map.set(node.id, node);\n  }\n  return map;\n};\n\n/**\n * Resolve a node's absolute position by walking up its `parentId` chain and\n * summing the relative offsets.\n *\n * React Flow stores a child node's `position` relative to its parent (see\n * https://reactflow.dev/examples/grouping/sub-flows), while edge handle\n * coordinates are always absolute. The pathfinding pipeline needs both in the\n * same (absolute) coordinate space, otherwise obstacles inside a subflow are\n * modeled in the wrong place. Assumes the default node `origin` of [0, 0].\n */\nconst getAbsolutePosition = (\n  node: Node,\n  nodeMap: Map<string, Node>,\n): XYPosition => {\n  let posX = node.position.x;\n  let posY = node.position.y;\n\n  let parentId = node.parentId;\n  const visited = new Set<string>([node.id]);\n\n  while (parentId && !visited.has(parentId)) {\n    visited.add(parentId);\n    const parent = nodeMap.get(parentId);\n    if (!parent) break;\n    posX += parent.position.x;\n    posY += parent.position.y;\n    parentId = parent.parentId;\n  }\n\n  return { x: posX, y: posY };\n};\n\n/**\n * Return a copy of the nodes where each `position` is its absolute coordinate\n * in the flow, resolving any `parentId` (subflow) offsets. Nodes without a\n * parent are returned unchanged (aside from being copied).\n */\nexport const getAbsoluteNodes = (nodes: Node[]): Node[] => {\n  const nodeMap = toNodeMap(nodes);\n\n  return nodes.map((node) => {\n    if (!node.parentId) return node;\n    return {\n      ...node,\n      position: getAbsolutePosition(node, nodeMap),\n    };\n  });\n};\n\n/**\n * Collect the ids of a node and all of its ancestors (its `parentId` chain).\n */\nconst collectAncestorIds = (\n  nodeId: string,\n  nodeMap: Map<string, Node>,\n): Set<string> => {\n  const ancestors = new Set<string>();\n  let parentId = nodeMap.get(nodeId)?.parentId;\n\n  while (parentId && !ancestors.has(parentId)) {\n    ancestors.add(parentId);\n    parentId = nodeMap.get(parentId)?.parentId;\n  }\n\n  return ancestors;\n};\n\n/**\n * Remove the container nodes that an edge lives inside from the obstacle set.\n *\n * An edge's own subflow container (and any node further up the chain) should\n * not be treated as a solid obstacle, otherwise the path is forced to route\n * around the container instead of within it. Sibling and unrelated group nodes\n * are kept so the edge still avoids them.\n */\nexport const excludeEdgeAncestorNodes = (\n  nodes: Node[],\n  sourceId: string,\n  targetId: string,\n): Node[] => {\n  const nodeMap = toNodeMap(nodes);\n  const ancestors = collectAncestorIds(sourceId, nodeMap);\n  for (const ancestorId of collectAncestorIds(targetId, nodeMap)) {\n    ancestors.add(ancestorId);\n  }\n\n  if (ancestors.size === 0) return nodes;\n\n  return nodes.filter((node) => !ancestors.has(node.id));\n};\n","import { useCallback, useContext, useSyncExternalStore } from \"react\";\nimport { SmartEdgeRoutingContext } from \"../routing/routingContext\";\nimport { nativeStepPolyline } from \"../routing/obstacleIndex\";\nimport {\n  computeEdgeHops,\n  drawOrthogonalHopPath,\n  toPolyline,\n} from \"../functions\";\nimport type { RegisteredSmartEdge } from \"../routing/scheduler\";\nimport type { SmartEdgeRouteResult } from \"../routing/providerStore\";\nimport type { SmartEdgePreset } from \"../smartEdgePresets\";\nimport type { XYPosition } from \"@xyflow/react\";\n\nconst isStepLikePreset = (preset: SmartEdgePreset): boolean =>\n  preset === \"step\" || preset === \"smoothstep\";\n\n/** Drops duplicate and collinear vertices so a straight native step run is\n * one segment. Needed for hop detection: `nativeStepPolyline` splits at the\n * unused-axis midpoint, which is often exactly where two edges cross. */\nconst simplifyPolyline = (points: XYPosition[]): XYPosition[] => {\n  const last = points[points.length - 1];\n  const interior = points.slice(1, -1).map((point) => [point.x, point.y]);\n  return toPolyline(points[0], last, interior);\n};\n\nconst nativeStepAsPolyline = (\n  source: XYPosition,\n  target: XYPosition,\n  registration: RegisteredSmartEdge,\n): XYPosition[] =>\n  simplifyPolyline(\n    nativeStepPolyline(\n      source.x,\n      source.y,\n      registration.sourcePosition,\n      target.x,\n      target.y,\n      registration.targetPosition,\n    ),\n  );\n\n/**\n * Configuration for circuit-style \"hops\": small bridge arcs drawn where a\n * smart edge crosses another orthogonal smart edge rendered beneath it.\n */\nexport interface HopOptions {\n  /** Radius of the bridge arc drawn at each crossing. Default `6`. */\n  radius?: number;\n  /**\n   * Corner rounding radius applied to the whole edge while hops are active.\n   * `0` (default) keeps sharp step corners; a positive value gives a\n   * smooth-step look. For smooth-step edges set this to match your border\n   * radius (React Flow's default is `5`).\n   */\n  borderRadius?: number;\n  /** Axis-alignment / crossing tolerance in pixels. Default `0.5`. */\n  epsilon?: number;\n}\n\n/** The `hops` value as accepted on options: a flag, a config object, or unset. */\nexport type HopSetting = boolean | HopOptions | undefined;\n\ninterface ResolvedHopConfig {\n  radius: number;\n  borderRadius: number;\n  epsilon: number;\n}\n\nconst DEFAULT_HOP_CONFIG: ResolvedHopConfig = {\n  radius: 6,\n  borderRadius: 0,\n  epsilon: 0.5,\n};\n\nconst resolveHopConfig = (hops: HopSetting): ResolvedHopConfig => {\n  if (!hops || hops === true) return DEFAULT_HOP_CONFIG;\n  return {\n    radius: hops.radius ?? DEFAULT_HOP_CONFIG.radius,\n    borderRadius: hops.borderRadius ?? DEFAULT_HOP_CONFIG.borderRadius,\n    epsilon: hops.epsilon ?? DEFAULT_HOP_CONFIG.epsilon,\n  };\n};\n\nexport interface ComputeHoppedPathParams {\n  /** This edge's own routed polyline, already resolved from its published\n   * route (see `resolveOwnPolyline`). */\n  ownPolyline: XYPosition[];\n  /** The polylines of every same-preset edge painted underneath this one\n   * that currently has a resolvable route. */\n  underneathPolylines: XYPosition[][];\n  /** The hop configuration (`true` for defaults, or an object to tune). */\n  hops: HopSetting;\n}\n\n/**\n * Draws this edge's SVG path with circuit-style hops: a small bridge arc at\n * every crossing between `ownPolyline` and one of `underneathPolylines`.\n * Pure geometry — every polyline is already resolved from published routes\n * by the caller, so this does no pathfinding and no store reads.\n */\nexport const computeHoppedPath = (params: ComputeHoppedPathParams): string => {\n  const config = resolveHopConfig(params.hops);\n  const hops = computeEdgeHops(\n    params.ownPolyline,\n    params.underneathPolylines,\n    config.epsilon,\n  );\n\n  return drawOrthogonalHopPath(params.ownPolyline, hops, {\n    hopRadius: config.radius,\n    borderRadius: config.borderRadius,\n  });\n};\n\n/**\n * Resolves this edge's own rendered polyline from its published route.\n * Routed edges walk their pathfinding points; clear step/smooth-step edges\n * use the native Z/L skeleton so hops still draw when\n * `routeOnlyWhenBlocked` skipped A*. Returns `null` while the route is\n * pending, or for a clear non-step preset (those keep the native fallback\n * with no hop drawing).\n */\nconst resolveOwnPolyline = (\n  source: XYPosition,\n  target: XYPosition,\n  registration: RegisteredSmartEdge,\n  route: SmartEdgeRouteResult | undefined,\n): XYPosition[] | null => {\n  if (route?.kind === \"routed\") {\n    return toPolyline(source, target, route.points);\n  }\n  if (route?.kind === \"clear\" && isStepLikePreset(registration.preset)) {\n    return nativeStepAsPolyline(source, target, registration);\n  }\n  return null;\n};\n\n/**\n * Resolves one underneath registration's rendered polyline from its\n * published route: a routed edge walks its actual path points, a clear edge\n * falls back to the native step skeleton between its registered endpoints\n * (still enough geometry to detect a crossing). Returns `null` when that\n * edge has no published route yet, in which case it contributes no\n * crossing rather than a stale one.\n */\nconst resolveUnderneathPolyline = (\n  registration: RegisteredSmartEdge,\n  route: SmartEdgeRouteResult | undefined,\n): XYPosition[] | null => {\n  /* v8 ignore next -- pending underneath edge; covered by unit tests */\n  if (route === undefined) return null;\n\n  const source: XYPosition = {\n    x: registration.sourceX,\n    y: registration.sourceY,\n  };\n  const target: XYPosition = {\n    x: registration.targetX,\n    y: registration.targetY,\n  };\n\n  if (route.kind === \"routed\") return toPolyline(source, target, route.points);\n\n  return nativeStepAsPolyline(source, target, registration);\n};\n\n/** Every same-preset registration painted strictly underneath `ownOrder`,\n * resolved to a polyline via `getRoute` and filtered down to the ones with a\n * resolvable route. */\nconst collectUnderneathPolylines = (\n  registrations: RegisteredSmartEdge[],\n  ownOrder: number,\n  ownPreset: SmartEdgePreset,\n  getRoute: (edgeId: string) => SmartEdgeRouteResult | undefined,\n): XYPosition[][] => {\n  const isPolyline = (\n    polyline: XYPosition[] | null,\n  ): polyline is XYPosition[] => polyline !== null;\n\n  return registrations\n    .filter(\n      (registration) =>\n        registration.order < ownOrder && registration.preset === ownPreset,\n    )\n    .map((registration) =>\n      resolveUnderneathPolyline(registration, getRoute(registration.id)),\n    )\n    .filter(isPolyline);\n};\n\n/** No provider, hops disabled, or nothing to watch: never notifies. */\nconst inertUnsubscribe = (): void => {\n  // Nothing to unsubscribe from.\n};\n\n/* v8 ignore next 3 -- useSyncExternalStore server snapshot; unused in browser */\nconst getInertRoutesVersion = (): number => 0;\n\nexport interface UseHoppedPathParams {\n  edgeId: string;\n  sourceX: number;\n  sourceY: number;\n  targetX: number;\n  targetY: number;\n  /** The hop configuration (`true` for defaults, or an object to tune). */\n  hops: HopSetting;\n  /** Editable/checkpoint edges use a different draw pipeline and skip hops. */\n  editable?: boolean;\n  checkpoints?: boolean;\n}\n\n/**\n * Returns the hopped SVG path for this edge, or `null` when hops are\n * disabled, there is no provider, this edge's own route is not yet\n * published, a clear non-step edge has nothing to bridge, or its\n * registration cannot be found. Clear step/smooth-step edges still get a\n * path (the native skeleton, with hops if they cross a neighbor).\n *\n * Reads only already-published routes from the provider's store —\n * `getRegistrationsInOrder`/`getRoute` are imperative getters, so a\n * `subscribeAllRoutes` subscription (via `useSyncExternalStore`, keyed off a\n * version counter) drives re-renders whenever any edge's route changes, own\n * or a neighbor's. This makes hop cost pure geometry over cached polylines\n * instead of a per-edge re-route.\n */\nexport const useHoppedPath = (params: UseHoppedPathParams): string | null => {\n  const context = useContext(SmartEdgeRoutingContext);\n  const hopsRequested =\n    Boolean(params.hops) && !params.editable && !params.checkpoints;\n  const store = hopsRequested && context !== null ? context.store : undefined;\n\n  const subscribe = useCallback(\n    (listener: () => void): (() => void) => {\n      if (!store) return inertUnsubscribe;\n      return store.subscribeAllRoutes(listener);\n    },\n    [store],\n  );\n  const getSnapshot = useCallback(\n    () => store?.getRoutesVersion() ?? 0,\n    [store],\n  );\n\n  useSyncExternalStore(subscribe, getSnapshot, getInertRoutesVersion);\n\n  if (!hopsRequested || context === null) return null;\n\n  const registrations = context.getRegistrationsInOrder();\n  const ownRegistration = registrations.find(\n    (registration) => registration.id === params.edgeId,\n  );\n  /* v8 ignore next -- registration race during unmount; covered by unit tests */\n  if (!ownRegistration) return null;\n\n  const source: XYPosition = { x: params.sourceX, y: params.sourceY };\n  const target: XYPosition = { x: params.targetX, y: params.targetY };\n\n  const ownPolyline = resolveOwnPolyline(\n    source,\n    target,\n    ownRegistration,\n    context.store.getRoute(params.edgeId),\n  );\n  if (!ownPolyline) return null;\n\n  const underneathPolylines = collectUnderneathPolylines(\n    registrations,\n    ownRegistration.order,\n    ownRegistration.preset,\n    (edgeId) => context.store.getRoute(edgeId),\n  );\n\n  return computeHoppedPath({\n    ownPolyline,\n    underneathPolylines,\n    hops: params.hops,\n  });\n};\n","import type { XYPosition } from \"@xyflow/react\";\nimport type { ControlPointData } from \"./ControlPoint\";\n\n/**\n * Returns the point at the given fraction (0..1) of a polyline's arc length.\n */\nexport const pointAlongPolyline = (\n  polyline: number[][],\n  fraction: number,\n): XYPosition => {\n  if (polyline.length < 2) {\n    const [posX, posY] = polyline[0] ?? [0, 0];\n    return { x: posX, y: posY };\n  }\n\n  let total = 0;\n  for (let index = 1; index < polyline.length; index++) {\n    total += Math.hypot(\n      polyline[index][0] - polyline[index - 1][0],\n      polyline[index][1] - polyline[index - 1][1],\n    );\n  }\n\n  const targetDistance = total * fraction;\n  let accumulated = 0;\n  for (let index = 1; index < polyline.length; index++) {\n    const segmentLength = Math.hypot(\n      polyline[index][0] - polyline[index - 1][0],\n      polyline[index][1] - polyline[index - 1][1],\n    );\n    if (accumulated + segmentLength >= targetDistance) {\n      const remaining = targetDistance - accumulated;\n      const segmentFraction =\n        segmentLength === 0 ? 0 : remaining / segmentLength;\n      return {\n        x:\n          polyline[index - 1][0] +\n          (polyline[index][0] - polyline[index - 1][0]) * segmentFraction,\n        y:\n          polyline[index - 1][1] +\n          (polyline[index][1] - polyline[index - 1][1]) * segmentFraction,\n      };\n    }\n    accumulated += segmentLength;\n  }\n\n  const [posX, posY] = polyline[polyline.length - 1];\n  return { x: posX, y: posY };\n};\n\n/**\n * Index of the polyline vertex closest to `point`, searching from `from`\n * (exclusive of the endpoints) onward.\n */\nexport const closestVertexIndex = (\n  polyline: number[][],\n  point: ControlPointData,\n  from: number,\n): number => {\n  let bestIndex = from;\n  let bestDistance = Infinity;\n\n  for (let index = from; index < polyline.length - 1; index++) {\n    const distance =\n      (polyline[index][0] - point.x) ** 2 + (polyline[index][1] - point.y) ** 2;\n    if (distance < bestDistance) {\n      bestDistance = distance;\n      bestIndex = index;\n    }\n  }\n\n  return bestIndex;\n};\n\n/**\n * Splits the routed polyline into one sub-polyline per segment, cutting at the\n * vertex nearest each waypoint.\n */\nexport const splitPolylineAtWaypoints = (\n  polyline: number[][],\n  waypoints: ControlPointData[],\n): number[][][] => {\n  if (polyline.length === 0) return [[]];\n  if (waypoints.length === 0) return [polyline];\n\n  const segments: number[][][] = [];\n  let start = 0;\n  let searchFrom = 1;\n\n  for (const waypoint of waypoints) {\n    const cut = closestVertexIndex(polyline, waypoint, searchFrom);\n    segments.push(polyline.slice(start, cut + 1));\n    start = cut;\n    searchFrom = cut + 1;\n  }\n\n  segments.push(polyline.slice(start));\n  return segments;\n};\n\n/**\n * Builds the interleaved control point list `[inactive, active, inactive, ...]`\n * from the active waypoints plus an inactive \"insert\" point at the midpoint of\n * each routed segment.\n */\nexport const buildControlPoints = (\n  source: XYPosition,\n  target: XYPosition,\n  activePoints: ControlPointData[],\n  routedInterior: number[][],\n): ControlPointData[] => {\n  const polyline: number[][] = [\n    [source.x, source.y],\n    ...routedInterior,\n    [target.x, target.y],\n  ];\n  const segments = splitPolylineAtWaypoints(polyline, activePoints);\n  const result: ControlPointData[] = [];\n\n  segments.forEach((segment, segmentIndex) => {\n    const midpoint = pointAlongPolyline(segment, 0.5);\n    result.push({\n      id: `__inactive-${String(segmentIndex)}`,\n      x: midpoint.x,\n      y: midpoint.y,\n      active: false,\n    });\n\n    if (segmentIndex < activePoints.length) {\n      result.push(activePoints[segmentIndex]);\n    }\n  });\n\n  return result;\n};\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useReactFlow, useStore } from \"@xyflow/react\";\nimport type { XYPosition } from \"@xyflow/react\";\n\n/**\n * A single waypoint of an editable smart edge. Stored (active) points live in\n * `edge.data.points`; inactive points are computed on the fly for adding new\n * waypoints and are not persisted.\n */\nexport type ControlPointData = XYPosition & {\n  id: string;\n  active?: boolean;\n};\n\nexport type SetControlPoints = (\n  update: (points: ControlPointData[]) => ControlPointData[],\n) => void;\n\nexport interface ControlPointProps {\n  id: string;\n  index: number;\n  x: number;\n  y: number;\n  color: string;\n  active?: boolean;\n  setControlPoints: SetControlPoints;\n}\n\nconst NUDGE = 5;\n\nexport function ControlPoint({\n  id,\n  index,\n  x,\n  y,\n  color,\n  active,\n  setControlPoints,\n}: Readonly<ControlPointProps>) {\n  const container = useStore((store) => store.domNode);\n  const { screenToFlowPosition } = useReactFlow();\n  const [dragging, setDragging] = useState(false);\n  const ref = useRef<SVGCircleElement>(null);\n\n  const updatePosition = useCallback(\n    (position: XYPosition) => {\n      setControlPoints((points) => {\n        const shouldActivate = !active;\n\n        if (shouldActivate) {\n          // Promote an inactive (insert) point to a new active waypoint with a\n          // fresh id. Inactive points sit at even visual indices, active points\n          // at odd indices, so the active-list insertion index is `index/2 - 1`.\n          const newPoint: ControlPointData = {\n            ...position,\n            id: crypto.randomUUID(),\n            active: true,\n          };\n\n          if (index !== 0) {\n            const insertAfter = index * 0.5 - 1;\n            return points.flatMap((point, pointIndex) =>\n              pointIndex === insertAfter ? [point, newPoint] : [point],\n            );\n          }\n          return [newPoint, ...points];\n        }\n\n        return points.map((point) =>\n          point.id === id ? { ...point, ...position } : point,\n        );\n      });\n    },\n    [id, active, index, setControlPoints],\n  );\n\n  const deletePoint = useCallback(() => {\n    setControlPoints((points) => points.filter((point) => point.id !== id));\n  }, [id, setControlPoints]);\n\n  const handleKeyDown = useCallback(\n    (event: React.KeyboardEvent) => {\n      switch (event.key) {\n        case \"Enter\":\n        case \" \":\n          if (!active) {\n            event.preventDefault();\n          }\n          updatePosition({ x, y });\n          break;\n        case \"Backspace\":\n        case \"Delete\":\n          event.stopPropagation();\n          deletePoint();\n          break;\n        case \"ArrowLeft\":\n          updatePosition({ x: x - NUDGE, y });\n          break;\n        case \"ArrowRight\":\n          updatePosition({ x: x + NUDGE, y });\n          break;\n        case \"ArrowUp\":\n          updatePosition({ x, y: y - NUDGE });\n          break;\n        case \"ArrowDown\":\n          updatePosition({ x, y: y + NUDGE });\n          break;\n        default:\n          break;\n      }\n    },\n    [x, y, active, updatePosition, deletePoint],\n  );\n\n  useEffect(() => {\n    if (!container || !dragging) {\n      return undefined;\n    }\n\n    const onPointerMove = (event: PointerEvent) => {\n      updatePosition(\n        screenToFlowPosition({ x: event.clientX, y: event.clientY }),\n      );\n    };\n\n    const onPointerUp = (event: PointerEvent) => {\n      setDragging(false);\n      updatePosition(\n        screenToFlowPosition({ x: event.clientX, y: event.clientY }),\n      );\n    };\n\n    container.addEventListener(\"pointermove\", onPointerMove);\n    container.addEventListener(\"pointerup\", onPointerUp, { once: true });\n    container.addEventListener(\"pointerleave\", onPointerUp, { once: true });\n\n    return () => {\n      container.removeEventListener(\"pointermove\", onPointerMove);\n      container.removeEventListener(\"pointerup\", onPointerUp);\n      container.removeEventListener(\"pointerleave\", onPointerUp);\n    };\n  }, [container, dragging, screenToFlowPosition, updatePosition]);\n\n  return (\n    <circle\n      ref={ref}\n      tabIndex={0}\n      data-testid=\"smart-edge-control-point\"\n      className={`nopan nodrag${active ? \" active\" : \"\"}`}\n      cx={x}\n      cy={y}\n      r={active ? 4 : 3}\n      strokeWidth={1}\n      strokeOpacity={active ? 1 : 0.3}\n      stroke={color}\n      fill={active ? color : \"white\"}\n      style={{ pointerEvents: \"all\", cursor: \"pointer\" }}\n      onContextMenu={(event) => {\n        event.preventDefault();\n        if (active) {\n          deletePoint();\n        }\n      }}\n      onPointerDown={(event) => {\n        if (event.button === 2) return;\n        updatePosition({ x, y });\n        // Only active waypoints drag continuously; an inactive point is\n        // promoted in place and can then be dragged on a subsequent gesture.\n        if (active) {\n          setDragging(true);\n        }\n      }}\n      onKeyDown={handleKeyDown}\n      onPointerUp={() => {\n        setDragging(false);\n      }}\n    />\n  );\n}\n","import type { XYPosition } from \"@xyflow/react\";\nimport type { ControlPointData } from \"./ControlPoint\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === \"object\" && value !== null;\n\nconst hasStringField = (\n  record: Record<string, unknown>,\n  field: string,\n): boolean => field in record && typeof record[field] === \"string\";\n\nconst hasNumberField = (\n  record: Record<string, unknown>,\n  field: string,\n): boolean => field in record && typeof record[field] === \"number\";\n\nconst isControlPointData = (value: unknown): value is ControlPointData =>\n  isRecord(value) &&\n  hasStringField(value, \"id\") &&\n  hasNumberField(value, \"x\") &&\n  hasNumberField(value, \"y\");\n\nconst isControlPointArray = (value: unknown): value is ControlPointData[] =>\n  Array.isArray(value) && value.every(isControlPointData);\n\nconst isXYPosition = (value: unknown): value is XYPosition =>\n  isRecord(value) && hasNumberField(value, \"x\") && hasNumberField(value, \"y\");\n\n/**\n * Reads the active waypoints from an edge's `data`, tolerating any data shape\n * (returns an empty list when absent or malformed).\n */\nexport const readControlPoints = (data: unknown): ControlPointData[] => {\n  if (\n    isRecord(data) &&\n    \"points\" in data &&\n    isControlPointArray(data[\"points\"])\n  ) {\n    return data[\"points\"];\n  }\n  return [];\n};\n\n/**\n * Reads fixed checkpoints from an edge's `data`, tolerating any data shape\n * (returns an empty list when absent or malformed).\n */\nexport const readCheckpoints = (data: unknown): XYPosition[] => {\n  if (\n    isRecord(data) &&\n    \"checkpoints\" in data &&\n    Array.isArray(data[\"checkpoints\"])\n  ) {\n    return data[\"checkpoints\"].filter(isXYPosition);\n  }\n  return [];\n};\n","import { getFloatingEdgeParams } from \"../functions\";\nimport type { Node, Position } from \"@xyflow/react\";\nimport { readCheckpoints } from \"./smartEdgeData\";\nimport type { ControlPointData } from \"./ControlPoint\";\n\nexport interface WaypointRoutingOptions {\n  editable?: boolean;\n  checkpoints?: boolean;\n}\n\nexport interface EdgeEndpointCoordinates {\n  sourceX: number;\n  sourceY: number;\n  targetX: number;\n  targetY: number;\n  sourcePosition: Position;\n  targetPosition: Position;\n}\n\nexport const applyFloatingEdgeCoordinates = ({\n  floating,\n  sourceNodeId,\n  targetNodeId,\n  absoluteNodes,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n}: {\n  floating: boolean | undefined;\n  sourceNodeId: string;\n  targetNodeId: string;\n  absoluteNodes: Node[];\n  sourceX: number;\n  sourceY: number;\n  targetX: number;\n  targetY: number;\n  sourcePosition: Position;\n  targetPosition: Position;\n}): EdgeEndpointCoordinates => {\n  if (!floating || sourceNodeId === targetNodeId) {\n    return {\n      sourceX,\n      sourceY,\n      targetX,\n      targetY,\n      sourcePosition,\n      targetPosition,\n    };\n  }\n\n  const sourceNode = absoluteNodes.find((node) => node.id === sourceNodeId);\n  const targetNode = absoluteNodes.find((node) => node.id === targetNodeId);\n\n  if (!sourceNode?.measured || !targetNode?.measured) {\n    return {\n      sourceX,\n      sourceY,\n      targetX,\n      targetY,\n      sourcePosition,\n      targetPosition,\n    };\n  }\n\n  const { sx, sy, tx, ty, sourcePos, targetPos } = getFloatingEdgeParams(\n    sourceNode,\n    targetNode,\n  );\n\n  return {\n    sourceX: sx,\n    sourceY: sy,\n    targetX: tx,\n    targetY: ty,\n    sourcePosition: sourcePos,\n    targetPosition: targetPos,\n  };\n};\n\nexport const resolveWaypointParams = (\n  options: WaypointRoutingOptions,\n  data: unknown,\n  activePoints: ControlPointData[],\n): { x: number; y: number }[] => {\n  if (options.editable) {\n    return activePoints.map((point) => ({ x: point.x, y: point.y }));\n  }\n\n  if (options.checkpoints) {\n    return readCheckpoints(data);\n  }\n\n  return [];\n};\n","import { BaseEdge, useReactFlow } from \"@xyflow/react\";\nimport { useCallback } from \"react\";\nimport type { ComponentType, CSSProperties } from \"react\";\nimport { buildControlPoints } from \"./controlPointGeometry\";\nimport { ControlPoint } from \"./ControlPoint\";\nimport { readControlPoints } from \"./smartEdgeData\";\nimport {\n  applyFloatingEdgeCoordinates,\n  resolveWaypointParams,\n} from \"./smartEdgeRouting\";\nimport type { ControlPointData, SetControlPoints } from \"./ControlPoint\";\nimport type { EdgeEndpointCoordinates } from \"./smartEdgeRouting\";\nimport type { SmartEdgeOptions } from \"./index\";\nimport type { SmartEdgeContextValue } from \"../routing/routingContext\";\nimport type { SmartEdgeRouteResult } from \"../routing/providerStore\";\nimport type { Edge, EdgeProps, Node, XYPosition } from \"@xyflow/react\";\n\n/** The routed variant of a published route (never `clear`). */\nexport type RoutedRoute = Extract<SmartEdgeRouteResult, { kind: \"routed\" }>;\n\nconst DEFAULT_CONTROL_POINT_COLOR = \"#3367d9\";\n\n/** Stable empty node list used when there is no provider. */\nconst EMPTY_NODES: Node[] = [];\n\n/** Prefers the hopped path when hops produced one, else the routed path. */\nconst resolvePath = (hopped: string | null, routed: string): string =>\n  hopped ?? routed;\n\n/**\n * A `RoutedRoute` stand-in so a clear step edge can still render through\n * `RoutedSmartEdge` when hops produced a path (native skeleton plus any\n * bridge arcs). `wasRouted` stays `true` because that field is required on\n * the routed variant; the provider store still records `kind: \"clear\"`.\n */\nexport const hoppedClearRoute = (\n  path: string,\n  endpoints: EdgeEndpointCoordinates,\n): RoutedRoute => ({\n  kind: \"routed\",\n  wasRouted: true,\n  svgPathString: path,\n  edgeCenterX: (endpoints.sourceX + endpoints.targetX) / 2,\n  edgeCenterY: (endpoints.sourceY + endpoints.targetY) / 2,\n  points: [],\n});\n\n/** Static dash used while a route is pending. Matches xyflow's animated\n * stroke visually without running `dashdraw` on every pending path. */\nconst PENDING_PLACEHOLDER_DASHARRAY = \"5 5\";\n\n/**\n * Native fallback used while a route is pending or drag-deferred. Drag\n * wraps the path so xyflow's `.react-flow__edge.animated path` CSS\n * (`dashdraw` 0.5s infinite) applies. Pending uses a static dash instead:\n * infinite SVG path animation on every edge starves the routing worker on\n * large graphs (the 750-node fixture's `batchLatencyMs` jumped from ~6.5s\n * to ~12s on 2-vCPU GitHub runners). Not a public export.\n */\nexport function PlaceholderFallback({\n  FallbackEdge,\n  edgeProps,\n  dragFallbackStyle,\n  animated,\n}: Readonly<{\n  FallbackEdge: ComponentType<EdgeProps>;\n  edgeProps: EdgeProps;\n  dragFallbackStyle: CSSProperties;\n  animated: boolean;\n}>) {\n  const style = {\n    ...(animated ? {} : { strokeDasharray: PENDING_PLACEHOLDER_DASHARRAY }),\n    ...edgeProps.style,\n    ...dragFallbackStyle,\n  };\n  return (\n    <g\n      className={animated ? \"react-flow__edge animated\" : undefined}\n      data-smart-edge-placeholder=\"\"\n    >\n      <FallbackEdge {...edgeProps} style={style} />\n    </g>\n  );\n}\n\n/**\n * Applies a control-point update to the one matching edge in a React Flow edge\n * list, rewriting only its `data.points` and leaving every other edge (and the\n * matched edge's other data) untouched.\n */\nexport const applyControlPointsUpdate = (\n  edges: Edge[],\n  edgeId: string,\n  update: (points: ControlPointData[]) => ControlPointData[],\n): Edge[] =>\n  edges.map((edge) => {\n    if (edge.id !== edgeId) return edge;\n    const points = readControlPoints(edge.data);\n    return { ...edge, data: { ...edge.data, points: update(points) } };\n  });\n\n/** The per-render geometry a smart edge derives from its props and the\n * provider node snapshot: floating-resolved endpoints, the active editable\n * waypoints, and the waypoint list registered for routing. */\nexport interface PreparedEdge {\n  endpoints: EdgeEndpointCoordinates;\n  activePoints: ControlPointData[];\n  waypoints: XYPosition[];\n}\n\n/**\n * Resolves an edge's routable geometry: floating endpoint override (using the\n * provider's absolute node snapshot, empty without a provider) and the\n * editable/checkpoint waypoints. Pure — no hooks — so the component stays a\n * thin orchestrator.\n */\nexport const prepareEdge = (\n  context: SmartEdgeContextValue | null,\n  options: SmartEdgeOptions,\n  edgeProps: EdgeProps,\n): PreparedEdge => {\n  const absoluteNodes = context ? context.getNodesSnapshot() : EMPTY_NODES;\n  const endpoints = applyFloatingEdgeCoordinates({\n    floating: context ? options.floating : false,\n    sourceNodeId: edgeProps.source,\n    targetNodeId: edgeProps.target,\n    absoluteNodes,\n    sourceX: edgeProps.sourceX,\n    sourceY: edgeProps.sourceY,\n    targetX: edgeProps.targetX,\n    targetY: edgeProps.targetY,\n    sourcePosition: edgeProps.sourcePosition,\n    targetPosition: edgeProps.targetPosition,\n  });\n  const activePoints = options.editable\n    ? readControlPoints(edgeProps.data)\n    : [];\n  const waypoints = resolveWaypointParams(\n    options,\n    edgeProps.data,\n    activePoints,\n  );\n\n  return { endpoints, activePoints, waypoints };\n};\n\nexport interface RoutedSmartEdgeProps {\n  edgeProps: EdgeProps;\n  route: RoutedRoute;\n  endpoints: EdgeEndpointCoordinates;\n  hoppedPathString: string | null;\n  options: SmartEdgeOptions;\n  activePoints: ControlPointData[];\n  showControlPoints: boolean;\n}\n\n/**\n * Renders a routed smart edge: the `BaseEdge` with the routed (or hopped) path,\n * plus the draggable control points overlay for editable edges when they are\n * revealed. Owns the `edge.data.points` persistence (via `setEdges`) so its\n * memoized updater only reads this edge's props, never the parent's.\n */\nexport function RoutedSmartEdge({\n  edgeProps,\n  route,\n  endpoints,\n  hoppedPathString,\n  options,\n  activePoints,\n  showControlPoints,\n}: Readonly<RoutedSmartEdgeProps>) {\n  const { id } = edgeProps;\n  const { setEdges } = useReactFlow();\n\n  const setControlPoints = useCallback<SetControlPoints>(\n    (update) => {\n      setEdges((edges) => applyControlPointsUpdate(edges, id, update));\n    },\n    [id, setEdges],\n  );\n\n  const baseEdge = (\n    <BaseEdge\n      path={resolvePath(hoppedPathString, route.svgPathString)}\n      labelX={route.edgeCenterX}\n      labelY={route.edgeCenterY}\n      label={edgeProps.label}\n      labelStyle={edgeProps.labelStyle}\n      labelShowBg={edgeProps.labelShowBg}\n      labelBgStyle={edgeProps.labelBgStyle}\n      labelBgPadding={edgeProps.labelBgPadding}\n      labelBgBorderRadius={edgeProps.labelBgBorderRadius}\n      style={edgeProps.style}\n      markerStart={edgeProps.markerStart}\n      markerEnd={edgeProps.markerEnd}\n      interactionWidth={edgeProps.interactionWidth}\n    />\n  );\n\n  if (!options.editable) {\n    return baseEdge;\n  }\n\n  const controlPoints = buildControlPoints(\n    { x: endpoints.sourceX, y: endpoints.sourceY },\n    { x: endpoints.targetX, y: endpoints.targetY },\n    activePoints,\n    route.points,\n  );\n  const color = options.controlPointColor ?? DEFAULT_CONTROL_POINT_COLOR;\n\n  return (\n    <>\n      {baseEdge}\n      {showControlPoints &&\n        controlPoints.map((point, index) => (\n          <ControlPoint\n            key={point.id}\n            index={index}\n            x={point.x}\n            y={point.y}\n            id={point.id}\n            active={point.active}\n            color={color}\n            setControlPoints={setControlPoints}\n          />\n        ))}\n    </>\n  );\n}\n","import { BezierEdge } from \"@xyflow/react\";\nimport { useContext } from \"react\";\nimport type { ComponentType } from \"react\";\nimport { SmartEdgeRoutingContext } from \"../routing/routingContext\";\nimport { useSmartEdgePath } from \"../routing/useSmartEdgePath\";\nimport type { ControlPointData } from \"./ControlPoint\";\nimport { warnOnceNoProvider } from \"./noProviderWarning\";\nimport { useEndpointNodesSelected } from \"./smartEdgeSelection\";\nimport { useHoppedPath } from \"./smartEdgeHops\";\nimport {\n  prepareEdge,\n  RoutedSmartEdge,\n  hoppedClearRoute,\n  PlaceholderFallback,\n} from \"./renderDecision\";\nimport type { HopOptions } from \"./smartEdgeHops\";\nimport type { SmartEdgeBatchItemOptions } from \"../routing/routeBatch\";\nimport type { SmartEdgePreset } from \"../smartEdgePresets\";\nimport type { GetSmartEdgeOptions } from \"../getSmartEdge\";\nimport type { EdgeProps, Edge, XYPosition } from \"@xyflow/react\";\n\nexport type { HopOptions } from \"./smartEdgeHops\";\n\nexport type SmartEdgeOptions = GetSmartEdgeOptions & {\n  fallback?: ComponentType<EdgeProps<Edge>>;\n  /**\n   * When enabled, the edge's source/target connection points are computed\n   * dynamically from node geometry (the nearest border facing the other node)\n   * instead of using the fixed handle positions, mirroring React Flow's\n   * floating edges. See https://github.com/tisoap/react-flow-smart-edge/issues/13\n   */\n  floating?: boolean;\n  /**\n   * When enabled, the edge renders draggable control points (waypoints) that\n   * the path is routed through, still avoiding nodes between each waypoint.\n   * Waypoints are read from and persisted to `edge.data.points`; the consumer\n   * owns persistence via React Flow's edge state. Click an inactive point to\n   * add a waypoint, drag to move, right-click or press Delete to remove.\n   * See https://github.com/tisoap/react-flow-smart-edge/issues/36\n   */\n  editable?: boolean;\n  /**\n   * When enabled, the edge is routed through fixed waypoints read from\n   * `edge.data.checkpoints` without rendering draggable control points. Each\n   * segment still uses pathfinding. Ignored when `editable` is also `true`.\n   */\n  checkpoints?: boolean;\n  /**\n   * Color used to render the editable control points. Defaults to a blue.\n   */\n  controlPointColor?: string;\n  /**\n   * Corner radius for the `smoothstep` preset. Serializable, so unlike a custom\n   * `drawEdge` it survives the provider's routing pipeline to the worker.\n   */\n  borderRadius?: number;\n  /**\n   * Circuit-style \"hops\": where this edge crosses another smart edge of the\n   * same `type` rendered beneath it, draw a small bridge arc over the crossing\n   * so intersecting wires read cleanly (like a schematic). Only the step and\n   * smooth-step variants are orthogonal enough for this; it is ignored on\n   * editable/checkpoint edges. Pass `true` for defaults or a {@link HopOptions}\n   * object to tune the arc radius, corner rounding, and tolerance.\n   * See https://github.com/tisoap/react-flow-smart-edge/issues/61\n   */\n  hops?: boolean | HopOptions;\n};\n\n/**\n * The `edge.data` shape consumed by editable smart edges: the ordered list of\n * active waypoints the edge is routed through.\n */\nexport interface SmartEditableEdgeData extends Record<string, unknown> {\n  points?: ControlPointData[];\n}\n\n/**\n * The `edge.data` shape consumed by checkpoint smart edges: the ordered list\n * of fixed graph-coordinate points the edge is routed through.\n */\nexport interface SmartCheckpointEdgeData extends Record<string, unknown> {\n  checkpoints?: XYPosition[];\n}\n\nexport interface SmartEdgeProps<\n  EdgeType extends Edge = Edge,\n> extends EdgeProps<EdgeType> {\n  /** Which preset (`bezier`, `step`, …) resolves this edge's draw/pathfinding\n   * on the routing side. Set for you by `createSmartEdge`. */\n  preset: SmartEdgePreset;\n  options: SmartEdgeOptions;\n}\n\n/** Extracts only the structured-clone-safe options the provider pipeline can\n * forward to the routing worker. Function options (`drawEdge`/`generatePath`)\n * cannot cross that boundary and are resolved from `preset` instead. */\nconst toBatchItemOptions = (\n  options: SmartEdgeOptions,\n): SmartEdgeBatchItemOptions => ({\n  gridRatio: options.gridRatio,\n  nodePadding: options.nodePadding,\n  avoidAreas: options.avoidAreas,\n  borderRadius: options.borderRadius,\n});\n\n/**\n * Routes an edge through the nearest `SmartEdgeProvider` and renders its path,\n * falling back to the preset's native edge whenever routing is unavailable\n * (no provider), pending, deferred (an endpoint is dragging and\n * `routeWhileDragging` is off), or the corridor is clear (unless hops are\n * enabled on a step/smooth-step edge, in which case the native skeleton is\n * still drawn so crossings can bridge).\n *\n * Requires a `SmartEdgeProvider` ancestor to route; without one it warns once\n * (in development) and renders the fallback edge. Custom `drawEdge` /\n * `generatePath` functions passed via `options` are ignored here — the\n * provider resolves those from `preset` — and remain available only through\n * the synchronous `getSmartEdge` API.\n */\nexport function SmartEdge<EdgeType extends Edge = Edge>({\n  preset,\n  options,\n  ...edgeProps\n}: Readonly<SmartEdgeProps<EdgeType>>) {\n  const { id, source, target } = edgeProps;\n  const context = useContext(SmartEdgeRoutingContext);\n\n  const { endpoints, activePoints, waypoints } = prepareEdge(\n    context,\n    options,\n    edgeProps,\n  );\n\n  const { route, isDragging } = useSmartEdgePath({\n    id,\n    source,\n    target,\n    sourceX: endpoints.sourceX,\n    sourceY: endpoints.sourceY,\n    targetX: endpoints.targetX,\n    targetY: endpoints.targetY,\n    sourcePosition: endpoints.sourcePosition,\n    targetPosition: endpoints.targetPosition,\n    preset,\n    options: toBatchItemOptions(options),\n    waypoints,\n  });\n\n  const hoppedPathString = useHoppedPath({\n    edgeId: id,\n    sourceX: endpoints.sourceX,\n    sourceY: endpoints.sourceY,\n    targetX: endpoints.targetX,\n    targetY: endpoints.targetY,\n    hops: options.hops,\n    editable: options.editable,\n    checkpoints: options.checkpoints,\n  });\n\n  const endpointsSelected = useEndpointNodesSelected(\n    context?.store,\n    source,\n    target,\n  );\n\n  const FallbackEdge = options.fallback ?? BezierEdge;\n\n  if (context === null) {\n    warnOnceNoProvider();\n    return <FallbackEdge {...edgeProps} />;\n  }\n\n  const dragPlaceholder = isDragging && !context.options.routeWhileDragging;\n\n  if (dragPlaceholder || route === null) {\n    return (\n      <PlaceholderFallback\n        FallbackEdge={FallbackEdge}\n        edgeProps={edgeProps}\n        dragFallbackStyle={context.options.dragFallbackStyle}\n        animated={dragPlaceholder}\n      />\n    );\n  }\n\n  if (route.kind === \"clear\") {\n    if (hoppedPathString === null) {\n      return <FallbackEdge {...edgeProps} />;\n    }\n\n    return (\n      <RoutedSmartEdge\n        edgeProps={edgeProps}\n        route={hoppedClearRoute(hoppedPathString, endpoints)}\n        endpoints={endpoints}\n        hoppedPathString={hoppedPathString}\n        options={options}\n        activePoints={activePoints}\n        showControlPoints={Boolean(edgeProps.selected) || endpointsSelected}\n      />\n    );\n  }\n\n  return (\n    <RoutedSmartEdge\n      edgeProps={edgeProps}\n      route={route}\n      endpoints={endpoints}\n      hoppedPathString={hoppedPathString}\n      options={options}\n      activePoints={activePoints}\n      showControlPoints={Boolean(edgeProps.selected) || endpointsSelected}\n    />\n  );\n}\n\nexport type SmartEdgeFunction = typeof SmartEdge;\n","import {\n  BezierEdge,\n  StraightEdge,\n  StepEdge,\n  SmoothStepEdge,\n  SimpleBezierEdge,\n} from \"@xyflow/react\";\nimport {\n  svgDrawSmoothLinePath,\n  svgDrawStraightLinePath,\n  svgDrawSmoothStepLinePath,\n  svgDrawSimpleBezierLinePath,\n  pathfindingAStarDiagonal,\n  pathfindingJumpPointNoDiagonal,\n} from \"./functions\";\nimport type { SmartEdgeOptions } from \"./SmartEdge\";\n\nexport const smartEdgePresets = {\n  bezier: {\n    drawEdge: svgDrawSmoothLinePath,\n    generatePath: pathfindingAStarDiagonal,\n    fallback: BezierEdge,\n  },\n  straight: {\n    drawEdge: svgDrawStraightLinePath,\n    generatePath: pathfindingAStarDiagonal,\n    fallback: StraightEdge,\n  },\n  step: {\n    drawEdge: svgDrawStraightLinePath,\n    generatePath: pathfindingJumpPointNoDiagonal,\n    fallback: StepEdge,\n  },\n  smoothstep: {\n    drawEdge: svgDrawSmoothStepLinePath(),\n    generatePath: pathfindingJumpPointNoDiagonal,\n    fallback: SmoothStepEdge,\n  },\n  simplebezier: {\n    drawEdge: svgDrawSimpleBezierLinePath,\n    generatePath: pathfindingAStarDiagonal,\n    fallback: SimpleBezierEdge,\n  },\n} as const satisfies Record<string, SmartEdgeOptions>;\n\nexport type SmartEdgePreset = keyof typeof smartEdgePresets;\n","import { SmartEdge } from \"../SmartEdge\";\nimport { smartEdgePresets } from \"../smartEdgePresets\";\nimport type { SmartEdgeOptions } from \"../SmartEdge\";\nimport type { SmartEdgePreset } from \"../smartEdgePresets\";\nimport type { EdgeProps, EdgeTypes } from \"@xyflow/react\";\n\ntype SmartEdgeComponent = EdgeTypes[string];\n\nexport type ConfigureSmartEdgeOptions = Partial<SmartEdgeOptions>;\n\nexport function createSmartEdge(\n  preset: SmartEdgePreset,\n  options?: ConfigureSmartEdgeOptions,\n): SmartEdgeComponent {\n  const mergedOptions: SmartEdgeOptions = {\n    ...smartEdgePresets[preset],\n    ...options,\n  };\n\n  function ConfiguredSmartEdge(props: EdgeProps) {\n    return <SmartEdge {...props} preset={preset} options={mergedOptions} />;\n  }\n\n  ConfiguredSmartEdge.displayName = `SmartEdge(${preset})`;\n\n  return ConfiguredSmartEdge;\n}\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartBezierEdge = createSmartEdge(\"bezier\");\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartStraightEdge = createSmartEdge(\"straight\");\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartStepEdge = createSmartEdge(\"step\");\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartSmoothStepEdge = createSmartEdge(\"smoothstep\");\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartSimpleBezierEdge = createSmartEdge(\"simplebezier\");\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartFloatingEdge = createSmartEdge(\"bezier\", { floating: true });\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartEditableEdge = createSmartEdge(\"bezier\", { editable: true });\n","import { createSmartEdge } from \"../createSmartEdge\";\n\nexport const SmartCheckpointEdge = createSmartEdge(\"bezier\", {\n  checkpoints: true,\n});\n","import { getBoundingBoxes } from \"../functions\";\nimport { buildObstacleBoxes, rectIntersectsBox } from \"./obstacleIndex\";\nimport type {\n  GraphBoundingBox,\n  NodeBoundingBox,\n  PointInfo,\n} from \"../functions\";\nimport type { ObstacleBox } from \"./obstacleIndex\";\nimport type { Node, Rect } from \"@xyflow/react\";\n\n/**\n * Widening margins (in grid cells) tried before falling back to a full-graph\n * routing pass. `getSmartEdge` routes on a small sub-grid cropped to the\n * endpoints first (see `buildCorridorAttempt`), only paying for a bigger grid\n * when the narrower one has no path.\n */\nexport const CORRIDOR_MARGIN_CELLS = [8, 16, 32] as const;\n\n/** Axis-aligned rect (graph coordinates) covering both routing endpoints. */\ninterface EndpointRect {\n  xMin: number;\n  yMin: number;\n  xMax: number;\n  yMax: number;\n}\n\n/**\n * The endpoint bounding box inflated by `marginCells * gridRatio` plus\n * `nodePadding` on every side. Obstacles whose own padded rect intersects\n * this box are considered \"local\" to the corridor attempt.\n */\nconst buildCorridorRect = (\n  source: PointInfo,\n  target: PointInfo,\n  nodePadding: number,\n  gridRatio: number,\n  marginCells: number,\n): EndpointRect => {\n  const inflate = marginCells * gridRatio + nodePadding;\n\n  return {\n    xMin: Math.min(source.x, target.x) - inflate,\n    yMin: Math.min(source.y, target.y) - inflate,\n    xMax: Math.max(source.x, target.x) + inflate,\n    yMax: Math.max(source.y, target.y) + inflate,\n  };\n};\n\n/**\n * Indexes of the items whose corresponding obstacle box (same index) falls\n * inside `rect`. `boxes` must be parallel to the item array (i.e. built from\n * it by `buildObstacleBoxes`).\n */\nconst selectIntersectingIndexes = (\n  boxes: ObstacleBox[],\n  rect: EndpointRect,\n): number[] => {\n  const indexes: number[] = [];\n\n  boxes.forEach((box, index) => {\n    if (rectIntersectsBox(rect.xMin, rect.yMin, rect.xMax, rect.yMax, box)) {\n      indexes.push(index);\n    }\n  });\n\n  return indexes;\n};\n\n/** Bounding-box output for a single corridor (or full-graph) routing attempt. */\nexport interface CorridorAttempt {\n  graphBox: GraphBoundingBox;\n  nodeBoxes: NodeBoundingBox[];\n  avoidBoxes: NodeBoundingBox[];\n}\n\n/** Runs `getBoundingBoxes` over the nodes/areas at `nodeIndexes`/`areaIndexes`. */\nconst buildAttemptFromIndexes = (\n  nodes: Node[],\n  avoidAreas: Rect[],\n  nodeIndexes: number[],\n  areaIndexes: number[],\n  nodePadding: number,\n  gridRatio: number,\n  source: PointInfo,\n  target: PointInfo,\n): CorridorAttempt => {\n  const filteredNodes = nodeIndexes.map((index) => nodes[index]);\n  const filteredAreas = areaIndexes.map((index) => avoidAreas[index]);\n\n  const { graphBox, nodeBoxes, avoidBoxes } = getBoundingBoxes(\n    filteredNodes,\n    nodePadding,\n    gridRatio,\n    filteredAreas,\n    [\n      { x: source.x, y: source.y },\n      { x: target.x, y: target.y },\n    ],\n  );\n\n  return { graphBox, nodeBoxes, avoidBoxes };\n};\n\n/** Whether re-selecting against the latest graph box picked up no new\n * obstacles compared to the previous round, i.e. the selection has reached\n * a fixpoint. */\nconst isStableSelection = (\n  previousNodeCount: number,\n  previousAreaCount: number,\n  nextNodeIndexes: number[],\n  nextAreaIndexes: number[],\n): boolean =>\n  nextNodeIndexes.length === previousNodeCount &&\n  nextAreaIndexes.length === previousAreaCount;\n\n/**\n * Builds the bounding boxes for one rung of the corridor retry ladder: crop\n * `nodes` and `avoidAreas` down to whichever ones are local to the endpoints\n * at this margin, then hand the filtered sets to `getBoundingBoxes` so the\n * grid geometry, rounding, and border padding come out byte-identical to a\n * full-grid run over that same (smaller) obstacle set.\n *\n * A single corridor-rect filter pass is not enough: a large obstacle that\n * merely *touches* the corridor is included whole, which can stretch the\n * resulting graph box well past the corridor's own edge. A second obstacle\n * that didn't intersect the original (smaller) corridor rect can end up\n * sitting inside that stretched box — if it stayed excluded, the grid would\n * silently treat its space as walkable. So after computing the graph box, we\n * re-select every node/area against *that box* and rebuild; each round's\n * selection is always a superset of the previous one (a box built from a\n * given selection necessarily contains every member of it, so re-selecting\n * against that box re-includes them all), so the selected count only grows,\n * bounded by the total obstacle count — the loop always reaches a fixpoint\n * (no new obstacles picked up) within that many rounds, typically the very\n * first one.\n */\nexport const buildCorridorAttempt = (\n  source: PointInfo,\n  target: PointInfo,\n  nodes: Node[],\n  nodePadding: number,\n  gridRatio: number,\n  avoidAreas: Rect[],\n  marginCells: number,\n): CorridorAttempt => {\n  const corridor = buildCorridorRect(\n    source,\n    target,\n    nodePadding,\n    gridRatio,\n    marginCells,\n  );\n\n  const nodeObstacles = buildObstacleBoxes(nodes, nodePadding);\n  const areaObstacles = buildObstacleBoxes([], nodePadding, avoidAreas);\n\n  let nodeIndexes = selectIntersectingIndexes(nodeObstacles, corridor);\n  let areaIndexes = selectIntersectingIndexes(areaObstacles, corridor);\n\n  for (;;) {\n    const attempt = buildAttemptFromIndexes(\n      nodes,\n      avoidAreas,\n      nodeIndexes,\n      areaIndexes,\n      nodePadding,\n      gridRatio,\n      source,\n      target,\n    );\n\n    const nextNodeIndexes = selectIntersectingIndexes(\n      nodeObstacles,\n      attempt.graphBox,\n    );\n    const nextAreaIndexes = selectIntersectingIndexes(\n      areaObstacles,\n      attempt.graphBox,\n    );\n\n    if (\n      isStableSelection(\n        nodeIndexes.length,\n        areaIndexes.length,\n        nextNodeIndexes,\n        nextAreaIndexes,\n      )\n    ) {\n      return attempt;\n    }\n\n    nodeIndexes = nextNodeIndexes;\n    areaIndexes = nextAreaIndexes;\n  }\n};\n","import {\n  alignEndpoints,\n  createGrid,\n  getBoundingBoxes,\n  gridToGraphPoint,\n  pathfindingAStarDiagonal,\n  svgDrawSmoothLinePath,\n  toInteger,\n  NO_PATH_FOUND_ERROR,\n} from \"../functions\";\nimport type {\n  PointInfo,\n  PathFindingFunction,\n  DrawEdgeFunction,\n  GraphBoundingBox,\n  NodeBoundingBox,\n  Direction,\n} from \"../functions\";\nimport {\n  CORRIDOR_MARGIN_CELLS,\n  buildCorridorAttempt,\n} from \"../routing/corridor\";\nimport type { Node, EdgeProps, Rect } from \"@xyflow/react\";\n\n/**\n * `sourceX`/`sourceY`/`targetX`/`targetY` come straight from React Flow's\n * `EdgeProps`. `sourcePosition`/`targetPosition` are widened from `EdgeProps`'\n * nominal `Position` enum to the plain `Direction` string union, so callers\n * that only have a synthetic handle side (no real `Position`, e.g. waypoint\n * routing's `sideFacing`) can supply one too. Every real `Position` value is\n * still assignable here, so this is non-breaking for existing callers.\n */\nexport type EdgeParams = Pick<\n  EdgeProps,\n  \"sourceX\" | \"sourceY\" | \"targetX\" | \"targetY\"\n> & {\n  sourcePosition: Direction;\n  targetPosition: Direction;\n};\n\nexport interface GetSmartEdgeOptions {\n  gridRatio?: number;\n  nodePadding?: number;\n  drawEdge?: DrawEdgeFunction;\n  generatePath?: PathFindingFunction;\n  /**\n   * Extra rectangular areas (in graph coordinates) the path should route\n   * around, in addition to the nodes. Useful for keeping edges clear of edge\n   * labels or any other arbitrary regions. Each area uses the same\n   * `nodePadding` clearance as nodes.\n   */\n  avoidAreas?: Rect[];\n}\n\nexport type GetSmartEdgeParams<\n  NodeDataType extends Record<string, unknown> = Record<string, unknown>,\n> = EdgeParams & {\n  options?: GetSmartEdgeOptions;\n  nodes: Node<NodeDataType>[];\n};\n\nexport interface GetSmartEdgeReturn {\n  svgPathString: string;\n  edgeCenterX: number;\n  edgeCenterY: number;\n  /**\n   * The sequence of graph-coordinate points that make up the routed path\n   * (after endpoint alignment), excluding the source/target handle points.\n   * Used by waypoint routing to stitch multiple segments into a single edge.\n   */\n  points: number[][];\n  /**\n   * Always `true` for a result produced by this synchronous routing pass.\n   * The provider's `SmartEdgeRouteResult` uses `wasRouted: false` on its\n   * `\"clear\"` variant (native path, no A*). Pending provider routes are\n   * `null` from `useSmartEdgePath`, not a `wasRouted: false` placeholder.\n   */\n  wasRouted: true;\n}\n\n/** The output of one routing attempt: the grid geometry it ran on and the\n * raw grid-coordinate path `generatePath` returned. */\ninterface RouteAttempt {\n  graphBox: GraphBoundingBox;\n  fullPath: number[][];\n}\n\n/** Whether `error` is the \"no path found\" failure `generatePath` throws,\n * as opposed to some other error that should abort the retry ladder. */\nconst isNoPathFoundError = (error: unknown): boolean =>\n  error instanceof Error && error.message === NO_PATH_FOUND_ERROR;\n\n/** Builds a grid from a corridor (or full-graph) bounding-box result and\n * routes it, sharing the create-grid-then-generate-path shape across every\n * rung of the ladder and the final full-graph run. */\nconst routeOnBoxes = (\n  graphBox: GraphBoundingBox,\n  nodeBoxes: NodeBoundingBox[],\n  avoidBoxes: NodeBoundingBox[],\n  source: PointInfo,\n  target: PointInfo,\n  gridRatio: number,\n  generatePath: PathFindingFunction,\n): RouteAttempt => {\n  const { grid, start, end } = createGrid(\n    graphBox,\n    [...nodeBoxes, ...avoidBoxes],\n    source,\n    target,\n    gridRatio,\n  );\n\n  return { graphBox, fullPath: generatePath(grid, start, end) };\n};\n\n/** The final rung of the ladder: every node and avoid area, exactly like the\n * pre-corridor behavior (including `extraPoints` endpoint coverage). */\nconst routeFullGraph = (\n  nodes: Node[],\n  nodePadding: number,\n  gridRatio: number,\n  avoidAreas: Rect[],\n  source: PointInfo,\n  target: PointInfo,\n  generatePath: PathFindingFunction,\n): RouteAttempt => {\n  const { graphBox, nodeBoxes, avoidBoxes } = getBoundingBoxes(\n    nodes,\n    nodePadding,\n    gridRatio,\n    avoidAreas,\n    [\n      { x: source.x, y: source.y },\n      { x: target.x, y: target.y },\n    ],\n  );\n\n  return routeOnBoxes(\n    graphBox,\n    nodeBoxes,\n    avoidBoxes,\n    source,\n    target,\n    gridRatio,\n    generatePath,\n  );\n};\n\n/**\n * Routes on a sub-grid cropped around the endpoints first, widening the\n * margin at each rung of `CORRIDOR_MARGIN_CELLS` and finally falling back to\n * the full graph, so routing cost stays proportional to the obstacle density\n * local to the edge instead of the whole canvas. A rung is retried only when\n * `generatePath` reports `NO_PATH_FOUND_ERROR`; any other thrown error aborts\n * the ladder immediately so the outer `try`/`catch` can surface it.\n */\nconst routeWithCorridorLadder = (\n  nodes: Node[],\n  nodePadding: number,\n  gridRatio: number,\n  avoidAreas: Rect[],\n  source: PointInfo,\n  target: PointInfo,\n  generatePath: PathFindingFunction,\n): RouteAttempt => {\n  for (const marginCells of CORRIDOR_MARGIN_CELLS) {\n    const { graphBox, nodeBoxes, avoidBoxes } = buildCorridorAttempt(\n      source,\n      target,\n      nodes,\n      nodePadding,\n      gridRatio,\n      avoidAreas,\n      marginCells,\n    );\n\n    try {\n      return routeOnBoxes(\n        graphBox,\n        nodeBoxes,\n        avoidBoxes,\n        source,\n        target,\n        gridRatio,\n        generatePath,\n      );\n    } catch (error) {\n      if (!isNoPathFoundError(error)) {\n        throw error;\n      }\n    }\n  }\n\n  return routeFullGraph(\n    nodes,\n    nodePadding,\n    gridRatio,\n    avoidAreas,\n    source,\n    target,\n    generatePath,\n  );\n};\n\nexport const getSmartEdge = <\n  NodeDataType extends Record<string, unknown> = Record<string, unknown>,\n>({\n  options = {},\n  nodes,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n}: GetSmartEdgeParams<NodeDataType>): GetSmartEdgeReturn | Error => {\n  try {\n    const {\n      drawEdge = svgDrawSmoothLinePath,\n      generatePath = pathfindingAStarDiagonal,\n      avoidAreas = [],\n    } = options;\n\n    let { gridRatio = 10, nodePadding = 10 } = options;\n    gridRatio = toInteger(gridRatio);\n    nodePadding = toInteger(nodePadding);\n\n    const source: PointInfo = {\n      x: sourceX,\n      y: sourceY,\n      position: sourcePosition,\n    };\n\n    const target: PointInfo = {\n      x: targetX,\n      y: targetY,\n      position: targetPosition,\n    };\n\n    // Route on a small sub-grid cropped around the endpoints first, widening\n    // the margin at each rung and finally falling back to the full graph, so\n    // routing cost stays proportional to the obstacles local to this edge\n    // instead of the whole canvas (see `routeWithCorridorLadder`).\n    const { graphBox, fullPath } = routeWithCorridorLadder(\n      nodes,\n      nodePadding,\n      gridRatio,\n      avoidAreas,\n      source,\n      target,\n      generatePath,\n    );\n\n    // Here we convert the grid path to a sequence of graph coordinates.\n    const graphPath = fullPath.map((gridPoint) => {\n      const [posX, posY] = gridPoint;\n      const graphPoint = gridToGraphPoint(\n        { x: posX, y: posY },\n        graphBox.xMin,\n        graphBox.yMin,\n        gridRatio,\n      );\n      return [graphPoint.x, graphPoint.y];\n    });\n\n    // Insert orthogonal alignment waypoints between the actual handle\n    // coordinates and the grid-snapped path, so edges leave/enter their nodes\n    // perpendicular to the handle instead of taking a small diagonal toward\n    // the first/last grid cell.\n    const alignedPath = alignEndpoints(source, target, graphPath);\n\n    // Finally, we can use the graph path to draw the edge\n    const svgPathString = drawEdge(source, target, alignedPath);\n\n    // Compute the edge's middle point using the full path, so users can use\n    // it to position their custom labels\n    const index = Math.floor(fullPath.length / 2);\n    const middlePoint = fullPath[index];\n    const [middleX, middleY] = middlePoint;\n    const { x: edgeCenterX, y: edgeCenterY } = gridToGraphPoint(\n      { x: middleX, y: middleY },\n      graphBox.xMin,\n      graphBox.yMin,\n      gridRatio,\n    );\n\n    return {\n      svgPathString,\n      edgeCenterX,\n      edgeCenterY,\n      points: alignedPath,\n      wasRouted: true,\n    };\n  } catch (error) {\n    /* v8 ignore else -- generatePath throws Error; unknown values are not part of the contract */\n    if (error instanceof Error) {\n      return error;\n    } else {\n      return new Error(`Unknown error: ${String(error)}`);\n    }\n  }\n};\n\nexport type GetSmartEdgeFunction = typeof getSmartEdge;\n","import { useNodes } from \"@xyflow/react\";\nimport { getSmartEdge } from \"../getSmartEdge\";\nimport { smartEdgePresets } from \"../smartEdgePresets\";\nimport {\n  getAbsoluteNodes,\n  getFloatingEdgeParams,\n  svgDrawSmoothLinePath,\n} from \"../functions\";\nimport type {\n  ConnectionLineComponentProps,\n  Node,\n  XYPosition,\n} from \"@xyflow/react\";\nimport type { GetSmartEdgeOptions } from \"../getSmartEdge\";\n\nexport type SmartFloatingConnectionLineProps = ConnectionLineComponentProps & {\n  options?: GetSmartEdgeOptions;\n};\n\n/**\n * A connection line component (for React Flow's `connectionLineComponent` prop)\n * that previews a smart, obstacle-avoiding floating edge while the user drags a\n * new connection. The source point is derived from the origin node's geometry\n * (floating), the target follows the cursor, and the line is routed with the\n * same pathfinding as {@link SmartFloatingEdge}.\n *\n * Falls back to a straight preview line when no valid smart path can be found.\n */\nexport function SmartFloatingConnectionLine({\n  fromNode,\n  toX,\n  toY,\n  toPosition,\n  connectionLineStyle,\n  options,\n}: Readonly<SmartFloatingConnectionLineProps>) {\n  const nodes = useNodes();\n  const absoluteNodes = getAbsoluteNodes(nodes);\n\n  const cursorPosition: XYPosition = { x: toX, y: toY };\n  const cursorNode: Node = {\n    id: \"smart-floating-connection-target\",\n    position: cursorPosition,\n    data: {},\n    measured: { width: 1, height: 1 },\n  };\n\n  const sourceNode: Node = {\n    id: fromNode.id,\n    position: fromNode.internals.positionAbsolute,\n    data: {},\n    measured: fromNode.measured,\n  };\n\n  const { sx, sy, sourcePos } = getFloatingEdgeParams(sourceNode, cursorNode);\n\n  const mergedOptions: GetSmartEdgeOptions = {\n    drawEdge: smartEdgePresets.bezier.drawEdge,\n    generatePath: smartEdgePresets.bezier.generatePath,\n    ...options,\n  };\n\n  const smartResponse = getSmartEdge({\n    sourcePosition: sourcePos,\n    targetPosition: toPosition,\n    sourceX: sx,\n    sourceY: sy,\n    targetX: toX,\n    targetY: toY,\n    nodes: absoluteNodes,\n    options: mergedOptions,\n  });\n\n  const svgPathString =\n    smartResponse instanceof Error\n      ? svgDrawSmoothLinePath({ x: sx, y: sy }, cursorPosition, [])\n      : smartResponse.svgPathString;\n\n  return (\n    <g>\n      <path\n        fill=\"none\"\n        className=\"react-flow__connection-path\"\n        style={connectionLineStyle}\n        d={svgPathString}\n      />\n      <circle cx={toX} cy={toY} r={3} stroke=\"black\" strokeWidth={1.5} />\n    </g>\n  );\n}\n\nexport type SmartFloatingConnectionLineFunction =\n  typeof SmartFloatingConnectionLine;\n","import { getSmartEdge } from \".\";\nimport { svgDrawSmoothLinePath, round, toInteger } from \"../functions\";\nimport type { XYPosition, Node } from \"@xyflow/react\";\nimport type { Direction } from \"../functions\";\nimport type {\n  GetSmartEdgeOptions,\n  GetSmartEdgeParams,\n  GetSmartEdgeReturn,\n} from \".\";\n\nexport type GetSmartEdgeWaypointsParams<\n  NodeDataType extends Record<string, unknown> = Record<string, unknown>,\n> = GetSmartEdgeParams<NodeDataType> & {\n  /**\n   * Intermediate points (in graph coordinates) the edge must pass through, in\n   * order from source to target. With no waypoints this behaves exactly like\n   * {@link getSmartEdge}.\n   */\n  waypoints: XYPosition[];\n};\n\n/**\n * Returns the side of `from` (top/right/bottom/left) that faces `to`, used as\n * the synthetic handle position for a free waypoint endpoint so each routed\n * segment leaves/enters the waypoint pointing along the path.\n *\n * Returns plain string literals rather than `Position` enum values: `Position`\n * is only a type import in this module (see the module-level note on worker\n * reachability), and `Position`'s values are exactly these strings, so real\n * `Position` values widen into `Direction` at every call site that mixes them\n * with this function's output (e.g. {@link routeChainSegments}).\n */\nconst sideFacing = (point: XYPosition, toward: XYPosition): Direction => {\n  const deltaX = toward.x - point.x;\n  const deltaY = toward.y - point.y;\n  if (Math.abs(deltaX) >= Math.abs(deltaY)) {\n    return deltaX >= 0 ? \"right\" : \"left\";\n  }\n  return deltaY >= 0 ? \"bottom\" : \"top\";\n};\n\ninterface StitchPoint {\n  x: number;\n  y: number;\n  /** Whether this point is a user waypoint (must lie exactly on the path). */\n  waypoint: boolean;\n}\n\n/**\n * Collapses consecutive duplicate coordinates (segment join artifacts),\n * preserving the waypoint flag if either side of the duplicate was a waypoint.\n */\nconst dedupeStitchPoints = (raw: StitchPoint[]): StitchPoint[] => {\n  const result: StitchPoint[] = [];\n  for (const point of raw) {\n    const lastIndex = result.length - 1;\n    if (\n      lastIndex >= 0 &&\n      result[lastIndex].x === point.x &&\n      result[lastIndex].y === point.y\n    ) {\n      result[lastIndex].waypoint = result[lastIndex].waypoint || point.waypoint;\n    } else {\n      result.push({ ...point });\n    }\n  }\n  return result;\n};\n\n/**\n * Routes each consecutive pair in the endpoint chain with {@link getSmartEdge}\n * and collects the stitched points. Free waypoint endpoints get a synthetic\n * handle position derived from the travel direction, and the waypoint\n * coordinates themselves are flagged so they can be pinned onto the path.\n */\nconst routeChainSegments = <\n  NodeDataType extends Record<string, unknown> = Record<string, unknown>,\n>(\n  chain: XYPosition[],\n  sourcePosition: Direction,\n  targetPosition: Direction,\n  nodes: Node<NodeDataType>[],\n  options: GetSmartEdgeOptions,\n): StitchPoint[] => {\n  const lastIndex = chain.length - 1;\n  const raw: StitchPoint[] = [];\n\n  for (let index = 0; index < lastIndex; index++) {\n    const from = chain[index];\n    const targetPoint = chain[index + 1];\n\n    const fromPosition =\n      index === 0 ? sourcePosition : sideFacing(from, targetPoint);\n    const toPosition =\n      index + 1 === lastIndex ? targetPosition : sideFacing(targetPoint, from);\n\n    const segment = getSmartEdge<NodeDataType>({\n      sourceX: from.x,\n      sourceY: from.y,\n      sourcePosition: fromPosition,\n      targetX: targetPoint.x,\n      targetY: targetPoint.y,\n      targetPosition: toPosition,\n      nodes,\n      options,\n    });\n\n    // On a per-segment failure we omit its routed points; the single\n    // `drawEdge` pass then connects the surrounding endpoints in the edge's own\n    // line style (bezier/straight/step) instead of dropping the edge.\n    if (!(segment instanceof Error)) {\n      for (const [posX, posY] of segment.points) {\n        raw.push({ x: posX, y: posY, waypoint: false });\n      }\n    }\n\n    // Pin the path to the actual waypoint coordinate (the control point\n    // handle). The final target point is the target handle, added by `drawEdge`.\n    if (index + 1 < lastIndex) {\n      raw.push({ x: targetPoint.x, y: targetPoint.y, waypoint: true });\n    }\n  }\n\n  return raw;\n};\n\n/** Fraction of the nearest-neighbor distance used for waypoint tangents. */\nconst STRADDLE_FRACTION = 0.5;\n\n/**\n * The logical routed path: every stitch point once, with waypoints kept at\n * their exact coordinates. Used for the edge center and for placing control\n * points (which match waypoints by exact coordinate).\n */\nconst toLogicalPoints = (points: StitchPoint[]): number[][] =>\n  points.map((point) => [point.x, point.y]);\n\n/**\n * Returns a pair of points straddling a waypoint along the local path tangent,\n * whose midpoint is exactly the waypoint.\n *\n * The smooth (quadratic) draw function passes through the midpoint of each\n * consecutive control pair, so a lone waypoint control would only pull the\n * curve toward it, and a duplicated waypoint would stop the curve dead (a\n * zero-velocity cusp) — both look like a kink rather than a single flow.\n * Emitting two points around the waypoint instead makes the curve pass\n * smoothly through it, tangent to the routed path. The pair is bounded by the\n * neighbor spacing so it never overshoots into a loop, and stays collinear\n * through the waypoint for the straight/step draw functions.\n */\nconst straddleWaypoint = (\n  waypoint: { x: number; y: number },\n  before: { x: number; y: number },\n  after: { x: number; y: number },\n): number[][] => {\n  const dirX = after.x - before.x;\n  const dirY = after.y - before.y;\n  const dirLength = Math.hypot(dirX, dirY);\n\n  if (dirLength === 0) {\n    return [[waypoint.x, waypoint.y]];\n  }\n\n  const unitX = dirX / dirLength;\n  const unitY = dirY / dirLength;\n\n  const distBefore = Math.hypot(waypoint.x - before.x, waypoint.y - before.y);\n  const distAfter = Math.hypot(waypoint.x - after.x, waypoint.y - after.y);\n  const length = Math.min(distBefore, distAfter) * STRADDLE_FRACTION;\n\n  return [\n    [waypoint.x - unitX * length, waypoint.y - unitY * length],\n    [waypoint.x + unitX * length, waypoint.y + unitY * length],\n  ];\n};\n\n/**\n * The points fed to `drawEdge`: identical to the logical path, except each\n * waypoint is replaced by a straddling pair so the rendered curve flows\n * smoothly through it (see {@link straddleWaypoint}).\n */\nconst toDrawPoints = (\n  points: StitchPoint[],\n  source: XYPosition,\n  target: XYPosition,\n): number[][] => {\n  const result: number[][] = [];\n\n  for (let index = 0; index < points.length; index++) {\n    const point = points[index];\n\n    if (!point.waypoint) {\n      result.push([point.x, point.y]);\n      continue;\n    }\n\n    const before = index > 0 ? points[index - 1] : source;\n    const after = index < points.length - 1 ? points[index + 1] : target;\n    result.push(...straddleWaypoint(point, before, after));\n  }\n\n  return result;\n};\n\n/**\n * Routes a smart edge that passes through a set of user-defined waypoints.\n *\n * Each consecutive pair in `[source, ...waypoints, target]` is routed\n * independently with {@link getSmartEdge} (so every segment still avoids\n * nodes), then the resulting point lists are stitched together and drawn as a\n * single continuous SVG path. Free waypoint endpoints are given a synthetic\n * handle position derived from the travel direction.\n *\n * If an individual segment fails to route (for example a waypoint dropped\n * inside a node), that segment degrades to a straight line between its\n * endpoints instead of blanking the whole edge.\n */\nexport const getSmartEdgeWaypoints = <\n  NodeDataType extends Record<string, unknown> = Record<string, unknown>,\n>(\n  params: GetSmartEdgeWaypointsParams<NodeDataType>,\n): GetSmartEdgeReturn | Error => {\n  const {\n    waypoints,\n    options = {},\n    nodes,\n    sourceX,\n    sourceY,\n    targetX,\n    targetY,\n    sourcePosition,\n    targetPosition,\n  } = params;\n\n  // No waypoints: identical to a plain smart edge.\n  if (waypoints.length === 0) {\n    return getSmartEdge(params);\n  }\n\n  try {\n    const { drawEdge = svgDrawSmoothLinePath } = options;\n    const gridRatio = Math.max(toInteger(options.gridRatio ?? 10), 1);\n\n    const source: XYPosition = { x: sourceX, y: sourceY };\n    const target: XYPosition = { x: targetX, y: targetY };\n\n    // Snap each waypoint to the nearest path-finding grid coordinate. The\n    // routed path is always grid-aligned, so routing/drawing through the exact\n    // (off-grid) drag position would force a sub-grid jog where the path meets\n    // the waypoint (a visible \"bump\"). Snapping keeps the path on the grid; the\n    // draggable control point itself still renders at the exact drag position.\n    const snappedWaypoints = waypoints.map((point) => ({\n      x: round(point.x, gridRatio),\n      y: round(point.y, gridRatio),\n    }));\n    const chain: XYPosition[] = [source, ...snappedWaypoints, target];\n\n    const segmentOptions = options;\n\n    const raw = routeChainSegments(\n      chain,\n      sourcePosition,\n      targetPosition,\n      nodes,\n      segmentOptions,\n    );\n\n    const stitched = dedupeStitchPoints(raw);\n    const points = toLogicalPoints(stitched);\n    const svgPathString = drawEdge(\n      { x: sourceX, y: sourceY, position: sourcePosition },\n      { x: targetX, y: targetY, position: targetPosition },\n      toDrawPoints(stitched, source, target),\n    );\n\n    const fullPath = [[source.x, source.y], ...points, [target.x, target.y]];\n    const middle = fullPath[Math.floor(fullPath.length / 2)];\n    const [edgeCenterX, edgeCenterY] = middle;\n\n    return { svgPathString, edgeCenterX, edgeCenterY, points, wasRouted: true };\n  } catch (error) {\n    /* v8 ignore else -- drawEdge throws Error; unknown values are not part of the contract */\n    if (error instanceof Error) {\n      return error;\n    } else {\n      return new Error(`Unknown error: ${String(error)}`);\n    }\n  }\n};\n\nexport type GetSmartEdgeWaypointsFunction = typeof getSmartEdgeWaypoints;\n","import type { GetSmartEdgeReturn } from \"../getSmartEdge\";\n\n/**\n * One edge's routing outcome as tracked by the provider store. `\"routed\"`\n * carries a full `getSmartEdge` result; `\"clear\"` tells the edge component\n * to render its native (non-smart) path locally instead, e.g. while an\n * async route is still pending or after routing failed.\n */\nexport type SmartEdgeRouteResult =\n  | ({ kind: \"routed\"; wasRouted: true } & GetSmartEdgeReturn)\n  | { kind: \"clear\"; wasRouted: false };\n\n/**\n * Keyed external store for smart-edge routes plus graph-wide node\n * drag/selection state, shaped for `useSyncExternalStore`: every getter\n * returns a stable reference until the slice it reads actually changes, so\n * React can skip re-rendering subscribers whose data was untouched.\n */\nexport interface SmartEdgeStore {\n  /** Subscribes to changes for one edge's route. Returns an unsubscribe\n   * function. */\n  subscribeEdge(edgeId: string, listener: () => void): () => void;\n  /** The edge's current route, or `undefined` if none has been set (or it\n   * was removed). Stable by reference until that edge's route changes. */\n  getRoute(edgeId: string): SmartEdgeRouteResult | undefined;\n  /** Applies a batch of route updates, notifying only the listeners of ids\n   * whose stored value reference actually changed. */\n  mergeRoutes(patch: Record<string, SmartEdgeRouteResult>): void;\n  /** Deletes the stored route for each id and notifies its listeners (who\n   * will then read `undefined` from `getRoute`). */\n  removeRoutes(edgeIds: string[]): void;\n  /** Subscribes to every route change: any `mergeRoutes`/`removeRoutes` call\n   * that actually changed at least one stored route. Coarser than\n   * `subscribeEdge` (fires for any edge's change, not just one id) — used by\n   * consumers that need to react to a neighbor's route without tracking\n   * every neighbor individually (e.g. hop bridges). Returns an unsubscribe\n   * function. */\n  subscribeAllRoutes(listener: () => void): () => void;\n  /** A counter incremented once per actual route change (any id). Read\n   * through `useSyncExternalStore` alongside `subscribeAllRoutes`: a bare\n   * number is trivially stable-by-value, so snapshots never need a custom\n   * equality check. */\n  getRoutesVersion(): number;\n  /** Subscribes to changes in dragging/selected node state. Returns an\n   * unsubscribe function. */\n  subscribeNodeState(listener: () => void): () => void;\n  /** The current set of dragging node ids. Stable by reference until the\n   * set's contents change. */\n  getDraggingNodeIds(): ReadonlySet<string>;\n  /** The current set of selected node ids. Stable by reference until the\n   * set's contents change. */\n  getSelectedNodeIds(): ReadonlySet<string>;\n  /** Replaces the dragging/selected node sets, notifying node-state\n   * listeners only if either set's contents actually changed. */\n  setNodeState(dragging: Set<string>, selected: Set<string>): void;\n}\n\n/**\n * True when two sets contain exactly the same elements, regardless of\n * insertion order. Used to decide whether a node-state update actually\n * changed anything, so `useSyncExternalStore` snapshots stay referentially\n * stable across no-op updates and React does not loop re-rendering.\n */\nconst setsEqual = (\n  first: ReadonlySet<string>,\n  second: ReadonlySet<string>,\n): boolean => {\n  if (first.size !== second.size) return false;\n\n  for (const item of first) {\n    if (!second.has(item)) return false;\n  }\n\n  return true;\n};\n\n/** Calls every listener currently in a set, if any are registered. Shared\n * by the per-edge and node-state notification paths. */\nconst notifyAll = (listeners: Set<() => void> | undefined): void => {\n  if (listeners === undefined) return;\n\n  listeners.forEach((listener) => {\n    listener();\n  });\n};\n\n/**\n * Creates a fresh, isolated `SmartEdgeStore`. One instance is meant to live\n * for the lifetime of a `SmartEdgeProvider`; this factory holds no shared\n * module-level state, so multiple providers never interfere.\n */\nexport const createSmartEdgeStore = (): SmartEdgeStore => {\n  const routes = new Map<string, SmartEdgeRouteResult>();\n  const edgeListeners = new Map<string, Set<() => void>>();\n  const allRoutesListeners = new Set<() => void>();\n  const nodeStateListeners = new Set<() => void>();\n\n  let draggingNodeIds: ReadonlySet<string> = new Set();\n  let selectedNodeIds: ReadonlySet<string> = new Set();\n  let routesVersion = 0;\n\n  /** Bumps `routesVersion` and notifies `subscribeAllRoutes` listeners.\n   * Called once per `mergeRoutes`/`removeRoutes` call that changed at least\n   * one route — never per individual id — so a batch touching many edges\n   * still only produces one version bump. */\n  const notifyRoutesChanged = (): void => {\n    routesVersion += 1;\n    notifyAll(allRoutesListeners);\n  };\n\n  return {\n    subscribeEdge(edgeId, listener) {\n      let listeners = edgeListeners.get(edgeId);\n      if (listeners === undefined) {\n        listeners = new Set();\n        edgeListeners.set(edgeId, listeners);\n      }\n      listeners.add(listener);\n\n      return () => {\n        listeners.delete(listener);\n        if (listeners.size === 0) edgeListeners.delete(edgeId);\n      };\n    },\n\n    getRoute(edgeId) {\n      return routes.get(edgeId);\n    },\n\n    mergeRoutes(patch) {\n      let changed = false;\n\n      for (const [edgeId, value] of Object.entries(patch)) {\n        if (routes.get(edgeId) === value) continue;\n\n        routes.set(edgeId, value);\n        notifyAll(edgeListeners.get(edgeId));\n        changed = true;\n      }\n\n      if (changed) notifyRoutesChanged();\n    },\n\n    removeRoutes(edgeIds) {\n      let changed = false;\n\n      for (const edgeId of edgeIds) {\n        if (routes.has(edgeId)) changed = true;\n        routes.delete(edgeId);\n        notifyAll(edgeListeners.get(edgeId));\n      }\n\n      if (changed) notifyRoutesChanged();\n    },\n\n    subscribeAllRoutes(listener) {\n      allRoutesListeners.add(listener);\n\n      return () => {\n        allRoutesListeners.delete(listener);\n      };\n    },\n\n    getRoutesVersion() {\n      return routesVersion;\n    },\n\n    subscribeNodeState(listener) {\n      nodeStateListeners.add(listener);\n\n      return () => {\n        nodeStateListeners.delete(listener);\n      };\n    },\n\n    getDraggingNodeIds() {\n      return draggingNodeIds;\n    },\n\n    getSelectedNodeIds() {\n      return selectedNodeIds;\n    },\n\n    setNodeState(dragging, selected) {\n      let changed = false;\n\n      if (!setsEqual(draggingNodeIds, dragging)) {\n        draggingNodeIds = dragging;\n        changed = true;\n      }\n\n      if (!setsEqual(selectedNodeIds, selected)) {\n        selectedNodeIds = selected;\n        changed = true;\n      }\n\n      if (changed) notifyAll(nodeStateListeners);\n    },\n  };\n};\n","import type { ObstacleBox } from \"./obstacleIndex\";\n\n/** A generic least-recently-used cache keyed by string. `get` marks its key\n * as most recently used; `set` evicts the single oldest (least recently\n * used) entry once the cache holds more than `maxEntries`. Kept free of any\n * routing-result type so it can be shared before that type exists. */\nexport interface RouteCache<T> {\n  get(key: string): T | undefined;\n  set(key: string, value: T): void;\n  clear(): void;\n  readonly size: number;\n}\n\nconst DEFAULT_MAX_ENTRIES = 500;\n\n/**\n * Creates an LRU cache backed by a `Map` (whose keys iterate in insertion\n * order): `get` re-inserts its key so it sorts last, and `set` reads the\n * map's first key back out once capacity is exceeded to evict it.\n */\nexport const createRouteCache = <T>(\n  maxEntries: number = DEFAULT_MAX_ENTRIES,\n): RouteCache<T> => {\n  const store = new Map<string, T>();\n\n  return {\n    get(key) {\n      const value = store.get(key);\n      if (value === undefined) return undefined;\n\n      store.delete(key);\n      store.set(key, value);\n      return value;\n    },\n    set(key, value) {\n      store.delete(key);\n      store.set(key, value);\n\n      if (store.size <= maxEntries) return;\n      const [oldestKey] = store.keys();\n      store.delete(oldestKey);\n    },\n    clear() {\n      store.clear();\n    },\n    get size() {\n      return store.size;\n    },\n  };\n};\n\n/**\n * djb2 string hash, returned as a base-36 string. Used to compress the\n * corridor's obstacle list into a short, stable cache-key segment instead of\n * hashing the raw obstacle data on every lookup.\n */\nexport const hashString = (input: string): string => {\n  let hash = 5381;\n\n  for (let index = 0; index < input.length; index += 1) {\n    hash = (hash * 33 + input.charCodeAt(index)) | 0;\n  }\n\n  return (hash >>> 0).toString(36);\n};\n\n/** The subset of a routed edge's props that determine its route. Typed\n * locally (rather than imported from `@xyflow/react`) to keep this module\n * dependency-light. */\ninterface RouteCacheEdge {\n  sourceX: number;\n  sourceY: number;\n  targetX: number;\n  targetY: number;\n  sourcePosition: string;\n  targetPosition: string;\n  preset: string;\n  waypoints?: { x: number; y: number }[];\n  options?: {\n    gridRatio?: number;\n    nodePadding?: number;\n    avoidAreas?: { x: number; y: number; width: number; height: number }[];\n    borderRadius?: number;\n  };\n}\n\nconst quantize = (value: number): number => Math.round(value);\n\nconst obstacleKeyPart = (box: ObstacleBox): string =>\n  [box.id, box.xMin, box.yMin, box.xMax, box.yMax].join(\",\");\n\nconst waypointKeyPart = (point: { x: number; y: number }): string =>\n  `${String(quantize(point.x))}:${String(quantize(point.y))}`;\n\n/**\n * Builds a route-cache key for one edge. Endpoint and waypoint coordinates\n * are quantized with `Math.round` so sub-pixel layout jitter still hits the\n * cache, while the corridor's obstacle list is hashed so any change to the\n * *local* obstacle set (an added, removed, or moved node) busts it.\n * Per-edge options (`gridRatio`, `nodePadding`, `avoidAreas`, `borderRadius`)\n * are serialized so `createSmartEdge(..., { gridRatio: 5 })` cannot reuse a\n * path computed under different overrides. `corridorObstacles` is expected\n * to already be filtered to the routing corridor by the caller — an obstacle\n * outside it is simply absent from the list and cannot affect the key.\n */\nexport const routeCacheKey = (\n  edge: RouteCacheEdge,\n  optionsKey: string,\n  corridorObstacles: ObstacleBox[],\n): string => {\n  const waypointsKey = (edge.waypoints ?? []).map(waypointKeyPart).join(\";\");\n  const obstaclesKey = hashString(\n    corridorObstacles.map(obstacleKeyPart).join(\"|\"),\n  );\n\n  return [\n    quantize(edge.sourceX),\n    quantize(edge.sourceY),\n    quantize(edge.targetX),\n    quantize(edge.targetY),\n    edge.sourcePosition,\n    edge.targetPosition,\n    edge.preset,\n    waypointsKey,\n    optionsKey,\n    JSON.stringify(edge.options ?? null),\n    obstaclesKey,\n  ].join(\"|\");\n};\n","import type { Node } from \"@xyflow/react\";\n\n/**\n * A per-node snapshot of the state that can affect routing: absolute\n * position, measured size, and the dragging/selected flags. Absolute\n * positions are the caller's responsibility — this module only reads\n * `node.position` verbatim, so a provider resolving subflow offsets (see\n * `getAbsoluteNodes`) must do so before snapshotting.\n */\nexport interface NodeSnapshot {\n  id: string;\n  x: number;\n  y: number;\n  width: number;\n  height: number;\n  dragging: boolean;\n  selected: boolean;\n}\n\n/**\n * Captures every node's routing-relevant state, using the same\n * measured-size floor as `getBoundingBoxes`/`buildObstacleBoxes` (missing\n * dimensions collapse to 1px) so a snapshot's rect lines up with the\n * obstacle boxes built from the same nodes.\n */\nexport const snapshotNodes = (nodes: Node[]): NodeSnapshot[] =>\n  nodes.map((node) => ({\n    id: node.id,\n    x: node.position.x,\n    y: node.position.y,\n    width: Math.max(node.measured?.width ?? 0, 1),\n    height: Math.max(node.measured?.height ?? 0, 1),\n    dragging: node.dragging ?? false,\n    selected: node.selected ?? false,\n  }));\n\n/** Axis-aligned rect in graph coordinates (an `ObstacleBox` minus its `id`:\n * a changed rect no longer needs to identify an owning node). */\ninterface ChangedRect {\n  xMin: number;\n  yMin: number;\n  xMax: number;\n  yMax: number;\n}\n\n/**\n * Result of comparing two node snapshots: whether anything routing-relevant\n * changed, the padded rects touched by any moved/resized/added/removed node\n * (a moved/resized node contributes both its old and new rect, since a\n * cached route can be invalidated by either the space it vacated or the\n * space it now occupies), and the dragging/selected id sets read from\n * `next` — populated regardless of `changed`, so drag-preview fast paths\n * can key off them directly.\n */\nexport interface NodeDiff {\n  changed: boolean;\n  changedRects: ChangedRect[];\n  draggingNodeIds: Set<string>;\n  selectedNodeIds: Set<string>;\n}\n\nconst paddedRect = (\n  snapshot: NodeSnapshot,\n  nodePadding: number,\n): ChangedRect => ({\n  xMin: snapshot.x - nodePadding,\n  yMin: snapshot.y - nodePadding,\n  xMax: snapshot.x + snapshot.width + nodePadding,\n  yMax: snapshot.y + snapshot.height + nodePadding,\n});\n\nconst hasMoved = (previous: NodeSnapshot, current: NodeSnapshot): boolean =>\n  previous.x !== current.x ||\n  previous.y !== current.y ||\n  previous.width !== current.width ||\n  previous.height !== current.height;\n\n/**\n * Handles one previously-known node: pushes its old rect if it was removed,\n * both its old and new rect if it moved/resized, or nothing if it is still\n * present and unchanged. Returns whether this node contributed a change.\n */\nconst collectPreviousNodeRects = (\n  previousSnapshot: NodeSnapshot,\n  nextSnapshot: NodeSnapshot | undefined,\n  nodePadding: number,\n  changedRects: ChangedRect[],\n): boolean => {\n  if (nextSnapshot === undefined) {\n    changedRects.push(paddedRect(previousSnapshot, nodePadding));\n    return true;\n  }\n\n  if (hasMoved(previousSnapshot, nextSnapshot)) {\n    changedRects.push(paddedRect(previousSnapshot, nodePadding));\n    changedRects.push(paddedRect(nextSnapshot, nodePadding));\n    return true;\n  }\n\n  return false;\n};\n\n/**\n * Compares two node snapshots (matched by id) and reports what changed. A\n * node that neither moved nor resized contributes no rects; a moved/resized\n * node contributes both its old and new padded rect; an added or removed\n * node contributes the one rect it has.\n */\nexport const diffNodeSnapshots = (\n  previous: NodeSnapshot[],\n  next: NodeSnapshot[],\n  nodePadding: number,\n): NodeDiff => {\n  const previousById = new Map(\n    previous.map((snapshot) => [snapshot.id, snapshot]),\n  );\n  const nextById = new Map(next.map((snapshot) => [snapshot.id, snapshot]));\n\n  const changedRects: ChangedRect[] = [];\n  let changed = false;\n\n  previousById.forEach((previousSnapshot, nodeId) => {\n    const contributed = collectPreviousNodeRects(\n      previousSnapshot,\n      nextById.get(nodeId),\n      nodePadding,\n      changedRects,\n    );\n    if (contributed) changed = true;\n  });\n\n  nextById.forEach((nextSnapshot, nodeId) => {\n    if (previousById.has(nodeId)) return;\n    changedRects.push(paddedRect(nextSnapshot, nodePadding));\n    changed = true;\n  });\n\n  const draggingNodeIds = new Set<string>();\n  const selectedNodeIds = new Set<string>();\n\n  next.forEach((snapshot) => {\n    if (snapshot.dragging) draggingNodeIds.add(snapshot.id);\n    if (snapshot.selected) selectedNodeIds.add(snapshot.id);\n  });\n\n  return { changed, changedRects, draggingNodeIds, selectedNodeIds };\n};\n\n/**\n * Straightforward axis-aligned overlap test (exclusive bounds, same\n * convention as `rectIntersectsBox`) between a routing corridor and a\n * diff's changed rects: true as soon as any rect overlaps the corridor's\n * interior.\n */\nexport const corridorTouchesRects = (\n  corridor: ChangedRect,\n  rects: ChangedRect[],\n): boolean =>\n  rects.some(\n    (rect) =>\n      corridor.xMin < rect.xMax &&\n      corridor.xMax > rect.xMin &&\n      corridor.yMin < rect.yMax &&\n      corridor.yMax > rect.yMin,\n  );\n","import { rectIntersectsBox } from \"./obstacleIndex\";\nimport type { ObstacleBox } from \"./obstacleIndex\";\nimport type { XYPosition } from \"@xyflow/react\";\n\n/** Axis-aligned rect in graph coordinates — an `ObstacleBox` without the id\n * it doesn't need here. */\nexport type EdgeRect = Omit<ObstacleBox, \"id\">;\n\n/**\n * The bounding box of a polyline (graph coordinates), inflated by `pad` on\n * every side. Used to turn an edge's actual rendered path — the routed\n * `points`, or the native (unrouted) polyline it draws while clear — into\n * the region that determines whether that specific path is still valid: any\n * node entering or leaving this region can plausibly change it, so this is\n * the edge's real invalidation corridor, as opposed to a fixed guess based\n * only on its endpoints (see `schedulerFlush.ts`'s `buildEdgeCorridor`, kept\n * only to bucket the route cache's key, not to decide correctness).\n */\nexport const boundsOfPolyline = (\n  points: readonly XYPosition[],\n  pad: number,\n): EdgeRect => {\n  const xValues = points.map((point) => point.x);\n  const yValues = points.map((point) => point.y);\n\n  return {\n    xMin: Math.min(...xValues) - pad,\n    yMin: Math.min(...yValues) - pad,\n    xMax: Math.max(...xValues) + pad,\n    yMax: Math.max(...yValues) + pad,\n  };\n};\n\n/** Every obstacle box whose rect overlaps `corridor`. */\nexport const filterObstaclesInCorridor = (\n  corridor: EdgeRect,\n  boxes: ObstacleBox[],\n): ObstacleBox[] =>\n  boxes.filter((box) =>\n    rectIntersectsBox(\n      corridor.xMin,\n      corridor.yMin,\n      corridor.xMax,\n      corridor.yMax,\n      box,\n    ),\n  );\n\n/**\n * Raw obstacle signature (before hashing): an exact, order-sensitive string\n * two obstacle lists only share when every entry has the same id and rect.\n * Comparing this — rather than just a hashed cache key — catches the rare\n * hash collision a key alone can't detect, and, recomputed against an edge's\n * actual-path corridor instead of the cache key's rung-0 one, is what\n * actually re-validates a cache hit against the current obstacle set.\n */\nexport const obstaclesSignatureOf = (boxes: ObstacleBox[]): string =>\n  boxes\n    .map((box) => [box.id, box.xMin, box.yMin, box.xMax, box.yMax].join(\",\"))\n    .join(\"|\");\n","import {\n  buildObstacleBoxes,\n  isPolylineBlocked,\n  nativeStepPolyline,\n} from \"./obstacleIndex\";\nimport { routeCacheKey } from \"./routeCache\";\nimport { CORRIDOR_MARGIN_CELLS } from \"./corridor\";\nimport { diffNodeSnapshots, corridorTouchesRects } from \"./invalidation\";\nimport {\n  boundsOfPolyline,\n  filterObstaclesInCorridor,\n  obstaclesSignatureOf,\n} from \"./invalidationCorridor\";\nimport type { RouteCache } from \"./routeCache\";\nimport type { ObstacleBox } from \"./obstacleIndex\";\nimport type { EdgeRect } from \"./invalidationCorridor\";\nimport type { SmartEdgeRouteResult } from \"./providerStore\";\nimport type {\n  SchedulerDeps,\n  SchedulerState,\n  RegisteredSmartEdge,\n  ResolvedProviderOptions,\n  SmartEdgeMetrics,\n} from \"./scheduler\";\nimport type { XYPosition } from \"@xyflow/react\";\n\n/** A cached route plus enough to detect a cache-key hash collision (the raw\n * obstacle signature the key's hash was built from) and to re-validate the\n * hit against the current graph: `corridor` is the bbox of the actual path\n * this route drew (not the fixed rung-0 guess used only to bucket the\n * cache's key — see `buildEdgeCorridor`), and `obstaclesSignature` is the\n * raw signature of the obstacles that were local to that corridor when this\n * entry was stored. */\nexport interface CachedRoute {\n  result: SmartEdgeRouteResult;\n  obstaclesSignature: string;\n  corridor: EdgeRect;\n}\n\ntype SchedulerCounts = Pick<\n  SmartEdgeMetrics,\n  \"routed\" | \"cacheHits\" | \"clear\" | \"deferred\" | \"unchanged\"\n>;\n\n/** Everything one flush's per-edge decisions need that stays constant across\n * the whole flush, bundled so helpers take one argument instead of several. */\ninterface FlushContext {\n  draggingNodeIds: ReadonlySet<string>;\n  changedRects: EdgeRect[];\n  forcedEdgeIds: ReadonlySet<string>;\n  obstacleBoxes: ObstacleBox[];\n  optionsKey: string;\n  options: ResolvedProviderOptions;\n  cache: RouteCache<CachedRoute>;\n  signatures: Map<string, string>;\n  /** Each edge's actual-path invalidation corridor, mirroring `signatures`'\n   * lifecycle: set whenever a route is produced (clear, cache hit, or\n   * dispatch), left untouched for deferred/unchanged edges, and read by\n   * `isEdgeUnchanged` instead of a freshly-guessed one. */\n  pathCorridors: Map<string, EdgeRect>;\n}\n\ninterface FlushAccumulators {\n  nextDeferredIds: Set<string>;\n  dispatchItems: RegisteredSmartEdge[];\n  routeMerges: Record<string, SmartEdgeRouteResult>;\n  counts: SchedulerCounts;\n}\n\nconst STEP_LIKE_PRESETS = new Set([\"step\", \"smoothstep\"]);\n\n/** The subset of an edge's registration that determines its route. Distinct\n * from `routeCacheKey`'s (rounded, obstacle-hashed) key: this is an exact\n * comparison used only to detect \"nothing changed since last processed\". */\nconst edgeSignature = (edge: RegisteredSmartEdge): string =>\n  JSON.stringify({\n    sourceX: edge.sourceX,\n    sourceY: edge.sourceY,\n    targetX: edge.targetX,\n    targetY: edge.targetY,\n    sourcePosition: edge.sourcePosition,\n    targetPosition: edge.targetPosition,\n    preset: edge.preset,\n    options: edge.options,\n    waypoints: edge.waypoints,\n  });\n\n/** The endpoint bounding box inflated by the first (narrowest) corridor\n * rung's margin. Kept only to bucket the route cache's key: a wider search\n * that succeeds on a bigger rung can physically traverse space well outside\n * this box, so it is deliberately NOT used as an invalidation region (see\n * `pathCorridors` / `actualPathCorridor` for the region that actually is). */\nconst buildEdgeCorridor = (\n  edge: RegisteredSmartEdge,\n  gridRatio: number,\n): EdgeRect => {\n  const inflate = CORRIDOR_MARGIN_CELLS[0] * gridRatio;\n\n  return {\n    xMin: Math.min(edge.sourceX, edge.targetX) - inflate,\n    yMin: Math.min(edge.sourceY, edge.targetY) - inflate,\n    xMax: Math.max(edge.sourceX, edge.targetX) + inflate,\n    yMax: Math.max(edge.sourceY, edge.targetY) + inflate,\n  };\n};\n\n/** The bbox of an edge's actual rendered path (routed points or the native\n * polyline), inflated by `nodePadding` plus one grid cell — the edge's real\n * invalidation corridor. */\nconst actualPathCorridor = (\n  points: readonly XYPosition[],\n  context: FlushContext,\n): EdgeRect =>\n  boundsOfPolyline(\n    points,\n    context.options.nodePadding + context.options.gridRatio,\n  );\n\n/** Builds the route-cache key for one edge: coordinates/options plus a hash\n * of the obstacles local to the rung-0 corridor (a fast bucket only — a\n * cache-key hit is re-validated against the actual-path corridor by\n * `findCacheHit` before being trusted). */\nconst buildCacheKey = (\n  edge: RegisteredSmartEdge,\n  context: FlushContext,\n): string => {\n  const corridor = buildEdgeCorridor(edge, context.options.gridRatio);\n  const corridorObstacles = filterObstaclesInCorridor(\n    corridor,\n    context.obstacleBoxes,\n  );\n\n  return routeCacheKey(edge, context.optionsKey, corridorObstacles);\n};\n\n/** A cache-key hit only counts if the obstacles local to the cached route's\n * actual-path corridor still match what it was stored with — this is what\n * catches both a (extremely rare) djb2 hash collision the key can't detect\n * on its own, and a real obstacle change in the band the path traverses\n * that the rung-0 key bucket never saw. */\nconst findCacheHit = (\n  edge: RegisteredSmartEdge,\n  context: FlushContext,\n): CachedRoute | undefined => {\n  const cached = context.cache.get(buildCacheKey(edge, context));\n  if (cached === undefined) return undefined;\n\n  const currentObstacles = filterObstaclesInCorridor(\n    cached.corridor,\n    context.obstacleBoxes,\n  );\n  return cached.obstaclesSignature === obstaclesSignatureOf(currentObstacles)\n    ? cached\n    : undefined;\n};\n\n/** Whether either endpoint of `edge` is currently being dragged. */\nconst isEdgeDragging = (\n  edge: RegisteredSmartEdge,\n  draggingNodeIds: ReadonlySet<string>,\n): boolean =>\n  draggingNodeIds.has(edge.source) || draggingNodeIds.has(edge.target);\n\n/** Whether `edge` can skip this flush entirely: it has a stored actual-path\n * corridor from a previous flush (absent means it was never routed, or its\n * last result never produced one — e.g. a non-routed dispatch response —\n * either way it cannot be \"unchanged\"), it isn't forced past this check\n * (e.g. by a just-ended drag), its registration signature is exactly what it\n * was last time it was processed, and no changed rect this flush touches\n * that stored corridor. */\nconst isEdgeUnchanged = (\n  edgeId: string,\n  currentSignature: string,\n  context: FlushContext,\n): boolean => {\n  const corridor = context.pathCorridors.get(edgeId);\n\n  return (\n    corridor !== undefined &&\n    !context.forcedEdgeIds.has(edgeId) &&\n    context.signatures.get(edgeId) === currentSignature &&\n    !corridorTouchesRects(corridor, context.changedRects)\n  );\n};\n\n/** The polyline this edge would render natively (no smart routing): a\n * straight segment for bezier/straight/simplebezier presets, the Z/L step\n * skeleton for step/smoothstep. Used to answer \"is it already clear?\" and,\n * when it is, doubles as its actual-path corridor. */\nconst nativePolylineFor = (edge: RegisteredSmartEdge): XYPosition[] =>\n  STEP_LIKE_PRESETS.has(edge.preset)\n    ? nativeStepPolyline(\n        edge.sourceX,\n        edge.sourceY,\n        edge.sourcePosition,\n        edge.targetX,\n        edge.targetY,\n        edge.targetPosition,\n      )\n    : [\n        { x: edge.sourceX, y: edge.sourceY },\n        { x: edge.targetX, y: edge.targetY },\n      ];\n\n/** Decides and folds one edge's fate into the flush's running totals, in the\n * fixed priority order: deferred (still dragging) → unchanged (nothing\n * relevant moved, and not forced past this check) → cache hit → clear\n * (unblocked and only routed when blocked) → dispatch. Every outcome except\n * `deferred` and `unchanged` updates the edge's last-processed signature;\n * leaving it stale while deferred is what lets a drag-end force a real\n * re-decision even though the edge's own registration never changed while\n * it was being skipped. Every outcome that produces a route (cache hit,\n * clear) also records its actual-path corridor for the next flush; a\n * dispatched edge's corridor is recorded later, once its route arrives (see\n * `dispatchAndMerge`). */\nconst processEdge = (\n  edge: RegisteredSmartEdge,\n  context: FlushContext,\n  accumulators: FlushAccumulators,\n): void => {\n  if (\n    isEdgeDragging(edge, context.draggingNodeIds) &&\n    !context.options.routeWhileDragging\n  ) {\n    accumulators.counts.deferred += 1;\n    accumulators.nextDeferredIds.add(edge.id);\n    return;\n  }\n\n  const currentSignature = edgeSignature(edge);\n  if (isEdgeUnchanged(edge.id, currentSignature, context)) {\n    accumulators.counts.unchanged += 1;\n    return;\n  }\n\n  context.signatures.set(edge.id, currentSignature);\n\n  const cachedRoute = findCacheHit(edge, context);\n  if (cachedRoute) {\n    accumulators.counts.cacheHits += 1;\n    accumulators.routeMerges[edge.id] = cachedRoute.result;\n    context.pathCorridors.set(edge.id, cachedRoute.corridor);\n    return;\n  }\n\n  if (context.options.routeOnlyWhenBlocked) {\n    const polyline = nativePolylineFor(edge);\n    const excludeIds = new Set([edge.source, edge.target]);\n    if (!isPolylineBlocked(polyline, context.obstacleBoxes, excludeIds)) {\n      accumulators.counts.clear += 1;\n      accumulators.routeMerges[edge.id] = { kind: \"clear\", wasRouted: false };\n      context.pathCorridors.set(edge.id, actualPathCorridor(polyline, context));\n      return;\n    }\n  }\n\n  accumulators.counts.routed += 1;\n  accumulators.dispatchItems.push(edge);\n};\n\nconst buildFlushContext = (\n  deps: SchedulerDeps,\n  state: SchedulerState,\n): FlushContext => {\n  const diff = diffNodeSnapshots(\n    state.flushedSnapshot,\n    state.snapshot,\n    deps.options.nodePadding,\n  );\n  const draggingActive = diff.draggingNodeIds.size > 0;\n  const dragJustEnded = state.draggingWasActive && !draggingActive;\n  state.draggingWasActive = draggingActive;\n\n  return {\n    draggingNodeIds: diff.draggingNodeIds,\n    changedRects: diff.changedRects,\n    forcedEdgeIds: dragJustEnded ? state.deferredEdgeIds : new Set<string>(),\n    obstacleBoxes: buildObstacleBoxes(\n      state.absoluteNodes,\n      deps.options.nodePadding,\n      deps.options.avoidAreas,\n    ),\n    optionsKey: JSON.stringify({\n      nodePadding: deps.options.nodePadding,\n      gridRatio: deps.options.gridRatio,\n      avoidAreas: deps.options.avoidAreas,\n    }),\n    options: deps.options,\n    cache: state.cache,\n    signatures: state.signatures,\n    pathCorridors: state.pathCorridors,\n  };\n};\n\nconst emitMetrics = (\n  deps: SchedulerDeps,\n  batchId: number,\n  executedOn: \"worker\" | \"main\",\n  batchLatencyMs: number,\n  mainThreadBlockingMs: number,\n  counts: SchedulerCounts,\n): void => {\n  deps.onMetrics?.({\n    batchId,\n    executedOn,\n    batchLatencyMs,\n    mainThreadBlockingMs,\n    ...counts,\n  });\n};\n\nconst dispatchAndMerge = async (\n  deps: SchedulerDeps,\n  state: SchedulerState,\n  context: FlushContext,\n  accumulators: FlushAccumulators,\n  batchId: number,\n): Promise<void> => {\n  if (Object.keys(accumulators.routeMerges).length > 0) {\n    deps.store.mergeRoutes(accumulators.routeMerges);\n  }\n\n  if (accumulators.dispatchItems.length === 0) {\n    emitMetrics(deps, batchId, \"main\", 0, 0, accumulators.counts);\n    return;\n  }\n\n  const outcome = await deps.dispatch(\n    state.rawNodes,\n    accumulators.dispatchItems,\n  );\n  if (state.disposed || batchId !== state.batchId) return;\n\n  const dispatchMerges: Record<string, SmartEdgeRouteResult> = {};\n  Object.entries(outcome.results).forEach(([edgeId, result]) => {\n    dispatchMerges[edgeId] = result;\n    const dispatchedEdge = state.edges.get(edgeId);\n    if (!dispatchedEdge || result.kind !== \"routed\") return;\n\n    const corridor = actualPathCorridor(\n      result.points.map(([pointX, pointY]) => ({ x: pointX, y: pointY })),\n      context,\n    );\n    state.pathCorridors.set(edgeId, corridor);\n    state.cache.set(buildCacheKey(dispatchedEdge, context), {\n      result,\n      corridor,\n      obstaclesSignature: obstaclesSignatureOf(\n        filterObstaclesInCorridor(corridor, context.obstacleBoxes),\n      ),\n    });\n  });\n\n  if (Object.keys(dispatchMerges).length > 0) {\n    deps.store.mergeRoutes(dispatchMerges);\n  }\n\n  emitMetrics(\n    deps,\n    batchId,\n    outcome.executedOn,\n    outcome.durationMs,\n    outcome.executedOn === \"main\" ? outcome.durationMs : 0,\n    accumulators.counts,\n  );\n};\n\n/** Runs one flush to completion: decides every registered edge's fate,\n * merges cache/clear results into the store immediately, then (if anything\n * needs it) dispatches the remainder and merges/caches the response — unless\n * a later flush has already superseded this one's `batchId` by the time the\n * response arrives. */\nexport const runFlush = async (\n  deps: SchedulerDeps,\n  state: SchedulerState,\n): Promise<void> => {\n  if (state.disposed) return;\n\n  const context = buildFlushContext(deps, state);\n  const accumulators: FlushAccumulators = {\n    nextDeferredIds: new Set(),\n    dispatchItems: [],\n    routeMerges: {},\n    counts: { routed: 0, cacheHits: 0, clear: 0, deferred: 0, unchanged: 0 },\n  };\n  state.edges.forEach((edge) => {\n    processEdge(edge, context, accumulators);\n  });\n\n  state.deferredEdgeIds = accumulators.nextDeferredIds;\n  state.flushedSnapshot = state.snapshot;\n  state.batchId += 1;\n\n  await dispatchAndMerge(deps, state, context, accumulators, state.batchId);\n};\n","import { getAbsoluteNodes } from \"../functions\";\nimport { createRouteCache } from \"./routeCache\";\nimport { snapshotNodes, diffNodeSnapshots } from \"./invalidation\";\nimport { runFlush } from \"./schedulerFlush\";\nimport type { CachedRoute } from \"./schedulerFlush\";\nimport type { RouteCache } from \"./routeCache\";\nimport type { EdgeRect } from \"./invalidationCorridor\";\nimport type { SmartEdgeStore, SmartEdgeRouteResult } from \"./providerStore\";\nimport type { SmartEdgeBatchItem } from \"./routeBatch\";\nimport type { Node, Rect } from \"@xyflow/react\";\n\n/** Aggregate counters for one completed flush, reported via `onMetrics`. */\nexport interface SmartEdgeMetrics {\n  batchId: number;\n  executedOn: \"worker\" | \"main\";\n  batchLatencyMs: number;\n  /** Time spent running the batch on the main thread. `0` when the batch\n   * ran on the worker, and `0` when the flush dispatched no edges. */\n  mainThreadBlockingMs: number;\n  routed: number;\n  cacheHits: number;\n  clear: number;\n  deferred: number;\n  unchanged: number;\n}\n\n/** One edge registered with the scheduler: everything a routing batch needs,\n * plus its registration order (hop paint order, a later task). */\nexport interface RegisteredSmartEdge extends SmartEdgeBatchItem {\n  order: number;\n}\n\n/** The scheduling/routing options a provider resolves from its props (a\n * later task). Declared locally so this module has no dependency on that\n * provider; any structurally compatible object works here. */\nexport interface ResolvedProviderOptions {\n  routeOnlyWhenBlocked: boolean;\n  routeWhileDragging: boolean;\n  debounceMs: number;\n  nodePadding: number;\n  gridRatio: number;\n  avoidAreas: Rect[];\n  cacheSize: number;\n}\n\nexport interface SchedulerDeps {\n  store: SmartEdgeStore;\n  /** Executes a batch (worker or main thread) and resolves with results. */\n  dispatch: (\n    nodes: Node[],\n    edges: SmartEdgeBatchItem[],\n  ) => Promise<{\n    results: Record<string, SmartEdgeRouteResult>;\n    executedOn: \"worker\" | \"main\";\n    durationMs: number;\n  }>;\n  options: ResolvedProviderOptions;\n  onMetrics?: (metrics: SmartEdgeMetrics) => void;\n}\n\nexport interface RoutingScheduler {\n  registerEdge(edge: RegisteredSmartEdge): () => void;\n  setNodes(nodes: Node[]): void;\n  /** Drops cached routes and per-edge signatures so the next flush\n   * re-decides every registered edge (used when provider options that\n   * affect routing change). */\n  invalidateRoutes(): void;\n  /** Test hook: run the pending flush immediately. */\n  flush(): Promise<void>;\n  dispose(): void;\n}\n\ntype NodeSnapshotList = ReturnType<typeof snapshotNodes>;\n\n/** The scheduler's full mutable state, threaded through the free functions\n * below (and through `schedulerFlush.ts`'s flush pipeline) instead of\n * captured by closures — keeps every function short. */\nexport interface SchedulerState {\n  edges: Map<string, RegisteredSmartEdge>;\n  signatures: Map<string, string>;\n  /** Each edge's actual-path invalidation corridor (bbox of the path it last\n   * routed or cleared to, inflated by nodePadding + one grid cell), keyed by\n   * edge id and kept alongside `signatures` for the same lifetime: set\n   * whenever a route is produced, read by `isEdgeUnchanged` instead of a\n   * fixed guess based only on the endpoints. See `schedulerFlush.ts`. */\n  pathCorridors: Map<string, EdgeRect>;\n  cache: RouteCache<CachedRoute>;\n  deferredEdgeIds: Set<string>;\n  draggingWasActive: boolean;\n  rawNodes: Node[];\n  absoluteNodes: Node[];\n  snapshot: NodeSnapshotList;\n  flushedSnapshot: NodeSnapshotList;\n  batchId: number;\n  disposed: boolean;\n  flushScheduled: boolean;\n  timerHandle: ReturnType<typeof setTimeout> | null;\n  frameHandle: number | null;\n}\n\nconst setsEqual = (\n  first: ReadonlySet<string>,\n  second: ReadonlySet<string>,\n): boolean => {\n  if (first.size !== second.size) return false;\n  for (const item of first) {\n    if (!second.has(item)) return false;\n  }\n  return true;\n};\n\nconst cancelScheduledFlush = (state: SchedulerState): void => {\n  state.flushScheduled = false;\n  if (state.timerHandle !== null) {\n    clearTimeout(state.timerHandle);\n    state.timerHandle = null;\n  }\n  if (state.frameHandle !== null) {\n    cancelAnimationFrame(state.frameHandle);\n    state.frameHandle = null;\n  }\n};\n\n/** Arms the debounce timer that actually runs the flush. Only ever called\n * while `flushScheduled` is true and immediately before this same timer\n * would be the next thing to fire, so once it fires there is nothing left\n * to guard: `cancelScheduledFlush` always clears this timer in the same\n * synchronous step it flips `flushScheduled` back to false, so a fired\n * callback can only ever observe `flushScheduled` still true. */\nconst armTimer = (deps: SchedulerDeps, state: SchedulerState): void => {\n  state.timerHandle = setTimeout(() => {\n    state.timerHandle = null;\n    state.flushScheduled = false;\n    void runFlush(deps, state);\n  }, deps.options.debounceMs);\n};\n\n/** Coalesces same-tick registrations/node updates into one flush: the first\n * call after a flush arms a microtask → (frame →) timer chain; every call\n * while one is already pending is a no-op, since the eventual flush reads\n * live state and picks up every change made before it runs. */\nconst scheduleFlush = (deps: SchedulerDeps, state: SchedulerState): void => {\n  if (state.disposed || state.flushScheduled) return;\n  state.flushScheduled = true;\n\n  void Promise.resolve().then(() => {\n    if (!state.flushScheduled) return;\n\n    if (typeof requestAnimationFrame === \"undefined\") {\n      armTimer(deps, state);\n      return;\n    }\n\n    state.frameHandle = requestAnimationFrame(() => {\n      state.frameHandle = null;\n      armTimer(deps, state);\n    });\n  });\n};\n\nconst registerEdge = (\n  deps: SchedulerDeps,\n  state: SchedulerState,\n  edge: RegisteredSmartEdge,\n): (() => void) => {\n  state.edges.set(edge.id, edge);\n  scheduleFlush(deps, state);\n\n  return () => {\n    state.edges.delete(edge.id);\n    state.signatures.delete(edge.id);\n    state.pathCorridors.delete(edge.id);\n    state.deferredEdgeIds.delete(edge.id);\n  };\n};\n\nconst setNodes = (\n  deps: SchedulerDeps,\n  state: SchedulerState,\n  nodes: Node[],\n): void => {\n  if (state.disposed) return;\n\n  const previousDragging = new Set(\n    state.snapshot.filter((node) => node.dragging).map((node) => node.id),\n  );\n  const absoluteNodes = getAbsoluteNodes(nodes);\n  const nextSnapshot = snapshotNodes(absoluteNodes);\n  const diff = diffNodeSnapshots(\n    state.flushedSnapshot,\n    nextSnapshot,\n    deps.options.nodePadding,\n  );\n\n  state.rawNodes = nodes;\n  state.absoluteNodes = absoluteNodes;\n  state.snapshot = nextSnapshot;\n\n  deps.store.setNodeState(diff.draggingNodeIds, diff.selectedNodeIds);\n\n  if (!diff.changed && setsEqual(previousDragging, diff.draggingNodeIds)) {\n    return;\n  }\n\n  scheduleFlush(deps, state);\n};\n\nconst invalidateRoutes = (deps: SchedulerDeps, state: SchedulerState): void => {\n  if (state.disposed) return;\n\n  state.cache = createRouteCache<CachedRoute>(deps.options.cacheSize);\n  state.signatures.clear();\n  state.pathCorridors.clear();\n  scheduleFlush(deps, state);\n};\n\n/**\n * Coordinates routing for a set of registered smart edges as nodes change:\n * debounces registrations/node updates into one flush, decides per-edge\n * whether to defer, skip, serve from cache, mark clear, or dispatch to the\n * injected `dispatch`, then merges results, populates the cache, and reports\n * metrics. Pure — no React, no `Worker` construction; `dispatch` is supplied\n * by the caller (a provider wires the real worker/main-thread choice). The\n * per-flush decision pipeline lives in `schedulerFlush.ts`.\n */\nexport const createRoutingScheduler = (\n  deps: SchedulerDeps,\n): RoutingScheduler => {\n  const state: SchedulerState = {\n    edges: new Map(),\n    signatures: new Map(),\n    pathCorridors: new Map(),\n    cache: createRouteCache<CachedRoute>(deps.options.cacheSize),\n    deferredEdgeIds: new Set(),\n    draggingWasActive: false,\n    rawNodes: [],\n    absoluteNodes: [],\n    snapshot: [],\n    flushedSnapshot: [],\n    batchId: 0,\n    disposed: false,\n    flushScheduled: false,\n    timerHandle: null,\n    frameHandle: null,\n  };\n\n  return {\n    registerEdge: (edge) => registerEdge(deps, state, edge),\n    setNodes: (nodes) => {\n      setNodes(deps, state, nodes);\n    },\n    invalidateRoutes: () => {\n      invalidateRoutes(deps, state);\n    },\n    flush: () => {\n      cancelScheduledFlush(state);\n      return runFlush(deps, state);\n    },\n    dispose: () => {\n      state.disposed = true;\n      cancelScheduledFlush(state);\n    },\n  };\n};\n","import {\n  svgDrawSmoothLinePath,\n  svgDrawStraightLinePath,\n  svgDrawSmoothStepLinePath,\n  svgDrawSimpleBezierLinePath,\n  pathfindingAStarDiagonal,\n  pathfindingJumpPointNoDiagonal,\n} from \"../functions\";\nimport type { DrawEdgeFunction, PathFindingFunction } from \"../functions\";\nimport type { SmartEdgePreset } from \"../smartEdgePresets\";\n\n/** The pure routing functions a preset resolves to (no React fallback). */\nexport interface PresetRouting {\n  drawEdge: DrawEdgeFunction;\n  generatePath: PathFindingFunction;\n}\n\n/** All preset names the worker can route. Kept in sync with `SmartEdgePreset`. */\nexport const SMART_EDGE_PRESETS = [\n  \"bezier\",\n  \"straight\",\n  \"step\",\n  \"smoothstep\",\n  \"simplebezier\",\n] as const satisfies readonly SmartEdgePreset[];\n\n/** Type guard for a valid preset name coming from untrusted edge `data`. */\nexport const isSmartEdgePreset = (value: unknown): value is SmartEdgePreset =>\n  SMART_EDGE_PRESETS.some((preset) => preset === value);\n\n/**\n * Resolve a preset name to its `drawEdge`/`generatePath` functions, mirroring\n * `smartEdgePresets` but without the React fallback components. This keeps the\n * Web Worker bundle free of `@xyflow/react`/React, which cannot run off the\n * main thread. Kept in sync with `src/smartEdgePresets.ts`.\n */\nexport const resolvePresetRouting = (\n  preset: SmartEdgePreset,\n  borderRadius?: number,\n): PresetRouting => {\n  switch (preset) {\n    case \"bezier\":\n      return {\n        drawEdge: svgDrawSmoothLinePath,\n        generatePath: pathfindingAStarDiagonal,\n      };\n    case \"straight\":\n      return {\n        drawEdge: svgDrawStraightLinePath,\n        generatePath: pathfindingAStarDiagonal,\n      };\n    case \"step\":\n      return {\n        drawEdge: svgDrawStraightLinePath,\n        generatePath: pathfindingJumpPointNoDiagonal,\n      };\n    case \"smoothstep\":\n      return {\n        drawEdge: svgDrawSmoothStepLinePath({ borderRadius }),\n        generatePath: pathfindingJumpPointNoDiagonal,\n      };\n    case \"simplebezier\":\n      return {\n        drawEdge: svgDrawSimpleBezierLinePath,\n        generatePath: pathfindingAStarDiagonal,\n      };\n  }\n};\n","import { getSmartEdge } from \"../getSmartEdge\";\nimport { getSmartEdgeWaypoints } from \"../getSmartEdge/getSmartEdgeWaypoints\";\nimport { getAbsoluteNodes, excludeEdgeAncestorNodes } from \"../functions\";\nimport { resolvePresetRouting } from \"./routingRegistry\";\nimport type { SmartEdgeRouteResult } from \"./providerStore\";\nimport type { SmartEdgePreset } from \"../smartEdgePresets\";\nimport type { Node, Position, Rect, XYPosition } from \"@xyflow/react\";\n\n/**\n * The subset of `GetSmartEdgeOptions` that can be sent to the routing Web\n * Worker. Function options (`drawEdge`/`generatePath`) are excluded because\n * functions cannot cross the `postMessage` boundary; the worker resolves\n * those from the edge's `preset` instead.\n */\nexport interface SmartEdgeBatchItemOptions {\n  gridRatio?: number;\n  nodePadding?: number;\n  avoidAreas?: Rect[];\n  /** Corner radius for the `smoothstep` preset. */\n  borderRadius?: number;\n}\n\n/**\n * One edge to route, with endpoints already resolved on the main thread and\n * everything else stripped to structured-clone-safe data (no functions), so\n * the whole batch can cross the `postMessage` boundary to the routing worker.\n */\nexport interface SmartEdgeBatchItem {\n  id: string;\n  source: string;\n  target: string;\n  sourceX: number;\n  sourceY: number;\n  targetX: number;\n  targetY: number;\n  sourcePosition: Position;\n  targetPosition: Position;\n  preset: SmartEdgePreset;\n  options?: SmartEdgeBatchItemOptions;\n  /**\n   * Intermediate points (in graph coordinates) the edge must pass through,\n   * in order from source to target. Routes through `getSmartEdgeWaypoints`\n   * instead of plain `getSmartEdge` when present and non-empty.\n   */\n  waypoints?: XYPosition[];\n}\n\n/**\n * A batch of edges to route against one shared node set, tagged with a\n * monotonically increasing `requestId` so the caller can ignore responses\n * for superseded requests.\n */\nexport interface SmartEdgeBatchRequest {\n  requestId: number;\n  nodes: Node[];\n  edges: SmartEdgeBatchItem[];\n}\n\n/** The worker's reply: routed results keyed by edge id, plus how long\n * routing took. Edges that failed to route are omitted from `results`. */\nexport interface SmartEdgeBatchResponse {\n  requestId: number;\n  results: Record<string, SmartEdgeRouteResult>;\n  durationMs: number;\n}\n\n/**\n * Routes many smart edges against a shared node set in one pass. This is the\n * pure core used both inside the Web Worker and as the main-thread fallback,\n * so it must not depend on the DOM or React. Nodes resolve to absolute\n * (subflow-aware) coordinates once for the whole batch; each edge then\n * excludes its own ancestor containers from the obstacle set, resolves its\n * preset to the matching `drawEdge`/`generatePath`, and routes through\n * `getSmartEdgeWaypoints` when it carries waypoints or plain `getSmartEdge`\n * otherwise. Edges that fail to route are omitted so the edge component keeps\n * rendering its fallback.\n */\nexport const routeSmartEdgeBatch = (\n  nodes: Node[],\n  edges: SmartEdgeBatchItem[],\n): Record<string, SmartEdgeRouteResult> => {\n  const absoluteNodes = getAbsoluteNodes(nodes);\n  const results: Record<string, SmartEdgeRouteResult> = {};\n\n  for (const edge of edges) {\n    const preparedNodes = excludeEdgeAncestorNodes(\n      absoluteNodes,\n      edge.source,\n      edge.target,\n    );\n    const { drawEdge, generatePath } = resolvePresetRouting(\n      edge.preset,\n      edge.options?.borderRadius,\n    );\n\n    const params = {\n      sourceX: edge.sourceX,\n      sourceY: edge.sourceY,\n      targetX: edge.targetX,\n      targetY: edge.targetY,\n      sourcePosition: edge.sourcePosition,\n      targetPosition: edge.targetPosition,\n      nodes: preparedNodes,\n      options: {\n        drawEdge,\n        generatePath,\n        gridRatio: edge.options?.gridRatio,\n        nodePadding: edge.options?.nodePadding,\n        avoidAreas: edge.options?.avoidAreas,\n      },\n    };\n\n    const result = edge.waypoints?.length\n      ? getSmartEdgeWaypoints({ ...params, waypoints: edge.waypoints })\n      : getSmartEdge(params);\n\n    if (!(result instanceof Error)) {\n      results[edge.id] = { ...result, kind: \"routed\", wasRouted: true };\n    }\n  }\n\n  return results;\n};\n","import { routeSmartEdgeBatch } from \"./routeBatch\";\nimport type {\n  SmartEdgeBatchRequest,\n  SmartEdgeBatchResponse,\n} from \"./routeBatch\";\n\n/**\n * True when `data` is a routing batch we can run. Webpack's HMR runtime\n * posts its own messages (`{ type: \"webpackOk\" }`, etc.) into every worker;\n * those have no `nodes` array, and treating them as batches throws inside\n * `toNodeMap`.\n */\nexport const isSmartEdgeBatchRequest = (\n  data: unknown,\n): data is SmartEdgeBatchRequest => {\n  if (typeof data !== \"object\" || data === null) return false;\n  if (!(\"requestId\" in data) || !(\"nodes\" in data) || !(\"edges\" in data)) {\n    return false;\n  }\n\n  return (\n    typeof data.requestId === \"number\" &&\n    Array.isArray(data.nodes) &&\n    Array.isArray(data.edges)\n  );\n};\n\n/**\n * Pure request/response mapping run inside the routing Web Worker. Kept\n * separate from the worker bootstrap so it can be unit tested on the main\n * thread. Uses `Date.now()` rather than `performance.now()` for `durationMs`\n * since the High Resolution Time API is not guaranteed to be available in\n * every worker runtime this library targets.\n */\nexport const handleBatchRequest = (\n  request: SmartEdgeBatchRequest,\n): SmartEdgeBatchResponse => {\n  const startedAt = Date.now();\n  const results = routeSmartEdgeBatch(request.nodes, request.edges);\n\n  return {\n    requestId: request.requestId,\n    results,\n    durationMs: Date.now() - startedAt,\n  };\n};\n","import { handleBatchRequest } from \"./workerProtocol\";\nimport type {\n  SmartEdgeBatchItem,\n  SmartEdgeBatchRequest,\n  SmartEdgeBatchResponse,\n} from \"./routeBatch\";\nimport type { SmartEdgeRouteResult } from \"./providerStore\";\nimport type { Node } from \"@xyflow/react\";\n\n/** What one dispatch resolves with: the routed results plus where and how\n * long routing took, forwarded to the scheduler's `onMetrics`. */\nexport interface DispatchOutcome {\n  results: Record<string, SmartEdgeRouteResult>;\n  executedOn: \"worker\" | \"main\";\n  durationMs: number;\n}\n\n/**\n * Runs one batch synchronously on the calling thread: used both when no\n * `Worker` is available and as the fallback after a worker construction or\n * runtime error. Reuses `handleBatchRequest` — the same pure routing core the\n * worker itself runs — so main-thread and worker routing can never diverge.\n */\nexport const dispatchOnMainThread = (\n  nodes: Node[],\n  edges: SmartEdgeBatchItem[],\n): DispatchOutcome => {\n  const response = handleBatchRequest({ requestId: 0, nodes, edges });\n\n  return {\n    results: response.results,\n    executedOn: \"main\",\n    durationMs: response.durationMs,\n  };\n};\n\n/** The minimal `Worker` surface this module needs, small enough to fake in\n * tests without depending on the DOM's `Worker` global. A real `Worker`\n * instance satisfies this structurally. */\nexport interface WorkerLike {\n  postMessage: (request: SmartEdgeBatchRequest) => void;\n  terminate: () => void;\n  onmessage: ((event: MessageEvent<SmartEdgeBatchResponse>) => void) | null;\n  onerror: ((event: ErrorEvent) => void) | null;\n}\n\n/** One in-flight worker request: its own dispatch arguments (needed to\n * replay it on the main thread if the worker dies before responding) plus\n * the resolver for the promise `dispatch` handed back to its caller. */\ninterface PendingRequest {\n  nodes: Node[];\n  edges: SmartEdgeBatchItem[];\n  resolve: (outcome: DispatchOutcome) => void;\n}\n\nexport interface WorkerDispatcher {\n  /** Posts one batch to the worker and resolves once its matching response\n   * arrives — or immediately on the main thread, once the worker has\n   * errored. */\n  dispatch: (\n    nodes: Node[],\n    edges: SmartEdgeBatchItem[],\n  ) => Promise<DispatchOutcome>;\n}\n\n/**\n * Wraps a live `Worker` instance with request/response id matching. Once the\n * worker reports an error, every currently pending request is immediately\n * resolved on the main thread (so no caller is ever left hanging), the\n * worker is terminated, and every subsequent `dispatch` call also runs on\n * the main thread for the rest of this dispatcher's life.\n */\nexport const createWorkerDispatcher = (\n  worker: WorkerLike,\n): WorkerDispatcher => {\n  const pending = new Map<number, PendingRequest>();\n  let nextRequestId = 0;\n  let broken = false;\n\n  worker.onmessage = (event) => {\n    const request = pending.get(event.data.requestId);\n    if (!request) return;\n\n    pending.delete(event.data.requestId);\n    request.resolve({\n      results: event.data.results,\n      executedOn: \"worker\",\n      durationMs: event.data.durationMs,\n    });\n  };\n\n  worker.onerror = () => {\n    broken = true;\n    worker.terminate();\n    pending.forEach((request) => {\n      request.resolve(dispatchOnMainThread(request.nodes, request.edges));\n    });\n    pending.clear();\n  };\n\n  return {\n    dispatch: (nodes, edges) => {\n      if (broken) return Promise.resolve(dispatchOnMainThread(nodes, edges));\n\n      const requestId = (nextRequestId += 1);\n      return new Promise<DispatchOutcome>((resolve) => {\n        pending.set(requestId, { nodes, edges, resolve });\n        worker.postMessage({ requestId, nodes, edges });\n      });\n    },\n  };\n};\n","import { useEffect, useMemo, useState, useSyncExternalStore } from \"react\";\nimport { getAbsoluteNodes } from \"../functions\";\nimport RoutingWorker from \"./routing.worker?worker&inline\";\nimport { createSmartEdgeStore } from \"./providerStore\";\nimport { createRoutingScheduler } from \"./scheduler\";\nimport {\n  SmartEdgeRoutingContext,\n  diffResolvedProviderOptions,\n  resolveProviderOptions,\n} from \"./routingContext\";\nimport { dispatchOnMainThread, createWorkerDispatcher } from \"./workerDispatch\";\nimport type { DispatchOutcome, WorkerLike } from \"./workerDispatch\";\nimport type {\n  ResolvedProviderOptions,\n  SmartEdgeContextValue,\n  SmartEdgeProviderOptions,\n} from \"./routingContext\";\nimport type {\n  RegisteredSmartEdge,\n  RoutingScheduler,\n  SmartEdgeMetrics,\n} from \"./scheduler\";\nimport type { SmartEdgeBatchItem } from \"./routeBatch\";\nimport type { Node } from \"@xyflow/react\";\nimport type { ReactNode } from \"react\";\n\nexport interface SmartEdgeProviderProps {\n  /** All flow nodes (controlled pattern). Used as routing obstacles and as\n   * the source of drag/selection state. */\n  nodes: Node[];\n  options?: SmartEdgeProviderOptions;\n  onMetrics?: (metrics: SmartEdgeMetrics) => void;\n  children: ReactNode;\n}\n\n/**\n * Projects React Flow nodes to the minimal shape routing needs — id,\n * position, size, parent, and the drag/selection flags the scheduler tracks\n * for its defer/invalidate decisions — dropping consumer `data` so the\n * payload posted to the worker is small and always structured-clone safe.\n */\nconst toRoutingNodes = (nodes: Node[]): Node[] =>\n  nodes.map((node) => ({\n    id: node.id,\n    position: node.position,\n    measured: node.measured,\n    parentId: node.parentId,\n    dragging: node.dragging,\n    selected: node.selected,\n    data: {},\n  }));\n\n/** Wraps the synchronous main-thread routing core in a resolved promise, so\n * it matches `SchedulerDeps[\"dispatch\"]`'s async signature — the same shape\n * a worker dispatch resolves with, just without ever yielding to a worker. */\nconst dispatchOnMainThreadAsync = (\n  nodes: Node[],\n  edges: SmartEdgeBatchItem[],\n): Promise<DispatchOutcome> =>\n  Promise.resolve(dispatchOnMainThread(nodes, edges));\n\ntype DispatchFn = (\n  nodes: Node[],\n  edges: SmartEdgeBatchItem[],\n) => Promise<DispatchOutcome>;\n\n/**\n * One provider mount's whole mutable world, built once by `createController`\n * (held in `useState` so it survives strict-mode's double render) and\n * mutated only through its own methods afterward — the same encapsulation\n * style `createRoutingScheduler` uses.\n *\n * The scheduler itself is deliberately NOT a fixed field: React's Strict Mode\n * runs every effect's setup, then its cleanup, then its setup again, once\n * right after the initial mount — and `RoutingScheduler.dispose()` has no\n * undo. A scheduler tied to a plain \"dispose on cleanup\" effect would go\n * permanently dead after that one extra cleanup, silently breaking routing\n * for the rest of the component's real lifetime. `activateScheduler` /\n * `deactivateScheduler` instead give the scheduler the same \"create in\n * effect, tear down in cleanup, safe to repeat\" lifecycle already used for\n * the worker: each activation builds a fresh scheduler and immediately\n * replays every currently tracked registration and the latest routing nodes\n * into it, so nothing is lost across a strict-mode reset.\n */\ninterface Controller extends SmartEdgeContextValue {\n  /** Creates a fresh scheduler, seeds it with every current registration and\n   * the latest routing nodes, and makes it the active target for\n   * `registerEdge`/`setRoutingNodes`. Returns the new instance so the caller\n   * can dispose exactly that one later. */\n  activateScheduler: () => RoutingScheduler;\n  /** Disposes `scheduler` and, if it is still the active one, clears it. */\n  deactivateScheduler: (scheduler: RoutingScheduler) => void;\n  /** Replaces the routing-projected nodes forwarded to the active\n   * scheduler's `setNodes`. */\n  setRoutingNodes: (nodes: Node[]) => void;\n  /** Swaps the live worker/main-thread dispatch implementation. */\n  setDispatch: (dispatch: DispatchFn) => void;\n  /** Replaces the callback `onMetrics` forwards each completed flush to. */\n  setOnMetrics: (\n    onMetrics: ((metrics: SmartEdgeMetrics) => void) | undefined,\n  ) => void;\n  /** Replaces the absolute-node snapshot `getNodesSnapshot` returns. */\n  setAbsoluteNodes: (nodes: Node[]) => void;\n  /** Applies a newly resolved options object in place. Returns true when\n   * anything changed (so React context can re-render). Routing-affecting\n   * changes also drop the route cache. */\n  setOptions: (options: ResolvedProviderOptions) => boolean;\n  /** Subscribes to options changes applied by `setOptions`, in the shape\n   * `useSyncExternalStore` expects: the listener runs synchronously right\n   * after the live object is mutated, with no payload. Returns an\n   * unsubscribe function. */\n  subscribeOptions: (listener: () => void) => () => void;\n  /** Returns the cached options snapshot for `useSyncExternalStore`. The\n   * reference only changes when `setOptions` applies a real change, which\n   * is what makes a context re-render fire exactly then. */\n  getOptionsSnapshot: () => ResolvedProviderOptions;\n}\n\nconst createController = (\n  resolvedOptions: ResolvedProviderOptions,\n): Controller => {\n  let dispatchImpl: DispatchFn = dispatchOnMainThreadAsync;\n  let onMetricsImpl: ((metrics: SmartEdgeMetrics) => void) | undefined;\n  let absoluteNodes: Node[] = [];\n  let routingNodes: Node[] = [];\n  let nextOrder = 0;\n  let activeScheduler: RoutingScheduler | null = null;\n  // Immutable copy of `resolvedOptions` for React consumption. The live\n  // object keeps its identity across `setOptions` (the scheduler closes\n  // over it), so this snapshot is the only identity that tracks value\n  // changes; `useSyncExternalStore` compares it with `Object.is`.\n  let optionsSnapshot: ResolvedProviderOptions = { ...resolvedOptions };\n  const optionsListeners = new Set<() => void>();\n  const registrations = new Map<string, RegisteredSmartEdge>();\n  const schedulerUnregisterFns = new Map<string, () => void>();\n  // First-seen paint order per edge id. Never evicted for the lifetime of\n  // this provider: edge ids are expected to be stable within a flow, so an\n  // edge that unregisters (endpoint change, brief unmount) and re-registers\n  // under the same id keeps its original hop layering instead of jumping to\n  // the end — re-minting on every call would flip Task 15's hop order every\n  // time an edge's props merely change.\n  const orders = new Map<string, number>();\n\n  const store = createSmartEdgeStore();\n\n  const registerWithActiveScheduler = (edge: RegisteredSmartEdge): void => {\n    /* v8 ignore next -- register before activateScheduler; covered by unit tests */\n    if (!activeScheduler) return;\n    schedulerUnregisterFns.set(edge.id, activeScheduler.registerEdge(edge));\n  };\n\n  const orderFor = (edgeId: string): number => {\n    const existing = orders.get(edgeId);\n    if (existing !== undefined) return existing;\n\n    nextOrder += 1;\n    orders.set(edgeId, nextOrder);\n    return nextOrder;\n  };\n\n  const registerEdge = (\n    edge: Omit<RegisteredSmartEdge, \"order\">,\n  ): (() => void) => {\n    const registered: RegisteredSmartEdge = {\n      ...edge,\n      order: orderFor(edge.id),\n    };\n    registrations.set(registered.id, registered);\n    registerWithActiveScheduler(registered);\n\n    return () => {\n      registrations.delete(registered.id);\n      schedulerUnregisterFns.get(registered.id)?.();\n      schedulerUnregisterFns.delete(registered.id);\n    };\n  };\n\n  const activateScheduler = (): RoutingScheduler => {\n    const scheduler = createRoutingScheduler({\n      store,\n      options: resolvedOptions,\n      dispatch: (nodes, edges) => dispatchImpl(nodes, edges),\n      onMetrics: (metrics) => {\n        onMetricsImpl?.(metrics);\n      },\n    });\n\n    activeScheduler = scheduler;\n    schedulerUnregisterFns.clear();\n    registrations.forEach(registerWithActiveScheduler);\n    scheduler.setNodes(routingNodes);\n\n    return scheduler;\n  };\n\n  /** Disposes the given scheduler and clears it as the active one. Only ever\n   * called from the activation effect's own cleanup, which — because React\n   * always runs an effect's cleanup before that same effect's next setup —\n   * is always still targeting the current `activeScheduler` when it fires. */\n  const deactivateScheduler = (scheduler: RoutingScheduler): void => {\n    scheduler.dispose();\n    activeScheduler = null;\n  };\n\n  return {\n    store,\n    options: resolvedOptions,\n    registerEdge,\n    getRegistrationsInOrder: () =>\n      [...registrations.values()].sort(\n        (left, right) => left.order - right.order,\n      ),\n    getNodesSnapshot: () => absoluteNodes,\n    activateScheduler,\n    deactivateScheduler,\n    setRoutingNodes: (nodes) => {\n      routingNodes = nodes;\n      activeScheduler?.setNodes(nodes);\n    },\n    setDispatch: (dispatch) => {\n      dispatchImpl = dispatch;\n    },\n    setOnMetrics: (onMetrics) => {\n      onMetricsImpl = onMetrics;\n    },\n    setAbsoluteNodes: (nodes) => {\n      absoluteNodes = nodes;\n    },\n    setOptions: (next) => {\n      const diff = diffResolvedProviderOptions(resolvedOptions, next);\n      if (!diff.any) return false;\n      Object.assign(resolvedOptions, next);\n      if (diff.routing) activeScheduler?.invalidateRoutes();\n      optionsSnapshot = { ...resolvedOptions };\n      optionsListeners.forEach((listener) => {\n        listener();\n      });\n      return true;\n    },\n    subscribeOptions: (listener) => {\n      optionsListeners.add(listener);\n      return () => {\n        optionsListeners.delete(listener);\n      };\n    },\n    getOptionsSnapshot: () => optionsSnapshot,\n  };\n};\n\n/**\n * Owns one `SmartEdgeStore`, one `RoutingScheduler`, and one inline routing\n * Web Worker for its lifetime, exposing them to descendants through\n * `SmartEdgeRoutingContext`. Falls back to synchronous main-thread routing\n * whenever `Worker` is unavailable, its construction throws, or it reports a\n * runtime error — edges keep routing either way. The `nodes` prop drives both\n * the scheduler's obstacle/drag state and `getNodesSnapshot` (absolute,\n * subflow-resolved coordinates for floating edges).\n */\nexport function SmartEdgeProvider({\n  nodes,\n  options,\n  onMetrics,\n  children,\n}: Readonly<SmartEdgeProviderProps>) {\n  const [controller] = useState(() =>\n    createController(resolveProviderOptions(options)),\n  );\n\n  useEffect(() => {\n    controller.setOnMetrics(onMetrics);\n  }, [controller, onMetrics]);\n\n  // The controller's options object is external, mutable state from React's\n  // perspective (the scheduler closes over that exact reference, and\n  // `setOptions` mutates it in place), so syncing the `options` prop into\n  // it is a genuine effect.\n  useEffect(() => {\n    controller.setOptions(resolveProviderOptions(options));\n  }, [controller, options]);\n\n  // Identity of the snapshot changes exactly when `setOptions` applies a\n  // real change, which re-renders this component and refreshes the context\n  // value below; same-value inline `options={{...}}` props never notify.\n  const optionsSnapshot = useSyncExternalStore(\n    controller.subscribeOptions,\n    controller.getOptionsSnapshot,\n    // Server rendering never subscribes, and the snapshot is immutable until\n    // a real options change (which effects-only `setOptions` cannot cause on\n    // the server), so the same getter is a valid server snapshot.\n    controller.getOptionsSnapshot,\n  );\n\n  useEffect(() => {\n    controller.setAbsoluteNodes(getAbsoluteNodes(nodes));\n    controller.setRoutingNodes(toRoutingNodes(nodes));\n  }, [controller, nodes]);\n\n  useEffect(() => {\n    const scheduler = controller.activateScheduler();\n\n    return () => {\n      controller.deactivateScheduler(scheduler);\n    };\n  }, [controller]);\n\n  useEffect(() => {\n    if (typeof Worker === \"undefined\") return undefined;\n\n    let worker: WorkerLike;\n    try {\n      worker = new RoutingWorker();\n    } catch {\n      /* v8 ignore next -- Worker construction failures are embed-specific */\n      return undefined;\n    }\n\n    controller.setDispatch(createWorkerDispatcher(worker).dispatch);\n\n    return () => {\n      controller.setDispatch(dispatchOnMainThreadAsync);\n      worker.terminate();\n    };\n  }, [controller]);\n\n  const contextValue = useMemo<SmartEdgeContextValue>(\n    () => ({\n      store: controller.store,\n      options: optionsSnapshot,\n      registerEdge: controller.registerEdge,\n      getRegistrationsInOrder: controller.getRegistrationsInOrder,\n      getNodesSnapshot: controller.getNodesSnapshot,\n    }),\n    [controller, optionsSnapshot],\n  );\n\n  return (\n    <SmartEdgeRoutingContext.Provider value={contextValue}>\n      {children}\n    </SmartEdgeRoutingContext.Provider>\n  );\n}\n"],"mappings":"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"}