@dxos/app-graph 0.4.9 → 0.4.10-main.05b9ab6
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/lib/browser/index.mjs +3 -3
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +3 -13
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/graph.ts +3 -3
- package/src/stories/EchoGraph.stories.tsx +6 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// packages/sdk/app-graph/src/graph.ts
|
|
2
2
|
import { untracked } from "@preact/signals-core";
|
|
3
|
-
import
|
|
3
|
+
import { create } from "@dxos/echo-schema";
|
|
4
4
|
import { invariant } from "@dxos/invariant";
|
|
5
5
|
import { nonNullable } from "@dxos/util";
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ var Graph = class {
|
|
|
19
19
|
/**
|
|
20
20
|
* @internal
|
|
21
21
|
*/
|
|
22
|
-
this._nodes =
|
|
22
|
+
this._nodes = create({
|
|
23
23
|
[ROOT_ID]: {
|
|
24
24
|
id: ROOT_ID,
|
|
25
25
|
properties: {},
|
|
@@ -31,7 +31,7 @@ var Graph = class {
|
|
|
31
31
|
*/
|
|
32
32
|
// Key is the `${node.id}-${direction}` and value is an ordered list of node ids.
|
|
33
33
|
// Explicit type required because TS says this is not portable.
|
|
34
|
-
this._edges =
|
|
34
|
+
this._edges = create({});
|
|
35
35
|
this._constructNode = (nodeBase) => {
|
|
36
36
|
const node = {
|
|
37
37
|
...nodeBase,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/graph.ts", "../../../src/node.ts", "../../../src/graph-builder.ts", "../../../src/helpers.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { untracked } from '@preact/signals-core';\n\nimport * as E from '@dxos/echo-schema/schema';\nimport { invariant } from '@dxos/invariant';\nimport { nonNullable } from '@dxos/util';\n\nimport { isActionLike, type EdgeDirection, type Node, type NodeArg, type NodeBase } from './node';\n\nexport const ROOT_ID = 'root';\n\nexport type TraversalOptions = {\n /**\n * The node to start traversing from.\n *\n * @default root\n */\n node?: Node;\n\n /**\n * The direction to traverse graph edges.\n *\n * @default 'outbound'\n */\n direction?: EdgeDirection;\n\n /**\n * A predicate to filter nodes which are passed to the `visitor` callback.\n */\n filter?: (node: Node) => boolean;\n\n /**\n * A callback which is called for each node visited during traversal.\n */\n visitor?: (node: Node, path: string[]) => void;\n};\n\n/**\n * The Graph represents the structure of the application constructed via plugins.\n */\nexport class Graph {\n /**\n * @internal\n */\n readonly _nodes = E.object<Record<string, NodeBase>>({\n [ROOT_ID]: { id: ROOT_ID, properties: {}, data: null },\n });\n\n /**\n * @internal\n */\n // Key is the `${node.id}-${direction}` and value is an ordered list of node ids.\n // Explicit type required because TS says this is not portable.\n readonly _edges = E.object<Record<string, string[]>>({});\n\n /**\n * Alias for `findNode('root')`.\n */\n get root() {\n return this.findNode(ROOT_ID)!;\n }\n\n /**\n * Convert the graph to a JSON object.\n */\n toJSON({ id = ROOT_ID, maxLength = 32 }: { id?: string; maxLength?: number } = {}) {\n const toJSON = (node: Node): any => {\n const nodes = node.nodes();\n const obj: Record<string, any> = {\n id: node.id.length > maxLength ? `${node.id.slice(0, maxLength - 3)}...` : node.id,\n };\n if (node.properties.label) {\n obj.label = node.properties.label;\n }\n if (nodes.length) {\n obj.nodes = nodes.map((node) => toJSON(node));\n }\n return obj;\n };\n\n const root = this.findNode(id);\n invariant(root, `Node not found: ${id}`);\n return toJSON(root);\n }\n\n /**\n * Find the node with the given id in the graph.\n */\n findNode(id: string): Node | undefined {\n const nodeBase = this._nodes[id];\n if (!nodeBase) {\n return undefined;\n }\n\n return this._constructNode(nodeBase);\n }\n\n private _constructNode = (nodeBase: NodeBase): Node => {\n const node: Node = {\n ...nodeBase,\n edges: ({ direction = 'outbound' } = {}) => {\n return this._edges[this.getEdgeKey(node.id, direction)];\n },\n nodes: ({ direction, filter } = {}) => {\n const nodes = this._getNodes({ id: node.id, direction }).filter((n) => !isActionLike(n));\n return filter ? nodes.filter((n) => filter(n, node)) : nodes;\n },\n node: (id: string) => {\n return this._getNodes({ id }).find((node) => node.id === id);\n },\n actions: () => {\n return this._getNodes({ id: node.id }).filter(isActionLike);\n },\n };\n\n return node;\n };\n\n private _getNodes({ id, direction = 'outbound' }: { id: string; direction?: EdgeDirection }): Node[] {\n const edges = this._edges[this.getEdgeKey(id, direction)];\n if (!edges) {\n return [];\n }\n\n return edges.map((id) => this.findNode(id)).filter(nonNullable);\n }\n\n private getEdgeKey(id: string, direction: EdgeDirection) {\n return `${id}-${direction}`;\n }\n\n /**\n * Add nodes to the graph.\n */\n addNodes<TData = null, TProperties extends Record<string, any> = Record<string, any>>(\n ...nodes: NodeArg<TData, TProperties>[]\n ): Node<TData, TProperties>[] {\n return nodes.map((node) => this._addNode(node));\n }\n\n private _addNode<TData, TProperties extends Record<string, any> = Record<string, any>>({\n nodes,\n edges,\n ..._node\n }: NodeArg<TData, TProperties>): Node<TData, TProperties> {\n return untracked(() => {\n const node = { data: null, properties: {}, ..._node };\n this._nodes[node.id] = node;\n\n if (nodes) {\n nodes.forEach((subNode) => {\n this._addNode(subNode);\n this.addEdge({ source: node.id, target: subNode.id });\n });\n }\n\n if (edges) {\n edges.forEach(([id, direction]) =>\n direction === 'outbound'\n ? this.addEdge({ source: node.id, target: id })\n : this.addEdge({ source: id, target: node.id }),\n );\n }\n\n return this._constructNode(node) as Node<TData, TProperties>;\n });\n }\n\n /**\n * Remove nodes from the graph.\n *\n * @param id The id of the node to remove.\n * @param edges Whether to remove edges connected to the node from the graph as well.\n */\n removeNode(id: string, edges = false) {\n untracked(() => {\n const node = this.findNode(id);\n if (!node) {\n return;\n }\n\n if (edges) {\n // Remove edges from node.\n delete this._edges[this.getEdgeKey(id, 'outbound')];\n delete this._edges[this.getEdgeKey(id, 'inbound')];\n\n // Remove edges from connected nodes.\n this._getNodes({ id }).forEach((node) => this.removeEdge({ source: id, target: node.id }));\n this._getNodes({ id, direction: 'inbound' }).forEach((node) =>\n this.removeEdge({ source: node.id, target: id }),\n );\n }\n\n // Remove node.\n delete this._nodes[id];\n });\n }\n\n /**\n * Add an edge to the graph.\n */\n addEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outbound = this._edges[this.getEdgeKey(source, 'outbound')];\n if (!outbound) {\n this._edges[this.getEdgeKey(source, 'outbound')] = [target];\n } else if (!outbound.includes(target)) {\n outbound.push(target);\n }\n\n const inbound = this._edges[this.getEdgeKey(target, 'inbound')];\n if (!inbound) {\n this._edges[this.getEdgeKey(target, 'inbound')] = [source];\n } else if (!inbound.includes(source)) {\n inbound.push(source);\n }\n });\n }\n\n /**\n * Sort edges for a node.\n *\n * Edges not included in the sorted list are appended to the end of the list.\n *\n * @param nodeId The id of the node to sort edges for.\n * @param direction The direction of the edges from the node to sort.\n * @param edges The ordered list of edges.\n */\n sortEdges(nodeId: string, direction: EdgeDirection, edges: string[]) {\n untracked(() => {\n const current = this._edges[this.getEdgeKey(nodeId, direction)];\n if (current) {\n const unsorted = current.filter((id) => !edges.includes(id)) ?? [];\n const sorted = edges.filter((id) => current.includes(id)) ?? [];\n current.splice(0, current.length, ...[...sorted, ...unsorted]);\n }\n });\n }\n\n /**\n * Remove an edge from the graph.\n */\n removeEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outboundIndex = this._edges[this.getEdgeKey(source, 'outbound')]?.findIndex((id) => id === target);\n if (outboundIndex !== -1) {\n this._edges[this.getEdgeKey(source, 'outbound')].splice(outboundIndex, 1);\n }\n\n const inboundIndex = this._edges[this.getEdgeKey(target, 'inbound')]?.findIndex((id) => id === source);\n if (inboundIndex !== -1) {\n this._edges[this.getEdgeKey(target, 'inbound')].splice(inboundIndex, 1);\n }\n });\n }\n\n /**\n * Recursive depth-first traversal.\n *\n * @param options.node The node to start traversing from.\n * @param options.direction The direction to traverse graph edges.\n * @param options.filter A predicate to filter nodes which are passed to the `visitor` callback.\n * @param options.visitor A callback which is called for each node visited during traversal.\n */\n traverse({ node = this.root, direction = 'outbound', filter, visitor }: TraversalOptions, path: string[] = []): void {\n // Break cycles.\n if (path.includes(node.id)) {\n return;\n }\n\n if (!filter || filter(node)) {\n visitor?.(node, [...path, node.id]);\n }\n\n Object.values(this._getNodes({ id: node.id, direction })).forEach((child) =>\n this.traverse({ node: child, direction, filter, visitor }, [...path, node.id]),\n );\n }\n\n /**\n * Get the path between two nodes in the graph.\n */\n getPath({ source = 'root', target }: { source?: string; target: string }): string[] | undefined {\n const start = this.findNode(source);\n if (!start) {\n return undefined;\n }\n\n let found: string[] | undefined;\n this.traverse({\n node: start,\n filter: () => !found,\n visitor: (node, path) => {\n if (node.id === target) {\n found = path;\n }\n },\n });\n\n return found;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type MaybePromise, type MakeOptional } from '@dxos/util';\n\n/**\n * Represents a node in the graph.\n */\n// TODO(wittjosiah): Use Effect Schema.\nexport type NodeBase<TData = any, TProperties extends Record<string, any> = Record<string, any>> = {\n /**\n * Globally unique ID.\n */\n id: string;\n\n /**\n * Properties of the node relevant to displaying the node.\n */\n properties: TProperties;\n\n /**\n * Data the node represents.\n */\n // TODO(burdon): Type system (e.g., minimally provide identifier string vs. TypedObject vs. Graph mixin type system)?\n // type field would prevent convoluted sniffing of object properties. And allow direct pass-through for ECHO TypedObjects.\n data: TData;\n};\n\nexport type NodeFilter<T = any, U extends Record<string, any> = Record<string, any>> = (\n node: Node<unknown, Record<string, any>>,\n connectedNode: Node,\n) => node is Node<T, U>;\n\nexport type EdgeDirection = 'outbound' | 'inbound';\n\nexport type ConnectedNodes = {\n /**\n * Edges that this node is connected to in default order.\n */\n edges(params?: { direction?: EdgeDirection }): Readonly<string[]>;\n\n /**\n * Nodes that this node is connected to in default order.\n */\n nodes<T = any, U extends Record<string, any> = Record<string, any>>(params?: {\n direction?: EdgeDirection;\n filter?: NodeFilter<T, U>;\n }): Node<T>[];\n\n /**\n * Get a specific connected node by id.\n */\n node(id: string): Node | undefined;\n};\n\nexport type ConnectedActions = {\n /**\n * Actions or action groups that this node is connected to in default order.\n */\n actions(): ActionLike[];\n};\n\nexport type Node<TData = any, TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<TData, TProperties>, 'properties'> & { properties: Readonly<TProperties> } & ConnectedNodes &\n ConnectedActions\n>;\n\nexport const isGraphNode = (data: unknown): data is Node =>\n data && typeof data === 'object' && 'id' in data && 'properties' in data && data.properties\n ? typeof data.properties === 'object' && 'data' in data\n : false;\n\nexport type NodeArg<TData, TProperties extends Record<string, any> = Record<string, any>> = MakeOptional<\n NodeBase<TData, TProperties>,\n 'data' | 'properties'\n> & {\n /** Will automatically add nodes with an edge from this node to each. */\n nodes?: NodeArg<unknown>[];\n\n /** Will automatically add specified edges. */\n edges?: [string, EdgeDirection][];\n};\n\n//\n// Actions\n//\n\nexport type InvokeParams = {\n /** Node the invoked action is connected to. */\n node: Node;\n\n caller?: string;\n};\n\nexport type ActionData = (params: InvokeParams) => MaybePromise<void>;\n\nexport type Action<TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<ActionData, TProperties>, 'properties'> & {\n properties: Readonly<TProperties>;\n } & ConnectedNodes\n>;\n\nexport const isAction = (data: unknown): data is Action =>\n isGraphNode(data) ? typeof data.data === 'function' : false;\n\nexport const actionGroupSymbol = Symbol('ActionGroup');\n\nexport type ActionGroup = Readonly<\n Omit<NodeBase<typeof actionGroupSymbol, Record<string, any>>, 'properties'> & {\n properties: Readonly<Record<string, any>>;\n } & ConnectedActions\n>;\n\nexport const isActionGroup = (data: unknown): data is ActionGroup =>\n isGraphNode(data) ? data.data === actionGroupSymbol : false;\n\nexport type ActionLike = Action | ActionGroup;\n\nexport const isActionLike = (data: unknown): data is Action | ActionGroup => isAction(data) || isActionGroup(data);\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { EventSubscriptions, type UnsubscribeCallback } from '@dxos/async';\n\nimport { Graph } from './graph';\n\nexport type BuilderExtension = (graph: Graph) => UnsubscribeCallback | void;\n\n/**\n * The builder provides an extensible way to compose the construction of the graph.\n */\nexport class GraphBuilder {\n private readonly _extensions = new Map<string, BuilderExtension>();\n private readonly _unsubscribe = new EventSubscriptions();\n\n /**\n * Register a node builder which will be called in order to construct the graph.\n */\n addExtension(id: string, extension: BuilderExtension): GraphBuilder {\n this._extensions.set(id, extension);\n return this;\n }\n\n /**\n * Remove a node builder from the graph builder.\n */\n removeExtension(id: string): GraphBuilder {\n this._extensions.delete(id);\n return this;\n }\n\n /**\n * Construct the graph, starting by calling all registered extensions.\n * @param previousGraph If provided, the graph will be updated in place.\n */\n build(previousGraph?: Graph): Graph {\n // Clear previous extension subscriptions.\n this._unsubscribe.clear();\n\n const graph: Graph = previousGraph ?? new Graph();\n\n Array.from(this._extensions.values()).forEach((builder) => {\n const unsubscribe = builder(graph);\n unsubscribe && this._unsubscribe.add(unsubscribe);\n });\n\n return graph;\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Graph } from './graph';\nimport { type Node, type NodeArg } from './node';\n\n/**\n * If the condition is true, adds the nodes to the graph, otherwise removes the nodes from the graph.\n */\nexport const manageNodes = <TData = null, TProperties extends Record<string, any> = Record<string, any>>({\n graph,\n condition,\n nodes,\n removeEdges,\n}: {\n graph: Graph;\n condition: boolean;\n nodes: NodeArg<TData, TProperties>[];\n removeEdges?: boolean;\n}): Node<TData, TProperties>[] | void => {\n if (condition) {\n return graph.addNodes(...nodes);\n } else {\n nodes.forEach(({ id }) => graph.removeNode(id, removeEdges));\n }\n};\n"],
|
|
5
|
-
"mappings": ";AAIA,SAASA,iBAAiB;AAE1B,
|
|
6
|
-
"names": ["untracked", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { untracked } from '@preact/signals-core';\n\nimport { create } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { nonNullable } from '@dxos/util';\n\nimport { isActionLike, type EdgeDirection, type Node, type NodeArg, type NodeBase } from './node';\n\nexport const ROOT_ID = 'root';\n\nexport type TraversalOptions = {\n /**\n * The node to start traversing from.\n *\n * @default root\n */\n node?: Node;\n\n /**\n * The direction to traverse graph edges.\n *\n * @default 'outbound'\n */\n direction?: EdgeDirection;\n\n /**\n * A predicate to filter nodes which are passed to the `visitor` callback.\n */\n filter?: (node: Node) => boolean;\n\n /**\n * A callback which is called for each node visited during traversal.\n */\n visitor?: (node: Node, path: string[]) => void;\n};\n\n/**\n * The Graph represents the structure of the application constructed via plugins.\n */\nexport class Graph {\n /**\n * @internal\n */\n readonly _nodes = create<Record<string, NodeBase>>({\n [ROOT_ID]: { id: ROOT_ID, properties: {}, data: null },\n });\n\n /**\n * @internal\n */\n // Key is the `${node.id}-${direction}` and value is an ordered list of node ids.\n // Explicit type required because TS says this is not portable.\n readonly _edges = create<Record<string, string[]>>({});\n\n /**\n * Alias for `findNode('root')`.\n */\n get root() {\n return this.findNode(ROOT_ID)!;\n }\n\n /**\n * Convert the graph to a JSON object.\n */\n toJSON({ id = ROOT_ID, maxLength = 32 }: { id?: string; maxLength?: number } = {}) {\n const toJSON = (node: Node): any => {\n const nodes = node.nodes();\n const obj: Record<string, any> = {\n id: node.id.length > maxLength ? `${node.id.slice(0, maxLength - 3)}...` : node.id,\n };\n if (node.properties.label) {\n obj.label = node.properties.label;\n }\n if (nodes.length) {\n obj.nodes = nodes.map((node) => toJSON(node));\n }\n return obj;\n };\n\n const root = this.findNode(id);\n invariant(root, `Node not found: ${id}`);\n return toJSON(root);\n }\n\n /**\n * Find the node with the given id in the graph.\n */\n findNode(id: string): Node | undefined {\n const nodeBase = this._nodes[id];\n if (!nodeBase) {\n return undefined;\n }\n\n return this._constructNode(nodeBase);\n }\n\n private _constructNode = (nodeBase: NodeBase): Node => {\n const node: Node = {\n ...nodeBase,\n edges: ({ direction = 'outbound' } = {}) => {\n return this._edges[this.getEdgeKey(node.id, direction)];\n },\n nodes: ({ direction, filter } = {}) => {\n const nodes = this._getNodes({ id: node.id, direction }).filter((n) => !isActionLike(n));\n return filter ? nodes.filter((n) => filter(n, node)) : nodes;\n },\n node: (id: string) => {\n return this._getNodes({ id }).find((node) => node.id === id);\n },\n actions: () => {\n return this._getNodes({ id: node.id }).filter(isActionLike);\n },\n };\n\n return node;\n };\n\n private _getNodes({ id, direction = 'outbound' }: { id: string; direction?: EdgeDirection }): Node[] {\n const edges = this._edges[this.getEdgeKey(id, direction)];\n if (!edges) {\n return [];\n }\n\n return edges.map((id) => this.findNode(id)).filter(nonNullable);\n }\n\n private getEdgeKey(id: string, direction: EdgeDirection) {\n return `${id}-${direction}`;\n }\n\n /**\n * Add nodes to the graph.\n */\n addNodes<TData = null, TProperties extends Record<string, any> = Record<string, any>>(\n ...nodes: NodeArg<TData, TProperties>[]\n ): Node<TData, TProperties>[] {\n return nodes.map((node) => this._addNode(node));\n }\n\n private _addNode<TData, TProperties extends Record<string, any> = Record<string, any>>({\n nodes,\n edges,\n ..._node\n }: NodeArg<TData, TProperties>): Node<TData, TProperties> {\n return untracked(() => {\n const node = { data: null, properties: {}, ..._node };\n this._nodes[node.id] = node;\n\n if (nodes) {\n nodes.forEach((subNode) => {\n this._addNode(subNode);\n this.addEdge({ source: node.id, target: subNode.id });\n });\n }\n\n if (edges) {\n edges.forEach(([id, direction]) =>\n direction === 'outbound'\n ? this.addEdge({ source: node.id, target: id })\n : this.addEdge({ source: id, target: node.id }),\n );\n }\n\n return this._constructNode(node) as Node<TData, TProperties>;\n });\n }\n\n /**\n * Remove nodes from the graph.\n *\n * @param id The id of the node to remove.\n * @param edges Whether to remove edges connected to the node from the graph as well.\n */\n removeNode(id: string, edges = false) {\n untracked(() => {\n const node = this.findNode(id);\n if (!node) {\n return;\n }\n\n if (edges) {\n // Remove edges from node.\n delete this._edges[this.getEdgeKey(id, 'outbound')];\n delete this._edges[this.getEdgeKey(id, 'inbound')];\n\n // Remove edges from connected nodes.\n this._getNodes({ id }).forEach((node) => this.removeEdge({ source: id, target: node.id }));\n this._getNodes({ id, direction: 'inbound' }).forEach((node) =>\n this.removeEdge({ source: node.id, target: id }),\n );\n }\n\n // Remove node.\n delete this._nodes[id];\n });\n }\n\n /**\n * Add an edge to the graph.\n */\n addEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outbound = this._edges[this.getEdgeKey(source, 'outbound')];\n if (!outbound) {\n this._edges[this.getEdgeKey(source, 'outbound')] = [target];\n } else if (!outbound.includes(target)) {\n outbound.push(target);\n }\n\n const inbound = this._edges[this.getEdgeKey(target, 'inbound')];\n if (!inbound) {\n this._edges[this.getEdgeKey(target, 'inbound')] = [source];\n } else if (!inbound.includes(source)) {\n inbound.push(source);\n }\n });\n }\n\n /**\n * Sort edges for a node.\n *\n * Edges not included in the sorted list are appended to the end of the list.\n *\n * @param nodeId The id of the node to sort edges for.\n * @param direction The direction of the edges from the node to sort.\n * @param edges The ordered list of edges.\n */\n sortEdges(nodeId: string, direction: EdgeDirection, edges: string[]) {\n untracked(() => {\n const current = this._edges[this.getEdgeKey(nodeId, direction)];\n if (current) {\n const unsorted = current.filter((id) => !edges.includes(id)) ?? [];\n const sorted = edges.filter((id) => current.includes(id)) ?? [];\n current.splice(0, current.length, ...[...sorted, ...unsorted]);\n }\n });\n }\n\n /**\n * Remove an edge from the graph.\n */\n removeEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outboundIndex = this._edges[this.getEdgeKey(source, 'outbound')]?.findIndex((id) => id === target);\n if (outboundIndex !== -1) {\n this._edges[this.getEdgeKey(source, 'outbound')].splice(outboundIndex, 1);\n }\n\n const inboundIndex = this._edges[this.getEdgeKey(target, 'inbound')]?.findIndex((id) => id === source);\n if (inboundIndex !== -1) {\n this._edges[this.getEdgeKey(target, 'inbound')].splice(inboundIndex, 1);\n }\n });\n }\n\n /**\n * Recursive depth-first traversal.\n *\n * @param options.node The node to start traversing from.\n * @param options.direction The direction to traverse graph edges.\n * @param options.filter A predicate to filter nodes which are passed to the `visitor` callback.\n * @param options.visitor A callback which is called for each node visited during traversal.\n */\n traverse({ node = this.root, direction = 'outbound', filter, visitor }: TraversalOptions, path: string[] = []): void {\n // Break cycles.\n if (path.includes(node.id)) {\n return;\n }\n\n if (!filter || filter(node)) {\n visitor?.(node, [...path, node.id]);\n }\n\n Object.values(this._getNodes({ id: node.id, direction })).forEach((child) =>\n this.traverse({ node: child, direction, filter, visitor }, [...path, node.id]),\n );\n }\n\n /**\n * Get the path between two nodes in the graph.\n */\n getPath({ source = 'root', target }: { source?: string; target: string }): string[] | undefined {\n const start = this.findNode(source);\n if (!start) {\n return undefined;\n }\n\n let found: string[] | undefined;\n this.traverse({\n node: start,\n filter: () => !found,\n visitor: (node, path) => {\n if (node.id === target) {\n found = path;\n }\n },\n });\n\n return found;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type MaybePromise, type MakeOptional } from '@dxos/util';\n\n/**\n * Represents a node in the graph.\n */\n// TODO(wittjosiah): Use Effect Schema.\nexport type NodeBase<TData = any, TProperties extends Record<string, any> = Record<string, any>> = {\n /**\n * Globally unique ID.\n */\n id: string;\n\n /**\n * Properties of the node relevant to displaying the node.\n */\n properties: TProperties;\n\n /**\n * Data the node represents.\n */\n // TODO(burdon): Type system (e.g., minimally provide identifier string vs. TypedObject vs. Graph mixin type system)?\n // type field would prevent convoluted sniffing of object properties. And allow direct pass-through for ECHO TypedObjects.\n data: TData;\n};\n\nexport type NodeFilter<T = any, U extends Record<string, any> = Record<string, any>> = (\n node: Node<unknown, Record<string, any>>,\n connectedNode: Node,\n) => node is Node<T, U>;\n\nexport type EdgeDirection = 'outbound' | 'inbound';\n\nexport type ConnectedNodes = {\n /**\n * Edges that this node is connected to in default order.\n */\n edges(params?: { direction?: EdgeDirection }): Readonly<string[]>;\n\n /**\n * Nodes that this node is connected to in default order.\n */\n nodes<T = any, U extends Record<string, any> = Record<string, any>>(params?: {\n direction?: EdgeDirection;\n filter?: NodeFilter<T, U>;\n }): Node<T>[];\n\n /**\n * Get a specific connected node by id.\n */\n node(id: string): Node | undefined;\n};\n\nexport type ConnectedActions = {\n /**\n * Actions or action groups that this node is connected to in default order.\n */\n actions(): ActionLike[];\n};\n\nexport type Node<TData = any, TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<TData, TProperties>, 'properties'> & { properties: Readonly<TProperties> } & ConnectedNodes &\n ConnectedActions\n>;\n\nexport const isGraphNode = (data: unknown): data is Node =>\n data && typeof data === 'object' && 'id' in data && 'properties' in data && data.properties\n ? typeof data.properties === 'object' && 'data' in data\n : false;\n\nexport type NodeArg<TData, TProperties extends Record<string, any> = Record<string, any>> = MakeOptional<\n NodeBase<TData, TProperties>,\n 'data' | 'properties'\n> & {\n /** Will automatically add nodes with an edge from this node to each. */\n nodes?: NodeArg<unknown>[];\n\n /** Will automatically add specified edges. */\n edges?: [string, EdgeDirection][];\n};\n\n//\n// Actions\n//\n\nexport type InvokeParams = {\n /** Node the invoked action is connected to. */\n node: Node;\n\n caller?: string;\n};\n\nexport type ActionData = (params: InvokeParams) => MaybePromise<void>;\n\nexport type Action<TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<ActionData, TProperties>, 'properties'> & {\n properties: Readonly<TProperties>;\n } & ConnectedNodes\n>;\n\nexport const isAction = (data: unknown): data is Action =>\n isGraphNode(data) ? typeof data.data === 'function' : false;\n\nexport const actionGroupSymbol = Symbol('ActionGroup');\n\nexport type ActionGroup = Readonly<\n Omit<NodeBase<typeof actionGroupSymbol, Record<string, any>>, 'properties'> & {\n properties: Readonly<Record<string, any>>;\n } & ConnectedActions\n>;\n\nexport const isActionGroup = (data: unknown): data is ActionGroup =>\n isGraphNode(data) ? data.data === actionGroupSymbol : false;\n\nexport type ActionLike = Action | ActionGroup;\n\nexport const isActionLike = (data: unknown): data is Action | ActionGroup => isAction(data) || isActionGroup(data);\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { EventSubscriptions, type UnsubscribeCallback } from '@dxos/async';\n\nimport { Graph } from './graph';\n\nexport type BuilderExtension = (graph: Graph) => UnsubscribeCallback | void;\n\n/**\n * The builder provides an extensible way to compose the construction of the graph.\n */\nexport class GraphBuilder {\n private readonly _extensions = new Map<string, BuilderExtension>();\n private readonly _unsubscribe = new EventSubscriptions();\n\n /**\n * Register a node builder which will be called in order to construct the graph.\n */\n addExtension(id: string, extension: BuilderExtension): GraphBuilder {\n this._extensions.set(id, extension);\n return this;\n }\n\n /**\n * Remove a node builder from the graph builder.\n */\n removeExtension(id: string): GraphBuilder {\n this._extensions.delete(id);\n return this;\n }\n\n /**\n * Construct the graph, starting by calling all registered extensions.\n * @param previousGraph If provided, the graph will be updated in place.\n */\n build(previousGraph?: Graph): Graph {\n // Clear previous extension subscriptions.\n this._unsubscribe.clear();\n\n const graph: Graph = previousGraph ?? new Graph();\n\n Array.from(this._extensions.values()).forEach((builder) => {\n const unsubscribe = builder(graph);\n unsubscribe && this._unsubscribe.add(unsubscribe);\n });\n\n return graph;\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Graph } from './graph';\nimport { type Node, type NodeArg } from './node';\n\n/**\n * If the condition is true, adds the nodes to the graph, otherwise removes the nodes from the graph.\n */\nexport const manageNodes = <TData = null, TProperties extends Record<string, any> = Record<string, any>>({\n graph,\n condition,\n nodes,\n removeEdges,\n}: {\n graph: Graph;\n condition: boolean;\n nodes: NodeArg<TData, TProperties>[];\n removeEdges?: boolean;\n}): Node<TData, TProperties>[] | void => {\n if (condition) {\n return graph.addNodes(...nodes);\n } else {\n nodes.forEach(({ id }) => graph.removeNode(id, removeEdges));\n }\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["untracked", "create", "invariant", "nonNullable", "isGraphNode", "data", "properties", "isAction", "actionGroupSymbol", "Symbol", "isActionGroup", "isActionLike", "ROOT_ID", "Graph", "_nodes", "create", "id", "properties", "data", "_edges", "_constructNode", "nodeBase", "node", "edges", "direction", "getEdgeKey", "nodes", "filter", "_getNodes", "n", "isActionLike", "find", "actions", "root", "findNode", "toJSON", "maxLength", "obj", "length", "slice", "label", "map", "invariant", "undefined", "nonNullable", "addNodes", "_addNode", "_node", "untracked", "forEach", "subNode", "addEdge", "source", "target", "removeNode", "removeEdge", "outbound", "includes", "push", "inbound", "sortEdges", "nodeId", "current", "unsorted", "sorted", "splice", "outboundIndex", "findIndex", "inboundIndex", "traverse", "visitor", "path", "Object", "values", "child", "getPath", "start", "found", "EventSubscriptions", "GraphBuilder", "_extensions", "Map", "_unsubscribe", "EventSubscriptions", "addExtension", "id", "extension", "set", "removeExtension", "delete", "build", "previousGraph", "clear", "graph", "Graph", "Array", "from", "values", "forEach", "builder", "unsubscribe", "add", "manageNodes", "graph", "condition", "nodes", "removeEdges", "addNodes", "forEach", "id", "removeNode"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/sdk/app-graph/src/node.ts":{"bytes":6411,"imports":[],"format":"esm"},"packages/sdk/app-graph/src/graph.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/sdk/app-graph/src/node.ts":{"bytes":6411,"imports":[],"format":"esm"},"packages/sdk/app-graph/src/graph.ts":{"bytes":30335,"imports":[{"path":"@preact/signals-core","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/sdk/app-graph/src/node.ts","kind":"import-statement","original":"./node"}],"format":"esm"},"packages/sdk/app-graph/src/graph-builder.ts":{"bytes":4608,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"packages/sdk/app-graph/src/graph.ts","kind":"import-statement","original":"./graph"}],"format":"esm"},"packages/sdk/app-graph/src/helpers.ts":{"bytes":2039,"imports":[],"format":"esm"},"packages/sdk/app-graph/src/index.ts":{"bytes":723,"imports":[{"path":"packages/sdk/app-graph/src/graph.ts","kind":"import-statement","original":"./graph"},{"path":"packages/sdk/app-graph/src/graph-builder.ts","kind":"import-statement","original":"./graph-builder"},{"path":"packages/sdk/app-graph/src/helpers.ts","kind":"import-statement","original":"./helpers"},{"path":"packages/sdk/app-graph/src/node.ts","kind":"import-statement","original":"./node"}],"format":"esm"}},"outputs":{"packages/sdk/app-graph/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":22525},"packages/sdk/app-graph/dist/lib/browser/index.mjs":{"imports":[{"path":"@preact/signals-core","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true}],"exports":["Graph","GraphBuilder","ROOT_ID","actionGroupSymbol","isAction","isActionGroup","isActionLike","isGraphNode","manageNodes"],"entryPoint":"packages/sdk/app-graph/src/index.ts","inputs":{"packages/sdk/app-graph/src/graph.ts":{"bytesInOutput":7674},"packages/sdk/app-graph/src/node.ts":{"bytesInOutput":477},"packages/sdk/app-graph/src/index.ts":{"bytesInOutput":0},"packages/sdk/app-graph/src/graph-builder.ts":{"bytesInOutput":983},"packages/sdk/app-graph/src/helpers.ts":{"bytesInOutput":206}},"bytes":9726}}}
|
package/dist/lib/node/index.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
var node_exports = {};
|
|
30
20
|
__export(node_exports, {
|
|
@@ -40,7 +30,7 @@ __export(node_exports, {
|
|
|
40
30
|
});
|
|
41
31
|
module.exports = __toCommonJS(node_exports);
|
|
42
32
|
var import_signals_core = require("@preact/signals-core");
|
|
43
|
-
var
|
|
33
|
+
var import_echo_schema = require("@dxos/echo-schema");
|
|
44
34
|
var import_invariant = require("@dxos/invariant");
|
|
45
35
|
var import_util = require("@dxos/util");
|
|
46
36
|
var import_async = require("@dxos/async");
|
|
@@ -53,14 +43,14 @@ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph
|
|
|
53
43
|
var ROOT_ID = "root";
|
|
54
44
|
var Graph = class {
|
|
55
45
|
constructor() {
|
|
56
|
-
this._nodes =
|
|
46
|
+
this._nodes = (0, import_echo_schema.create)({
|
|
57
47
|
[ROOT_ID]: {
|
|
58
48
|
id: ROOT_ID,
|
|
59
49
|
properties: {},
|
|
60
50
|
data: null
|
|
61
51
|
}
|
|
62
52
|
});
|
|
63
|
-
this._edges =
|
|
53
|
+
this._edges = (0, import_echo_schema.create)({});
|
|
64
54
|
this._constructNode = (nodeBase) => {
|
|
65
55
|
const node = {
|
|
66
56
|
...nodeBase,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/graph.ts", "../../../src/node.ts", "../../../src/graph-builder.ts", "../../../src/helpers.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { untracked } from '@preact/signals-core';\n\nimport * as E from '@dxos/echo-schema/schema';\nimport { invariant } from '@dxos/invariant';\nimport { nonNullable } from '@dxos/util';\n\nimport { isActionLike, type EdgeDirection, type Node, type NodeArg, type NodeBase } from './node';\n\nexport const ROOT_ID = 'root';\n\nexport type TraversalOptions = {\n /**\n * The node to start traversing from.\n *\n * @default root\n */\n node?: Node;\n\n /**\n * The direction to traverse graph edges.\n *\n * @default 'outbound'\n */\n direction?: EdgeDirection;\n\n /**\n * A predicate to filter nodes which are passed to the `visitor` callback.\n */\n filter?: (node: Node) => boolean;\n\n /**\n * A callback which is called for each node visited during traversal.\n */\n visitor?: (node: Node, path: string[]) => void;\n};\n\n/**\n * The Graph represents the structure of the application constructed via plugins.\n */\nexport class Graph {\n /**\n * @internal\n */\n readonly _nodes = E.object<Record<string, NodeBase>>({\n [ROOT_ID]: { id: ROOT_ID, properties: {}, data: null },\n });\n\n /**\n * @internal\n */\n // Key is the `${node.id}-${direction}` and value is an ordered list of node ids.\n // Explicit type required because TS says this is not portable.\n readonly _edges = E.object<Record<string, string[]>>({});\n\n /**\n * Alias for `findNode('root')`.\n */\n get root() {\n return this.findNode(ROOT_ID)!;\n }\n\n /**\n * Convert the graph to a JSON object.\n */\n toJSON({ id = ROOT_ID, maxLength = 32 }: { id?: string; maxLength?: number } = {}) {\n const toJSON = (node: Node): any => {\n const nodes = node.nodes();\n const obj: Record<string, any> = {\n id: node.id.length > maxLength ? `${node.id.slice(0, maxLength - 3)}...` : node.id,\n };\n if (node.properties.label) {\n obj.label = node.properties.label;\n }\n if (nodes.length) {\n obj.nodes = nodes.map((node) => toJSON(node));\n }\n return obj;\n };\n\n const root = this.findNode(id);\n invariant(root, `Node not found: ${id}`);\n return toJSON(root);\n }\n\n /**\n * Find the node with the given id in the graph.\n */\n findNode(id: string): Node | undefined {\n const nodeBase = this._nodes[id];\n if (!nodeBase) {\n return undefined;\n }\n\n return this._constructNode(nodeBase);\n }\n\n private _constructNode = (nodeBase: NodeBase): Node => {\n const node: Node = {\n ...nodeBase,\n edges: ({ direction = 'outbound' } = {}) => {\n return this._edges[this.getEdgeKey(node.id, direction)];\n },\n nodes: ({ direction, filter } = {}) => {\n const nodes = this._getNodes({ id: node.id, direction }).filter((n) => !isActionLike(n));\n return filter ? nodes.filter((n) => filter(n, node)) : nodes;\n },\n node: (id: string) => {\n return this._getNodes({ id }).find((node) => node.id === id);\n },\n actions: () => {\n return this._getNodes({ id: node.id }).filter(isActionLike);\n },\n };\n\n return node;\n };\n\n private _getNodes({ id, direction = 'outbound' }: { id: string; direction?: EdgeDirection }): Node[] {\n const edges = this._edges[this.getEdgeKey(id, direction)];\n if (!edges) {\n return [];\n }\n\n return edges.map((id) => this.findNode(id)).filter(nonNullable);\n }\n\n private getEdgeKey(id: string, direction: EdgeDirection) {\n return `${id}-${direction}`;\n }\n\n /**\n * Add nodes to the graph.\n */\n addNodes<TData = null, TProperties extends Record<string, any> = Record<string, any>>(\n ...nodes: NodeArg<TData, TProperties>[]\n ): Node<TData, TProperties>[] {\n return nodes.map((node) => this._addNode(node));\n }\n\n private _addNode<TData, TProperties extends Record<string, any> = Record<string, any>>({\n nodes,\n edges,\n ..._node\n }: NodeArg<TData, TProperties>): Node<TData, TProperties> {\n return untracked(() => {\n const node = { data: null, properties: {}, ..._node };\n this._nodes[node.id] = node;\n\n if (nodes) {\n nodes.forEach((subNode) => {\n this._addNode(subNode);\n this.addEdge({ source: node.id, target: subNode.id });\n });\n }\n\n if (edges) {\n edges.forEach(([id, direction]) =>\n direction === 'outbound'\n ? this.addEdge({ source: node.id, target: id })\n : this.addEdge({ source: id, target: node.id }),\n );\n }\n\n return this._constructNode(node) as Node<TData, TProperties>;\n });\n }\n\n /**\n * Remove nodes from the graph.\n *\n * @param id The id of the node to remove.\n * @param edges Whether to remove edges connected to the node from the graph as well.\n */\n removeNode(id: string, edges = false) {\n untracked(() => {\n const node = this.findNode(id);\n if (!node) {\n return;\n }\n\n if (edges) {\n // Remove edges from node.\n delete this._edges[this.getEdgeKey(id, 'outbound')];\n delete this._edges[this.getEdgeKey(id, 'inbound')];\n\n // Remove edges from connected nodes.\n this._getNodes({ id }).forEach((node) => this.removeEdge({ source: id, target: node.id }));\n this._getNodes({ id, direction: 'inbound' }).forEach((node) =>\n this.removeEdge({ source: node.id, target: id }),\n );\n }\n\n // Remove node.\n delete this._nodes[id];\n });\n }\n\n /**\n * Add an edge to the graph.\n */\n addEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outbound = this._edges[this.getEdgeKey(source, 'outbound')];\n if (!outbound) {\n this._edges[this.getEdgeKey(source, 'outbound')] = [target];\n } else if (!outbound.includes(target)) {\n outbound.push(target);\n }\n\n const inbound = this._edges[this.getEdgeKey(target, 'inbound')];\n if (!inbound) {\n this._edges[this.getEdgeKey(target, 'inbound')] = [source];\n } else if (!inbound.includes(source)) {\n inbound.push(source);\n }\n });\n }\n\n /**\n * Sort edges for a node.\n *\n * Edges not included in the sorted list are appended to the end of the list.\n *\n * @param nodeId The id of the node to sort edges for.\n * @param direction The direction of the edges from the node to sort.\n * @param edges The ordered list of edges.\n */\n sortEdges(nodeId: string, direction: EdgeDirection, edges: string[]) {\n untracked(() => {\n const current = this._edges[this.getEdgeKey(nodeId, direction)];\n if (current) {\n const unsorted = current.filter((id) => !edges.includes(id)) ?? [];\n const sorted = edges.filter((id) => current.includes(id)) ?? [];\n current.splice(0, current.length, ...[...sorted, ...unsorted]);\n }\n });\n }\n\n /**\n * Remove an edge from the graph.\n */\n removeEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outboundIndex = this._edges[this.getEdgeKey(source, 'outbound')]?.findIndex((id) => id === target);\n if (outboundIndex !== -1) {\n this._edges[this.getEdgeKey(source, 'outbound')].splice(outboundIndex, 1);\n }\n\n const inboundIndex = this._edges[this.getEdgeKey(target, 'inbound')]?.findIndex((id) => id === source);\n if (inboundIndex !== -1) {\n this._edges[this.getEdgeKey(target, 'inbound')].splice(inboundIndex, 1);\n }\n });\n }\n\n /**\n * Recursive depth-first traversal.\n *\n * @param options.node The node to start traversing from.\n * @param options.direction The direction to traverse graph edges.\n * @param options.filter A predicate to filter nodes which are passed to the `visitor` callback.\n * @param options.visitor A callback which is called for each node visited during traversal.\n */\n traverse({ node = this.root, direction = 'outbound', filter, visitor }: TraversalOptions, path: string[] = []): void {\n // Break cycles.\n if (path.includes(node.id)) {\n return;\n }\n\n if (!filter || filter(node)) {\n visitor?.(node, [...path, node.id]);\n }\n\n Object.values(this._getNodes({ id: node.id, direction })).forEach((child) =>\n this.traverse({ node: child, direction, filter, visitor }, [...path, node.id]),\n );\n }\n\n /**\n * Get the path between two nodes in the graph.\n */\n getPath({ source = 'root', target }: { source?: string; target: string }): string[] | undefined {\n const start = this.findNode(source);\n if (!start) {\n return undefined;\n }\n\n let found: string[] | undefined;\n this.traverse({\n node: start,\n filter: () => !found,\n visitor: (node, path) => {\n if (node.id === target) {\n found = path;\n }\n },\n });\n\n return found;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type MaybePromise, type MakeOptional } from '@dxos/util';\n\n/**\n * Represents a node in the graph.\n */\n// TODO(wittjosiah): Use Effect Schema.\nexport type NodeBase<TData = any, TProperties extends Record<string, any> = Record<string, any>> = {\n /**\n * Globally unique ID.\n */\n id: string;\n\n /**\n * Properties of the node relevant to displaying the node.\n */\n properties: TProperties;\n\n /**\n * Data the node represents.\n */\n // TODO(burdon): Type system (e.g., minimally provide identifier string vs. TypedObject vs. Graph mixin type system)?\n // type field would prevent convoluted sniffing of object properties. And allow direct pass-through for ECHO TypedObjects.\n data: TData;\n};\n\nexport type NodeFilter<T = any, U extends Record<string, any> = Record<string, any>> = (\n node: Node<unknown, Record<string, any>>,\n connectedNode: Node,\n) => node is Node<T, U>;\n\nexport type EdgeDirection = 'outbound' | 'inbound';\n\nexport type ConnectedNodes = {\n /**\n * Edges that this node is connected to in default order.\n */\n edges(params?: { direction?: EdgeDirection }): Readonly<string[]>;\n\n /**\n * Nodes that this node is connected to in default order.\n */\n nodes<T = any, U extends Record<string, any> = Record<string, any>>(params?: {\n direction?: EdgeDirection;\n filter?: NodeFilter<T, U>;\n }): Node<T>[];\n\n /**\n * Get a specific connected node by id.\n */\n node(id: string): Node | undefined;\n};\n\nexport type ConnectedActions = {\n /**\n * Actions or action groups that this node is connected to in default order.\n */\n actions(): ActionLike[];\n};\n\nexport type Node<TData = any, TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<TData, TProperties>, 'properties'> & { properties: Readonly<TProperties> } & ConnectedNodes &\n ConnectedActions\n>;\n\nexport const isGraphNode = (data: unknown): data is Node =>\n data && typeof data === 'object' && 'id' in data && 'properties' in data && data.properties\n ? typeof data.properties === 'object' && 'data' in data\n : false;\n\nexport type NodeArg<TData, TProperties extends Record<string, any> = Record<string, any>> = MakeOptional<\n NodeBase<TData, TProperties>,\n 'data' | 'properties'\n> & {\n /** Will automatically add nodes with an edge from this node to each. */\n nodes?: NodeArg<unknown>[];\n\n /** Will automatically add specified edges. */\n edges?: [string, EdgeDirection][];\n};\n\n//\n// Actions\n//\n\nexport type InvokeParams = {\n /** Node the invoked action is connected to. */\n node: Node;\n\n caller?: string;\n};\n\nexport type ActionData = (params: InvokeParams) => MaybePromise<void>;\n\nexport type Action<TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<ActionData, TProperties>, 'properties'> & {\n properties: Readonly<TProperties>;\n } & ConnectedNodes\n>;\n\nexport const isAction = (data: unknown): data is Action =>\n isGraphNode(data) ? typeof data.data === 'function' : false;\n\nexport const actionGroupSymbol = Symbol('ActionGroup');\n\nexport type ActionGroup = Readonly<\n Omit<NodeBase<typeof actionGroupSymbol, Record<string, any>>, 'properties'> & {\n properties: Readonly<Record<string, any>>;\n } & ConnectedActions\n>;\n\nexport const isActionGroup = (data: unknown): data is ActionGroup =>\n isGraphNode(data) ? data.data === actionGroupSymbol : false;\n\nexport type ActionLike = Action | ActionGroup;\n\nexport const isActionLike = (data: unknown): data is Action | ActionGroup => isAction(data) || isActionGroup(data);\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { EventSubscriptions, type UnsubscribeCallback } from '@dxos/async';\n\nimport { Graph } from './graph';\n\nexport type BuilderExtension = (graph: Graph) => UnsubscribeCallback | void;\n\n/**\n * The builder provides an extensible way to compose the construction of the graph.\n */\nexport class GraphBuilder {\n private readonly _extensions = new Map<string, BuilderExtension>();\n private readonly _unsubscribe = new EventSubscriptions();\n\n /**\n * Register a node builder which will be called in order to construct the graph.\n */\n addExtension(id: string, extension: BuilderExtension): GraphBuilder {\n this._extensions.set(id, extension);\n return this;\n }\n\n /**\n * Remove a node builder from the graph builder.\n */\n removeExtension(id: string): GraphBuilder {\n this._extensions.delete(id);\n return this;\n }\n\n /**\n * Construct the graph, starting by calling all registered extensions.\n * @param previousGraph If provided, the graph will be updated in place.\n */\n build(previousGraph?: Graph): Graph {\n // Clear previous extension subscriptions.\n this._unsubscribe.clear();\n\n const graph: Graph = previousGraph ?? new Graph();\n\n Array.from(this._extensions.values()).forEach((builder) => {\n const unsubscribe = builder(graph);\n unsubscribe && this._unsubscribe.add(unsubscribe);\n });\n\n return graph;\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Graph } from './graph';\nimport { type Node, type NodeArg } from './node';\n\n/**\n * If the condition is true, adds the nodes to the graph, otherwise removes the nodes from the graph.\n */\nexport const manageNodes = <TData = null, TProperties extends Record<string, any> = Record<string, any>>({\n graph,\n condition,\n nodes,\n removeEdges,\n}: {\n graph: Graph;\n condition: boolean;\n nodes: NodeArg<TData, TProperties>[];\n removeEdges?: boolean;\n}): Node<TData, TProperties>[] | void => {\n if (condition) {\n return graph.addNodes(...nodes);\n } else {\n nodes.forEach(({ id }) => graph.removeNode(id, removeEdges));\n }\n};\n"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["isGraphNode", "data", "properties", "isAction", "actionGroupSymbol", "Symbol", "isActionGroup", "isActionLike", "ROOT_ID", "Graph", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { untracked } from '@preact/signals-core';\n\nimport { create } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { nonNullable } from '@dxos/util';\n\nimport { isActionLike, type EdgeDirection, type Node, type NodeArg, type NodeBase } from './node';\n\nexport const ROOT_ID = 'root';\n\nexport type TraversalOptions = {\n /**\n * The node to start traversing from.\n *\n * @default root\n */\n node?: Node;\n\n /**\n * The direction to traverse graph edges.\n *\n * @default 'outbound'\n */\n direction?: EdgeDirection;\n\n /**\n * A predicate to filter nodes which are passed to the `visitor` callback.\n */\n filter?: (node: Node) => boolean;\n\n /**\n * A callback which is called for each node visited during traversal.\n */\n visitor?: (node: Node, path: string[]) => void;\n};\n\n/**\n * The Graph represents the structure of the application constructed via plugins.\n */\nexport class Graph {\n /**\n * @internal\n */\n readonly _nodes = create<Record<string, NodeBase>>({\n [ROOT_ID]: { id: ROOT_ID, properties: {}, data: null },\n });\n\n /**\n * @internal\n */\n // Key is the `${node.id}-${direction}` and value is an ordered list of node ids.\n // Explicit type required because TS says this is not portable.\n readonly _edges = create<Record<string, string[]>>({});\n\n /**\n * Alias for `findNode('root')`.\n */\n get root() {\n return this.findNode(ROOT_ID)!;\n }\n\n /**\n * Convert the graph to a JSON object.\n */\n toJSON({ id = ROOT_ID, maxLength = 32 }: { id?: string; maxLength?: number } = {}) {\n const toJSON = (node: Node): any => {\n const nodes = node.nodes();\n const obj: Record<string, any> = {\n id: node.id.length > maxLength ? `${node.id.slice(0, maxLength - 3)}...` : node.id,\n };\n if (node.properties.label) {\n obj.label = node.properties.label;\n }\n if (nodes.length) {\n obj.nodes = nodes.map((node) => toJSON(node));\n }\n return obj;\n };\n\n const root = this.findNode(id);\n invariant(root, `Node not found: ${id}`);\n return toJSON(root);\n }\n\n /**\n * Find the node with the given id in the graph.\n */\n findNode(id: string): Node | undefined {\n const nodeBase = this._nodes[id];\n if (!nodeBase) {\n return undefined;\n }\n\n return this._constructNode(nodeBase);\n }\n\n private _constructNode = (nodeBase: NodeBase): Node => {\n const node: Node = {\n ...nodeBase,\n edges: ({ direction = 'outbound' } = {}) => {\n return this._edges[this.getEdgeKey(node.id, direction)];\n },\n nodes: ({ direction, filter } = {}) => {\n const nodes = this._getNodes({ id: node.id, direction }).filter((n) => !isActionLike(n));\n return filter ? nodes.filter((n) => filter(n, node)) : nodes;\n },\n node: (id: string) => {\n return this._getNodes({ id }).find((node) => node.id === id);\n },\n actions: () => {\n return this._getNodes({ id: node.id }).filter(isActionLike);\n },\n };\n\n return node;\n };\n\n private _getNodes({ id, direction = 'outbound' }: { id: string; direction?: EdgeDirection }): Node[] {\n const edges = this._edges[this.getEdgeKey(id, direction)];\n if (!edges) {\n return [];\n }\n\n return edges.map((id) => this.findNode(id)).filter(nonNullable);\n }\n\n private getEdgeKey(id: string, direction: EdgeDirection) {\n return `${id}-${direction}`;\n }\n\n /**\n * Add nodes to the graph.\n */\n addNodes<TData = null, TProperties extends Record<string, any> = Record<string, any>>(\n ...nodes: NodeArg<TData, TProperties>[]\n ): Node<TData, TProperties>[] {\n return nodes.map((node) => this._addNode(node));\n }\n\n private _addNode<TData, TProperties extends Record<string, any> = Record<string, any>>({\n nodes,\n edges,\n ..._node\n }: NodeArg<TData, TProperties>): Node<TData, TProperties> {\n return untracked(() => {\n const node = { data: null, properties: {}, ..._node };\n this._nodes[node.id] = node;\n\n if (nodes) {\n nodes.forEach((subNode) => {\n this._addNode(subNode);\n this.addEdge({ source: node.id, target: subNode.id });\n });\n }\n\n if (edges) {\n edges.forEach(([id, direction]) =>\n direction === 'outbound'\n ? this.addEdge({ source: node.id, target: id })\n : this.addEdge({ source: id, target: node.id }),\n );\n }\n\n return this._constructNode(node) as Node<TData, TProperties>;\n });\n }\n\n /**\n * Remove nodes from the graph.\n *\n * @param id The id of the node to remove.\n * @param edges Whether to remove edges connected to the node from the graph as well.\n */\n removeNode(id: string, edges = false) {\n untracked(() => {\n const node = this.findNode(id);\n if (!node) {\n return;\n }\n\n if (edges) {\n // Remove edges from node.\n delete this._edges[this.getEdgeKey(id, 'outbound')];\n delete this._edges[this.getEdgeKey(id, 'inbound')];\n\n // Remove edges from connected nodes.\n this._getNodes({ id }).forEach((node) => this.removeEdge({ source: id, target: node.id }));\n this._getNodes({ id, direction: 'inbound' }).forEach((node) =>\n this.removeEdge({ source: node.id, target: id }),\n );\n }\n\n // Remove node.\n delete this._nodes[id];\n });\n }\n\n /**\n * Add an edge to the graph.\n */\n addEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outbound = this._edges[this.getEdgeKey(source, 'outbound')];\n if (!outbound) {\n this._edges[this.getEdgeKey(source, 'outbound')] = [target];\n } else if (!outbound.includes(target)) {\n outbound.push(target);\n }\n\n const inbound = this._edges[this.getEdgeKey(target, 'inbound')];\n if (!inbound) {\n this._edges[this.getEdgeKey(target, 'inbound')] = [source];\n } else if (!inbound.includes(source)) {\n inbound.push(source);\n }\n });\n }\n\n /**\n * Sort edges for a node.\n *\n * Edges not included in the sorted list are appended to the end of the list.\n *\n * @param nodeId The id of the node to sort edges for.\n * @param direction The direction of the edges from the node to sort.\n * @param edges The ordered list of edges.\n */\n sortEdges(nodeId: string, direction: EdgeDirection, edges: string[]) {\n untracked(() => {\n const current = this._edges[this.getEdgeKey(nodeId, direction)];\n if (current) {\n const unsorted = current.filter((id) => !edges.includes(id)) ?? [];\n const sorted = edges.filter((id) => current.includes(id)) ?? [];\n current.splice(0, current.length, ...[...sorted, ...unsorted]);\n }\n });\n }\n\n /**\n * Remove an edge from the graph.\n */\n removeEdge({ source, target }: { source: string; target: string }) {\n untracked(() => {\n const outboundIndex = this._edges[this.getEdgeKey(source, 'outbound')]?.findIndex((id) => id === target);\n if (outboundIndex !== -1) {\n this._edges[this.getEdgeKey(source, 'outbound')].splice(outboundIndex, 1);\n }\n\n const inboundIndex = this._edges[this.getEdgeKey(target, 'inbound')]?.findIndex((id) => id === source);\n if (inboundIndex !== -1) {\n this._edges[this.getEdgeKey(target, 'inbound')].splice(inboundIndex, 1);\n }\n });\n }\n\n /**\n * Recursive depth-first traversal.\n *\n * @param options.node The node to start traversing from.\n * @param options.direction The direction to traverse graph edges.\n * @param options.filter A predicate to filter nodes which are passed to the `visitor` callback.\n * @param options.visitor A callback which is called for each node visited during traversal.\n */\n traverse({ node = this.root, direction = 'outbound', filter, visitor }: TraversalOptions, path: string[] = []): void {\n // Break cycles.\n if (path.includes(node.id)) {\n return;\n }\n\n if (!filter || filter(node)) {\n visitor?.(node, [...path, node.id]);\n }\n\n Object.values(this._getNodes({ id: node.id, direction })).forEach((child) =>\n this.traverse({ node: child, direction, filter, visitor }, [...path, node.id]),\n );\n }\n\n /**\n * Get the path between two nodes in the graph.\n */\n getPath({ source = 'root', target }: { source?: string; target: string }): string[] | undefined {\n const start = this.findNode(source);\n if (!start) {\n return undefined;\n }\n\n let found: string[] | undefined;\n this.traverse({\n node: start,\n filter: () => !found,\n visitor: (node, path) => {\n if (node.id === target) {\n found = path;\n }\n },\n });\n\n return found;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type MaybePromise, type MakeOptional } from '@dxos/util';\n\n/**\n * Represents a node in the graph.\n */\n// TODO(wittjosiah): Use Effect Schema.\nexport type NodeBase<TData = any, TProperties extends Record<string, any> = Record<string, any>> = {\n /**\n * Globally unique ID.\n */\n id: string;\n\n /**\n * Properties of the node relevant to displaying the node.\n */\n properties: TProperties;\n\n /**\n * Data the node represents.\n */\n // TODO(burdon): Type system (e.g., minimally provide identifier string vs. TypedObject vs. Graph mixin type system)?\n // type field would prevent convoluted sniffing of object properties. And allow direct pass-through for ECHO TypedObjects.\n data: TData;\n};\n\nexport type NodeFilter<T = any, U extends Record<string, any> = Record<string, any>> = (\n node: Node<unknown, Record<string, any>>,\n connectedNode: Node,\n) => node is Node<T, U>;\n\nexport type EdgeDirection = 'outbound' | 'inbound';\n\nexport type ConnectedNodes = {\n /**\n * Edges that this node is connected to in default order.\n */\n edges(params?: { direction?: EdgeDirection }): Readonly<string[]>;\n\n /**\n * Nodes that this node is connected to in default order.\n */\n nodes<T = any, U extends Record<string, any> = Record<string, any>>(params?: {\n direction?: EdgeDirection;\n filter?: NodeFilter<T, U>;\n }): Node<T>[];\n\n /**\n * Get a specific connected node by id.\n */\n node(id: string): Node | undefined;\n};\n\nexport type ConnectedActions = {\n /**\n * Actions or action groups that this node is connected to in default order.\n */\n actions(): ActionLike[];\n};\n\nexport type Node<TData = any, TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<TData, TProperties>, 'properties'> & { properties: Readonly<TProperties> } & ConnectedNodes &\n ConnectedActions\n>;\n\nexport const isGraphNode = (data: unknown): data is Node =>\n data && typeof data === 'object' && 'id' in data && 'properties' in data && data.properties\n ? typeof data.properties === 'object' && 'data' in data\n : false;\n\nexport type NodeArg<TData, TProperties extends Record<string, any> = Record<string, any>> = MakeOptional<\n NodeBase<TData, TProperties>,\n 'data' | 'properties'\n> & {\n /** Will automatically add nodes with an edge from this node to each. */\n nodes?: NodeArg<unknown>[];\n\n /** Will automatically add specified edges. */\n edges?: [string, EdgeDirection][];\n};\n\n//\n// Actions\n//\n\nexport type InvokeParams = {\n /** Node the invoked action is connected to. */\n node: Node;\n\n caller?: string;\n};\n\nexport type ActionData = (params: InvokeParams) => MaybePromise<void>;\n\nexport type Action<TProperties extends Record<string, any> = Record<string, any>> = Readonly<\n Omit<NodeBase<ActionData, TProperties>, 'properties'> & {\n properties: Readonly<TProperties>;\n } & ConnectedNodes\n>;\n\nexport const isAction = (data: unknown): data is Action =>\n isGraphNode(data) ? typeof data.data === 'function' : false;\n\nexport const actionGroupSymbol = Symbol('ActionGroup');\n\nexport type ActionGroup = Readonly<\n Omit<NodeBase<typeof actionGroupSymbol, Record<string, any>>, 'properties'> & {\n properties: Readonly<Record<string, any>>;\n } & ConnectedActions\n>;\n\nexport const isActionGroup = (data: unknown): data is ActionGroup =>\n isGraphNode(data) ? data.data === actionGroupSymbol : false;\n\nexport type ActionLike = Action | ActionGroup;\n\nexport const isActionLike = (data: unknown): data is Action | ActionGroup => isAction(data) || isActionGroup(data);\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { EventSubscriptions, type UnsubscribeCallback } from '@dxos/async';\n\nimport { Graph } from './graph';\n\nexport type BuilderExtension = (graph: Graph) => UnsubscribeCallback | void;\n\n/**\n * The builder provides an extensible way to compose the construction of the graph.\n */\nexport class GraphBuilder {\n private readonly _extensions = new Map<string, BuilderExtension>();\n private readonly _unsubscribe = new EventSubscriptions();\n\n /**\n * Register a node builder which will be called in order to construct the graph.\n */\n addExtension(id: string, extension: BuilderExtension): GraphBuilder {\n this._extensions.set(id, extension);\n return this;\n }\n\n /**\n * Remove a node builder from the graph builder.\n */\n removeExtension(id: string): GraphBuilder {\n this._extensions.delete(id);\n return this;\n }\n\n /**\n * Construct the graph, starting by calling all registered extensions.\n * @param previousGraph If provided, the graph will be updated in place.\n */\n build(previousGraph?: Graph): Graph {\n // Clear previous extension subscriptions.\n this._unsubscribe.clear();\n\n const graph: Graph = previousGraph ?? new Graph();\n\n Array.from(this._extensions.values()).forEach((builder) => {\n const unsubscribe = builder(graph);\n unsubscribe && this._unsubscribe.add(unsubscribe);\n });\n\n return graph;\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Graph } from './graph';\nimport { type Node, type NodeArg } from './node';\n\n/**\n * If the condition is true, adds the nodes to the graph, otherwise removes the nodes from the graph.\n */\nexport const manageNodes = <TData = null, TProperties extends Record<string, any> = Record<string, any>>({\n graph,\n condition,\n nodes,\n removeEdges,\n}: {\n graph: Graph;\n condition: boolean;\n nodes: NodeArg<TData, TProperties>[];\n removeEdges?: boolean;\n}): Node<TData, TProperties>[] | void => {\n if (condition) {\n return graph.addNodes(...nodes);\n } else {\n nodes.forEach(({ id }) => graph.removeNode(id, removeEdges));\n }\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["isGraphNode", "data", "properties", "isAction", "actionGroupSymbol", "Symbol", "isActionGroup", "isActionLike", "ROOT_ID", "Graph", "create", "id", "_constructNode", "nodeBase", "node", "edges", "direction", "_edges", "getEdgeKey", "nodes", "filter", "_getNodes", "n", "find", "actions", "root", "findNode", "toJSON", "maxLength", "obj", "length", "slice", "label", "map", "invariant", "_nodes", "undefined", "nonNullable", "addNodes", "_addNode", "_node", "untracked", "forEach", "subNode", "addEdge", "source", "target", "removeNode", "removeEdge", "outbound", "includes", "push", "inbound", "sortEdges", "nodeId", "current", "unsorted", "sorted", "splice", "outboundIndex", "findIndex", "inboundIndex", "traverse", "visitor", "path", "Object", "values", "child", "getPath", "start", "found", "GraphBuilder", "_extensions", "Map", "_unsubscribe", "EventSubscriptions", "addExtension", "extension", "set", "removeExtension", "delete", "build", "previousGraph", "clear", "graph", "Array", "from", "builder", "unsubscribe", "add", "manageNodes", "condition", "removeEdges"]
|
|
7
7
|
}
|
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/sdk/app-graph/src/node.ts":{"bytes":6411,"imports":[],"format":"esm"},"packages/sdk/app-graph/src/graph.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/sdk/app-graph/src/node.ts":{"bytes":6411,"imports":[],"format":"esm"},"packages/sdk/app-graph/src/graph.ts":{"bytes":30335,"imports":[{"path":"@preact/signals-core","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"packages/sdk/app-graph/src/node.ts","kind":"import-statement","original":"./node"}],"format":"esm"},"packages/sdk/app-graph/src/graph-builder.ts":{"bytes":4608,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"packages/sdk/app-graph/src/graph.ts","kind":"import-statement","original":"./graph"}],"format":"esm"},"packages/sdk/app-graph/src/helpers.ts":{"bytes":2039,"imports":[],"format":"esm"},"packages/sdk/app-graph/src/index.ts":{"bytes":723,"imports":[{"path":"packages/sdk/app-graph/src/graph.ts","kind":"import-statement","original":"./graph"},{"path":"packages/sdk/app-graph/src/graph-builder.ts","kind":"import-statement","original":"./graph-builder"},{"path":"packages/sdk/app-graph/src/helpers.ts","kind":"import-statement","original":"./helpers"},{"path":"packages/sdk/app-graph/src/node.ts","kind":"import-statement","original":"./node"}],"format":"esm"}},"outputs":{"packages/sdk/app-graph/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":22525},"packages/sdk/app-graph/dist/lib/node/index.cjs":{"imports":[{"path":"@preact/signals-core","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true}],"exports":["Graph","GraphBuilder","ROOT_ID","actionGroupSymbol","isAction","isActionGroup","isActionLike","isGraphNode","manageNodes"],"entryPoint":"packages/sdk/app-graph/src/index.ts","inputs":{"packages/sdk/app-graph/src/graph.ts":{"bytesInOutput":7674},"packages/sdk/app-graph/src/node.ts":{"bytesInOutput":477},"packages/sdk/app-graph/src/index.ts":{"bytesInOutput":0},"packages/sdk/app-graph/src/graph-builder.ts":{"bytesInOutput":983},"packages/sdk/app-graph/src/helpers.ts":{"bytesInOutput":206}},"bytes":9726}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EchoGraph.stories.d.ts","sourceRoot":"","sources":["../../../../src/stories/EchoGraph.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;;;;;
|
|
1
|
+
{"version":3,"file":"EchoGraph.stories.d.ts","sourceRoot":"","sources":["../../../../src/stories/EchoGraph.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;;;;;AAsBpB,wBAGE;AAgPF,eAAO,MAAM,OAAO;;CAEnB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/app-graph",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10-main.05b9ab6",
|
|
4
4
|
"description": "Constructs knowledge graphs for the purpose of building applications on top of",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -21,25 +21,25 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@preact/signals-core": "^1.6.0",
|
|
24
|
-
"@dxos/async": "0.4.
|
|
25
|
-
"@dxos/
|
|
26
|
-
"@dxos/
|
|
27
|
-
"@dxos/echo-signals": "0.4.
|
|
28
|
-
"@dxos/invariant": "0.4.
|
|
29
|
-
"@dxos/util": "0.4.
|
|
24
|
+
"@dxos/async": "0.4.10-main.05b9ab6",
|
|
25
|
+
"@dxos/debug": "0.4.10-main.05b9ab6",
|
|
26
|
+
"@dxos/echo-schema": "0.4.10-main.05b9ab6",
|
|
27
|
+
"@dxos/echo-signals": "0.4.10-main.05b9ab6",
|
|
28
|
+
"@dxos/invariant": "0.4.10-main.05b9ab6",
|
|
29
|
+
"@dxos/util": "0.4.10-main.05b9ab6"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@phosphor-icons/react": "^2.
|
|
32
|
+
"@phosphor-icons/react": "^2.1.5",
|
|
33
33
|
"@types/react": "^18.0.21",
|
|
34
34
|
"@types/react-dom": "^18.0.6",
|
|
35
35
|
"react": "^18.2.0",
|
|
36
36
|
"react-dom": "^18.2.0",
|
|
37
|
-
"vite": "^5.
|
|
38
|
-
"@dxos/random": "0.4.
|
|
39
|
-
"@dxos/react-
|
|
40
|
-
"@dxos/react-ui": "0.4.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
37
|
+
"vite": "^5.2.9",
|
|
38
|
+
"@dxos/random": "0.4.10-main.05b9ab6",
|
|
39
|
+
"@dxos/react-ui-theme": "0.4.10-main.05b9ab6",
|
|
40
|
+
"@dxos/react-ui": "0.4.10-main.05b9ab6",
|
|
41
|
+
"@dxos/storybook-utils": "0.4.10-main.05b9ab6",
|
|
42
|
+
"@dxos/react-client": "0.4.10-main.05b9ab6"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"react": "^18.2.0",
|
package/src/graph.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { untracked } from '@preact/signals-core';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import { create } from '@dxos/echo-schema';
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
9
|
import { nonNullable } from '@dxos/util';
|
|
10
10
|
|
|
@@ -45,7 +45,7 @@ export class Graph {
|
|
|
45
45
|
/**
|
|
46
46
|
* @internal
|
|
47
47
|
*/
|
|
48
|
-
readonly _nodes =
|
|
48
|
+
readonly _nodes = create<Record<string, NodeBase>>({
|
|
49
49
|
[ROOT_ID]: { id: ROOT_ID, properties: {}, data: null },
|
|
50
50
|
});
|
|
51
51
|
|
|
@@ -54,7 +54,7 @@ export class Graph {
|
|
|
54
54
|
*/
|
|
55
55
|
// Key is the `${node.id}-${direction}` and value is an ordered list of node ids.
|
|
56
56
|
// Explicit type required because TS says this is not portable.
|
|
57
|
-
readonly _edges =
|
|
57
|
+
readonly _edges = create<Record<string, string[]>>({});
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Alias for `findNode('root')`.
|
|
@@ -9,10 +9,11 @@ import { effect } from '@preact/signals-core';
|
|
|
9
9
|
import React, { useEffect, useState } from 'react';
|
|
10
10
|
|
|
11
11
|
import { EventSubscriptions } from '@dxos/async';
|
|
12
|
+
import { create, type EchoReactiveObject } from '@dxos/echo-schema';
|
|
12
13
|
import { registerSignalRuntime } from '@dxos/echo-signals';
|
|
13
14
|
import { faker } from '@dxos/random';
|
|
14
15
|
import { Client } from '@dxos/react-client';
|
|
15
|
-
import {
|
|
16
|
+
import { type Space, SpaceState } from '@dxos/react-client/echo';
|
|
16
17
|
import { ClientRepeater, TestBuilder } from '@dxos/react-client/testing';
|
|
17
18
|
import { Button, DensityProvider, Input, Select } from '@dxos/react-ui';
|
|
18
19
|
import { getSize, mx } from '@dxos/react-ui-theme';
|
|
@@ -56,6 +57,7 @@ const spaceBuilderExtension = (graph: Graph) => {
|
|
|
56
57
|
);
|
|
57
58
|
|
|
58
59
|
const query = space.db.query();
|
|
60
|
+
subscriptions.add(query.subscribe());
|
|
59
61
|
subscriptions.add(
|
|
60
62
|
effect(() => {
|
|
61
63
|
query.objects.forEach((object) => {
|
|
@@ -94,7 +96,8 @@ const objectBuilderExtension = (graph: Graph) => {
|
|
|
94
96
|
subscriptions.clear();
|
|
95
97
|
spaces.forEach((space) => {
|
|
96
98
|
const query = space.db.query({ type: 'test' });
|
|
97
|
-
|
|
99
|
+
subscriptions.add(query.subscribe());
|
|
100
|
+
let previousObjects: EchoReactiveObject<any>[] = [];
|
|
98
101
|
subscriptions.add(
|
|
99
102
|
effect(() => {
|
|
100
103
|
const removedObjects = previousObjects.filter((object) => !query.objects.includes(object));
|
|
@@ -180,7 +183,7 @@ const runAction = (action: Action) => {
|
|
|
180
183
|
}
|
|
181
184
|
|
|
182
185
|
case Action.ADD_OBJECT:
|
|
183
|
-
getSpace()?.db.add(
|
|
186
|
+
getSpace()?.db.add(create({ type: 'test', name: faker.commerce.productName() }));
|
|
184
187
|
break;
|
|
185
188
|
|
|
186
189
|
case Action.REMOVE_OBJECT: {
|