@dxos/app-graph 0.8.4-main.f9ba587 → 0.8.4-main.fcfe5033a5

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 (61) hide show
  1. package/dist/lib/browser/chunk-W47H2NND.mjs +1604 -0
  2. package/dist/lib/browser/chunk-W47H2NND.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +27 -767
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +39 -0
  7. package/dist/lib/browser/testing/index.mjs.map +7 -0
  8. package/dist/lib/node-esm/chunk-LYZWNJ7J.mjs +1605 -0
  9. package/dist/lib/node-esm/chunk-LYZWNJ7J.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +27 -768
  11. package/dist/lib/node-esm/index.mjs.map +4 -4
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/testing/index.mjs +40 -0
  14. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  15. package/dist/types/src/atoms.d.ts +8 -0
  16. package/dist/types/src/atoms.d.ts.map +1 -0
  17. package/dist/types/src/graph-builder.d.ts +117 -60
  18. package/dist/types/src/graph-builder.d.ts.map +1 -1
  19. package/dist/types/src/graph.d.ts +188 -218
  20. package/dist/types/src/graph.d.ts.map +1 -1
  21. package/dist/types/src/index.d.ts +7 -3
  22. package/dist/types/src/index.d.ts.map +1 -1
  23. package/dist/types/src/node-matcher.d.ts +244 -0
  24. package/dist/types/src/node-matcher.d.ts.map +1 -0
  25. package/dist/types/src/node-matcher.test.d.ts +2 -0
  26. package/dist/types/src/node-matcher.test.d.ts.map +1 -0
  27. package/dist/types/src/node.d.ts +50 -5
  28. package/dist/types/src/node.d.ts.map +1 -1
  29. package/dist/types/src/stories/EchoGraph.stories.d.ts +8 -10
  30. package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
  31. package/dist/types/src/testing/index.d.ts +2 -0
  32. package/dist/types/src/testing/index.d.ts.map +1 -0
  33. package/dist/types/src/testing/setup-graph-builder.d.ts +31 -0
  34. package/dist/types/src/testing/setup-graph-builder.d.ts.map +1 -0
  35. package/dist/types/src/util.d.ts +39 -0
  36. package/dist/types/src/util.d.ts.map +1 -0
  37. package/dist/types/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +48 -38
  39. package/src/atoms.ts +25 -0
  40. package/src/graph-builder.test.ts +1067 -126
  41. package/src/graph-builder.ts +734 -264
  42. package/src/graph.test.ts +451 -123
  43. package/src/graph.ts +1057 -407
  44. package/src/index.ts +10 -3
  45. package/src/node-matcher.test.ts +301 -0
  46. package/src/node-matcher.ts +314 -0
  47. package/src/node.ts +83 -7
  48. package/src/stories/EchoGraph.stories.tsx +180 -140
  49. package/src/stories/Tree.tsx +1 -1
  50. package/src/testing/index.ts +5 -0
  51. package/src/testing/setup-graph-builder.ts +41 -0
  52. package/src/util.ts +95 -0
  53. package/dist/types/src/experimental/graph-projections.test.d.ts +0 -25
  54. package/dist/types/src/experimental/graph-projections.test.d.ts.map +0 -1
  55. package/dist/types/src/signals-integration.test.d.ts +0 -2
  56. package/dist/types/src/signals-integration.test.d.ts.map +0 -1
  57. package/dist/types/src/testing.d.ts +0 -5
  58. package/dist/types/src/testing.d.ts.map +0 -1
  59. package/src/experimental/graph-projections.test.ts +0 -56
  60. package/src/signals-integration.test.ts +0 -218
  61. package/src/testing.ts +0 -20
@@ -0,0 +1,1604 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/node.ts
8
+ var node_exports = {};
9
+ __export(node_exports, {
10
+ ActionGroupType: () => ActionGroupType,
11
+ ActionType: () => ActionType,
12
+ RootId: () => RootId,
13
+ RootType: () => RootType,
14
+ actionGroupSymbol: () => actionGroupSymbol,
15
+ actionRelation: () => actionRelation,
16
+ childRelation: () => childRelation,
17
+ isAction: () => isAction,
18
+ isActionGroup: () => isActionGroup,
19
+ isActionLike: () => isActionLike,
20
+ isGraphNode: () => isGraphNode,
21
+ make: () => make,
22
+ makeAction: () => makeAction,
23
+ makeActionGroup: () => makeActionGroup,
24
+ relation: () => relation
25
+ });
26
+ var RootId = "root";
27
+ var RootType = "org.dxos.type.graphRoot";
28
+ var ActionType = "org.dxos.type.graphAction";
29
+ var ActionGroupType = "org.dxos.type.graphActionGroup";
30
+ var relation = (kind, direction = "outbound") => ({
31
+ kind,
32
+ direction
33
+ });
34
+ var childRelation = (direction = "outbound") => relation("child", direction);
35
+ var actionRelation = (direction = "outbound") => relation("action", direction);
36
+ var isGraphNode = (data) => data && typeof data === "object" && "id" in data && "properties" in data && data.properties ? typeof data.properties === "object" && "data" in data : false;
37
+ var isAction = (data) => isGraphNode(data) ? typeof data.data === "function" && data.type === ActionType : false;
38
+ var actionGroupSymbol = /* @__PURE__ */ Symbol("ActionGroup");
39
+ var isActionGroup = (data) => isGraphNode(data) ? data.data === actionGroupSymbol && data.type === ActionGroupType : false;
40
+ var isActionLike = (data) => isAction(data) || isActionGroup(data);
41
+ var make = (arg) => arg;
42
+ var makeAction = (arg) => ({
43
+ ...arg,
44
+ type: ActionType
45
+ });
46
+ var makeActionGroup = (arg) => ({
47
+ ...arg,
48
+ type: ActionGroupType,
49
+ data: actionGroupSymbol
50
+ });
51
+
52
+ // src/util.ts
53
+ import { invariant } from "@dxos/invariant";
54
+ var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-graph/src/util.ts";
55
+ var PRIMARY = "";
56
+ var SECONDARY = "";
57
+ var PATH = "/";
58
+ var primaryKey = (...parts) => parts.join(PRIMARY);
59
+ var primaryParts = (key) => key.split(PRIMARY);
60
+ var secondaryKey = (...parts) => parts.join(SECONDARY);
61
+ var secondaryParts = (key) => key.split(SECONDARY);
62
+ var normalizeRelation = (relation2) => relation2 == null ? childRelation() : typeof relation2 === "string" ? relation(relation2) : relation2;
63
+ var shallowEqual = (a, b) => {
64
+ if (a === b) return true;
65
+ if (a == null || b == null || typeof a !== "object" || typeof b !== "object") return false;
66
+ const keysA = Object.keys(a);
67
+ const keysB = Object.keys(b);
68
+ if (keysA.length !== keysB.length) {
69
+ return false;
70
+ }
71
+ return keysA.every((k) => a[k] === b[k]);
72
+ };
73
+ var nodeArgsUnchanged = (prev, next) => {
74
+ if (prev.length !== next.length) {
75
+ return false;
76
+ }
77
+ return prev.every((prevNode, idx) => {
78
+ const nextNode = next[idx];
79
+ return prevNode.id === nextNode.id && prevNode.type === nextNode.type && shallowEqual(prevNode.data, nextNode.data) && shallowEqual(prevNode.properties, nextNode.properties);
80
+ });
81
+ };
82
+ var qualifyId = (parentId, ...segmentIds) => [
83
+ parentId,
84
+ ...segmentIds
85
+ ].join(PATH);
86
+ var validateSegmentId = (id) => {
87
+ invariant(!id.includes(PATH), `Node segment ID must not contain '${PATH}': ${id}`, {
88
+ F: __dxlog_file,
89
+ L: 78,
90
+ S: void 0,
91
+ A: [
92
+ "!id.includes(PATH)",
93
+ "`Node segment ID must not contain '${PATH}': ${id}`"
94
+ ]
95
+ });
96
+ };
97
+ var getParentId = (qualifiedId) => {
98
+ const lastSlash = qualifiedId.lastIndexOf(PATH);
99
+ return lastSlash > 0 ? qualifiedId.slice(0, lastSlash) : void 0;
100
+ };
101
+ var getSegmentId = (qualifiedId) => {
102
+ return qualifiedId.split(PATH).pop() ?? qualifiedId;
103
+ };
104
+
105
+ // src/graph.ts
106
+ var graph_exports = {};
107
+ __export(graph_exports, {
108
+ GraphKind: () => GraphKind,
109
+ GraphTypeId: () => GraphTypeId,
110
+ addEdge: () => addEdge,
111
+ addEdges: () => addEdges,
112
+ addNode: () => addNode,
113
+ addNodes: () => addNodes,
114
+ expand: () => expand,
115
+ getActions: () => getActions,
116
+ getConnections: () => getConnections,
117
+ getEdges: () => getEdges,
118
+ getGraph: () => getGraph,
119
+ getNode: () => getNode,
120
+ getNodeOrThrow: () => getNodeOrThrow,
121
+ getPath: () => getPath,
122
+ getRoot: () => getRoot,
123
+ initialize: () => initialize,
124
+ make: () => make2,
125
+ relationFromKey: () => relationFromKey,
126
+ relationKey: () => relationKey,
127
+ removeEdge: () => removeEdge,
128
+ removeEdges: () => removeEdges,
129
+ removeNode: () => removeNode,
130
+ removeNodes: () => removeNodes,
131
+ sortEdges: () => sortEdges,
132
+ toJSON: () => toJSON,
133
+ traverse: () => traverse,
134
+ waitForPath: () => waitForPath
135
+ });
136
+ import { Atom, Registry } from "@effect-atom/atom-react";
137
+ import * as Function from "effect/Function";
138
+ import * as Option from "effect/Option";
139
+ import * as Pipeable from "effect/Pipeable";
140
+ import * as Record from "effect/Record";
141
+ import { Event, Trigger } from "@dxos/async";
142
+ import { todo } from "@dxos/debug";
143
+ import { invariant as invariant2 } from "@dxos/invariant";
144
+ import { log } from "@dxos/log";
145
+ import { isNonNullable } from "@dxos/util";
146
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph.ts";
147
+ var graphSymbol = /* @__PURE__ */ Symbol("graph");
148
+ var getGraph = (node) => {
149
+ const graph = node[graphSymbol];
150
+ invariant2(graph, "Node is not associated with a graph.", {
151
+ F: __dxlog_file2,
152
+ L: 33,
153
+ S: void 0,
154
+ A: [
155
+ "graph",
156
+ "'Node is not associated with a graph.'"
157
+ ]
158
+ });
159
+ return graph;
160
+ };
161
+ var GraphTypeId = /* @__PURE__ */ Symbol.for("@dxos/app-graph/Graph");
162
+ var GraphKind = /* @__PURE__ */ Symbol.for("@dxos/app-graph/GraphKind");
163
+ var GraphImpl = class {
164
+ [GraphTypeId] = GraphTypeId;
165
+ [GraphKind] = "writable";
166
+ pipe() {
167
+ return Pipeable.pipeArguments(this, arguments);
168
+ }
169
+ onNodeChanged = new Event();
170
+ _onExpand;
171
+ _onInitialize;
172
+ _onRemoveNode;
173
+ _registry;
174
+ _expanded = Record.empty();
175
+ _pendingExpands = /* @__PURE__ */ new Set();
176
+ _initialized = Record.empty();
177
+ _initialEdges = Record.empty();
178
+ _initialNodes = Record.fromEntries([
179
+ [
180
+ RootId,
181
+ this._constructNode({
182
+ id: RootId,
183
+ type: RootType,
184
+ data: null,
185
+ properties: {}
186
+ })
187
+ ]
188
+ ]);
189
+ /** @internal */
190
+ _node = Atom.family((id) => {
191
+ const initial = Option.flatten(Record.get(this._initialNodes, id));
192
+ return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:node:${id}`));
193
+ });
194
+ _nodeOrThrow = Atom.family((id) => {
195
+ return Atom.make((get2) => {
196
+ const node = get2(this._node(id));
197
+ invariant2(Option.isSome(node), `Node not available: ${id}`, {
198
+ F: __dxlog_file2,
199
+ L: 172,
200
+ S: this,
201
+ A: [
202
+ "Option.isSome(node)",
203
+ "`Node not available: ${id}`"
204
+ ]
205
+ });
206
+ return node.value;
207
+ });
208
+ });
209
+ _edges = Atom.family((id) => {
210
+ const initial = Record.get(this._initialEdges, id).pipe(Option.getOrElse(() => ({})));
211
+ return Atom.make(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:edges:${id}`));
212
+ });
213
+ // NOTE: Currently the argument to the family needs to be referentially stable for the atom to be referentially stable.
214
+ // TODO(wittjosiah): Atom feature request, support for something akin to `ComplexMap` to allow for complex arguments.
215
+ _connections = Atom.family((key) => {
216
+ return Atom.make((get2) => {
217
+ const parts = key ? primaryParts(key) : [];
218
+ if (parts.length < 2 || !parts[0]) {
219
+ return [];
220
+ }
221
+ const { id, relation: relation2 } = relationFromConnectionKey(key);
222
+ const edges = get2(this._edges(id));
223
+ return (edges[relationKey(relation2)] ?? []).map((id2) => get2(this._node(id2))).filter(Option.isSome).map((o) => o.value);
224
+ }).pipe(Atom.withLabel(`graph:connections:${key}`));
225
+ });
226
+ _actions = Atom.family((id) => {
227
+ return Atom.make((get2) => {
228
+ if (!id) {
229
+ return [];
230
+ }
231
+ return get2(this._connections(connectionKey(id, actionRelation())));
232
+ }).pipe(Atom.withLabel(`graph:actions:${id}`));
233
+ });
234
+ _json = Atom.family((id) => {
235
+ return Atom.make((get2) => {
236
+ const toJSON2 = (node, seen = []) => {
237
+ const nodes = get2(this._connections(connectionKey(node.id, "child")));
238
+ const obj = {
239
+ id: node.id,
240
+ type: node.type
241
+ };
242
+ if (node.properties.label) {
243
+ obj.label = node.properties.label;
244
+ }
245
+ if (nodes.length) {
246
+ obj.nodes = nodes.map((n) => {
247
+ const nextSeen = [
248
+ ...seen,
249
+ node.id
250
+ ];
251
+ return nextSeen.includes(n.id) ? void 0 : toJSON2(n, nextSeen);
252
+ }).filter(isNonNullable);
253
+ }
254
+ return obj;
255
+ };
256
+ const root = get2(this._nodeOrThrow(id));
257
+ return toJSON2(root);
258
+ }).pipe(Atom.withLabel(`graph:json:${id}`));
259
+ });
260
+ constructor({ registry, nodes, edges, onInitialize, onExpand, onRemoveNode } = {}) {
261
+ this._registry = registry ?? Registry.make();
262
+ this._onInitialize = onInitialize;
263
+ this._onExpand = onExpand;
264
+ this._onRemoveNode = onRemoveNode;
265
+ if (nodes) {
266
+ nodes.forEach((node) => {
267
+ Record.set(this._initialNodes, node.id, this._constructNode(node));
268
+ });
269
+ }
270
+ if (edges) {
271
+ Object.entries(edges).forEach(([source, edges2]) => {
272
+ Record.set(this._initialEdges, source, edges2);
273
+ });
274
+ }
275
+ }
276
+ json(id = RootId) {
277
+ return jsonImpl(this, id);
278
+ }
279
+ node(id) {
280
+ return nodeImpl(this, id);
281
+ }
282
+ nodeOrThrow(id) {
283
+ return nodeOrThrowImpl(this, id);
284
+ }
285
+ connections(id, relation2) {
286
+ return connectionsImpl(this, id, relation2);
287
+ }
288
+ actions(id) {
289
+ return actionsImpl(this, id);
290
+ }
291
+ edges(id) {
292
+ return edgesImpl(this, id);
293
+ }
294
+ /** @internal */
295
+ _constructNode(node) {
296
+ return Option.some({
297
+ [graphSymbol]: this,
298
+ data: null,
299
+ properties: {},
300
+ ...node
301
+ });
302
+ }
303
+ };
304
+ var getInternal = (graph) => {
305
+ return graph;
306
+ };
307
+ var toJSON = (graph, id = RootId) => {
308
+ const internal = getInternal(graph);
309
+ return internal._registry.get(internal._json(id));
310
+ };
311
+ var jsonImpl = (graph, id = RootId) => {
312
+ const internal = getInternal(graph);
313
+ return internal._json(id);
314
+ };
315
+ var nodeImpl = (graph, id) => {
316
+ const internal = getInternal(graph);
317
+ return internal._node(id);
318
+ };
319
+ var nodeOrThrowImpl = (graph, id) => {
320
+ const internal = getInternal(graph);
321
+ return internal._nodeOrThrow(id);
322
+ };
323
+ var connectionsImpl = (graph, id, relation2) => {
324
+ const internal = getInternal(graph);
325
+ return internal._connections(connectionKey(id, relation2));
326
+ };
327
+ var actionsImpl = (graph, id) => {
328
+ const internal = getInternal(graph);
329
+ return internal._actions(id);
330
+ };
331
+ var edgesImpl = (graph, id) => {
332
+ const internal = getInternal(graph);
333
+ return internal._edges(id);
334
+ };
335
+ var getNodeImpl = (graph, id) => {
336
+ const internal = getInternal(graph);
337
+ return internal._registry.get(nodeImpl(graph, id));
338
+ };
339
+ function getNode(graphOrId, id) {
340
+ if (typeof graphOrId === "string") {
341
+ const id2 = graphOrId;
342
+ return (graph) => getNodeImpl(graph, id2);
343
+ } else {
344
+ const graph = graphOrId;
345
+ return getNodeImpl(graph, id);
346
+ }
347
+ }
348
+ var getNodeOrThrowImpl = (graph, id) => {
349
+ const internal = getInternal(graph);
350
+ return internal._registry.get(nodeOrThrowImpl(graph, id));
351
+ };
352
+ function getNodeOrThrow(graphOrId, id) {
353
+ if (typeof graphOrId === "string") {
354
+ const id2 = graphOrId;
355
+ return (graph) => getNodeOrThrowImpl(graph, id2);
356
+ } else {
357
+ const graph = graphOrId;
358
+ return getNodeOrThrowImpl(graph, id);
359
+ }
360
+ }
361
+ function getRoot(graph) {
362
+ return getNodeOrThrowImpl(graph, RootId);
363
+ }
364
+ var getConnectionsImpl = (graph, id, relation2) => {
365
+ const internal = getInternal(graph);
366
+ return internal._registry.get(connectionsImpl(graph, id, relation2));
367
+ };
368
+ function getConnections(graphOrId, idOrRelation, relation2) {
369
+ if (typeof graphOrId === "string") {
370
+ const id = graphOrId;
371
+ const rel = idOrRelation;
372
+ return (graph) => getConnectionsImpl(graph, id, rel);
373
+ } else {
374
+ const graph = graphOrId;
375
+ const id = idOrRelation;
376
+ invariant2(relation2 !== void 0, "Relation is required.", {
377
+ F: __dxlog_file2,
378
+ L: 449,
379
+ S: this,
380
+ A: [
381
+ "relation !== undefined",
382
+ "'Relation is required.'"
383
+ ]
384
+ });
385
+ const rel = relation2;
386
+ return getConnectionsImpl(graph, id, rel);
387
+ }
388
+ }
389
+ var getActionsImpl = (graph, id) => {
390
+ const internal = getInternal(graph);
391
+ return internal._registry.get(actionsImpl(graph, id));
392
+ };
393
+ function getActions(graphOrId, id) {
394
+ if (typeof graphOrId === "string") {
395
+ const id2 = graphOrId;
396
+ return (graph) => getActionsImpl(graph, id2);
397
+ } else {
398
+ const graph = graphOrId;
399
+ return getActionsImpl(graph, id);
400
+ }
401
+ }
402
+ var getEdgesImpl = (graph, id) => {
403
+ const internal = getInternal(graph);
404
+ return internal._registry.get(edgesImpl(graph, id));
405
+ };
406
+ function getEdges(graphOrId, id) {
407
+ if (typeof graphOrId === "string") {
408
+ const id2 = graphOrId;
409
+ return (graph) => getEdgesImpl(graph, id2);
410
+ } else {
411
+ const graph = graphOrId;
412
+ return getEdgesImpl(graph, id);
413
+ }
414
+ }
415
+ var traverseImpl = (graph, options, path = []) => {
416
+ const { visitor, source = RootId, relation: relation2 } = options;
417
+ if (path.includes(source)) {
418
+ return;
419
+ }
420
+ const node = getNodeOrThrow(graph, source);
421
+ const shouldContinue = visitor(node, [
422
+ ...path,
423
+ source
424
+ ]);
425
+ if (shouldContinue === false) {
426
+ return;
427
+ }
428
+ const relations = Array.isArray(relation2) ? relation2 : [
429
+ relation2
430
+ ];
431
+ const seen = /* @__PURE__ */ new Set();
432
+ for (const rel of relations) {
433
+ for (const connected of getConnections(graph, source, rel)) {
434
+ if (!seen.has(connected.id)) {
435
+ seen.add(connected.id);
436
+ traverseImpl(graph, {
437
+ source: connected.id,
438
+ relation: relation2,
439
+ visitor
440
+ }, [
441
+ ...path,
442
+ source
443
+ ]);
444
+ }
445
+ }
446
+ }
447
+ };
448
+ function traverse(graphOrOptions, optionsOrPath, path) {
449
+ if (typeof graphOrOptions === "object" && "visitor" in graphOrOptions) {
450
+ const options = graphOrOptions;
451
+ const pathArg = Array.isArray(optionsOrPath) ? optionsOrPath : void 0;
452
+ return (graph) => traverseImpl(graph, options, pathArg);
453
+ } else {
454
+ const graph = graphOrOptions;
455
+ const options = optionsOrPath;
456
+ const pathArg = path ?? (Array.isArray(optionsOrPath) ? optionsOrPath : void 0);
457
+ return traverseImpl(graph, options, pathArg);
458
+ }
459
+ }
460
+ var getPathImpl = (graph, params) => {
461
+ return Function.pipe(getNode(graph, params.source ?? "root"), Option.flatMap((node) => {
462
+ let found = Option.none();
463
+ traverseImpl(graph, {
464
+ source: node.id,
465
+ relation: "child",
466
+ visitor: (node2, path) => {
467
+ if (Option.isSome(found)) {
468
+ return false;
469
+ }
470
+ if (node2.id === params.target) {
471
+ found = Option.some(path);
472
+ }
473
+ }
474
+ });
475
+ return found;
476
+ }));
477
+ };
478
+ function getPath(graphOrParams, params) {
479
+ if (params === void 0 && typeof graphOrParams === "object" && "target" in graphOrParams) {
480
+ const params2 = graphOrParams;
481
+ return (graph) => getPathImpl(graph, params2);
482
+ } else {
483
+ const graph = graphOrParams;
484
+ return getPathImpl(graph, params);
485
+ }
486
+ }
487
+ var waitForPathImpl = (graph, params, options) => {
488
+ const { timeout = 5e3, interval = 500 } = options ?? {};
489
+ const path = getPathImpl(graph, params);
490
+ if (Option.isSome(path)) {
491
+ return Promise.resolve(path.value);
492
+ }
493
+ const trigger = new Trigger();
494
+ const i = setInterval(() => {
495
+ const path2 = getPathImpl(graph, params);
496
+ if (Option.isSome(path2)) {
497
+ trigger.wake(path2.value);
498
+ }
499
+ }, interval);
500
+ return trigger.wait({
501
+ timeout
502
+ }).finally(() => clearInterval(i));
503
+ };
504
+ function waitForPath(graphOrParams, paramsOrOptions, options) {
505
+ if (typeof graphOrParams === "object" && "target" in graphOrParams) {
506
+ const params = graphOrParams;
507
+ const opts = typeof paramsOrOptions === "object" && !("target" in paramsOrOptions) ? paramsOrOptions : void 0;
508
+ return (graph) => waitForPathImpl(graph, params, opts);
509
+ } else {
510
+ const graph = graphOrParams;
511
+ const params = paramsOrOptions;
512
+ return waitForPathImpl(graph, params, options);
513
+ }
514
+ }
515
+ var initializeImpl = async (graph, id) => {
516
+ const internal = getInternal(graph);
517
+ const initialized = Record.get(internal._initialized, id).pipe(Option.getOrElse(() => false));
518
+ log("initialize", {
519
+ id,
520
+ initialized
521
+ }, {
522
+ F: __dxlog_file2,
523
+ L: 671,
524
+ S: void 0,
525
+ C: (f, a) => f(...a)
526
+ });
527
+ if (!initialized) {
528
+ Record.set(internal._initialized, id, true);
529
+ await internal._onInitialize?.(id);
530
+ }
531
+ return graph;
532
+ };
533
+ function initialize(graphOrId, id) {
534
+ if (typeof graphOrId === "string") {
535
+ const id2 = graphOrId;
536
+ return (graph) => initializeImpl(graph, id2);
537
+ } else {
538
+ const graph = graphOrId;
539
+ return initializeImpl(graph, id);
540
+ }
541
+ }
542
+ var expandImpl = (graph, id, relation2) => {
543
+ const internal = getInternal(graph);
544
+ const normalizedRelation = normalizeRelation(relation2);
545
+ const key = primaryKey(id, relationKey(normalizedRelation));
546
+ const nodeOpt = internal._registry.get(internal._node(id));
547
+ if (Option.isNone(nodeOpt)) {
548
+ internal._pendingExpands.add(key);
549
+ log("expand", {
550
+ key,
551
+ deferred: true
552
+ }, {
553
+ F: __dxlog_file2,
554
+ L: 717,
555
+ S: void 0,
556
+ C: (f, a) => f(...a)
557
+ });
558
+ return graph;
559
+ }
560
+ const expanded = Record.get(internal._expanded, key).pipe(Option.getOrElse(() => false));
561
+ log("expand", {
562
+ key,
563
+ expanded
564
+ }, {
565
+ F: __dxlog_file2,
566
+ L: 722,
567
+ S: void 0,
568
+ C: (f, a) => f(...a)
569
+ });
570
+ if (!expanded) {
571
+ Record.set(internal._expanded, key, true);
572
+ internal._onExpand?.(id, normalizedRelation);
573
+ }
574
+ return graph;
575
+ };
576
+ function expand(graphOrId, idOrRelation, relation2) {
577
+ if (typeof graphOrId === "string") {
578
+ const id = graphOrId;
579
+ const rel = idOrRelation;
580
+ return (graph) => expandImpl(graph, id, rel);
581
+ } else {
582
+ const graph = graphOrId;
583
+ const id = idOrRelation;
584
+ invariant2(relation2 !== void 0, "Relation is required.", {
585
+ F: __dxlog_file2,
586
+ L: 758,
587
+ S: this,
588
+ A: [
589
+ "relation !== undefined",
590
+ "'Relation is required.'"
591
+ ]
592
+ });
593
+ const rel = relation2;
594
+ return expandImpl(graph, id, rel);
595
+ }
596
+ }
597
+ var sortEdgesImpl = (graph, id, relation2, order) => {
598
+ const internal = getInternal(graph);
599
+ const edgesAtom = internal._edges(id);
600
+ const edges = internal._registry.get(edgesAtom);
601
+ const relationId = relationKey(relation2);
602
+ const current = edges[relationId] ?? [];
603
+ const unsorted = current.filter((id2) => !order.includes(id2));
604
+ const sorted = order.filter((id2) => current.includes(id2));
605
+ const newOrder = [
606
+ ...sorted,
607
+ ...unsorted
608
+ ];
609
+ if (newOrder.length === current.length && newOrder.every((id2, i) => id2 === current[i])) {
610
+ return graph;
611
+ }
612
+ internal._registry.set(edgesAtom, {
613
+ ...edges,
614
+ [relationId]: newOrder
615
+ });
616
+ return graph;
617
+ };
618
+ function sortEdges(graphOrId, idOrRelation, relationOrOrder, order) {
619
+ if (typeof graphOrId === "string") {
620
+ const id = graphOrId;
621
+ const relation2 = idOrRelation;
622
+ const order2 = relationOrOrder;
623
+ return (graph) => sortEdgesImpl(graph, id, relation2, order2);
624
+ } else {
625
+ const graph = graphOrId;
626
+ const id = idOrRelation;
627
+ const relation2 = relationOrOrder;
628
+ return sortEdgesImpl(graph, id, relation2, order);
629
+ }
630
+ }
631
+ var addNodesImpl = (graph, nodes) => {
632
+ Atom.batch(() => {
633
+ nodes.map((node) => addNodeImpl(graph, node));
634
+ });
635
+ return graph;
636
+ };
637
+ function addNodes(graphOrNodes, nodes) {
638
+ if (nodes === void 0) {
639
+ const nodes2 = graphOrNodes;
640
+ return (graph) => addNodesImpl(graph, nodes2);
641
+ } else {
642
+ const graph = graphOrNodes;
643
+ return addNodesImpl(graph, nodes);
644
+ }
645
+ }
646
+ var addNodeImpl = (graph, nodeArg) => {
647
+ const internal = getInternal(graph);
648
+ const { nodes, edges, id, type, data = null, properties = {}, ...rest } = nodeArg;
649
+ const nodeAtom = internal._node(id);
650
+ const existingNode = internal._registry.get(nodeAtom);
651
+ Option.match(existingNode, {
652
+ onSome: (existing) => {
653
+ const typeChanged = existing.type !== type;
654
+ const dataChanged = !shallowEqual(existing.data, data);
655
+ const propertiesChanged = Object.keys(properties).some((key) => existing.properties[key] !== properties[key]);
656
+ log("existing node", {
657
+ id,
658
+ typeChanged,
659
+ dataChanged,
660
+ propertiesChanged
661
+ }, {
662
+ F: __dxlog_file2,
663
+ L: 880,
664
+ S: void 0,
665
+ C: (f, a) => f(...a)
666
+ });
667
+ if (typeChanged || dataChanged || propertiesChanged) {
668
+ log("updating node", {
669
+ id,
670
+ type,
671
+ data,
672
+ properties
673
+ }, {
674
+ F: __dxlog_file2,
675
+ L: 887,
676
+ S: void 0,
677
+ C: (f, a) => f(...a)
678
+ });
679
+ const newNode = Option.some({
680
+ ...existing,
681
+ ...rest,
682
+ type,
683
+ data,
684
+ properties: {
685
+ ...existing.properties,
686
+ ...properties
687
+ }
688
+ });
689
+ internal._registry.set(nodeAtom, newNode);
690
+ graph.onNodeChanged.emit({
691
+ id,
692
+ node: newNode
693
+ });
694
+ }
695
+ },
696
+ onNone: () => {
697
+ log("new node", {
698
+ id,
699
+ type,
700
+ data,
701
+ properties
702
+ }, {
703
+ F: __dxlog_file2,
704
+ L: 900,
705
+ S: void 0,
706
+ C: (f, a) => f(...a)
707
+ });
708
+ const newNode = internal._constructNode({
709
+ id,
710
+ type,
711
+ data,
712
+ properties,
713
+ ...rest
714
+ });
715
+ internal._registry.set(nodeAtom, newNode);
716
+ graph.onNodeChanged.emit({
717
+ id,
718
+ node: newNode
719
+ });
720
+ const toApply = [
721
+ ...internal._pendingExpands
722
+ ].filter((k) => primaryParts(k)[0] === id);
723
+ for (const pendingKey of toApply) {
724
+ internal._pendingExpands.delete(pendingKey);
725
+ const relation2 = relationFromKey(primaryParts(pendingKey)[1]);
726
+ Record.set(internal._expanded, pendingKey, true);
727
+ internal._onExpand?.(id, relation2);
728
+ }
729
+ }
730
+ });
731
+ if (nodes) {
732
+ addNodesImpl(graph, nodes);
733
+ const _edges = nodes.map((node) => ({
734
+ source: id,
735
+ target: node.id,
736
+ relation: "child"
737
+ }));
738
+ addEdgesImpl(graph, _edges);
739
+ }
740
+ if (edges) {
741
+ todo();
742
+ }
743
+ return graph;
744
+ };
745
+ function addNode(graphOrNodeArg, nodeArg) {
746
+ if (nodeArg === void 0) {
747
+ const nodeArg2 = graphOrNodeArg;
748
+ return (graph) => addNodeImpl(graph, nodeArg2);
749
+ } else {
750
+ const graph = graphOrNodeArg;
751
+ return addNodeImpl(graph, nodeArg);
752
+ }
753
+ }
754
+ var removeNodesImpl = (graph, ids, edges = false) => {
755
+ Atom.batch(() => {
756
+ ids.map((id) => removeNodeImpl(graph, id, edges));
757
+ });
758
+ return graph;
759
+ };
760
+ function removeNodes(graphOrIds, idsOrEdges, edges) {
761
+ if (Array.isArray(graphOrIds)) {
762
+ const ids = graphOrIds;
763
+ const edgesArg = typeof idsOrEdges === "boolean" ? idsOrEdges : false;
764
+ return (graph) => removeNodesImpl(graph, ids, edgesArg);
765
+ } else {
766
+ const graph = graphOrIds;
767
+ const ids = idsOrEdges;
768
+ const edgesArg = edges ?? false;
769
+ return removeNodesImpl(graph, ids, edgesArg);
770
+ }
771
+ }
772
+ var removeNodeImpl = (graph, id, edges = false) => {
773
+ const internal = getInternal(graph);
774
+ const nodeAtom = internal._node(id);
775
+ internal._registry.set(nodeAtom, Option.none());
776
+ graph.onNodeChanged.emit({
777
+ id,
778
+ node: Option.none()
779
+ });
780
+ if (edges) {
781
+ const nodeEdges = internal._registry.get(internal._edges(id));
782
+ const edgesToRemove = [];
783
+ for (const [relationKeyValue, relatedIds] of Object.entries(nodeEdges)) {
784
+ const relation2 = relationFromKey(relationKeyValue);
785
+ const isInboundRelation = relation2.direction === "inbound";
786
+ for (const relatedId of relatedIds) {
787
+ if (isInboundRelation) {
788
+ edgesToRemove.push({
789
+ source: relatedId,
790
+ target: id,
791
+ relation: inverseRelation(relation2)
792
+ });
793
+ } else {
794
+ edgesToRemove.push({
795
+ source: id,
796
+ target: relatedId,
797
+ relation: relation2
798
+ });
799
+ }
800
+ }
801
+ }
802
+ removeEdgesImpl(graph, edgesToRemove);
803
+ }
804
+ internal._onRemoveNode?.(id);
805
+ return graph;
806
+ };
807
+ function removeNode(graphOrId, idOrEdges, edges) {
808
+ if (typeof graphOrId === "string") {
809
+ const id = graphOrId;
810
+ const edgesArg = typeof idOrEdges === "boolean" ? idOrEdges : false;
811
+ return (graph) => removeNodeImpl(graph, id, edgesArg);
812
+ } else {
813
+ const graph = graphOrId;
814
+ const id = idOrEdges;
815
+ const edgesArg = edges ?? false;
816
+ return removeNodeImpl(graph, id, edgesArg);
817
+ }
818
+ }
819
+ var addEdgesImpl = (graph, edges) => {
820
+ Atom.batch(() => {
821
+ edges.map((edge) => addEdgeImpl(graph, edge));
822
+ });
823
+ return graph;
824
+ };
825
+ function addEdges(graphOrEdges, edges) {
826
+ if (edges === void 0) {
827
+ const edges2 = graphOrEdges;
828
+ return (graph) => addEdgesImpl(graph, edges2);
829
+ } else {
830
+ const graph = graphOrEdges;
831
+ return addEdgesImpl(graph, edges);
832
+ }
833
+ }
834
+ var addEdgeImpl = (graph, edgeArg) => {
835
+ const relation2 = normalizeRelation(edgeArg.relation);
836
+ const relationId = relationKey(relation2);
837
+ const inverse = inverseRelation(relation2);
838
+ const inverseId = relationKey(inverse);
839
+ const internal = getInternal(graph);
840
+ const sourceAtom = internal._edges(edgeArg.source);
841
+ const source = internal._registry.get(sourceAtom);
842
+ const sourceList = source[relationId] ?? [];
843
+ if (!sourceList.includes(edgeArg.target)) {
844
+ log("add edge", {
845
+ source: edgeArg.source,
846
+ target: edgeArg.target,
847
+ relation: relationId
848
+ }, {
849
+ F: __dxlog_file2,
850
+ L: 1083,
851
+ S: void 0,
852
+ C: (f, a) => f(...a)
853
+ });
854
+ internal._registry.set(sourceAtom, {
855
+ ...source,
856
+ [relationId]: [
857
+ ...sourceList,
858
+ edgeArg.target
859
+ ]
860
+ });
861
+ }
862
+ const targetAtom = internal._edges(edgeArg.target);
863
+ const target = internal._registry.get(targetAtom);
864
+ const targetList = target[inverseId] ?? [];
865
+ if (!targetList.includes(edgeArg.source)) {
866
+ log("add inverse edge", {
867
+ source: edgeArg.source,
868
+ target: edgeArg.target,
869
+ relation: inverseId
870
+ }, {
871
+ F: __dxlog_file2,
872
+ L: 1091,
873
+ S: void 0,
874
+ C: (f, a) => f(...a)
875
+ });
876
+ internal._registry.set(targetAtom, {
877
+ ...target,
878
+ [inverseId]: [
879
+ ...targetList,
880
+ edgeArg.source
881
+ ]
882
+ });
883
+ }
884
+ return graph;
885
+ };
886
+ function addEdge(graphOrEdgeArg, edgeArg) {
887
+ if (edgeArg === void 0) {
888
+ const edgeArg2 = graphOrEdgeArg;
889
+ return (graph) => addEdgeImpl(graph, edgeArg2);
890
+ } else {
891
+ const graph = graphOrEdgeArg;
892
+ return addEdgeImpl(graph, edgeArg);
893
+ }
894
+ }
895
+ var removeEdgesImpl = (graph, edges, removeOrphans = false) => {
896
+ Atom.batch(() => {
897
+ edges.map((edge) => removeEdgeImpl(graph, edge, removeOrphans));
898
+ });
899
+ return graph;
900
+ };
901
+ function removeEdges(graphOrEdges, edgesOrRemoveOrphans, removeOrphans) {
902
+ if (Array.isArray(graphOrEdges)) {
903
+ const edges = graphOrEdges;
904
+ const removeOrphansArg = typeof edgesOrRemoveOrphans === "boolean" ? edgesOrRemoveOrphans : false;
905
+ return (graph) => removeEdgesImpl(graph, edges, removeOrphansArg);
906
+ } else {
907
+ const graph = graphOrEdges;
908
+ const edges = edgesOrRemoveOrphans;
909
+ const removeOrphansArg = removeOrphans ?? false;
910
+ return removeEdgesImpl(graph, edges, removeOrphansArg);
911
+ }
912
+ }
913
+ var removeEdgeImpl = (graph, edgeArg, removeOrphans = false) => {
914
+ const relation2 = normalizeRelation(edgeArg.relation);
915
+ const relationId = relationKey(relation2);
916
+ const inverse = inverseRelation(relation2);
917
+ const inverseId = relationKey(inverse);
918
+ const internal = getInternal(graph);
919
+ const sourceAtom = internal._edges(edgeArg.source);
920
+ const source = internal._registry.get(sourceAtom);
921
+ const sourceList = source[relationId] ?? [];
922
+ if (sourceList.includes(edgeArg.target)) {
923
+ internal._registry.set(sourceAtom, {
924
+ ...source,
925
+ [relationId]: sourceList.filter((id) => id !== edgeArg.target)
926
+ });
927
+ }
928
+ const targetAtom = internal._edges(edgeArg.target);
929
+ const target = internal._registry.get(targetAtom);
930
+ const targetList = target[inverseId] ?? [];
931
+ if (targetList.includes(edgeArg.source)) {
932
+ internal._registry.set(targetAtom, {
933
+ ...target,
934
+ [inverseId]: targetList.filter((id) => id !== edgeArg.source)
935
+ });
936
+ }
937
+ if (removeOrphans) {
938
+ const sourceAfter = internal._registry.get(sourceAtom);
939
+ const targetAfter = internal._registry.get(targetAtom);
940
+ const isEmpty = (edges) => Object.values(edges).every((ids) => ids.length === 0);
941
+ if (isEmpty(sourceAfter) && edgeArg.source !== RootId) {
942
+ removeNodesImpl(graph, [
943
+ edgeArg.source
944
+ ]);
945
+ }
946
+ if (isEmpty(targetAfter) && edgeArg.target !== RootId) {
947
+ removeNodesImpl(graph, [
948
+ edgeArg.target
949
+ ]);
950
+ }
951
+ }
952
+ return graph;
953
+ };
954
+ function removeEdge(graphOrEdgeArg, edgeArgOrRemoveOrphans, removeOrphans) {
955
+ if (edgeArgOrRemoveOrphans === void 0 || typeof edgeArgOrRemoveOrphans === "boolean" || "source" in graphOrEdgeArg) {
956
+ const edgeArg = graphOrEdgeArg;
957
+ const removeOrphansArg = typeof edgeArgOrRemoveOrphans === "boolean" ? edgeArgOrRemoveOrphans : false;
958
+ return (graph) => removeEdgeImpl(graph, edgeArg, removeOrphansArg);
959
+ } else {
960
+ const graph = graphOrEdgeArg;
961
+ const edgeArg = edgeArgOrRemoveOrphans;
962
+ const removeOrphansArg = removeOrphans ?? false;
963
+ return removeEdgeImpl(graph, edgeArg, removeOrphansArg);
964
+ }
965
+ }
966
+ var make2 = (params) => {
967
+ return new GraphImpl(params);
968
+ };
969
+ var relationKey = (relation2) => {
970
+ const normalized = normalizeRelation(relation2);
971
+ return secondaryKey(normalized.kind, normalized.direction);
972
+ };
973
+ var relationFromKey = (encoded) => {
974
+ const parts = secondaryParts(encoded);
975
+ invariant2(parts.length === 2 && parts[0].length > 0 && parts[1].length > 0, `Invalid relation key: ${encoded}`, {
976
+ F: __dxlog_file2,
977
+ L: 1236,
978
+ S: void 0,
979
+ A: [
980
+ "parts.length === 2 && parts[0].length > 0 && parts[1].length > 0",
981
+ "`Invalid relation key: ${encoded}`"
982
+ ]
983
+ });
984
+ const [kind, directionRaw] = parts;
985
+ invariant2(directionRaw === "outbound" || directionRaw === "inbound", `Invalid relation direction: ${directionRaw}`, {
986
+ F: __dxlog_file2,
987
+ L: 1238,
988
+ S: void 0,
989
+ A: [
990
+ "directionRaw === 'outbound' || directionRaw === 'inbound'",
991
+ "`Invalid relation direction: ${directionRaw}`"
992
+ ]
993
+ });
994
+ return relation(kind, directionRaw);
995
+ };
996
+ var connectionKey = (id, relation2) => primaryKey(id, relationKey(relation2));
997
+ var relationFromConnectionKey = (key) => {
998
+ const [id, encodedRelation] = primaryParts(key);
999
+ invariant2(id && encodedRelation, `Invalid connection key: ${key}`, {
1000
+ F: __dxlog_file2,
1001
+ L: 1246,
1002
+ S: void 0,
1003
+ A: [
1004
+ "id && encodedRelation",
1005
+ "`Invalid connection key: ${key}`"
1006
+ ]
1007
+ });
1008
+ return {
1009
+ id,
1010
+ relation: relationFromKey(encodedRelation)
1011
+ };
1012
+ };
1013
+ var inverseRelation = (relation2) => {
1014
+ const normalized = normalizeRelation(relation2);
1015
+ return relation(normalized.kind, normalized.direction === "outbound" ? "inbound" : "outbound");
1016
+ };
1017
+
1018
+ // src/node-matcher.ts
1019
+ var node_matcher_exports = {};
1020
+ __export(node_matcher_exports, {
1021
+ whenAll: () => whenAll,
1022
+ whenAny: () => whenAny,
1023
+ whenEchoObject: () => whenEchoObject,
1024
+ whenEchoObjectMatches: () => whenEchoObjectMatches,
1025
+ whenEchoType: () => whenEchoType,
1026
+ whenEchoTypeMatches: () => whenEchoTypeMatches,
1027
+ whenId: () => whenId,
1028
+ whenNodeType: () => whenNodeType,
1029
+ whenNot: () => whenNot,
1030
+ whenRoot: () => whenRoot
1031
+ });
1032
+ import * as Option2 from "effect/Option";
1033
+ import { Obj } from "@dxos/echo";
1034
+ var whenRoot = (node) => node.id === RootId ? Option2.some(node) : Option2.none();
1035
+ var whenId = (id) => (node) => node.id === id ? Option2.some(node) : Option2.none();
1036
+ var whenNodeType = (type) => (node) => node.type === type ? Option2.some(node) : Option2.none();
1037
+ var whenEchoType = (type) => (node) => Obj.instanceOf(type, node.data) ? Option2.some(node.data) : Option2.none();
1038
+ var whenEchoObject = (node) => Obj.isObject(node.data) ? Option2.some(node.data) : Option2.none();
1039
+ var whenAll = (...matchers) => (node) => {
1040
+ let first = Option2.none();
1041
+ for (const candidate of matchers) {
1042
+ const result = candidate(node);
1043
+ if (Option2.isNone(result)) {
1044
+ return Option2.none();
1045
+ }
1046
+ if (Option2.isNone(first)) {
1047
+ first = result;
1048
+ }
1049
+ }
1050
+ return first;
1051
+ };
1052
+ var whenAny = (...matchers) => (node) => {
1053
+ for (const candidate of matchers) {
1054
+ const result = candidate(node);
1055
+ if (Option2.isSome(result)) {
1056
+ return result;
1057
+ }
1058
+ }
1059
+ return Option2.none();
1060
+ };
1061
+ var whenEchoTypeMatches = (type) => (node) => Obj.instanceOf(type, node.data) ? Option2.some(node) : Option2.none();
1062
+ var whenEchoObjectMatches = (node) => Obj.isObject(node.data) ? Option2.some(node) : Option2.none();
1063
+ var whenNot = (matcher) => (node) => Option2.isNone(matcher(node)) ? Option2.some(node) : Option2.none();
1064
+
1065
+ // src/graph-builder.ts
1066
+ var graph_builder_exports = {};
1067
+ __export(graph_builder_exports, {
1068
+ GraphBuilderTypeId: () => GraphBuilderTypeId,
1069
+ addExtension: () => addExtension,
1070
+ createConnector: () => createConnector,
1071
+ createExtension: () => createExtension,
1072
+ createExtensionRaw: () => createExtensionRaw,
1073
+ createTypeExtension: () => createTypeExtension,
1074
+ destroy: () => destroy,
1075
+ explore: () => explore,
1076
+ flattenExtensions: () => flattenExtensions,
1077
+ flush: () => flush,
1078
+ from: () => from,
1079
+ make: () => make3,
1080
+ removeExtension: () => removeExtension
1081
+ });
1082
+ import { Atom as Atom2, Registry as Registry2 } from "@effect-atom/atom-react";
1083
+ import * as Array2 from "effect/Array";
1084
+ import * as Effect from "effect/Effect";
1085
+ import * as Function2 from "effect/Function";
1086
+ import * as Option3 from "effect/Option";
1087
+ import * as Pipeable2 from "effect/Pipeable";
1088
+ import * as Record2 from "effect/Record";
1089
+ import { scheduleTask, yieldOrContinue } from "main-thread-scheduling";
1090
+ import { log as log2 } from "@dxos/log";
1091
+ import { byPosition, getDebugName, isNonNullable as isNonNullable2 } from "@dxos/util";
1092
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/sdk/app-graph/src/graph-builder.ts";
1093
+ var GraphBuilderTypeId = /* @__PURE__ */ Symbol.for("@dxos/app-graph/GraphBuilder");
1094
+ var GraphBuilderImpl = class {
1095
+ [GraphBuilderTypeId] = GraphBuilderTypeId;
1096
+ pipe() {
1097
+ return Pipeable2.pipeArguments(this, arguments);
1098
+ }
1099
+ // TODO(wittjosiah): Use Context.
1100
+ /** Active subscriptions keyed by composite ID, cleaned up on node removal. */
1101
+ _subscriptions = /* @__PURE__ */ new Map();
1102
+ /** Connector updates pending flush, keyed by connector key. */
1103
+ _dirtyConnectors = /* @__PURE__ */ new Map();
1104
+ /** Last-flushed node IDs per connector key, used for edge removal on update. */
1105
+ _connectorPrevious = /* @__PURE__ */ new Map();
1106
+ /** Last-flushed node args per connector key, used for change detection. */
1107
+ _connectorPreviousArgs = /* @__PURE__ */ new Map();
1108
+ /** Whether a dirty-flush task is already scheduled. */
1109
+ _flushScheduled = false;
1110
+ /** Resolves when the current flush completes. */
1111
+ _flushPromise = Promise.resolve();
1112
+ /** Registered builder extensions keyed by extension ID. */
1113
+ _extensions = Atom2.make(Record2.empty()).pipe(Atom2.keepAlive, Atom2.withLabel("graph-builder:extensions"));
1114
+ /** Triggers signalling that a node's resolver has fired at least once. */
1115
+ _initialized = {};
1116
+ /** Shared atom registry for reactive subscriptions. */
1117
+ _registry;
1118
+ /** Backing graph with internal accessors for node atoms and construction. */
1119
+ _graph;
1120
+ constructor({ registry, ...params } = {}) {
1121
+ this._registry = registry ?? Registry2.make();
1122
+ const graph = make2({
1123
+ ...params,
1124
+ registry: this._registry,
1125
+ onExpand: (id, relation2) => this._onExpand(id, relation2),
1126
+ onInitialize: (id) => this._onInitialize(id),
1127
+ onRemoveNode: (id) => this._onRemoveNode(id)
1128
+ });
1129
+ this._graph = graph;
1130
+ }
1131
+ get graph() {
1132
+ return this._graph;
1133
+ }
1134
+ get extensions() {
1135
+ return this._extensions;
1136
+ }
1137
+ /** Apply a set of node changes for a single connector key. */
1138
+ _applyConnectorUpdate(key, nodes, previous) {
1139
+ const { id, relation: relation2 } = relationFromConnectorKey(key);
1140
+ const ids = nodes.map((node) => node.id);
1141
+ const removed = previous.filter((pid) => !ids.includes(pid));
1142
+ this._connectorPrevious.set(key, ids);
1143
+ this._connectorPreviousArgs.set(key, nodes);
1144
+ removeEdges(this._graph, removed.map((target) => ({
1145
+ source: id,
1146
+ target,
1147
+ relation: relation2
1148
+ })), true);
1149
+ addNodes(this._graph, nodes);
1150
+ addEdges(this._graph, nodes.map((node) => ({
1151
+ source: id,
1152
+ target: node.id,
1153
+ relation: relation2
1154
+ })));
1155
+ if (ids.length > 0) {
1156
+ const sortedIds = [
1157
+ ...nodes
1158
+ ].sort((a, b) => byPosition(a.properties ?? {}, b.properties ?? {})).map((n) => n.id);
1159
+ sortEdges(this._graph, id, relation2, sortedIds);
1160
+ }
1161
+ }
1162
+ _scheduleDirtyFlush() {
1163
+ if (!this._flushScheduled) {
1164
+ this._flushScheduled = true;
1165
+ this._flushPromise = scheduleTask(() => {
1166
+ this._flushScheduled = false;
1167
+ while (this._dirtyConnectors.size > 0) {
1168
+ const entries = [
1169
+ ...this._dirtyConnectors.entries()
1170
+ ];
1171
+ this._dirtyConnectors.clear();
1172
+ Atom2.batch(() => {
1173
+ for (const [key, { nodes, previous }] of entries) {
1174
+ this._applyConnectorUpdate(key, nodes, previous);
1175
+ }
1176
+ });
1177
+ }
1178
+ }, {
1179
+ strategy: "smooth"
1180
+ });
1181
+ }
1182
+ }
1183
+ _resolvers = Atom2.family((id) => {
1184
+ return Atom2.make((get2) => {
1185
+ return Function2.pipe(get2(this._extensions), Record2.values, Array2.sortBy(byPosition), Array2.map(({ resolver }) => resolver), Array2.filter(isNonNullable2), Array2.map((resolver) => get2(resolver(id))), Array2.filter(isNonNullable2), Array2.head);
1186
+ });
1187
+ });
1188
+ _connectors = Atom2.family((key) => {
1189
+ return Atom2.make((get2) => {
1190
+ const { id, relation: relation2 } = relationFromConnectorKey(key);
1191
+ const node = this._graph.node(id);
1192
+ const sourceNode = Option3.getOrElse(get2(node), () => void 0);
1193
+ if (!sourceNode) {
1194
+ return [];
1195
+ }
1196
+ const extensions = Function2.pipe(get2(this._extensions), Record2.values, Array2.sortBy(byPosition), Array2.filter((ext) => relationKey(ext.relation ?? "child") === relationKey(relation2) && ext.connector != null));
1197
+ const nodes = [];
1198
+ for (const ext of extensions) {
1199
+ const result = get2(ext.connector(node));
1200
+ nodes.push(...result);
1201
+ }
1202
+ return nodes;
1203
+ }).pipe(Atom2.withLabel(`graph-builder:connectors:${key}`));
1204
+ });
1205
+ _onExpand(id, relation2) {
1206
+ log2("onExpand", {
1207
+ id,
1208
+ relation: relation2,
1209
+ registry: getDebugName(this._registry)
1210
+ }, {
1211
+ F: __dxlog_file3,
1212
+ L: 269,
1213
+ S: this,
1214
+ C: (f, a) => f(...a)
1215
+ });
1216
+ this._expandRelation(id, relation2);
1217
+ if (relation2.kind === "child" && relation2.direction === "outbound") {
1218
+ expand(this._graph, id, "action");
1219
+ }
1220
+ }
1221
+ _expandRelation(id, relation2) {
1222
+ const key = connectorKey(id, relation2);
1223
+ const connectors = this._connectors(key);
1224
+ const cancel = this._registry.subscribe(connectors, (rawNodes) => {
1225
+ const nodes = qualifyNodeArgs(id)(rawNodes);
1226
+ const previous = this._connectorPrevious.get(key) ?? [];
1227
+ const ids = nodes.map((n) => n.id);
1228
+ if (ids.length === previous.length && ids.every((nodeId, idx) => nodeId === previous[idx])) {
1229
+ const prevArgs = this._connectorPreviousArgs.get(key);
1230
+ if (prevArgs && nodeArgsUnchanged(prevArgs, nodes)) {
1231
+ return;
1232
+ }
1233
+ }
1234
+ log2("update", {
1235
+ id,
1236
+ relation: relation2,
1237
+ ids
1238
+ }, {
1239
+ F: __dxlog_file3,
1240
+ L: 296,
1241
+ S: this,
1242
+ C: (f, a) => f(...a)
1243
+ });
1244
+ this._dirtyConnectors.set(key, {
1245
+ nodes,
1246
+ previous
1247
+ });
1248
+ this._scheduleDirtyFlush();
1249
+ }, {
1250
+ immediate: true
1251
+ });
1252
+ this._subscriptions.set(subscriptionKey(id, "expand", key), cancel);
1253
+ }
1254
+ async _onInitialize(id) {
1255
+ log2("onInitialize", {
1256
+ id
1257
+ }, {
1258
+ F: __dxlog_file3,
1259
+ L: 307,
1260
+ S: this,
1261
+ C: (f, a) => f(...a)
1262
+ });
1263
+ const resolver = this._resolvers(id);
1264
+ const cancel = this._registry.subscribe(resolver, (node) => {
1265
+ const trigger = this._initialized[id];
1266
+ const connectorOwned = [
1267
+ ...this._connectorPrevious.values()
1268
+ ].some((ids) => ids.includes(id));
1269
+ Option3.match(node, {
1270
+ onSome: (node2) => {
1271
+ if (!connectorOwned) {
1272
+ addNodes(this._graph, [
1273
+ node2
1274
+ ]);
1275
+ const parentId = getParentId(id);
1276
+ if (parentId) {
1277
+ addEdges(this._graph, [
1278
+ {
1279
+ source: parentId,
1280
+ target: id,
1281
+ relation: "child"
1282
+ }
1283
+ ]);
1284
+ }
1285
+ }
1286
+ trigger?.wake();
1287
+ },
1288
+ onNone: () => {
1289
+ trigger?.wake();
1290
+ if (!connectorOwned) {
1291
+ removeNodes(this._graph, [
1292
+ id
1293
+ ]);
1294
+ }
1295
+ }
1296
+ });
1297
+ }, {
1298
+ immediate: true
1299
+ });
1300
+ this._subscriptions.set(subscriptionKey(id, "init"), cancel);
1301
+ }
1302
+ _onRemoveNode(id) {
1303
+ for (const [key, cleanup] of this._subscriptions) {
1304
+ if (primaryParts(key)[0] === id) {
1305
+ cleanup();
1306
+ this._subscriptions.delete(key);
1307
+ }
1308
+ }
1309
+ }
1310
+ };
1311
+ var make3 = (params) => {
1312
+ return new GraphBuilderImpl(params);
1313
+ };
1314
+ var from = (pickle, registry) => {
1315
+ if (!pickle) {
1316
+ return make3({
1317
+ registry
1318
+ });
1319
+ }
1320
+ const { nodes, edges } = JSON.parse(pickle);
1321
+ return make3({
1322
+ nodes,
1323
+ edges,
1324
+ registry
1325
+ });
1326
+ };
1327
+ var addExtensionImpl = (builder, extensions) => {
1328
+ const internal = builder;
1329
+ flattenExtensions(extensions).forEach((extension) => {
1330
+ const extensions2 = internal._registry.get(internal._extensions);
1331
+ internal._registry.set(internal._extensions, Record2.set(extensions2, extension.id, extension));
1332
+ });
1333
+ return builder;
1334
+ };
1335
+ function addExtension(builderOrExtensions, extensions) {
1336
+ if (extensions === void 0) {
1337
+ const extensions2 = builderOrExtensions;
1338
+ return (builder) => addExtensionImpl(builder, extensions2);
1339
+ } else {
1340
+ const builder = builderOrExtensions;
1341
+ return addExtensionImpl(builder, extensions);
1342
+ }
1343
+ }
1344
+ var removeExtensionImpl = (builder, id) => {
1345
+ const internal = builder;
1346
+ const extensions = internal._registry.get(internal._extensions);
1347
+ internal._registry.set(internal._extensions, Record2.remove(extensions, id));
1348
+ return builder;
1349
+ };
1350
+ function removeExtension(builderOrId, id) {
1351
+ if (typeof builderOrId === "string") {
1352
+ const id2 = builderOrId;
1353
+ return (builder) => removeExtensionImpl(builder, id2);
1354
+ } else {
1355
+ const builder = builderOrId;
1356
+ return removeExtensionImpl(builder, id);
1357
+ }
1358
+ }
1359
+ var exploreImpl = async (builder, options, path = []) => {
1360
+ const internal = builder;
1361
+ const { registry = Registry2.make(), source = RootId, relation: relation2, visitor } = options;
1362
+ if (path.includes(source)) {
1363
+ return;
1364
+ }
1365
+ await yieldOrContinue("idle");
1366
+ const node = registry.get(internal._graph.nodeOrThrow(source));
1367
+ const shouldContinue = await visitor(node, [
1368
+ ...path,
1369
+ node.id
1370
+ ]);
1371
+ if (shouldContinue === false) {
1372
+ return;
1373
+ }
1374
+ const nodes = Function2.pipe(internal._registry.get(internal._extensions), Record2.values, Array2.map((extension) => extension.connector), Array2.filter(isNonNullable2), Array2.flatMap((connector) => registry.get(connector(internal._graph.node(source)))), qualifyNodeArgs(source));
1375
+ await Promise.all(nodes.map((nodeArg) => {
1376
+ registry.set(internal._graph._node(nodeArg.id), internal._graph._constructNode(nodeArg));
1377
+ return exploreImpl(builder, {
1378
+ registry,
1379
+ source: nodeArg.id,
1380
+ relation: relation2,
1381
+ visitor
1382
+ }, [
1383
+ ...path,
1384
+ node.id
1385
+ ]);
1386
+ }));
1387
+ if (registry !== internal._registry) {
1388
+ registry.reset();
1389
+ registry.dispose();
1390
+ }
1391
+ };
1392
+ function explore(builderOrOptions, optionsOrPath, path) {
1393
+ if (typeof builderOrOptions === "object" && "visitor" in builderOrOptions) {
1394
+ const options = builderOrOptions;
1395
+ const path2 = Array2.isArray(optionsOrPath) ? optionsOrPath : void 0;
1396
+ return (builder) => exploreImpl(builder, options, path2);
1397
+ } else {
1398
+ const builder = builderOrOptions;
1399
+ const options = optionsOrPath;
1400
+ const pathArg = path ?? (Array2.isArray(optionsOrPath) ? optionsOrPath : void 0);
1401
+ return exploreImpl(builder, options, pathArg);
1402
+ }
1403
+ }
1404
+ var destroyImpl = (builder) => {
1405
+ const internal = builder;
1406
+ internal._subscriptions.forEach((unsubscribe) => unsubscribe());
1407
+ internal._subscriptions.clear();
1408
+ };
1409
+ function destroy(builder) {
1410
+ if (builder === void 0) {
1411
+ return (builder2) => destroyImpl(builder2);
1412
+ } else {
1413
+ return destroyImpl(builder);
1414
+ }
1415
+ }
1416
+ var flush = (builder) => {
1417
+ return builder._flushPromise;
1418
+ };
1419
+ var createExtensionRaw = (extension) => {
1420
+ const { id, position = "static", relation: relation2 = "child", resolver: _resolver, connector: _connector, actions: _actions, actionGroups: _actionGroups } = extension;
1421
+ const normalizedRelation = normalizeRelation(relation2);
1422
+ const getId = (key) => `${id}/${key}`;
1423
+ const resolver = _resolver && Atom2.family((id2) => _resolver(id2).pipe(Atom2.withLabel(`graph-builder:_resolver:${id2}`)));
1424
+ const connector = _connector && Atom2.family((node) => _connector(node).pipe(Atom2.withLabel(`graph-builder:_connector:${id}`)));
1425
+ const actionGroups = _actionGroups && Atom2.family((node) => _actionGroups(node).pipe(Atom2.withLabel(`graph-builder:_actionGroups:${id}`)));
1426
+ const actions = _actions && Atom2.family((node) => _actions(node).pipe(Atom2.withLabel(`graph-builder:_actions:${id}`)));
1427
+ return [
1428
+ resolver ? {
1429
+ id: getId("resolver"),
1430
+ position,
1431
+ resolver
1432
+ } : void 0,
1433
+ connector ? {
1434
+ id: getId("connector"),
1435
+ position,
1436
+ relation: normalizedRelation,
1437
+ connector: Atom2.family((node) => Atom2.make((get2) => {
1438
+ try {
1439
+ return get2(connector(node));
1440
+ } catch (error) {
1441
+ log2.warn("Error in connector", {
1442
+ id: getId("connector"),
1443
+ node,
1444
+ error
1445
+ }, {
1446
+ F: __dxlog_file3,
1447
+ L: 609,
1448
+ S: void 0,
1449
+ C: (f, a) => f(...a)
1450
+ });
1451
+ return [];
1452
+ }
1453
+ }).pipe(Atom2.withLabel(`graph-builder:connector:${id}`)))
1454
+ } : void 0,
1455
+ actionGroups ? {
1456
+ id: getId("actionGroups"),
1457
+ position,
1458
+ relation: actionRelation(),
1459
+ connector: Atom2.family((node) => Atom2.make((get2) => {
1460
+ try {
1461
+ return get2(actionGroups(node)).map((arg) => ({
1462
+ ...arg,
1463
+ data: actionGroupSymbol,
1464
+ type: ActionGroupType
1465
+ }));
1466
+ } catch (error) {
1467
+ log2.warn("Error in actionGroups", {
1468
+ id: getId("actionGroups"),
1469
+ node,
1470
+ error
1471
+ }, {
1472
+ F: __dxlog_file3,
1473
+ L: 630,
1474
+ S: void 0,
1475
+ C: (f, a) => f(...a)
1476
+ });
1477
+ return [];
1478
+ }
1479
+ }).pipe(Atom2.withLabel(`graph-builder:connector:actionGroups:${id}`)))
1480
+ } : void 0,
1481
+ actions ? {
1482
+ id: getId("actions"),
1483
+ position,
1484
+ relation: actionRelation(),
1485
+ connector: Atom2.family((node) => Atom2.make((get2) => {
1486
+ try {
1487
+ return get2(actions(node)).map((arg) => ({
1488
+ ...arg,
1489
+ type: ActionType
1490
+ }));
1491
+ } catch (error) {
1492
+ log2.warn("Error in actions", {
1493
+ id: getId("actions"),
1494
+ node,
1495
+ error
1496
+ }, {
1497
+ F: __dxlog_file3,
1498
+ L: 647,
1499
+ S: void 0,
1500
+ C: (f, a) => f(...a)
1501
+ });
1502
+ return [];
1503
+ }
1504
+ }).pipe(Atom2.withLabel(`graph-builder:connector:actions:${id}`)))
1505
+ } : void 0
1506
+ ].filter(isNonNullable2);
1507
+ };
1508
+ var runEffectSyncWithFallback = (effect, context2, extensionId, fallback) => {
1509
+ return Effect.runSync(effect.pipe(Effect.provide(context2), Effect.catchAll((error) => {
1510
+ log2.warn("Extension failed", {
1511
+ extension: extensionId,
1512
+ error
1513
+ }, {
1514
+ F: __dxlog_file3,
1515
+ L: 690,
1516
+ S: void 0,
1517
+ C: (f, a) => f(...a)
1518
+ });
1519
+ return Effect.succeed(fallback);
1520
+ })));
1521
+ };
1522
+ var createExtension = (options) => Effect.map(Effect.context(), (context2) => {
1523
+ const { id, match: match3, actions, connector, resolver, relation: relation2, position } = options;
1524
+ const connectorExtension = connector ? createConnectorWithRuntime(id, match3, connector, context2) : void 0;
1525
+ const actionsExtension = actions ? (node) => Atom2.make((get2) => Function2.pipe(get2(node), Option3.flatMap(match3), Option3.map((matched) => runEffectSyncWithFallback(actions(matched, get2), context2, id, []).map((action) => ({
1526
+ ...action,
1527
+ // Attach captured context for action execution.
1528
+ _actionContext: context2
1529
+ }))), Option3.getOrElse(() => []))) : void 0;
1530
+ const resolverExtension = resolver ? (nodeId) => Atom2.make((get2) => runEffectSyncWithFallback(resolver(nodeId, get2), context2, id, null) ?? null) : void 0;
1531
+ return createExtensionRaw({
1532
+ id,
1533
+ relation: relation2,
1534
+ position,
1535
+ connector: connectorExtension,
1536
+ actions: actionsExtension,
1537
+ resolver: resolverExtension
1538
+ });
1539
+ });
1540
+ var createConnector = (matcher, factory) => {
1541
+ return (node) => Atom2.make((get2) => Function2.pipe(get2(node), Option3.flatMap(matcher), Option3.map((data) => factory(data, get2)), Option3.getOrElse(() => [])));
1542
+ };
1543
+ var createConnectorWithRuntime = (extensionId, matcher, factory, context2) => {
1544
+ return (node) => Atom2.make((get2) => Function2.pipe(get2(node), Option3.flatMap(matcher), Option3.map((data) => runEffectSyncWithFallback(factory(data, get2), context2, extensionId, [])), Option3.getOrElse(() => [])));
1545
+ };
1546
+ var createTypeExtension = (options) => {
1547
+ const { id, type, actions, connector, relation: relation2, position } = options;
1548
+ return createExtension({
1549
+ id,
1550
+ match: whenEchoType(type),
1551
+ actions,
1552
+ connector,
1553
+ relation: relation2,
1554
+ position
1555
+ });
1556
+ };
1557
+ var qualifyNodeArgs = (parentId) => (nodes) => nodes.map((node) => {
1558
+ validateSegmentId(node.id);
1559
+ const qualified = qualifyId(parentId, node.id);
1560
+ return {
1561
+ ...node,
1562
+ id: qualified,
1563
+ nodes: node.nodes ? qualifyNodeArgs(qualified)(node.nodes) : void 0
1564
+ };
1565
+ });
1566
+ var connectorKey = (id, relation2) => primaryKey(id, relationKey(relation2));
1567
+ var relationFromConnectorKey = (key) => {
1568
+ const [id, encodedRelation] = primaryParts(key);
1569
+ return {
1570
+ id,
1571
+ relation: relationFromKey(encodedRelation)
1572
+ };
1573
+ };
1574
+ var subscriptionKey = (id, kind, detail) => detail != null ? primaryKey(id, kind, detail) : primaryKey(id, kind);
1575
+ var flattenExtensions = (extension, acc = []) => {
1576
+ if (Array2.isArray(extension)) {
1577
+ return [
1578
+ ...acc,
1579
+ ...extension.flatMap((ext) => flattenExtensions(ext, acc))
1580
+ ];
1581
+ } else {
1582
+ return [
1583
+ ...acc,
1584
+ extension
1585
+ ];
1586
+ }
1587
+ };
1588
+
1589
+ export {
1590
+ __export,
1591
+ node_exports,
1592
+ qualifyId,
1593
+ getParentId,
1594
+ getSegmentId,
1595
+ getNode,
1596
+ expand,
1597
+ graph_exports,
1598
+ node_matcher_exports,
1599
+ make3 as make,
1600
+ addExtension,
1601
+ flush,
1602
+ graph_builder_exports
1603
+ };
1604
+ //# sourceMappingURL=chunk-W47H2NND.mjs.map