@dxos/app-graph 0.8.3 → 0.8.4-main.03d5cd7b56

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.
Files changed (73) hide show
  1. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  2. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  3. package/dist/lib/neutral/chunk-WJJ5KEOH.mjs +1477 -0
  4. package/dist/lib/neutral/chunk-WJJ5KEOH.mjs.map +7 -0
  5. package/dist/lib/neutral/index.mjs +40 -0
  6. package/dist/lib/neutral/index.mjs.map +7 -0
  7. package/dist/lib/neutral/meta.json +1 -0
  8. package/dist/lib/neutral/scheduler.mjs +15 -0
  9. package/dist/lib/neutral/scheduler.mjs.map +7 -0
  10. package/dist/lib/neutral/testing/index.mjs +40 -0
  11. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  12. package/dist/types/src/atoms.d.ts +8 -0
  13. package/dist/types/src/atoms.d.ts.map +1 -0
  14. package/dist/types/src/graph-builder.d.ts +117 -60
  15. package/dist/types/src/graph-builder.d.ts.map +1 -1
  16. package/dist/types/src/graph.d.ts +188 -218
  17. package/dist/types/src/graph.d.ts.map +1 -1
  18. package/dist/types/src/index.d.ts +7 -3
  19. package/dist/types/src/index.d.ts.map +1 -1
  20. package/dist/types/src/node-matcher.d.ts +244 -0
  21. package/dist/types/src/node-matcher.d.ts.map +1 -0
  22. package/dist/types/src/node-matcher.test.d.ts +2 -0
  23. package/dist/types/src/node-matcher.test.d.ts.map +1 -0
  24. package/dist/types/src/node.d.ts +50 -5
  25. package/dist/types/src/node.d.ts.map +1 -1
  26. package/dist/types/src/scheduler.browser.d.ts +2 -0
  27. package/dist/types/src/scheduler.browser.d.ts.map +1 -0
  28. package/dist/types/src/scheduler.d.ts +8 -0
  29. package/dist/types/src/scheduler.d.ts.map +1 -0
  30. package/dist/types/src/stories/EchoGraph.stories.d.ts +6 -13
  31. package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
  32. package/dist/types/src/testing/index.d.ts +2 -0
  33. package/dist/types/src/testing/index.d.ts.map +1 -0
  34. package/dist/types/src/testing/setup-graph-builder.d.ts +31 -0
  35. package/dist/types/src/testing/setup-graph-builder.d.ts.map +1 -0
  36. package/dist/types/src/util.d.ts +40 -0
  37. package/dist/types/src/util.d.ts.map +1 -0
  38. package/dist/types/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +53 -42
  40. package/src/atoms.ts +25 -0
  41. package/src/graph-builder.test.ts +1193 -126
  42. package/src/graph-builder.ts +753 -264
  43. package/src/graph.test.ts +451 -123
  44. package/src/graph.ts +1057 -407
  45. package/src/index.ts +10 -3
  46. package/src/node-matcher.test.ts +301 -0
  47. package/src/node-matcher.ts +314 -0
  48. package/src/node.ts +83 -7
  49. package/src/scheduler.browser.ts +5 -0
  50. package/src/scheduler.ts +17 -0
  51. package/src/stories/EchoGraph.stories.tsx +178 -255
  52. package/src/stories/Tree.tsx +1 -1
  53. package/src/testing/index.ts +5 -0
  54. package/src/testing/setup-graph-builder.ts +41 -0
  55. package/src/util.ts +101 -0
  56. package/dist/lib/browser/index.mjs +0 -778
  57. package/dist/lib/browser/index.mjs.map +0 -7
  58. package/dist/lib/browser/meta.json +0 -1
  59. package/dist/lib/node/index.cjs +0 -816
  60. package/dist/lib/node/index.cjs.map +0 -7
  61. package/dist/lib/node/meta.json +0 -1
  62. package/dist/lib/node-esm/index.mjs +0 -780
  63. package/dist/lib/node-esm/index.mjs.map +0 -7
  64. package/dist/lib/node-esm/meta.json +0 -1
  65. package/dist/types/src/experimental/graph-projections.test.d.ts +0 -25
  66. package/dist/types/src/experimental/graph-projections.test.d.ts.map +0 -1
  67. package/dist/types/src/signals-integration.test.d.ts +0 -2
  68. package/dist/types/src/signals-integration.test.d.ts.map +0 -1
  69. package/dist/types/src/testing.d.ts +0 -5
  70. package/dist/types/src/testing.d.ts.map +0 -1
  71. package/src/experimental/graph-projections.test.ts +0 -56
  72. package/src/signals-integration.test.ts +0 -218
  73. package/src/testing.ts +0 -20
@@ -1,780 +0,0 @@
1
- import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
-
3
- // packages/sdk/app-graph/src/graph.ts
4
- import { Registry, Rx } from "@effect-rx/rx-react";
5
- import { Option, pipe, Record } from "effect";
6
- import { Event, Trigger } from "@dxos/async";
7
- import { todo } from "@dxos/debug";
8
- import { invariant } from "@dxos/invariant";
9
- import { log } from "@dxos/log";
10
- import { isNonNullable } from "@dxos/util";
11
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
12
- var graphSymbol = Symbol("graph");
13
- var getGraph = (node) => {
14
- const graph = node[graphSymbol];
15
- invariant(graph, "Node is not associated with a graph.", {
16
- F: __dxlog_file,
17
- L: 25,
18
- S: void 0,
19
- A: [
20
- "graph",
21
- "'Node is not associated with a graph.'"
22
- ]
23
- });
24
- return graph;
25
- };
26
- var ROOT_ID = "root";
27
- var ROOT_TYPE = "dxos.org/type/GraphRoot";
28
- var ACTION_TYPE = "dxos.org/type/GraphAction";
29
- var ACTION_GROUP_TYPE = "dxos.org/type/GraphActionGroup";
30
- var Graph = class {
31
- constructor({ registry, nodes, edges, onExpand, onRemoveNode } = {}) {
32
- this.onNodeChanged = new Event();
33
- this._expanded = Record.empty();
34
- this._initialized = Record.empty();
35
- this._initialEdges = Record.empty();
36
- this._initialNodes = Record.fromEntries([
37
- [
38
- ROOT_ID,
39
- this._constructNode({
40
- id: ROOT_ID,
41
- type: ROOT_TYPE,
42
- data: null,
43
- properties: {}
44
- })
45
- ]
46
- ]);
47
- /** @internal */
48
- this._node = Rx.family((id) => {
49
- const initial = Option.flatten(Record.get(this._initialNodes, id));
50
- return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:node:${id}`));
51
- });
52
- this._nodeOrThrow = Rx.family((id) => {
53
- return Rx.make((get) => {
54
- const node = get(this._node(id));
55
- invariant(Option.isSome(node), `Node not available: ${id}`, {
56
- F: __dxlog_file,
57
- L: 253,
58
- S: this,
59
- A: [
60
- "Option.isSome(node)",
61
- "`Node not available: ${id}`"
62
- ]
63
- });
64
- return node.value;
65
- });
66
- });
67
- this._edges = Rx.family((id) => {
68
- const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({
69
- inbound: [],
70
- outbound: []
71
- })));
72
- return Rx.make(initial).pipe(Rx.keepAlive, Rx.withLabel(`graph:edges:${id}`));
73
- });
74
- // NOTE: Currently the argument to the family needs to be referentially stable for the rx to be referentially stable.
75
- // TODO(wittjosiah): Rx feature request, support for something akin to `ComplexMap` to allow for complex arguments.
76
- this._connections = Rx.family((key) => {
77
- return Rx.make((get) => {
78
- const [id, relation] = key.split("$");
79
- const edges = get(this._edges(id));
80
- return edges[relation].map((id2) => get(this._node(id2))).filter(Option.isSome).map((o) => o.value);
81
- }).pipe(Rx.withLabel(`graph:connections:${key}`));
82
- });
83
- this._actions = Rx.family((id) => {
84
- return Rx.make((get) => {
85
- return get(this._connections(`${id}$outbound`)).filter((node) => node.type === ACTION_TYPE || node.type === ACTION_GROUP_TYPE);
86
- }).pipe(Rx.withLabel(`graph:actions:${id}`));
87
- });
88
- this._json = Rx.family((id) => {
89
- return Rx.make((get) => {
90
- const toJSON = (node, seen = []) => {
91
- const nodes = get(this.connections(node.id));
92
- const obj = {
93
- id: node.id.length > 32 ? `${node.id.slice(0, 32)}...` : node.id,
94
- type: node.type
95
- };
96
- if (node.properties.label) {
97
- obj.label = node.properties.label;
98
- }
99
- if (nodes.length) {
100
- obj.nodes = nodes.map((n) => {
101
- const nextSeen = [
102
- ...seen,
103
- node.id
104
- ];
105
- return nextSeen.includes(n.id) ? void 0 : toJSON(n, nextSeen);
106
- }).filter(isNonNullable);
107
- }
108
- return obj;
109
- };
110
- const root = get(this.nodeOrThrow(id));
111
- return toJSON(root);
112
- }).pipe(Rx.withLabel(`graph:json:${id}`));
113
- });
114
- this._registry = registry ?? Registry.make();
115
- this._onExpand = onExpand;
116
- this._onRemoveNode = onRemoveNode;
117
- if (nodes) {
118
- nodes.forEach((node) => {
119
- Record.set(this._initialNodes, node.id, this._constructNode(node));
120
- });
121
- }
122
- if (edges) {
123
- Object.entries(edges).forEach(([source, edges2]) => {
124
- Record.set(this._initialEdges, source, edges2);
125
- });
126
- }
127
- }
128
- toJSON(id = ROOT_ID) {
129
- return this._registry.get(this._json(id));
130
- }
131
- json(id = ROOT_ID) {
132
- return this._json(id);
133
- }
134
- node(id) {
135
- return this._node(id);
136
- }
137
- nodeOrThrow(id) {
138
- return this._nodeOrThrow(id);
139
- }
140
- connections(id, relation = "outbound") {
141
- return this._connections(`${id}$${relation}`);
142
- }
143
- actions(id) {
144
- return this._actions(id);
145
- }
146
- edges(id) {
147
- return this._edges(id);
148
- }
149
- get root() {
150
- return this.getNodeOrThrow(ROOT_ID);
151
- }
152
- getNode(id) {
153
- return this._registry.get(this.node(id));
154
- }
155
- getNodeOrThrow(id) {
156
- return this._registry.get(this.nodeOrThrow(id));
157
- }
158
- getConnections(id, relation = "outbound") {
159
- return this._registry.get(this.connections(id, relation));
160
- }
161
- getActions(id) {
162
- return this._registry.get(this.actions(id));
163
- }
164
- getEdges(id) {
165
- return this._registry.get(this.edges(id));
166
- }
167
- // TODO(wittjosiah): On initialize to restore state from cache.
168
- // async initialize(id: string) {
169
- // const initialized = Record.get(this._initialized, id).pipe(Option.getOrElse(() => false));
170
- // log('initialize', { id, initialized });
171
- // if (!initialized) {
172
- // await this._onInitialize?.(id);
173
- // Record.set(this._initialized, id, true);
174
- // }
175
- // }
176
- expand(id, relation = "outbound") {
177
- const key = `${id}$${relation}`;
178
- const expanded = Record.get(this._expanded, key).pipe(Option.getOrElse(() => false));
179
- log("expand", {
180
- key,
181
- expanded
182
- }, {
183
- F: __dxlog_file,
184
- L: 395,
185
- S: this,
186
- C: (f, a) => f(...a)
187
- });
188
- if (!expanded) {
189
- this._onExpand?.(id, relation);
190
- Record.set(this._expanded, key, true);
191
- }
192
- }
193
- addNodes(nodes) {
194
- Rx.batch(() => {
195
- nodes.map((node) => this.addNode(node));
196
- });
197
- }
198
- addNode({ nodes, edges, ...nodeArg }) {
199
- const { id, type, data = null, properties = {} } = nodeArg;
200
- const nodeRx = this._node(id);
201
- const node = this._registry.get(nodeRx);
202
- Option.match(node, {
203
- onSome: (node2) => {
204
- const typeChanged = node2.type !== type;
205
- const dataChanged = node2.data !== data;
206
- const propertiesChanged = Object.keys(properties).some((key) => node2.properties[key] !== properties[key]);
207
- log("existing node", {
208
- id,
209
- typeChanged,
210
- dataChanged,
211
- propertiesChanged
212
- }, {
213
- F: __dxlog_file,
214
- L: 417,
215
- S: this,
216
- C: (f, a) => f(...a)
217
- });
218
- if (typeChanged || dataChanged || propertiesChanged) {
219
- log("updating node", {
220
- id,
221
- type,
222
- data,
223
- properties
224
- }, {
225
- F: __dxlog_file,
226
- L: 419,
227
- S: this,
228
- C: (f, a) => f(...a)
229
- });
230
- const newNode = Option.some({
231
- ...node2,
232
- type,
233
- data,
234
- properties: {
235
- ...node2.properties,
236
- ...properties
237
- }
238
- });
239
- this._registry.set(nodeRx, newNode);
240
- this.onNodeChanged.emit({
241
- id,
242
- node: newNode
243
- });
244
- }
245
- },
246
- onNone: () => {
247
- log("new node", {
248
- id,
249
- type,
250
- data,
251
- properties
252
- }, {
253
- F: __dxlog_file,
254
- L: 426,
255
- S: this,
256
- C: (f, a) => f(...a)
257
- });
258
- const newNode = this._constructNode({
259
- id,
260
- type,
261
- data,
262
- properties
263
- });
264
- this._registry.set(nodeRx, newNode);
265
- this.onNodeChanged.emit({
266
- id,
267
- node: newNode
268
- });
269
- }
270
- });
271
- if (nodes) {
272
- this.addNodes(nodes);
273
- const _edges = nodes.map((node2) => ({
274
- source: id,
275
- target: node2.id
276
- }));
277
- this.addEdges(_edges);
278
- }
279
- if (edges) {
280
- todo();
281
- }
282
- }
283
- removeNodes(ids, edges = false) {
284
- Rx.batch(() => {
285
- ids.map((id) => this.removeNode(id, edges));
286
- });
287
- }
288
- removeNode(id, edges = false) {
289
- const nodeRx = this._node(id);
290
- this._registry.set(nodeRx, Option.none());
291
- this.onNodeChanged.emit({
292
- id,
293
- node: Option.none()
294
- });
295
- if (edges) {
296
- const { inbound, outbound } = this._registry.get(this._edges(id));
297
- const edges2 = [
298
- ...inbound.map((source) => ({
299
- source,
300
- target: id
301
- })),
302
- ...outbound.map((target) => ({
303
- source: id,
304
- target
305
- }))
306
- ];
307
- this.removeEdges(edges2);
308
- }
309
- this._onRemoveNode?.(id);
310
- }
311
- addEdges(edges) {
312
- Rx.batch(() => {
313
- edges.map((edge) => this.addEdge(edge));
314
- });
315
- }
316
- addEdge(edgeArg) {
317
- const sourceRx = this._edges(edgeArg.source);
318
- const source = this._registry.get(sourceRx);
319
- if (!source.outbound.includes(edgeArg.target)) {
320
- log("add outbound edge", {
321
- source: edgeArg.source,
322
- target: edgeArg.target
323
- }, {
324
- F: __dxlog_file,
325
- L: 481,
326
- S: this,
327
- C: (f, a) => f(...a)
328
- });
329
- this._registry.set(sourceRx, {
330
- inbound: source.inbound,
331
- outbound: [
332
- ...source.outbound,
333
- edgeArg.target
334
- ]
335
- });
336
- }
337
- const targetRx = this._edges(edgeArg.target);
338
- const target = this._registry.get(targetRx);
339
- if (!target.inbound.includes(edgeArg.source)) {
340
- log("add inbound edge", {
341
- source: edgeArg.source,
342
- target: edgeArg.target
343
- }, {
344
- F: __dxlog_file,
345
- L: 488,
346
- S: this,
347
- C: (f, a) => f(...a)
348
- });
349
- this._registry.set(targetRx, {
350
- inbound: [
351
- ...target.inbound,
352
- edgeArg.source
353
- ],
354
- outbound: target.outbound
355
- });
356
- }
357
- }
358
- removeEdges(edges, removeOrphans = false) {
359
- Rx.batch(() => {
360
- edges.map((edge) => this.removeEdge(edge, removeOrphans));
361
- });
362
- }
363
- removeEdge(edgeArg, removeOrphans = false) {
364
- const sourceRx = this._edges(edgeArg.source);
365
- const source = this._registry.get(sourceRx);
366
- if (source.outbound.includes(edgeArg.target)) {
367
- this._registry.set(sourceRx, {
368
- inbound: source.inbound,
369
- outbound: source.outbound.filter((id) => id !== edgeArg.target)
370
- });
371
- }
372
- const targetRx = this._edges(edgeArg.target);
373
- const target = this._registry.get(targetRx);
374
- if (target.inbound.includes(edgeArg.source)) {
375
- this._registry.set(targetRx, {
376
- inbound: target.inbound.filter((id) => id !== edgeArg.source),
377
- outbound: target.outbound
378
- });
379
- }
380
- if (removeOrphans) {
381
- const source2 = this._registry.get(sourceRx);
382
- const target2 = this._registry.get(targetRx);
383
- if (source2.outbound.length === 0 && source2.inbound.length === 0 && edgeArg.source !== ROOT_ID) {
384
- this.removeNodes([
385
- edgeArg.source
386
- ]);
387
- }
388
- if (target2.outbound.length === 0 && target2.inbound.length === 0 && edgeArg.target !== ROOT_ID) {
389
- this.removeNodes([
390
- edgeArg.target
391
- ]);
392
- }
393
- }
394
- }
395
- sortEdges(id, relation, order) {
396
- const edgesRx = this._edges(id);
397
- const edges = this._registry.get(edgesRx);
398
- const unsorted = edges[relation].filter((id2) => !order.includes(id2)) ?? [];
399
- const sorted = order.filter((id2) => edges[relation].includes(id2)) ?? [];
400
- edges[relation].splice(0, edges[relation].length, ...[
401
- ...sorted,
402
- ...unsorted
403
- ]);
404
- this._registry.set(edgesRx, edges);
405
- }
406
- traverse({ visitor, source = ROOT_ID, relation = "outbound" }, path = []) {
407
- if (path.includes(source)) {
408
- return;
409
- }
410
- const node = this.getNodeOrThrow(source);
411
- const shouldContinue = visitor(node, [
412
- ...path,
413
- source
414
- ]);
415
- if (shouldContinue === false) {
416
- return;
417
- }
418
- Object.values(this.getConnections(source, relation)).forEach((child) => this.traverse({
419
- source: child.id,
420
- relation,
421
- visitor
422
- }, [
423
- ...path,
424
- source
425
- ]));
426
- }
427
- getPath({ source = "root", target }) {
428
- return pipe(this.getNode(source), Option.flatMap((node) => {
429
- let found = Option.none();
430
- this.traverse({
431
- source: node.id,
432
- visitor: (node2, path) => {
433
- if (Option.isSome(found)) {
434
- return false;
435
- }
436
- if (node2.id === target) {
437
- found = Option.some(path);
438
- }
439
- }
440
- });
441
- return found;
442
- }));
443
- }
444
- async waitForPath(params, { timeout = 5e3, interval = 500 } = {}) {
445
- const path = this.getPath(params);
446
- if (Option.isSome(path)) {
447
- return path.value;
448
- }
449
- const trigger = new Trigger();
450
- const i = setInterval(() => {
451
- const path2 = this.getPath(params);
452
- if (Option.isSome(path2)) {
453
- trigger.wake(path2.value);
454
- }
455
- }, interval);
456
- return trigger.wait({
457
- timeout
458
- }).finally(() => clearInterval(i));
459
- }
460
- /** @internal */
461
- _constructNode(node) {
462
- return Option.some({
463
- [graphSymbol]: this,
464
- data: null,
465
- properties: {},
466
- ...node
467
- });
468
- }
469
- };
470
-
471
- // packages/sdk/app-graph/src/graph-builder.ts
472
- import { Registry as Registry2, Rx as Rx2 } from "@effect-rx/rx-react";
473
- import { effect } from "@preact/signals-core";
474
- import { Array, pipe as pipe2, Record as Record2 } from "effect";
475
- import { log as log2 } from "@dxos/log";
476
- import { byPosition, getDebugName, isNode, isNonNullable as isNonNullable2 } from "@dxos/util";
477
-
478
- // packages/sdk/app-graph/src/node.ts
479
- var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
480
- var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" : false;
481
- var actionGroupSymbol = Symbol("ActionGroup");
482
- var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol : false;
483
- var isActionLike = (data) => isAction(data) || isActionGroup(data);
484
-
485
- // packages/sdk/app-graph/src/graph-builder.ts
486
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
487
- var createExtension = (extension) => {
488
- const { id, position = "static", relation = "outbound", connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
489
- const getId = (key) => `${id}/${key}`;
490
- const connector = _connector && Rx2.family((node) => _connector(node).pipe(Rx2.withLabel(`graph-builder:_connector:${id}`)));
491
- const actionGroups = _actionGroups && Rx2.family((node) => _actionGroups(node).pipe(Rx2.withLabel(`graph-builder:_actionGroups:${id}`)));
492
- const actions = _actions && Rx2.family((node) => _actions(node).pipe(Rx2.withLabel(`graph-builder:_actions:${id}`)));
493
- return [
494
- // resolver ? { id: getId('resolver'), position, resolver } : undefined,
495
- connector ? {
496
- id: getId("connector"),
497
- position,
498
- relation,
499
- connector: Rx2.family((node) => Rx2.make((get) => {
500
- try {
501
- return get(connector(node));
502
- } catch {
503
- log2.warn("Error in connector", {
504
- id: getId("connector"),
505
- node
506
- }, {
507
- F: __dxlog_file2,
508
- L: 101,
509
- S: void 0,
510
- C: (f, a) => f(...a)
511
- });
512
- return [];
513
- }
514
- }).pipe(Rx2.withLabel(`graph-builder:connector:${id}`)))
515
- } : void 0,
516
- actionGroups ? {
517
- id: getId("actionGroups"),
518
- position,
519
- relation: "outbound",
520
- connector: Rx2.family((node) => Rx2.make((get) => {
521
- try {
522
- return get(actionGroups(node)).map((arg) => ({
523
- ...arg,
524
- data: actionGroupSymbol,
525
- type: ACTION_GROUP_TYPE
526
- }));
527
- } catch {
528
- log2.warn("Error in actionGroups", {
529
- id: getId("actionGroups"),
530
- node
531
- }, {
532
- F: __dxlog_file2,
533
- L: 122,
534
- S: void 0,
535
- C: (f, a) => f(...a)
536
- });
537
- return [];
538
- }
539
- }).pipe(Rx2.withLabel(`graph-builder:connector:actionGroups:${id}`)))
540
- } : void 0,
541
- actions ? {
542
- id: getId("actions"),
543
- position,
544
- relation: "outbound",
545
- connector: Rx2.family((node) => Rx2.make((get) => {
546
- try {
547
- return get(actions(node)).map((arg) => ({
548
- ...arg,
549
- type: ACTION_TYPE
550
- }));
551
- } catch {
552
- log2.warn("Error in actions", {
553
- id: getId("actions"),
554
- node
555
- }, {
556
- F: __dxlog_file2,
557
- L: 139,
558
- S: void 0,
559
- C: (f, a) => f(...a)
560
- });
561
- return [];
562
- }
563
- }).pipe(Rx2.withLabel(`graph-builder:connector:actions:${id}`)))
564
- } : void 0
565
- ].filter(isNonNullable2);
566
- };
567
- var flattenExtensions = (extension, acc = []) => {
568
- if (Array.isArray(extension)) {
569
- return [
570
- ...acc,
571
- ...extension.flatMap((ext) => flattenExtensions(ext, acc))
572
- ];
573
- } else {
574
- return [
575
- ...acc,
576
- extension
577
- ];
578
- }
579
- };
580
- var GraphBuilder = class _GraphBuilder {
581
- constructor({ registry, ...params } = {}) {
582
- // TODO(wittjosiah): Use Context.
583
- this._connectorSubscriptions = /* @__PURE__ */ new Map();
584
- this._extensions = Rx2.make(Record2.empty()).pipe(Rx2.keepAlive, Rx2.withLabel("graph-builder:extensions"));
585
- this._connectors = Rx2.family((key) => {
586
- return Rx2.make((get) => {
587
- const [id, relation] = key.split("+");
588
- const node = this._graph.node(id);
589
- return pipe2(
590
- get(this._extensions),
591
- Record2.values,
592
- // TODO(wittjosiah): Sort on write rather than read.
593
- Array.sortBy(byPosition),
594
- Array.filter(({ relation: _relation = "outbound" }) => _relation === relation),
595
- Array.map(({ connector }) => connector?.(node)),
596
- Array.filter(isNonNullable2),
597
- Array.flatMap((result) => get(result))
598
- );
599
- }).pipe(Rx2.withLabel(`graph-builder:connectors:${key}`));
600
- });
601
- this._registry = registry ?? Registry2.make();
602
- this._graph = new Graph({
603
- ...params,
604
- registry: this._registry,
605
- onExpand: (id, relation) => this._onExpand(id, relation),
606
- // onInitialize: (id) => this._onInitialize(id),
607
- onRemoveNode: (id) => this._onRemoveNode(id)
608
- });
609
- }
610
- static from(pickle, registry) {
611
- if (!pickle) {
612
- return new _GraphBuilder({
613
- registry
614
- });
615
- }
616
- const { nodes, edges } = JSON.parse(pickle);
617
- return new _GraphBuilder({
618
- nodes,
619
- edges,
620
- registry
621
- });
622
- }
623
- get graph() {
624
- return this._graph;
625
- }
626
- get extensions() {
627
- return this._extensions;
628
- }
629
- addExtension(extensions) {
630
- flattenExtensions(extensions).forEach((extension) => {
631
- const extensions2 = this._registry.get(this._extensions);
632
- this._registry.set(this._extensions, Record2.set(extensions2, extension.id, extension));
633
- });
634
- return this;
635
- }
636
- removeExtension(id) {
637
- const extensions = this._registry.get(this._extensions);
638
- this._registry.set(this._extensions, Record2.remove(extensions, id));
639
- return this;
640
- }
641
- async explore({ registry = Registry2.make(), source = ROOT_ID, relation = "outbound", visitor }, path = []) {
642
- if (path.includes(source)) {
643
- return;
644
- }
645
- if (!isNode()) {
646
- const { yieldOrContinue } = await import("main-thread-scheduling");
647
- await yieldOrContinue("idle");
648
- }
649
- const node = registry.get(this._graph.nodeOrThrow(source));
650
- const shouldContinue = await visitor(node, [
651
- ...path,
652
- node.id
653
- ]);
654
- if (shouldContinue === false) {
655
- return;
656
- }
657
- const nodes = Object.values(this._registry.get(this._extensions)).filter((extension) => relation === (extension.relation ?? "outbound")).map((extension) => extension.connector).filter(isNonNullable2).flatMap((connector) => registry.get(connector(this._graph.node(source))));
658
- await Promise.all(nodes.map((nodeArg) => {
659
- registry.set(this._graph._node(nodeArg.id), this._graph._constructNode(nodeArg));
660
- return this.explore({
661
- registry,
662
- source: nodeArg.id,
663
- relation,
664
- visitor
665
- }, [
666
- ...path,
667
- node.id
668
- ]);
669
- }));
670
- if (registry !== this._registry) {
671
- registry.reset();
672
- registry.dispose();
673
- }
674
- }
675
- destroy() {
676
- this._connectorSubscriptions.forEach((unsubscribe) => unsubscribe());
677
- this._connectorSubscriptions.clear();
678
- }
679
- _onExpand(id, relation) {
680
- log2("onExpand", {
681
- id,
682
- relation,
683
- registry: getDebugName(this._registry)
684
- }, {
685
- F: __dxlog_file2,
686
- L: 301,
687
- S: this,
688
- C: (f, a) => f(...a)
689
- });
690
- const connectors = this._connectors(`${id}+${relation}`);
691
- let previous = [];
692
- const cancel = this._registry.subscribe(connectors, (nodes) => {
693
- const ids = nodes.map((n) => n.id);
694
- const removed = previous.filter((id2) => !ids.includes(id2));
695
- previous = ids;
696
- log2("update", {
697
- id,
698
- relation,
699
- ids,
700
- removed
701
- }, {
702
- F: __dxlog_file2,
703
- L: 312,
704
- S: this,
705
- C: (f, a) => f(...a)
706
- });
707
- const update = () => {
708
- Rx2.batch(() => {
709
- this._graph.removeEdges(removed.map((target) => ({
710
- source: id,
711
- target
712
- })), true);
713
- this._graph.addNodes(nodes);
714
- this._graph.addEdges(nodes.map((node) => relation === "outbound" ? {
715
- source: id,
716
- target: node.id
717
- } : {
718
- source: node.id,
719
- target: id
720
- }));
721
- this._graph.sortEdges(id, relation, nodes.map(({ id: id2 }) => id2));
722
- });
723
- };
724
- if (typeof requestAnimationFrame === "function") {
725
- requestAnimationFrame(update);
726
- } else {
727
- update();
728
- }
729
- }, {
730
- immediate: true
731
- });
732
- this._connectorSubscriptions.set(id, cancel);
733
- }
734
- // TODO(wittjosiah): On initialize to restore state from cache.
735
- // private async _onInitialize(id: string) {
736
- // log('onInitialize', { id });
737
- // }
738
- _onRemoveNode(id) {
739
- this._connectorSubscriptions.get(id)?.();
740
- this._connectorSubscriptions.delete(id);
741
- }
742
- };
743
- var rxFromSignal = (cb) => {
744
- return Rx2.make((get) => {
745
- const dispose = effect(() => {
746
- get.setSelf(cb());
747
- });
748
- get.addFinalizer(() => dispose());
749
- return cb();
750
- });
751
- };
752
- var observableFamily = Rx2.family((observable) => {
753
- return Rx2.make((get) => {
754
- const subscription = observable.subscribe((value) => get.setSelf(value));
755
- get.addFinalizer(() => subscription.unsubscribe());
756
- return observable.get();
757
- });
758
- });
759
- var rxFromObservable = (observable) => {
760
- return observableFamily(observable);
761
- };
762
- export {
763
- ACTION_GROUP_TYPE,
764
- ACTION_TYPE,
765
- Graph,
766
- GraphBuilder,
767
- ROOT_ID,
768
- ROOT_TYPE,
769
- actionGroupSymbol,
770
- createExtension,
771
- flattenExtensions,
772
- getGraph,
773
- isAction,
774
- isActionGroup,
775
- isActionLike,
776
- isGraphNode,
777
- rxFromObservable,
778
- rxFromSignal
779
- };
780
- //# sourceMappingURL=index.mjs.map