@liveblocks/react-flow 3.17.0-rc1 → 3.18.0-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/flow.cjs CHANGED
@@ -5,33 +5,9 @@ var react = require('@liveblocks/react');
5
5
  var _private = require('@liveblocks/react/_private');
6
6
  var react$2 = require('@xyflow/react');
7
7
  var react$1 = require('react');
8
- var constants = require('./constants.cjs');
9
8
  var helpers = require('./helpers.cjs');
10
9
 
11
10
  const EMPTY_ARRAY = [];
12
- function mergeAndBuildDataConfigCache(base, data) {
13
- if (!data)
14
- return () => base;
15
- const dataFallback = data["*"];
16
- const fallback = dataFallback ? { ...base, data: dataFallback } : base;
17
- const cache = /* @__PURE__ */ new Map();
18
- for (const type in data) {
19
- if (type === "*")
20
- continue;
21
- const specific = data[type];
22
- if (!specific)
23
- continue;
24
- const dataConfig = { ...dataFallback, ...specific };
25
- cache.set(type, { ...base, data: dataConfig });
26
- }
27
- return (type) => cache.get(type) || fallback;
28
- }
29
- function buildNodeConfigCache(nodeDataConfig) {
30
- return mergeAndBuildDataConfigCache(constants.NODE_BASE_CONFIG, nodeDataConfig);
31
- }
32
- function buildEdgeConfigCache(edgeDataConfig) {
33
- return mergeAndBuildDataConfigCache(constants.EDGE_BASE_CONFIG, edgeDataConfig);
34
- }
35
11
  function applyNodeChanges(changes, nodes, history, getNodeSyncConfig) {
36
12
  for (const change of changes) {
37
13
  switch (change.type) {
@@ -137,30 +113,46 @@ function applyEdgeChanges(changes, edges, getEdgeSyncConfig) {
137
113
  }
138
114
  }
139
115
  }
116
+ function nodeMapToList(nodesMap) {
117
+ if (nodesMap === null)
118
+ return null;
119
+ return Object.values(nodesMap);
120
+ }
121
+ function edgeMapToList(edgesMap) {
122
+ if (edgesMap === null)
123
+ return null;
124
+ return Object.values(edgesMap);
125
+ }
126
+ function useNodesAndEdges(storageKey) {
127
+ const nodesMap = react.useStorage((storage) => {
128
+ const flow = storage[storageKey];
129
+ return flow?.nodes ?? null;
130
+ });
131
+ const edgesMap = react.useStorage((storage) => {
132
+ const flow = storage[storageKey];
133
+ return flow?.edges ?? null;
134
+ });
135
+ const nodes = react$1.useMemo(() => nodeMapToList(nodesMap), [nodesMap]);
136
+ const edges = react$1.useMemo(() => edgeMapToList(edgesMap), [edgesMap]);
137
+ return { nodes, edges };
138
+ }
140
139
  function useLiveblocksFlow(options = {}) {
141
140
  const history = react.useHistory();
142
141
  const isStorageLoaded = react.useStorage(() => true) ?? false;
143
142
  const frozenOptions = _private.useInitial({
144
143
  nodes: options.nodes,
145
144
  edges: options.edges,
146
- storageKey: options.storageKey ?? constants.DEFAULT_STORAGE_KEY,
145
+ storageKey: options.storageKey ?? helpers.DEFAULT_STORAGE_KEY,
147
146
  suspense: options.suspense ?? false
148
147
  });
149
148
  const [getNodeSyncConfig, getEdgeSyncConfig] = react$1.useMemo(
150
149
  () => [
151
- buildNodeConfigCache(frozenOptions.nodes?.sync),
152
- buildEdgeConfigCache(frozenOptions.edges?.sync)
150
+ helpers.buildNodeConfigCache(frozenOptions.nodes?.sync),
151
+ helpers.buildEdgeConfigCache(frozenOptions.edges?.sync)
153
152
  ],
154
153
  [frozenOptions]
155
154
  );
156
- const nodes = react.useStorage((storage) => {
157
- const flow = storage[frozenOptions.storageKey];
158
- return flow?.nodes ? [...flow.nodes.values()] : null;
159
- });
160
- const edges = react.useStorage((storage) => {
161
- const flow = storage[frozenOptions.storageKey];
162
- return flow?.edges ? [...flow.edges.values()] : null;
163
- });
155
+ const { nodes, edges } = useNodesAndEdges(frozenOptions.storageKey);
164
156
  const onNodesChange = react.useMutation(
165
157
  ({ storage }, changes) => {
166
158
  const flow = storage.get(frozenOptions.storageKey);
package/dist/flow.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"flow.cjs","sources":["../src/flow.ts"],"sourcesContent":["import type {\n History,\n JsonObject,\n Resolve,\n ToImmutable,\n} from \"@liveblocks/core\";\nimport { kInternal, LiveMap, LiveObject } from \"@liveblocks/core\";\nimport { useHistory, useMutation, useStorage } from \"@liveblocks/react\";\nimport {\n useInitial,\n useSuspendUntilStorageReady,\n} from \"@liveblocks/react/_private\";\nimport type {\n BuiltInEdge,\n BuiltInNode,\n Connection,\n Edge,\n EdgeChange,\n Node,\n NodeChange,\n OnConnect,\n OnDelete,\n OnEdgesChange,\n OnNodesChange,\n} from \"@xyflow/react\";\nimport { addEdge as defaultAddEdge } from \"@xyflow/react\";\nimport { useEffect, useMemo } from \"react\";\n\nimport {\n DEFAULT_STORAGE_KEY,\n EDGE_BASE_CONFIG,\n NODE_BASE_CONFIG,\n} from \"./constants\";\nimport { toLiveblocksInternalEdge, toLiveblocksInternalNode } from \"./helpers\";\nimport type {\n EdgeSyncConfig,\n InternalLiveblocksEdge,\n InternalLiveblocksFlow,\n InternalLiveblocksNode,\n NodeSyncConfig,\n SyncConfig,\n} from \"./types\";\n\nconst EMPTY_ARRAY = [] as unknown[];\n\ntype UseLiveblocksFlowResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Resolve<\n (\n | {\n nodes: null;\n edges: null;\n isLoading: true;\n }\n | {\n nodes: N[];\n edges: E[];\n isLoading: false;\n }\n ) & {\n onNodesChange: OnNodesChange<N>;\n onEdgesChange: OnEdgesChange<E>;\n onConnect: OnConnect;\n onDelete: OnDelete<N, E>;\n }\n>;\n\ntype LiveblocksFlowSuspenseResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Extract<UseLiveblocksFlowResult<N, E>, { isLoading: false }>;\n\nfunction mergeAndBuildDataConfigCache(\n base: SyncConfig,\n data?: Record<string, SyncConfig | undefined>\n): (type: string | undefined) => SyncConfig {\n if (!data) return () => base;\n\n const dataFallback = data[\"*\"];\n const fallback = dataFallback ? { ...base, data: dataFallback } : base;\n\n // Pre-compute full node/edge sync configs for all explicitly declared types\n const cache = new Map<string | undefined, SyncConfig>();\n for (const type in data) {\n if (type === \"*\") continue;\n const specific = data[type];\n if (!specific) continue;\n const dataConfig: SyncConfig = { ...dataFallback, ...specific };\n cache.set(type, { ...base, data: dataConfig });\n }\n\n return (type) => cache.get(type) || fallback;\n}\n\nfunction buildNodeConfigCache<N extends Node>(\n /** The user-provided node data sync configuration, if any. */\n nodeDataConfig?: NodeSyncConfig<N>\n): (type: string | undefined) => SyncConfig {\n return mergeAndBuildDataConfigCache(NODE_BASE_CONFIG, nodeDataConfig);\n}\n\nfunction buildEdgeConfigCache<E extends Edge>(\n /** The user-provided edge data sync configuration, if any. */\n edgeDataConfig?: EdgeSyncConfig<E>\n): (type: string | undefined) => SyncConfig {\n return mergeAndBuildDataConfigCache(EDGE_BASE_CONFIG, edgeDataConfig);\n}\n\ntype UseLiveblocksFlowOptions<N extends Node, E extends Edge> = {\n nodes?: {\n /**\n * The initial React Flow nodes.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * initial: [\n * { id: \"1\", position: { x: 0, y: 0 }, data: { label: \"Node 1\" } },\n * { id: \"2\", position: { x: 0, y: 100 }, data: { label: \"Node 2\" } },\n * ],\n * },\n * });\n * ```\n */\n initial?: N[];\n /**\n * Per-type sync configuration for node data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all node types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * sync: {\n * \"*\": {\n * label: false, // Don't sync node.data.label\n * color: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomNode: {\n * showPreview: false, // Don't sync myCustomNode.data.showPreview\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: NodeSyncConfig<N>;\n };\n\n edges?: {\n initial?: E[];\n /**\n * Per-type sync configuration for edge data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all edge types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * edges: {\n * sync: {\n * \"*\": {\n * hovered: false, // Don't sync edge.data.hovered\n * style: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomEdge: {\n * isHighlighted: false, // Don't sync myCustomEdge.data.isHighlighted\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: EdgeSyncConfig<E>;\n };\n\n /**\n * The key used to store the React Flow diagram in Liveblocks Storage.\n * Defaults to `\"flow\"`.\n */\n storageKey?: string;\n\n /**\n * When true, suspends until Storage is ready (use a React `Suspense`\n * boundary). Then `nodes` and `edges` are always arrays and `isLoading` is\n * always false.\n */\n suspense?: boolean;\n};\n\n// Similar to React Flow's `applyNodeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-node-changes\nfunction applyNodeChanges<N extends Node>(\n changes: NodeChange<N>[],\n nodes: LiveMap<string, InternalLiveblocksNode>,\n history: History,\n getNodeSyncConfig: (nodeType: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getNodeSyncConfig(change.item.type);\n const existing = nodes.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n nodes.set(\n change.item.id,\n toLiveblocksInternalNode(change.item, config)\n );\n }\n break;\n }\n\n case \"position\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.dragging === true) {\n history.pause();\n }\n\n if (change.position !== undefined) {\n const prev = node.get(\"position\");\n if (prev?.x !== change.position.x || prev?.y !== change.position.y) {\n node.set(\"position\", change.position);\n }\n }\n\n if (change.dragging !== undefined) {\n if (change.dragging === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `dragging: false`\n node.setLocal(\"dragging\", change.dragging);\n }\n break;\n }\n\n case \"dimensions\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.resizing === true) {\n history.pause();\n }\n\n if (\n change.dimensions !== undefined &&\n change.setAttributes !== undefined\n ) {\n if (\n change.setAttributes === true ||\n change.setAttributes === \"width\"\n ) {\n node.set(\"width\", change.dimensions.width);\n }\n\n if (\n change.setAttributes === true ||\n change.setAttributes === \"height\"\n ) {\n node.set(\"height\", change.dimensions.height);\n }\n }\n\n if (change.dimensions !== undefined) {\n // Must match NODE_BASE_CONFIG's `measured: false`\n node.setLocal(\"measured\", change.dimensions);\n }\n\n if (change.resizing !== undefined) {\n if (change.resizing === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `resizing: false`\n node.setLocal(\"resizing\", change.resizing);\n }\n\n break;\n }\n\n case \"select\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Must match NODE_BASE_CONFIG's `selected: false`\n node.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n// Similar to React Flow's `applyEdgeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-edge-changes\nfunction applyEdgeChanges<E extends Edge>(\n changes: EdgeChange<E>[],\n edges: LiveMap<string, InternalLiveblocksEdge>,\n getEdgeSyncConfig: (type: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getEdgeSyncConfig(change.item.type);\n const existing = edges.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n edges.set(\n change.item.id,\n toLiveblocksInternalEdge(change.item, config)\n );\n }\n break;\n }\n\n case \"select\": {\n const edge = edges.get(change.id);\n if (!edge) break;\n // Must match EDGE_BASE_CONFIG's `selected: false`\n edge.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n/**\n * Returns a controlled React Flow state backed by Liveblocks Storage.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete, isLoading } = useLiveblocksFlow();\n *\n * if (isLoading) {\n * return <div>Loading…</div>\n * }\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n * Pass `{ suspense: true }` to suspend until Storage is ready, `nodes` and `edges` will never be `null`.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete } =\n * useLiveblocksFlow({ suspense: true });\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n */\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options?: UseLiveblocksFlowOptions<N, E> & { suspense?: false }\n): Resolve<UseLiveblocksFlowResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> & { suspense: true }\n): Resolve<LiveblocksFlowSuspenseResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> = {}\n): Resolve<UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>> {\n const history = useHistory();\n const isStorageLoaded = useStorage(() => true) ?? false;\n\n // These options are not reactive, only their initial values are used.\n const frozenOptions = useInitial({\n nodes: options.nodes,\n edges: options.edges,\n storageKey: options.storageKey ?? DEFAULT_STORAGE_KEY,\n suspense: options.suspense ?? false,\n });\n\n // Pre-compute sync config caches once (not on every render)\n const [getNodeSyncConfig, getEdgeSyncConfig] = useMemo(\n () =>\n [\n buildNodeConfigCache(frozenOptions.nodes?.sync),\n buildEdgeConfigCache(frozenOptions.edges?.sync),\n ] as const,\n [frozenOptions]\n );\n\n // Storage already includes local overlays via toImmutable(), so no\n // separate local layer is needed. Individual node/edge immutable references\n // are already stable (only change when the underlying LiveObject changes).\n const nodes = useStorage((storage) => {\n const flow = storage[frozenOptions.storageKey] as\n | ToImmutable<InternalLiveblocksFlow>\n | undefined;\n return flow?.nodes ? ([...flow.nodes.values()] as unknown as N[]) : null;\n });\n const edges = useStorage((storage) => {\n const flow = storage[frozenOptions.storageKey] as\n | ToImmutable<InternalLiveblocksFlow>\n | undefined;\n return flow?.edges ? ([...flow.edges.values()] as unknown as E[]) : null;\n });\n\n const onNodesChange = useMutation(\n ({ storage }, changes: NodeChange<N>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyNodeChanges(changes, flow.get(\"nodes\"), history, getNodeSyncConfig);\n },\n [history, frozenOptions, getNodeSyncConfig]\n );\n\n const onEdgesChange = useMutation(\n ({ storage }, changes: EdgeChange<E>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyEdgeChanges(changes, flow.get(\"edges\"), getEdgeSyncConfig);\n },\n [frozenOptions, getEdgeSyncConfig]\n );\n\n const onConnect = useMutation(\n ({ storage }, connection: Connection) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n // Delegate to React Flow's own `addEdge` helper for consistent default\n // edge ID generation, passing an empty array since de-duplication is\n // already handled above.\n const [newEdge] = defaultAddEdge(connection, [] as E[]);\n if (!newEdge) {\n return;\n }\n\n const edges = flow.get(\"edges\");\n const config = getEdgeSyncConfig(newEdge.type);\n edges.set(newEdge.id, toLiveblocksInternalEdge(newEdge, config));\n },\n [frozenOptions.storageKey, getEdgeSyncConfig]\n );\n\n const onDelete = useMutation(\n ({ storage }, params: { nodes: N[]; edges: E[] }) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n const nodesMap = flow.get(\"nodes\");\n const edgesMap = flow.get(\"edges\");\n\n for (const edge of params.edges) {\n edgesMap.delete(edge.id);\n }\n\n for (const node of params.nodes) {\n nodesMap.delete(node.id);\n }\n },\n [frozenOptions.storageKey]\n );\n\n const setInitialStorage = useMutation(\n ({ storage }) => {\n // Similarly to `initialStorage` on `Client.enterRoom` and `RoomProvider`, we only\n // initialize Storage if it doesn't already exist.\n if (storage.get(frozenOptions.storageKey) !== undefined) {\n return;\n }\n\n const initialNodes = frozenOptions.nodes?.initial ?? [];\n const initialEdges = frozenOptions.edges?.initial ?? [];\n\n storage.set(\n frozenOptions.storageKey,\n new LiveObject({\n nodes: new LiveMap(\n initialNodes.map((node) => [\n node.id,\n toLiveblocksInternalNode(node, getNodeSyncConfig(node.type)),\n ])\n ),\n edges: new LiveMap(\n initialEdges.map((edge) => [\n edge.id,\n toLiveblocksInternalEdge(edge, getEdgeSyncConfig(edge.type)),\n ])\n ),\n })\n );\n },\n [frozenOptions, getNodeSyncConfig, getEdgeSyncConfig]\n );\n\n useEffect(() => {\n if (isStorageLoaded) {\n history[kInternal].withoutHistory(() => {\n setInitialStorage();\n });\n }\n }, [isStorageLoaded, setInitialStorage, history]);\n\n if (frozenOptions.suspense) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `suspense` is frozen so this branch is stable\n useSuspendUntilStorageReady();\n }\n\n return {\n nodes: frozenOptions.suspense ? (nodes ?? (EMPTY_ARRAY as N[])) : nodes,\n edges: frozenOptions.suspense ? (edges ?? (EMPTY_ARRAY as E[])) : edges,\n isLoading: frozenOptions.suspense ? false : !isStorageLoaded,\n onNodesChange,\n onEdgesChange,\n onConnect,\n onDelete,\n } as UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>;\n}\n"],"names":["NODE_BASE_CONFIG","EDGE_BASE_CONFIG","toLiveblocksInternalNode","toLiveblocksInternalEdge","useHistory","useStorage","useInitial","DEFAULT_STORAGE_KEY","useMemo","useMutation","defaultAddEdge","edges","LiveObject","LiveMap","useEffect","kInternal","useSuspendUntilStorageReady"],"mappings":"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"}
1
+ {"version":3,"file":"flow.cjs","sources":["../src/flow.ts"],"sourcesContent":["import type {\n History,\n JsonObject,\n ReadonlyJsonObject,\n Resolve,\n ToJson,\n} from \"@liveblocks/core\";\nimport { kInternal, LiveMap, LiveObject } from \"@liveblocks/core\";\nimport { useHistory, useMutation, useStorage } from \"@liveblocks/react\";\nimport {\n useInitial,\n useSuspendUntilStorageReady,\n} from \"@liveblocks/react/_private\";\nimport type {\n BuiltInEdge,\n BuiltInNode,\n Connection,\n Edge,\n EdgeChange,\n Node,\n NodeChange,\n OnConnect,\n OnDelete,\n OnEdgesChange,\n OnNodesChange,\n} from \"@xyflow/react\";\nimport { addEdge as defaultAddEdge } from \"@xyflow/react\";\nimport { useEffect, useMemo } from \"react\";\n\nimport {\n buildEdgeConfigCache,\n buildNodeConfigCache,\n DEFAULT_STORAGE_KEY,\n toLiveblocksInternalEdge,\n toLiveblocksInternalNode,\n} from \"./helpers\";\nimport type {\n EdgeSyncConfig,\n InternalLiveblocksEdge,\n InternalLiveblocksFlow,\n InternalLiveblocksNode,\n NodeSyncConfig,\n SyncConfig,\n} from \"./types\";\n\nconst EMPTY_ARRAY = [] as unknown[];\n\ntype UseLiveblocksFlowResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Resolve<\n (\n | {\n nodes: null;\n edges: null;\n isLoading: true;\n }\n | {\n nodes: N[];\n edges: E[];\n isLoading: false;\n }\n ) & {\n onNodesChange: OnNodesChange<N>;\n onEdgesChange: OnEdgesChange<E>;\n onConnect: OnConnect;\n onDelete: OnDelete<N, E>;\n }\n>;\n\ntype LiveblocksFlowSuspenseResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Extract<UseLiveblocksFlowResult<N, E>, { isLoading: false }>;\n\ntype UseLiveblocksFlowOptions<N extends Node, E extends Edge> = {\n nodes?: {\n /**\n * The initial React Flow nodes.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * initial: [\n * { id: \"1\", position: { x: 0, y: 0 }, data: { label: \"Node 1\" } },\n * { id: \"2\", position: { x: 0, y: 100 }, data: { label: \"Node 2\" } },\n * ],\n * },\n * });\n * ```\n */\n initial?: N[];\n /**\n * Per-type sync configuration for node data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all node types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * sync: {\n * \"*\": {\n * label: false, // Don't sync node.data.label\n * color: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomNode: {\n * showPreview: false, // Don't sync myCustomNode.data.showPreview\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: NodeSyncConfig<N>;\n };\n\n edges?: {\n initial?: E[];\n /**\n * Per-type sync configuration for edge data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all edge types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * edges: {\n * sync: {\n * \"*\": {\n * hovered: false, // Don't sync edge.data.hovered\n * style: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomEdge: {\n * isHighlighted: false, // Don't sync myCustomEdge.data.isHighlighted\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: EdgeSyncConfig<E>;\n };\n\n /**\n * The key used to store the React Flow diagram in Liveblocks Storage.\n * Defaults to `\"flow\"`.\n */\n storageKey?: string;\n\n /**\n * When true, suspends until Storage is ready (use a React `Suspense`\n * boundary). Then `nodes` and `edges` are always arrays and `isLoading` is\n * always false.\n */\n suspense?: boolean;\n};\n\n// Similar to React Flow's `applyNodeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-node-changes\nfunction applyNodeChanges<N extends Node>(\n changes: NodeChange<N>[],\n nodes: LiveMap<string, InternalLiveblocksNode>,\n history: History,\n getNodeSyncConfig: (nodeType: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getNodeSyncConfig(change.item.type);\n const existing = nodes.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n nodes.set(\n change.item.id,\n toLiveblocksInternalNode(change.item, config)\n );\n }\n break;\n }\n\n case \"position\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.dragging === true) {\n history.pause();\n }\n\n if (change.position !== undefined) {\n const prev = node.get(\"position\");\n if (prev?.x !== change.position.x || prev?.y !== change.position.y) {\n node.set(\"position\", change.position);\n }\n }\n\n if (change.dragging !== undefined) {\n if (change.dragging === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `dragging: false`\n node.setLocal(\"dragging\", change.dragging);\n }\n break;\n }\n\n case \"dimensions\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.resizing === true) {\n history.pause();\n }\n\n if (\n change.dimensions !== undefined &&\n change.setAttributes !== undefined\n ) {\n if (\n change.setAttributes === true ||\n change.setAttributes === \"width\"\n ) {\n node.set(\"width\", change.dimensions.width);\n }\n\n if (\n change.setAttributes === true ||\n change.setAttributes === \"height\"\n ) {\n node.set(\"height\", change.dimensions.height);\n }\n }\n\n if (change.dimensions !== undefined) {\n // Must match NODE_BASE_CONFIG's `measured: false`\n node.setLocal(\"measured\", change.dimensions);\n }\n\n if (change.resizing !== undefined) {\n if (change.resizing === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `resizing: false`\n node.setLocal(\"resizing\", change.resizing);\n }\n\n break;\n }\n\n case \"select\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Must match NODE_BASE_CONFIG's `selected: false`\n node.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n// Similar to React Flow's `applyEdgeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-edge-changes\nfunction applyEdgeChanges<E extends Edge>(\n changes: EdgeChange<E>[],\n edges: LiveMap<string, InternalLiveblocksEdge>,\n getEdgeSyncConfig: (type: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getEdgeSyncConfig(change.item.type);\n const existing = edges.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n edges.set(\n change.item.id,\n toLiveblocksInternalEdge(change.item, config)\n );\n }\n break;\n }\n\n case \"select\": {\n const edge = edges.get(change.id);\n if (!edge) break;\n // Must match EDGE_BASE_CONFIG's `selected: false`\n edge.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n// TODO (LB-3665): To support sub-nodes, this function will need to emit nodes\n// in topological order (parents before children), deferring any node with a\n// parentId until its parent has been emitted.\nfunction nodeMapToList<N extends Node>(\n nodesMap: ReadonlyJsonObject | null\n): N[] | null {\n if (nodesMap === null) return null;\n return Object.values(nodesMap) as unknown as N[];\n}\n\nfunction edgeMapToList<E extends Edge>(\n edgesMap: ReadonlyJsonObject | null\n): E[] | null {\n if (edgesMap === null) return null;\n return Object.values(edgesMap) as unknown as E[];\n}\n\nfunction useNodesAndEdges<N extends Node, E extends Edge>(storageKey: string) {\n // Storage already includes local overlays via toJSON(), so no separate local\n // layer is needed. Individual node/edge immutable references are already\n // stable (only change when the underlying LiveObject changes).\n const nodesMap = useStorage((storage) => {\n const flow = storage[storageKey] as\n | ToJson<InternalLiveblocksFlow>\n | undefined;\n return flow?.nodes ?? null;\n });\n const edgesMap = useStorage((storage) => {\n const flow = storage[storageKey] as\n | ToJson<InternalLiveblocksFlow>\n | undefined;\n return flow?.edges ?? null;\n });\n\n const nodes = useMemo(() => nodeMapToList<N>(nodesMap), [nodesMap]);\n const edges = useMemo(() => edgeMapToList<E>(edgesMap), [edgesMap]);\n return { nodes, edges };\n}\n\n/**\n * Returns a controlled React Flow state backed by Liveblocks Storage.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete, isLoading } = useLiveblocksFlow();\n *\n * if (isLoading) {\n * return <div>Loading…</div>\n * }\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n * Pass `{ suspense: true }` to suspend until Storage is ready, `nodes` and `edges` will never be `null`.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete } =\n * useLiveblocksFlow({ suspense: true });\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n */\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options?: UseLiveblocksFlowOptions<N, E> & { suspense?: false }\n): Resolve<UseLiveblocksFlowResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> & { suspense: true }\n): Resolve<LiveblocksFlowSuspenseResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> = {}\n): Resolve<UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>> {\n const history = useHistory();\n const isStorageLoaded = useStorage(() => true) ?? false;\n\n // These options are not reactive, only their initial values are used.\n const frozenOptions = useInitial({\n nodes: options.nodes,\n edges: options.edges,\n storageKey: options.storageKey ?? DEFAULT_STORAGE_KEY,\n suspense: options.suspense ?? false,\n });\n\n // Pre-compute sync config caches once (not on every render)\n const [getNodeSyncConfig, getEdgeSyncConfig] = useMemo(\n () =>\n [\n buildNodeConfigCache(frozenOptions.nodes?.sync),\n buildEdgeConfigCache(frozenOptions.edges?.sync),\n ] as const,\n [frozenOptions]\n );\n\n const { nodes, edges } = useNodesAndEdges<N, E>(frozenOptions.storageKey);\n\n const onNodesChange = useMutation(\n ({ storage }, changes: NodeChange<N>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyNodeChanges(changes, flow.get(\"nodes\"), history, getNodeSyncConfig);\n },\n [history, frozenOptions, getNodeSyncConfig]\n );\n\n const onEdgesChange = useMutation(\n ({ storage }, changes: EdgeChange<E>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyEdgeChanges(changes, flow.get(\"edges\"), getEdgeSyncConfig);\n },\n [frozenOptions, getEdgeSyncConfig]\n );\n\n const onConnect = useMutation(\n ({ storage }, connection: Connection) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n // Delegate to React Flow's own `addEdge` helper for consistent default\n // edge ID generation, passing an empty array since de-duplication is\n // already handled above.\n const [newEdge] = defaultAddEdge(connection, [] as E[]);\n if (!newEdge) {\n return;\n }\n\n const edges = flow.get(\"edges\");\n const config = getEdgeSyncConfig(newEdge.type);\n edges.set(newEdge.id, toLiveblocksInternalEdge(newEdge, config));\n },\n [frozenOptions.storageKey, getEdgeSyncConfig]\n );\n\n const onDelete = useMutation(\n ({ storage }, params: { nodes: N[]; edges: E[] }) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n const nodesMap = flow.get(\"nodes\");\n const edgesMap = flow.get(\"edges\");\n\n for (const edge of params.edges) {\n edgesMap.delete(edge.id);\n }\n\n for (const node of params.nodes) {\n nodesMap.delete(node.id);\n }\n },\n [frozenOptions.storageKey]\n );\n\n const setInitialStorage = useMutation(\n ({ storage }) => {\n // Similarly to `initialStorage` on `Client.enterRoom` and `RoomProvider`, we only\n // initialize Storage if it doesn't already exist.\n if (storage.get(frozenOptions.storageKey) !== undefined) {\n return;\n }\n\n const initialNodes = frozenOptions.nodes?.initial ?? [];\n const initialEdges = frozenOptions.edges?.initial ?? [];\n\n storage.set(\n frozenOptions.storageKey,\n new LiveObject({\n nodes: new LiveMap(\n initialNodes.map((node) => [\n node.id,\n toLiveblocksInternalNode(node, getNodeSyncConfig(node.type)),\n ])\n ),\n edges: new LiveMap(\n initialEdges.map((edge) => [\n edge.id,\n toLiveblocksInternalEdge(edge, getEdgeSyncConfig(edge.type)),\n ])\n ),\n })\n );\n },\n [frozenOptions, getNodeSyncConfig, getEdgeSyncConfig]\n );\n\n useEffect(() => {\n if (isStorageLoaded) {\n history[kInternal].withoutHistory(() => {\n setInitialStorage();\n });\n }\n }, [isStorageLoaded, setInitialStorage, history]);\n\n if (frozenOptions.suspense) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `suspense` is frozen so this branch is stable\n useSuspendUntilStorageReady();\n }\n\n return {\n nodes: frozenOptions.suspense ? (nodes ?? (EMPTY_ARRAY as N[])) : nodes,\n edges: frozenOptions.suspense ? (edges ?? (EMPTY_ARRAY as E[])) : edges,\n isLoading: frozenOptions.suspense ? false : !isStorageLoaded,\n onNodesChange,\n onEdgesChange,\n onConnect,\n onDelete,\n } as UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>;\n}\n"],"names":["toLiveblocksInternalNode","toLiveblocksInternalEdge","useStorage","useMemo","useHistory","useInitial","DEFAULT_STORAGE_KEY","buildNodeConfigCache","buildEdgeConfigCache","useMutation","defaultAddEdge","edges","LiveObject","LiveMap","useEffect","kInternal","useSuspendUntilStorageReady"],"mappings":"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"}
package/dist/flow.js CHANGED
@@ -1,35 +1,11 @@
1
1
  import { LiveObject, LiveMap, kInternal } from '@liveblocks/core';
2
- import { useHistory, useStorage, useMutation } from '@liveblocks/react';
2
+ import { useStorage, useHistory, useMutation } from '@liveblocks/react';
3
3
  import { useInitial, useSuspendUntilStorageReady } from '@liveblocks/react/_private';
4
4
  import { addEdge } from '@xyflow/react';
5
5
  import { useMemo, useEffect } from 'react';
6
- import { NODE_BASE_CONFIG, EDGE_BASE_CONFIG, DEFAULT_STORAGE_KEY } from './constants.js';
7
- import { toLiveblocksInternalNode, toLiveblocksInternalEdge } from './helpers.js';
6
+ import { toLiveblocksInternalNode, toLiveblocksInternalEdge, DEFAULT_STORAGE_KEY, buildNodeConfigCache, buildEdgeConfigCache } from './helpers.js';
8
7
 
9
8
  const EMPTY_ARRAY = [];
10
- function mergeAndBuildDataConfigCache(base, data) {
11
- if (!data)
12
- return () => base;
13
- const dataFallback = data["*"];
14
- const fallback = dataFallback ? { ...base, data: dataFallback } : base;
15
- const cache = /* @__PURE__ */ new Map();
16
- for (const type in data) {
17
- if (type === "*")
18
- continue;
19
- const specific = data[type];
20
- if (!specific)
21
- continue;
22
- const dataConfig = { ...dataFallback, ...specific };
23
- cache.set(type, { ...base, data: dataConfig });
24
- }
25
- return (type) => cache.get(type) || fallback;
26
- }
27
- function buildNodeConfigCache(nodeDataConfig) {
28
- return mergeAndBuildDataConfigCache(NODE_BASE_CONFIG, nodeDataConfig);
29
- }
30
- function buildEdgeConfigCache(edgeDataConfig) {
31
- return mergeAndBuildDataConfigCache(EDGE_BASE_CONFIG, edgeDataConfig);
32
- }
33
9
  function applyNodeChanges(changes, nodes, history, getNodeSyncConfig) {
34
10
  for (const change of changes) {
35
11
  switch (change.type) {
@@ -135,6 +111,29 @@ function applyEdgeChanges(changes, edges, getEdgeSyncConfig) {
135
111
  }
136
112
  }
137
113
  }
114
+ function nodeMapToList(nodesMap) {
115
+ if (nodesMap === null)
116
+ return null;
117
+ return Object.values(nodesMap);
118
+ }
119
+ function edgeMapToList(edgesMap) {
120
+ if (edgesMap === null)
121
+ return null;
122
+ return Object.values(edgesMap);
123
+ }
124
+ function useNodesAndEdges(storageKey) {
125
+ const nodesMap = useStorage((storage) => {
126
+ const flow = storage[storageKey];
127
+ return flow?.nodes ?? null;
128
+ });
129
+ const edgesMap = useStorage((storage) => {
130
+ const flow = storage[storageKey];
131
+ return flow?.edges ?? null;
132
+ });
133
+ const nodes = useMemo(() => nodeMapToList(nodesMap), [nodesMap]);
134
+ const edges = useMemo(() => edgeMapToList(edgesMap), [edgesMap]);
135
+ return { nodes, edges };
136
+ }
138
137
  function useLiveblocksFlow(options = {}) {
139
138
  const history = useHistory();
140
139
  const isStorageLoaded = useStorage(() => true) ?? false;
@@ -151,14 +150,7 @@ function useLiveblocksFlow(options = {}) {
151
150
  ],
152
151
  [frozenOptions]
153
152
  );
154
- const nodes = useStorage((storage) => {
155
- const flow = storage[frozenOptions.storageKey];
156
- return flow?.nodes ? [...flow.nodes.values()] : null;
157
- });
158
- const edges = useStorage((storage) => {
159
- const flow = storage[frozenOptions.storageKey];
160
- return flow?.edges ? [...flow.edges.values()] : null;
161
- });
153
+ const { nodes, edges } = useNodesAndEdges(frozenOptions.storageKey);
162
154
  const onNodesChange = useMutation(
163
155
  ({ storage }, changes) => {
164
156
  const flow = storage.get(frozenOptions.storageKey);
package/dist/flow.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"flow.js","sources":["../src/flow.ts"],"sourcesContent":["import type {\n History,\n JsonObject,\n Resolve,\n ToImmutable,\n} from \"@liveblocks/core\";\nimport { kInternal, LiveMap, LiveObject } from \"@liveblocks/core\";\nimport { useHistory, useMutation, useStorage } from \"@liveblocks/react\";\nimport {\n useInitial,\n useSuspendUntilStorageReady,\n} from \"@liveblocks/react/_private\";\nimport type {\n BuiltInEdge,\n BuiltInNode,\n Connection,\n Edge,\n EdgeChange,\n Node,\n NodeChange,\n OnConnect,\n OnDelete,\n OnEdgesChange,\n OnNodesChange,\n} from \"@xyflow/react\";\nimport { addEdge as defaultAddEdge } from \"@xyflow/react\";\nimport { useEffect, useMemo } from \"react\";\n\nimport {\n DEFAULT_STORAGE_KEY,\n EDGE_BASE_CONFIG,\n NODE_BASE_CONFIG,\n} from \"./constants\";\nimport { toLiveblocksInternalEdge, toLiveblocksInternalNode } from \"./helpers\";\nimport type {\n EdgeSyncConfig,\n InternalLiveblocksEdge,\n InternalLiveblocksFlow,\n InternalLiveblocksNode,\n NodeSyncConfig,\n SyncConfig,\n} from \"./types\";\n\nconst EMPTY_ARRAY = [] as unknown[];\n\ntype UseLiveblocksFlowResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Resolve<\n (\n | {\n nodes: null;\n edges: null;\n isLoading: true;\n }\n | {\n nodes: N[];\n edges: E[];\n isLoading: false;\n }\n ) & {\n onNodesChange: OnNodesChange<N>;\n onEdgesChange: OnEdgesChange<E>;\n onConnect: OnConnect;\n onDelete: OnDelete<N, E>;\n }\n>;\n\ntype LiveblocksFlowSuspenseResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Extract<UseLiveblocksFlowResult<N, E>, { isLoading: false }>;\n\nfunction mergeAndBuildDataConfigCache(\n base: SyncConfig,\n data?: Record<string, SyncConfig | undefined>\n): (type: string | undefined) => SyncConfig {\n if (!data) return () => base;\n\n const dataFallback = data[\"*\"];\n const fallback = dataFallback ? { ...base, data: dataFallback } : base;\n\n // Pre-compute full node/edge sync configs for all explicitly declared types\n const cache = new Map<string | undefined, SyncConfig>();\n for (const type in data) {\n if (type === \"*\") continue;\n const specific = data[type];\n if (!specific) continue;\n const dataConfig: SyncConfig = { ...dataFallback, ...specific };\n cache.set(type, { ...base, data: dataConfig });\n }\n\n return (type) => cache.get(type) || fallback;\n}\n\nfunction buildNodeConfigCache<N extends Node>(\n /** The user-provided node data sync configuration, if any. */\n nodeDataConfig?: NodeSyncConfig<N>\n): (type: string | undefined) => SyncConfig {\n return mergeAndBuildDataConfigCache(NODE_BASE_CONFIG, nodeDataConfig);\n}\n\nfunction buildEdgeConfigCache<E extends Edge>(\n /** The user-provided edge data sync configuration, if any. */\n edgeDataConfig?: EdgeSyncConfig<E>\n): (type: string | undefined) => SyncConfig {\n return mergeAndBuildDataConfigCache(EDGE_BASE_CONFIG, edgeDataConfig);\n}\n\ntype UseLiveblocksFlowOptions<N extends Node, E extends Edge> = {\n nodes?: {\n /**\n * The initial React Flow nodes.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * initial: [\n * { id: \"1\", position: { x: 0, y: 0 }, data: { label: \"Node 1\" } },\n * { id: \"2\", position: { x: 0, y: 100 }, data: { label: \"Node 2\" } },\n * ],\n * },\n * });\n * ```\n */\n initial?: N[];\n /**\n * Per-type sync configuration for node data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all node types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * sync: {\n * \"*\": {\n * label: false, // Don't sync node.data.label\n * color: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomNode: {\n * showPreview: false, // Don't sync myCustomNode.data.showPreview\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: NodeSyncConfig<N>;\n };\n\n edges?: {\n initial?: E[];\n /**\n * Per-type sync configuration for edge data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all edge types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * edges: {\n * sync: {\n * \"*\": {\n * hovered: false, // Don't sync edge.data.hovered\n * style: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomEdge: {\n * isHighlighted: false, // Don't sync myCustomEdge.data.isHighlighted\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: EdgeSyncConfig<E>;\n };\n\n /**\n * The key used to store the React Flow diagram in Liveblocks Storage.\n * Defaults to `\"flow\"`.\n */\n storageKey?: string;\n\n /**\n * When true, suspends until Storage is ready (use a React `Suspense`\n * boundary). Then `nodes` and `edges` are always arrays and `isLoading` is\n * always false.\n */\n suspense?: boolean;\n};\n\n// Similar to React Flow's `applyNodeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-node-changes\nfunction applyNodeChanges<N extends Node>(\n changes: NodeChange<N>[],\n nodes: LiveMap<string, InternalLiveblocksNode>,\n history: History,\n getNodeSyncConfig: (nodeType: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getNodeSyncConfig(change.item.type);\n const existing = nodes.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n nodes.set(\n change.item.id,\n toLiveblocksInternalNode(change.item, config)\n );\n }\n break;\n }\n\n case \"position\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.dragging === true) {\n history.pause();\n }\n\n if (change.position !== undefined) {\n const prev = node.get(\"position\");\n if (prev?.x !== change.position.x || prev?.y !== change.position.y) {\n node.set(\"position\", change.position);\n }\n }\n\n if (change.dragging !== undefined) {\n if (change.dragging === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `dragging: false`\n node.setLocal(\"dragging\", change.dragging);\n }\n break;\n }\n\n case \"dimensions\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.resizing === true) {\n history.pause();\n }\n\n if (\n change.dimensions !== undefined &&\n change.setAttributes !== undefined\n ) {\n if (\n change.setAttributes === true ||\n change.setAttributes === \"width\"\n ) {\n node.set(\"width\", change.dimensions.width);\n }\n\n if (\n change.setAttributes === true ||\n change.setAttributes === \"height\"\n ) {\n node.set(\"height\", change.dimensions.height);\n }\n }\n\n if (change.dimensions !== undefined) {\n // Must match NODE_BASE_CONFIG's `measured: false`\n node.setLocal(\"measured\", change.dimensions);\n }\n\n if (change.resizing !== undefined) {\n if (change.resizing === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `resizing: false`\n node.setLocal(\"resizing\", change.resizing);\n }\n\n break;\n }\n\n case \"select\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Must match NODE_BASE_CONFIG's `selected: false`\n node.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n// Similar to React Flow's `applyEdgeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-edge-changes\nfunction applyEdgeChanges<E extends Edge>(\n changes: EdgeChange<E>[],\n edges: LiveMap<string, InternalLiveblocksEdge>,\n getEdgeSyncConfig: (type: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getEdgeSyncConfig(change.item.type);\n const existing = edges.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n edges.set(\n change.item.id,\n toLiveblocksInternalEdge(change.item, config)\n );\n }\n break;\n }\n\n case \"select\": {\n const edge = edges.get(change.id);\n if (!edge) break;\n // Must match EDGE_BASE_CONFIG's `selected: false`\n edge.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n/**\n * Returns a controlled React Flow state backed by Liveblocks Storage.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete, isLoading } = useLiveblocksFlow();\n *\n * if (isLoading) {\n * return <div>Loading…</div>\n * }\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n * Pass `{ suspense: true }` to suspend until Storage is ready, `nodes` and `edges` will never be `null`.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete } =\n * useLiveblocksFlow({ suspense: true });\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n */\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options?: UseLiveblocksFlowOptions<N, E> & { suspense?: false }\n): Resolve<UseLiveblocksFlowResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> & { suspense: true }\n): Resolve<LiveblocksFlowSuspenseResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> = {}\n): Resolve<UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>> {\n const history = useHistory();\n const isStorageLoaded = useStorage(() => true) ?? false;\n\n // These options are not reactive, only their initial values are used.\n const frozenOptions = useInitial({\n nodes: options.nodes,\n edges: options.edges,\n storageKey: options.storageKey ?? DEFAULT_STORAGE_KEY,\n suspense: options.suspense ?? false,\n });\n\n // Pre-compute sync config caches once (not on every render)\n const [getNodeSyncConfig, getEdgeSyncConfig] = useMemo(\n () =>\n [\n buildNodeConfigCache(frozenOptions.nodes?.sync),\n buildEdgeConfigCache(frozenOptions.edges?.sync),\n ] as const,\n [frozenOptions]\n );\n\n // Storage already includes local overlays via toImmutable(), so no\n // separate local layer is needed. Individual node/edge immutable references\n // are already stable (only change when the underlying LiveObject changes).\n const nodes = useStorage((storage) => {\n const flow = storage[frozenOptions.storageKey] as\n | ToImmutable<InternalLiveblocksFlow>\n | undefined;\n return flow?.nodes ? ([...flow.nodes.values()] as unknown as N[]) : null;\n });\n const edges = useStorage((storage) => {\n const flow = storage[frozenOptions.storageKey] as\n | ToImmutable<InternalLiveblocksFlow>\n | undefined;\n return flow?.edges ? ([...flow.edges.values()] as unknown as E[]) : null;\n });\n\n const onNodesChange = useMutation(\n ({ storage }, changes: NodeChange<N>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyNodeChanges(changes, flow.get(\"nodes\"), history, getNodeSyncConfig);\n },\n [history, frozenOptions, getNodeSyncConfig]\n );\n\n const onEdgesChange = useMutation(\n ({ storage }, changes: EdgeChange<E>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyEdgeChanges(changes, flow.get(\"edges\"), getEdgeSyncConfig);\n },\n [frozenOptions, getEdgeSyncConfig]\n );\n\n const onConnect = useMutation(\n ({ storage }, connection: Connection) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n // Delegate to React Flow's own `addEdge` helper for consistent default\n // edge ID generation, passing an empty array since de-duplication is\n // already handled above.\n const [newEdge] = defaultAddEdge(connection, [] as E[]);\n if (!newEdge) {\n return;\n }\n\n const edges = flow.get(\"edges\");\n const config = getEdgeSyncConfig(newEdge.type);\n edges.set(newEdge.id, toLiveblocksInternalEdge(newEdge, config));\n },\n [frozenOptions.storageKey, getEdgeSyncConfig]\n );\n\n const onDelete = useMutation(\n ({ storage }, params: { nodes: N[]; edges: E[] }) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n const nodesMap = flow.get(\"nodes\");\n const edgesMap = flow.get(\"edges\");\n\n for (const edge of params.edges) {\n edgesMap.delete(edge.id);\n }\n\n for (const node of params.nodes) {\n nodesMap.delete(node.id);\n }\n },\n [frozenOptions.storageKey]\n );\n\n const setInitialStorage = useMutation(\n ({ storage }) => {\n // Similarly to `initialStorage` on `Client.enterRoom` and `RoomProvider`, we only\n // initialize Storage if it doesn't already exist.\n if (storage.get(frozenOptions.storageKey) !== undefined) {\n return;\n }\n\n const initialNodes = frozenOptions.nodes?.initial ?? [];\n const initialEdges = frozenOptions.edges?.initial ?? [];\n\n storage.set(\n frozenOptions.storageKey,\n new LiveObject({\n nodes: new LiveMap(\n initialNodes.map((node) => [\n node.id,\n toLiveblocksInternalNode(node, getNodeSyncConfig(node.type)),\n ])\n ),\n edges: new LiveMap(\n initialEdges.map((edge) => [\n edge.id,\n toLiveblocksInternalEdge(edge, getEdgeSyncConfig(edge.type)),\n ])\n ),\n })\n );\n },\n [frozenOptions, getNodeSyncConfig, getEdgeSyncConfig]\n );\n\n useEffect(() => {\n if (isStorageLoaded) {\n history[kInternal].withoutHistory(() => {\n setInitialStorage();\n });\n }\n }, [isStorageLoaded, setInitialStorage, history]);\n\n if (frozenOptions.suspense) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `suspense` is frozen so this branch is stable\n useSuspendUntilStorageReady();\n }\n\n return {\n nodes: frozenOptions.suspense ? (nodes ?? (EMPTY_ARRAY as N[])) : nodes,\n edges: frozenOptions.suspense ? (edges ?? (EMPTY_ARRAY as E[])) : edges,\n isLoading: frozenOptions.suspense ? false : !isStorageLoaded,\n onNodesChange,\n onEdgesChange,\n onConnect,\n onDelete,\n } as UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>;\n}\n"],"names":["defaultAddEdge","edges"],"mappings":"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"}
1
+ {"version":3,"file":"flow.js","sources":["../src/flow.ts"],"sourcesContent":["import type {\n History,\n JsonObject,\n ReadonlyJsonObject,\n Resolve,\n ToJson,\n} from \"@liveblocks/core\";\nimport { kInternal, LiveMap, LiveObject } from \"@liveblocks/core\";\nimport { useHistory, useMutation, useStorage } from \"@liveblocks/react\";\nimport {\n useInitial,\n useSuspendUntilStorageReady,\n} from \"@liveblocks/react/_private\";\nimport type {\n BuiltInEdge,\n BuiltInNode,\n Connection,\n Edge,\n EdgeChange,\n Node,\n NodeChange,\n OnConnect,\n OnDelete,\n OnEdgesChange,\n OnNodesChange,\n} from \"@xyflow/react\";\nimport { addEdge as defaultAddEdge } from \"@xyflow/react\";\nimport { useEffect, useMemo } from \"react\";\n\nimport {\n buildEdgeConfigCache,\n buildNodeConfigCache,\n DEFAULT_STORAGE_KEY,\n toLiveblocksInternalEdge,\n toLiveblocksInternalNode,\n} from \"./helpers\";\nimport type {\n EdgeSyncConfig,\n InternalLiveblocksEdge,\n InternalLiveblocksFlow,\n InternalLiveblocksNode,\n NodeSyncConfig,\n SyncConfig,\n} from \"./types\";\n\nconst EMPTY_ARRAY = [] as unknown[];\n\ntype UseLiveblocksFlowResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Resolve<\n (\n | {\n nodes: null;\n edges: null;\n isLoading: true;\n }\n | {\n nodes: N[];\n edges: E[];\n isLoading: false;\n }\n ) & {\n onNodesChange: OnNodesChange<N>;\n onEdgesChange: OnEdgesChange<E>;\n onConnect: OnConnect;\n onDelete: OnDelete<N, E>;\n }\n>;\n\ntype LiveblocksFlowSuspenseResult<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n> = Extract<UseLiveblocksFlowResult<N, E>, { isLoading: false }>;\n\ntype UseLiveblocksFlowOptions<N extends Node, E extends Edge> = {\n nodes?: {\n /**\n * The initial React Flow nodes.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * initial: [\n * { id: \"1\", position: { x: 0, y: 0 }, data: { label: \"Node 1\" } },\n * { id: \"2\", position: { x: 0, y: 100 }, data: { label: \"Node 2\" } },\n * ],\n * },\n * });\n * ```\n */\n initial?: N[];\n /**\n * Per-type sync configuration for node data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all node types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * nodes: {\n * sync: {\n * \"*\": {\n * label: false, // Don't sync node.data.label\n * color: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomNode: {\n * showPreview: false, // Don't sync myCustomNode.data.showPreview\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: NodeSyncConfig<N>;\n };\n\n edges?: {\n initial?: E[];\n /**\n * Per-type sync configuration for edge data keys.\n *\n * Each key in the config is either:\n * - `false` - Local-only, not synced to other clients\n * - `\"atomic\"` - Synced as a single value (replaced as-a-whole, not patched)\n * - `true` (or absent) - Deep sync (default, objects/arrays are patched)\n * - `{ ... }` - Nested config, applies recursively to sub-keys\n *\n * Use `\"*\"` as a fallback for all edge types. Type-specific entries are\n * deep-merged on top of `\"*\"`, with explicitly named keys taking\n * precedence.\n *\n * @example\n * ```tsx\n * const { ... } = useLiveblocksFlow({\n * edges: {\n * sync: {\n * \"*\": {\n * hovered: false, // Don't sync edge.data.hovered\n * style: \"atomic\", // Sync as a single value, replaced as-a-whole\n * },\n * myCustomEdge: {\n * isHighlighted: false, // Don't sync myCustomEdge.data.isHighlighted\n * },\n * },\n * },\n * });\n * ```\n */\n sync?: EdgeSyncConfig<E>;\n };\n\n /**\n * The key used to store the React Flow diagram in Liveblocks Storage.\n * Defaults to `\"flow\"`.\n */\n storageKey?: string;\n\n /**\n * When true, suspends until Storage is ready (use a React `Suspense`\n * boundary). Then `nodes` and `edges` are always arrays and `isLoading` is\n * always false.\n */\n suspense?: boolean;\n};\n\n// Similar to React Flow's `applyNodeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-node-changes\nfunction applyNodeChanges<N extends Node>(\n changes: NodeChange<N>[],\n nodes: LiveMap<string, InternalLiveblocksNode>,\n history: History,\n getNodeSyncConfig: (nodeType: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getNodeSyncConfig(change.item.type);\n const existing = nodes.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n nodes.set(\n change.item.id,\n toLiveblocksInternalNode(change.item, config)\n );\n }\n break;\n }\n\n case \"position\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.dragging === true) {\n history.pause();\n }\n\n if (change.position !== undefined) {\n const prev = node.get(\"position\");\n if (prev?.x !== change.position.x || prev?.y !== change.position.y) {\n node.set(\"position\", change.position);\n }\n }\n\n if (change.dragging !== undefined) {\n if (change.dragging === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `dragging: false`\n node.setLocal(\"dragging\", change.dragging);\n }\n break;\n }\n\n case \"dimensions\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Pause before setting the first position change so it doesn't create a separate undo step.\n if (change.resizing === true) {\n history.pause();\n }\n\n if (\n change.dimensions !== undefined &&\n change.setAttributes !== undefined\n ) {\n if (\n change.setAttributes === true ||\n change.setAttributes === \"width\"\n ) {\n node.set(\"width\", change.dimensions.width);\n }\n\n if (\n change.setAttributes === true ||\n change.setAttributes === \"height\"\n ) {\n node.set(\"height\", change.dimensions.height);\n }\n }\n\n if (change.dimensions !== undefined) {\n // Must match NODE_BASE_CONFIG's `measured: false`\n node.setLocal(\"measured\", change.dimensions);\n }\n\n if (change.resizing !== undefined) {\n if (change.resizing === false) {\n history.resume();\n }\n // Must match NODE_BASE_CONFIG's `resizing: false`\n node.setLocal(\"resizing\", change.resizing);\n }\n\n break;\n }\n\n case \"select\": {\n const node = nodes.get(change.id);\n if (!node) break;\n\n // Must match NODE_BASE_CONFIG's `selected: false`\n node.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n// Similar to React Flow's `applyEdgeChanges()`, but writes local-only\n// properties via `setLocal()` / `delete()` on the LiveObject directly.\n// https://reactflow.dev/api-reference/utils/apply-edge-changes\nfunction applyEdgeChanges<E extends Edge>(\n changes: EdgeChange<E>[],\n edges: LiveMap<string, InternalLiveblocksEdge>,\n getEdgeSyncConfig: (type: string | undefined) => SyncConfig\n): void {\n for (const change of changes) {\n switch (change.type) {\n case \"add\":\n case \"replace\": {\n const config = getEdgeSyncConfig(change.item.type);\n const existing = edges.get(change.item.id);\n if (existing) {\n existing.reconcile(change.item as unknown as JsonObject, config);\n } else {\n edges.set(\n change.item.id,\n toLiveblocksInternalEdge(change.item, config)\n );\n }\n break;\n }\n\n case \"select\": {\n const edge = edges.get(change.id);\n if (!edge) break;\n // Must match EDGE_BASE_CONFIG's `selected: false`\n edge.setLocal(\"selected\", change.selected);\n break;\n }\n\n case \"remove\":\n // Removals are handled by onDelete for atomic undo\n break;\n }\n }\n}\n\n// TODO (LB-3665): To support sub-nodes, this function will need to emit nodes\n// in topological order (parents before children), deferring any node with a\n// parentId until its parent has been emitted.\nfunction nodeMapToList<N extends Node>(\n nodesMap: ReadonlyJsonObject | null\n): N[] | null {\n if (nodesMap === null) return null;\n return Object.values(nodesMap) as unknown as N[];\n}\n\nfunction edgeMapToList<E extends Edge>(\n edgesMap: ReadonlyJsonObject | null\n): E[] | null {\n if (edgesMap === null) return null;\n return Object.values(edgesMap) as unknown as E[];\n}\n\nfunction useNodesAndEdges<N extends Node, E extends Edge>(storageKey: string) {\n // Storage already includes local overlays via toJSON(), so no separate local\n // layer is needed. Individual node/edge immutable references are already\n // stable (only change when the underlying LiveObject changes).\n const nodesMap = useStorage((storage) => {\n const flow = storage[storageKey] as\n | ToJson<InternalLiveblocksFlow>\n | undefined;\n return flow?.nodes ?? null;\n });\n const edgesMap = useStorage((storage) => {\n const flow = storage[storageKey] as\n | ToJson<InternalLiveblocksFlow>\n | undefined;\n return flow?.edges ?? null;\n });\n\n const nodes = useMemo(() => nodeMapToList<N>(nodesMap), [nodesMap]);\n const edges = useMemo(() => edgeMapToList<E>(edgesMap), [edgesMap]);\n return { nodes, edges };\n}\n\n/**\n * Returns a controlled React Flow state backed by Liveblocks Storage.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete, isLoading } = useLiveblocksFlow();\n *\n * if (isLoading) {\n * return <div>Loading…</div>\n * }\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n * Pass `{ suspense: true }` to suspend until Storage is ready, `nodes` and `edges` will never be `null`.\n *\n * @example\n * ```tsx\n * const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onDelete } =\n * useLiveblocksFlow({ suspense: true });\n *\n * return <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDelete={onDelete} />;\n * ```\n */\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options?: UseLiveblocksFlowOptions<N, E> & { suspense?: false }\n): Resolve<UseLiveblocksFlowResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> & { suspense: true }\n): Resolve<LiveblocksFlowSuspenseResult<N, E>>;\nexport function useLiveblocksFlow<\n N extends Node = BuiltInNode,\n E extends Edge = BuiltInEdge,\n>(\n options: UseLiveblocksFlowOptions<N, E> = {}\n): Resolve<UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>> {\n const history = useHistory();\n const isStorageLoaded = useStorage(() => true) ?? false;\n\n // These options are not reactive, only their initial values are used.\n const frozenOptions = useInitial({\n nodes: options.nodes,\n edges: options.edges,\n storageKey: options.storageKey ?? DEFAULT_STORAGE_KEY,\n suspense: options.suspense ?? false,\n });\n\n // Pre-compute sync config caches once (not on every render)\n const [getNodeSyncConfig, getEdgeSyncConfig] = useMemo(\n () =>\n [\n buildNodeConfigCache(frozenOptions.nodes?.sync),\n buildEdgeConfigCache(frozenOptions.edges?.sync),\n ] as const,\n [frozenOptions]\n );\n\n const { nodes, edges } = useNodesAndEdges<N, E>(frozenOptions.storageKey);\n\n const onNodesChange = useMutation(\n ({ storage }, changes: NodeChange<N>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyNodeChanges(changes, flow.get(\"nodes\"), history, getNodeSyncConfig);\n },\n [history, frozenOptions, getNodeSyncConfig]\n );\n\n const onEdgesChange = useMutation(\n ({ storage }, changes: EdgeChange<E>[]) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n applyEdgeChanges(changes, flow.get(\"edges\"), getEdgeSyncConfig);\n },\n [frozenOptions, getEdgeSyncConfig]\n );\n\n const onConnect = useMutation(\n ({ storage }, connection: Connection) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n // Delegate to React Flow's own `addEdge` helper for consistent default\n // edge ID generation, passing an empty array since de-duplication is\n // already handled above.\n const [newEdge] = defaultAddEdge(connection, [] as E[]);\n if (!newEdge) {\n return;\n }\n\n const edges = flow.get(\"edges\");\n const config = getEdgeSyncConfig(newEdge.type);\n edges.set(newEdge.id, toLiveblocksInternalEdge(newEdge, config));\n },\n [frozenOptions.storageKey, getEdgeSyncConfig]\n );\n\n const onDelete = useMutation(\n ({ storage }, params: { nodes: N[]; edges: E[] }) => {\n const flow = storage.get(frozenOptions.storageKey) as\n | InternalLiveblocksFlow\n | undefined;\n if (!flow) {\n return;\n }\n\n const nodesMap = flow.get(\"nodes\");\n const edgesMap = flow.get(\"edges\");\n\n for (const edge of params.edges) {\n edgesMap.delete(edge.id);\n }\n\n for (const node of params.nodes) {\n nodesMap.delete(node.id);\n }\n },\n [frozenOptions.storageKey]\n );\n\n const setInitialStorage = useMutation(\n ({ storage }) => {\n // Similarly to `initialStorage` on `Client.enterRoom` and `RoomProvider`, we only\n // initialize Storage if it doesn't already exist.\n if (storage.get(frozenOptions.storageKey) !== undefined) {\n return;\n }\n\n const initialNodes = frozenOptions.nodes?.initial ?? [];\n const initialEdges = frozenOptions.edges?.initial ?? [];\n\n storage.set(\n frozenOptions.storageKey,\n new LiveObject({\n nodes: new LiveMap(\n initialNodes.map((node) => [\n node.id,\n toLiveblocksInternalNode(node, getNodeSyncConfig(node.type)),\n ])\n ),\n edges: new LiveMap(\n initialEdges.map((edge) => [\n edge.id,\n toLiveblocksInternalEdge(edge, getEdgeSyncConfig(edge.type)),\n ])\n ),\n })\n );\n },\n [frozenOptions, getNodeSyncConfig, getEdgeSyncConfig]\n );\n\n useEffect(() => {\n if (isStorageLoaded) {\n history[kInternal].withoutHistory(() => {\n setInitialStorage();\n });\n }\n }, [isStorageLoaded, setInitialStorage, history]);\n\n if (frozenOptions.suspense) {\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `suspense` is frozen so this branch is stable\n useSuspendUntilStorageReady();\n }\n\n return {\n nodes: frozenOptions.suspense ? (nodes ?? (EMPTY_ARRAY as N[])) : nodes,\n edges: frozenOptions.suspense ? (edges ?? (EMPTY_ARRAY as E[])) : edges,\n isLoading: frozenOptions.suspense ? false : !isStorageLoaded,\n onNodesChange,\n onEdgesChange,\n onConnect,\n onDelete,\n } as UseLiveblocksFlowResult<N, E> | LiveblocksFlowSuspenseResult<N, E>;\n}\n"],"names":["defaultAddEdge","edges"],"mappings":"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"}
package/dist/helpers.cjs CHANGED
@@ -1,8 +1,58 @@
1
1
  'use strict';
2
2
 
3
3
  var core = require('@liveblocks/core');
4
- var constants = require('./constants.cjs');
5
4
 
5
+ const DEFAULT_STORAGE_KEY = "flow";
6
+ const NODE_BASE_CONFIG = {
7
+ // Local-only (not synced)
8
+ selected: false,
9
+ dragging: false,
10
+ measured: false,
11
+ resizing: false,
12
+ // Atomic (synced as plain Json)
13
+ position: "atomic",
14
+ sourcePosition: "atomic",
15
+ targetPosition: "atomic",
16
+ extent: "atomic",
17
+ origin: "atomic",
18
+ handles: "atomic"
19
+ // Note: the `data` key is intentionally left out of this base config, as it
20
+ // is expected to be provided by the end user
21
+ };
22
+ const EDGE_BASE_CONFIG = {
23
+ // Local-only (not synced)
24
+ selected: false,
25
+ // Atomic (synced as plain Json)
26
+ markerStart: "atomic",
27
+ markerEnd: "atomic",
28
+ label: "atomic",
29
+ labelBgPadding: "atomic"
30
+ // Note: the `data` key is intentionally left out of this base config, as it
31
+ // is expected to be provided by the end user
32
+ };
33
+ function buildFlowDataConfigCache(base, data) {
34
+ if (!data)
35
+ return () => base;
36
+ const dataFallback = data["*"];
37
+ const fallback = dataFallback ? { ...base, data: dataFallback } : base;
38
+ const cache = /* @__PURE__ */ new Map();
39
+ for (const type in data) {
40
+ if (type === "*")
41
+ continue;
42
+ const specific = data[type];
43
+ if (!specific)
44
+ continue;
45
+ const dataConfig = { ...dataFallback, ...specific };
46
+ cache.set(type, { ...base, data: dataConfig });
47
+ }
48
+ return (type) => cache.get(type) || fallback;
49
+ }
50
+ function buildNodeConfigCache(nodeDataConfig) {
51
+ return buildFlowDataConfigCache(NODE_BASE_CONFIG, nodeDataConfig);
52
+ }
53
+ function buildEdgeConfigCache(edgeDataConfig) {
54
+ return buildFlowDataConfigCache(EDGE_BASE_CONFIG, edgeDataConfig);
55
+ }
6
56
  function toLiveblocksInternalNode(node, config) {
7
57
  return core.LiveObject.from(
8
58
  node,
@@ -15,21 +65,13 @@ function toLiveblocksInternalEdge(edge, config) {
15
65
  config
16
66
  );
17
67
  }
18
- function toLiveblocksNode(node, config) {
19
- return toLiveblocksInternalNode(node, {
20
- ...constants.NODE_BASE_CONFIG,
21
- data: config
22
- });
23
- }
24
- function toLiveblocksEdge(edge, config) {
25
- return toLiveblocksInternalEdge(edge, {
26
- ...constants.EDGE_BASE_CONFIG,
27
- data: config
28
- });
29
- }
30
68
 
31
- exports.toLiveblocksEdge = toLiveblocksEdge;
69
+ exports.DEFAULT_STORAGE_KEY = DEFAULT_STORAGE_KEY;
70
+ exports.EDGE_BASE_CONFIG = EDGE_BASE_CONFIG;
71
+ exports.NODE_BASE_CONFIG = NODE_BASE_CONFIG;
72
+ exports.buildEdgeConfigCache = buildEdgeConfigCache;
73
+ exports.buildFlowDataConfigCache = buildFlowDataConfigCache;
74
+ exports.buildNodeConfigCache = buildNodeConfigCache;
32
75
  exports.toLiveblocksInternalEdge = toLiveblocksInternalEdge;
33
76
  exports.toLiveblocksInternalNode = toLiveblocksInternalNode;
34
- exports.toLiveblocksNode = toLiveblocksNode;
35
77
  //# sourceMappingURL=helpers.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.cjs","sources":["../src/helpers.ts"],"sourcesContent":["import type { JsonObject, SyncConfig } from \"@liveblocks/core\";\nimport { LiveObject } from \"@liveblocks/core\";\nimport type { Edge, Node } from \"@xyflow/react\";\n\nimport { EDGE_BASE_CONFIG, NODE_BASE_CONFIG } from \"./constants\";\nimport type {\n InternalLiveblocksEdge,\n InternalLiveblocksNode,\n LiveblocksEdge,\n LiveblocksNode,\n} from \"./types\";\n\nexport function toLiveblocksInternalNode<N extends Node>(\n node: N,\n config: SyncConfig\n): InternalLiveblocksNode {\n return LiveObject.from(\n node as unknown as JsonObject,\n config\n ) as InternalLiveblocksNode;\n}\n\nexport function toLiveblocksInternalEdge<E extends Edge>(\n edge: E,\n config: SyncConfig\n): InternalLiveblocksEdge {\n return LiveObject.from(\n edge as unknown as JsonObject,\n config\n ) as InternalLiveblocksEdge;\n}\n\n/**\n * @experimental\n *\n * Converts a React Flow `Node` into a Liveblocks Storage version.\n * Keys marked `false` in config are set as local-only (not synced).\n * Keys marked `\"atomic\"` are stored as plain Json (no deep wrapping).\n * All other keys are deep-liveified (objects→LiveObject, arrays→LiveList).\n */\nexport function toLiveblocksNode<N extends Node>(\n node: N,\n config?: SyncConfig\n): LiveblocksNode<N> {\n return toLiveblocksInternalNode(node, {\n ...NODE_BASE_CONFIG,\n data: config,\n }) as unknown as LiveblocksNode<N>;\n}\n\n/**\n * @experimental\n *\n * Converts a React Flow `Edge` into a Liveblocks Storage version.\n * Keys marked `false` in config are set as local-only (not synced).\n * Keys marked `\"atomic\"` are stored as plain Json (no deep wrapping).\n * All other keys are deep-liveified (objects→LiveObject, arrays→LiveList).\n */\nexport function toLiveblocksEdge<E extends Edge>(\n edge: E,\n config?: SyncConfig\n): LiveblocksEdge<E> {\n return toLiveblocksInternalEdge(edge, {\n ...EDGE_BASE_CONFIG,\n data: config,\n }) as unknown as LiveblocksEdge<E>;\n}\n"],"names":["LiveObject","NODE_BASE_CONFIG","EDGE_BASE_CONFIG"],"mappings":";;;;;AAYgB,SAAA,wBAAA,CACd,MACA,MACwB,EAAA;AACxB,EAAA,OAAOA,eAAW,CAAA,IAAA;AAAA,IAChB,IAAA;AAAA,IACA,MAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEgB,SAAA,wBAAA,CACd,MACA,MACwB,EAAA;AACxB,EAAA,OAAOA,eAAW,CAAA,IAAA;AAAA,IAChB,IAAA;AAAA,IACA,MAAA;AAAA,GACF,CAAA;AACF,CAAA;AAUgB,SAAA,gBAAA,CACd,MACA,MACmB,EAAA;AACnB,EAAA,OAAO,yBAAyB,IAAM,EAAA;AAAA,IACpC,GAAGC,0BAAA;AAAA,IACH,IAAM,EAAA,MAAA;AAAA,GACP,CAAA,CAAA;AACH,CAAA;AAUgB,SAAA,gBAAA,CACd,MACA,MACmB,EAAA;AACnB,EAAA,OAAO,yBAAyB,IAAM,EAAA;AAAA,IACpC,GAAGC,0BAAA;AAAA,IACH,IAAM,EAAA,MAAA;AAAA,GACP,CAAA,CAAA;AACH;;;;;;;"}
1
+ {"version":3,"file":"helpers.cjs","sources":["../src/helpers.ts"],"sourcesContent":["import type { JsonObject, SyncConfig, SyncMode } from \"@liveblocks/core\";\nimport { LiveObject } from \"@liveblocks/core\";\nimport type { Edge, Node } from \"@xyflow/react\";\n\nimport type { InternalLiveblocksEdge, InternalLiveblocksNode } from \"./types\";\n\nexport const DEFAULT_STORAGE_KEY = \"flow\";\n\n// React Flow specific versions of `SyncConfig` that only allow keys that are actually exposed by React Flow.\ntype NodeSyncConfig = { [K in keyof Node]?: SyncMode };\ntype EdgeSyncConfig = { [K in keyof Edge]?: SyncMode };\n\nexport const NODE_BASE_CONFIG = {\n // Local-only (not synced)\n selected: false,\n dragging: false,\n measured: false,\n resizing: false,\n\n // Atomic (synced as plain Json)\n position: \"atomic\",\n sourcePosition: \"atomic\",\n targetPosition: \"atomic\",\n extent: \"atomic\",\n origin: \"atomic\",\n handles: \"atomic\",\n\n // Note: the `data` key is intentionally left out of this base config, as it\n // is expected to be provided by the end user\n} as const satisfies NodeSyncConfig;\n\nexport const EDGE_BASE_CONFIG = {\n // Local-only (not synced)\n selected: false,\n\n // Atomic (synced as plain Json)\n markerStart: \"atomic\",\n markerEnd: \"atomic\",\n label: \"atomic\",\n labelBgPadding: \"atomic\",\n\n // Note: the `data` key is intentionally left out of this base config, as it\n // is expected to be provided by the end user\n} as const satisfies EdgeSyncConfig;\n\n/**\n * Merges a base config with per-type user data configs, returning a lookup\n * function that resolves the full SyncConfig for a given type string.\n */\nexport function buildFlowDataConfigCache(\n base: SyncConfig,\n data?: Record<string, SyncConfig | undefined>\n): (type: string | undefined) => SyncConfig {\n if (!data) return () => base;\n\n const dataFallback = data[\"*\"];\n const fallback = dataFallback ? { ...base, data: dataFallback } : base;\n\n // Pre-compute full sync configs for all explicitly declared types\n const cache = new Map<string | undefined, SyncConfig>();\n for (const type in data) {\n if (type === \"*\") continue;\n const specific = data[type];\n if (!specific) continue;\n const dataConfig: SyncConfig = { ...dataFallback, ...specific };\n cache.set(type, { ...base, data: dataConfig });\n }\n\n return (type) => cache.get(type) || fallback;\n}\n\nexport function buildNodeConfigCache(\n nodeDataConfig?: Record<string, SyncConfig | undefined>\n): (type: string | undefined) => SyncConfig {\n return buildFlowDataConfigCache(NODE_BASE_CONFIG, nodeDataConfig);\n}\n\nexport function buildEdgeConfigCache(\n edgeDataConfig?: Record<string, SyncConfig | undefined>\n): (type: string | undefined) => SyncConfig {\n return buildFlowDataConfigCache(EDGE_BASE_CONFIG, edgeDataConfig);\n}\n\nexport function toLiveblocksInternalNode<N extends Node>(\n node: N,\n config: SyncConfig\n): InternalLiveblocksNode {\n return LiveObject.from(\n node as unknown as JsonObject,\n config\n ) as InternalLiveblocksNode;\n}\n\nexport function toLiveblocksInternalEdge<E extends Edge>(\n edge: E,\n config: SyncConfig\n): InternalLiveblocksEdge {\n return LiveObject.from(\n edge as unknown as JsonObject,\n config\n ) as InternalLiveblocksEdge;\n}\n"],"names":["LiveObject"],"mappings":";;;;AAMO,MAAM,mBAAsB,GAAA,OAAA;AAM5B,MAAM,gBAAmB,GAAA;AAAA;AAAA,EAE9B,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA;AAAA,EAGV,QAAU,EAAA,QAAA;AAAA,EACV,cAAgB,EAAA,QAAA;AAAA,EAChB,cAAgB,EAAA,QAAA;AAAA,EAChB,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA,QAAA;AAAA,EACR,OAAS,EAAA,QAAA;AAAA;AAAA;AAIX,EAAA;AAEO,MAAM,gBAAmB,GAAA;AAAA;AAAA,EAE9B,QAAU,EAAA,KAAA;AAAA;AAAA,EAGV,WAAa,EAAA,QAAA;AAAA,EACb,SAAW,EAAA,QAAA;AAAA,EACX,KAAO,EAAA,QAAA;AAAA,EACP,cAAgB,EAAA,QAAA;AAAA;AAAA;AAIlB,EAAA;AAMgB,SAAA,wBAAA,CACd,MACA,IAC0C,EAAA;AAC1C,EAAA,IAAI,CAAC,IAAA;AAAM,IAAA,OAAO,MAAM,IAAA,CAAA;AAExB,EAAM,MAAA,YAAA,GAAe,KAAK,GAAG,CAAA,CAAA;AAC7B,EAAA,MAAM,WAAW,YAAe,GAAA,EAAE,GAAG,IAAM,EAAA,IAAA,EAAM,cAAiB,GAAA,IAAA,CAAA;AAGlE,EAAM,MAAA,KAAA,uBAAY,GAAoC,EAAA,CAAA;AACtD,EAAA,KAAA,MAAW,QAAQ,IAAM,EAAA;AACvB,IAAA,IAAI,IAAS,KAAA,GAAA;AAAK,MAAA,SAAA;AAClB,IAAM,MAAA,QAAA,GAAW,KAAK,IAAI,CAAA,CAAA;AAC1B,IAAA,IAAI,CAAC,QAAA;AAAU,MAAA,SAAA;AACf,IAAA,MAAM,UAAyB,GAAA,EAAE,GAAG,YAAA,EAAc,GAAG,QAAS,EAAA,CAAA;AAC9D,IAAA,KAAA,CAAM,IAAI,IAAM,EAAA,EAAE,GAAG,IAAM,EAAA,IAAA,EAAM,YAAY,CAAA,CAAA;AAAA,GAC/C;AAEA,EAAA,OAAO,CAAC,IAAA,KAAS,KAAM,CAAA,GAAA,CAAI,IAAI,CAAK,IAAA,QAAA,CAAA;AACtC,CAAA;AAEO,SAAS,qBACd,cAC0C,EAAA;AAC1C,EAAO,OAAA,wBAAA,CAAyB,kBAAkB,cAAc,CAAA,CAAA;AAClE,CAAA;AAEO,SAAS,qBACd,cAC0C,EAAA;AAC1C,EAAO,OAAA,wBAAA,CAAyB,kBAAkB,cAAc,CAAA,CAAA;AAClE,CAAA;AAEgB,SAAA,wBAAA,CACd,MACA,MACwB,EAAA;AACxB,EAAA,OAAOA,eAAW,CAAA,IAAA;AAAA,IAChB,IAAA;AAAA,IACA,MAAA;AAAA,GACF,CAAA;AACF,CAAA;AAEgB,SAAA,wBAAA,CACd,MACA,MACwB,EAAA;AACxB,EAAA,OAAOA,eAAW,CAAA,IAAA;AAAA,IAChB,IAAA;AAAA,IACA,MAAA;AAAA,GACF,CAAA;AACF;;;;;;;;;;;"}
package/dist/helpers.js CHANGED
@@ -1,6 +1,56 @@
1
1
  import { LiveObject } from '@liveblocks/core';
2
- import { NODE_BASE_CONFIG, EDGE_BASE_CONFIG } from './constants.js';
3
2
 
3
+ const DEFAULT_STORAGE_KEY = "flow";
4
+ const NODE_BASE_CONFIG = {
5
+ // Local-only (not synced)
6
+ selected: false,
7
+ dragging: false,
8
+ measured: false,
9
+ resizing: false,
10
+ // Atomic (synced as plain Json)
11
+ position: "atomic",
12
+ sourcePosition: "atomic",
13
+ targetPosition: "atomic",
14
+ extent: "atomic",
15
+ origin: "atomic",
16
+ handles: "atomic"
17
+ // Note: the `data` key is intentionally left out of this base config, as it
18
+ // is expected to be provided by the end user
19
+ };
20
+ const EDGE_BASE_CONFIG = {
21
+ // Local-only (not synced)
22
+ selected: false,
23
+ // Atomic (synced as plain Json)
24
+ markerStart: "atomic",
25
+ markerEnd: "atomic",
26
+ label: "atomic",
27
+ labelBgPadding: "atomic"
28
+ // Note: the `data` key is intentionally left out of this base config, as it
29
+ // is expected to be provided by the end user
30
+ };
31
+ function buildFlowDataConfigCache(base, data) {
32
+ if (!data)
33
+ return () => base;
34
+ const dataFallback = data["*"];
35
+ const fallback = dataFallback ? { ...base, data: dataFallback } : base;
36
+ const cache = /* @__PURE__ */ new Map();
37
+ for (const type in data) {
38
+ if (type === "*")
39
+ continue;
40
+ const specific = data[type];
41
+ if (!specific)
42
+ continue;
43
+ const dataConfig = { ...dataFallback, ...specific };
44
+ cache.set(type, { ...base, data: dataConfig });
45
+ }
46
+ return (type) => cache.get(type) || fallback;
47
+ }
48
+ function buildNodeConfigCache(nodeDataConfig) {
49
+ return buildFlowDataConfigCache(NODE_BASE_CONFIG, nodeDataConfig);
50
+ }
51
+ function buildEdgeConfigCache(edgeDataConfig) {
52
+ return buildFlowDataConfigCache(EDGE_BASE_CONFIG, edgeDataConfig);
53
+ }
4
54
  function toLiveblocksInternalNode(node, config) {
5
55
  return LiveObject.from(
6
56
  node,
@@ -13,18 +63,6 @@ function toLiveblocksInternalEdge(edge, config) {
13
63
  config
14
64
  );
15
65
  }
16
- function toLiveblocksNode(node, config) {
17
- return toLiveblocksInternalNode(node, {
18
- ...NODE_BASE_CONFIG,
19
- data: config
20
- });
21
- }
22
- function toLiveblocksEdge(edge, config) {
23
- return toLiveblocksInternalEdge(edge, {
24
- ...EDGE_BASE_CONFIG,
25
- data: config
26
- });
27
- }
28
66
 
29
- export { toLiveblocksEdge, toLiveblocksInternalEdge, toLiveblocksInternalNode, toLiveblocksNode };
67
+ export { DEFAULT_STORAGE_KEY, EDGE_BASE_CONFIG, NODE_BASE_CONFIG, buildEdgeConfigCache, buildFlowDataConfigCache, buildNodeConfigCache, toLiveblocksInternalEdge, toLiveblocksInternalNode };
30
68
  //# sourceMappingURL=helpers.js.map