@dxos/app-graph 0.7.1 → 0.7.2-main.f1adc9f

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/src/graph.ts CHANGED
@@ -8,7 +8,7 @@ import { asyncTimeout, Trigger } from '@dxos/async';
8
8
  import { type ReactiveObject, create } from '@dxos/echo-schema';
9
9
  import { invariant } from '@dxos/invariant';
10
10
  import { log } from '@dxos/log';
11
- import { nonNullable } from '@dxos/util';
11
+ import { type MakeOptional, nonNullable, pick } from '@dxos/util';
12
12
 
13
13
  import { type Relation, type Node, type NodeArg, type NodeFilter, isActionLike, actionGroupSymbol } from './node';
14
14
 
@@ -66,8 +66,7 @@ export type GraphTraversalOptions = {
66
66
  };
67
67
 
68
68
  export type GraphParams = {
69
- // TODO(wittjosiah): Make data optional instead of omitting.
70
- nodes?: Omit<Node, 'data'>[];
69
+ nodes?: MakeOptional<Node, 'data' | 'cacheable'>[];
71
70
  edges?: Record<string, string[]>;
72
71
  onInitialNode?: Graph['_onInitialNode'];
73
72
  onInitialNodes?: Graph['_onInitialNodes'];
@@ -96,15 +95,26 @@ export class Graph {
96
95
  readonly _edges: Record<string, ReactiveObject<{ inbound: string[]; outbound: string[] }>> = {};
97
96
 
98
97
  constructor({ nodes, edges, onInitialNode, onInitialNodes, onRemoveNode }: GraphParams = {}) {
99
- this._nodes[ROOT_ID] = this._constructNode({ id: ROOT_ID, type: ROOT_TYPE, properties: {}, data: null });
98
+ this._onInitialNode = onInitialNode;
99
+ this._onInitialNodes = onInitialNodes;
100
+ this._onRemoveNode = onRemoveNode;
101
+
102
+ this._nodes[ROOT_ID] = this._constructNode({
103
+ id: ROOT_ID,
104
+ type: ROOT_TYPE,
105
+ cacheable: [],
106
+ properties: {},
107
+ data: null,
108
+ });
100
109
  if (nodes) {
101
110
  nodes.forEach((node) => {
111
+ const cacheable = Object.keys(node.properties ?? {});
102
112
  if (node.type === ACTION_TYPE) {
103
- this._addNode({ ...node, data: () => log.warn('Pickled action invocation') });
113
+ this._addNode({ cacheable, data: () => log.warn('Pickled action invocation'), ...node });
104
114
  } else if (node.type === ACTION_GROUP_TYPE) {
105
- this._addNode({ ...node, data: actionGroupSymbol });
115
+ this._addNode({ cacheable, data: actionGroupSymbol, ...node });
106
116
  } else {
107
- this._addNode(node);
117
+ this._addNode({ cacheable, ...node });
108
118
  }
109
119
  });
110
120
  }
@@ -118,10 +128,6 @@ export class Graph {
118
128
  this._sortEdges(source, 'outbound', edges);
119
129
  });
120
130
  }
121
-
122
- this._onInitialNode = onInitialNode;
123
- this._onInitialNodes = onInitialNodes;
124
- this._onRemoveNode = onRemoveNode;
125
131
  }
126
132
 
127
133
  static from(pickle: string, options: Omit<GraphParams, 'nodes' | 'edges'> = {}) {
@@ -167,17 +173,23 @@ export class Graph {
167
173
  }
168
174
 
169
175
  pickle() {
170
- const nodes = Object.values(this._nodes).map((node) => {
171
- return {
172
- id: node.id,
173
- type: node.type,
174
- properties: node.properties,
175
- };
176
- });
176
+ const nodes = Object.values(this._nodes)
177
+ .filter((node) => !!node.cacheable)
178
+ .map((node) => {
179
+ return {
180
+ id: node.id,
181
+ type: node.type,
182
+ properties: pick(node.properties, node.cacheable!),
183
+ };
184
+ });
185
+
186
+ const cacheable = new Set(nodes.map((node) => node.id));
177
187
 
178
188
  const edges = Object.fromEntries(
179
189
  Object.entries(this._edges)
180
- .map(([id, { outbound }]): [string, string[]] => [id, outbound])
190
+ .filter(([id]) => cacheable.has(id))
191
+ .map(([id, { outbound }]): [string, string[]] => [id, outbound.filter((nodeId) => cacheable.has(nodeId))])
192
+ // TODO(wittjosiah): Why sort?
181
193
  .toSorted(([a], [b]) => a.localeCompare(b)),
182
194
  );
183
195
 
@@ -440,7 +452,7 @@ export class Graph {
440
452
 
441
453
  private _removeNode(id: string, edges = false) {
442
454
  untracked(() => {
443
- const node = this.findNode(id);
455
+ const node = this.findNode(id, false);
444
456
  if (!node) {
445
457
  return;
446
458
  }
package/src/node.ts CHANGED
@@ -20,6 +20,13 @@ export type Node<TData = any, TProperties extends Record<string, any> = Record<s
20
20
  */
21
21
  type: string;
22
22
 
23
+ /**
24
+ * Keys in of the properties which should be cached.
25
+ * If defined, the node will be included in the cache.
26
+ * If undefined, the node will not be included in the cache.
27
+ */
28
+ cacheable?: string[];
29
+
23
30
  /**
24
31
  * Properties of the node relevant to displaying the node.
25
32
  */
@@ -47,7 +54,7 @@ export const isGraphNode = (data: unknown): data is Node =>
47
54
 
48
55
  export type NodeArg<TData, TProperties extends Record<string, any> = Record<string, any>> = MakeOptional<
49
56
  Node<TData, TProperties>,
50
- 'data' | 'properties'
57
+ 'data' | 'properties' | 'cacheable'
51
58
  > & {
52
59
  /** Will automatically add nodes with an edge from this node to each. */
53
60
  nodes?: NodeArg<unknown>[];
@@ -10,7 +10,7 @@ import React, { useEffect, useState } from 'react';
10
10
  import {
11
11
  create,
12
12
  type Echo,
13
- type EchoReactiveObject,
13
+ type ReactiveEchoObject,
14
14
  type FilterSource,
15
15
  type Space,
16
16
  SpaceState,
@@ -54,7 +54,7 @@ const actionWeights = {
54
54
  };
55
55
 
56
56
  // TODO(wittjosiah): Factor out.
57
- const memoizeQuery = <T extends EchoReactiveObject<any>>(
57
+ const memoizeQuery = <T extends ReactiveEchoObject<any>>(
58
58
  spaceOrEcho?: Space | Echo,
59
59
  filter?: FilterSource<T>,
60
60
  options?: QueryOptions,