@dxos/app-graph 0.8.4-main.fffef41 → 0.8.4-staging.60fe92afc8

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