@deck.gl-community/graph-layers 9.2.0-beta.2 → 9.2.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/graph-engine.d.ts +63 -21
- package/dist/core/graph-engine.d.ts.map +1 -1
- package/dist/core/graph-engine.js +155 -75
- package/dist/core/graph-engine.js.map +1 -1
- package/dist/core/graph-layout.d.ts +22 -18
- package/dist/core/graph-layout.d.ts.map +1 -1
- package/dist/core/graph-layout.js +33 -18
- package/dist/core/graph-layout.js.map +1 -1
- package/dist/core/interaction-manager.d.ts +2 -2
- package/dist/core/interaction-manager.d.ts.map +1 -1
- package/dist/core/interaction-manager.js +7 -5
- package/dist/core/interaction-manager.js.map +1 -1
- package/dist/graph/arrow-graph.d.ts +69 -0
- package/dist/graph/arrow-graph.d.ts.map +1 -0
- package/dist/graph/arrow-graph.js +513 -0
- package/dist/graph/arrow-graph.js.map +1 -0
- package/dist/graph/classic-graph.d.ts +169 -0
- package/dist/graph/classic-graph.d.ts.map +1 -0
- package/dist/graph/classic-graph.js +390 -0
- package/dist/graph/classic-graph.js.map +1 -0
- package/dist/graph/edge.d.ts +6 -6
- package/dist/graph/edge.d.ts.map +1 -1
- package/dist/graph/edge.js.map +1 -1
- package/dist/graph/functions/arrow-utils.d.ts +6 -0
- package/dist/graph/functions/arrow-utils.d.ts.map +1 -0
- package/dist/graph/functions/arrow-utils.js +67 -0
- package/dist/graph/functions/arrow-utils.js.map +1 -0
- package/dist/graph/functions/create-graph-from-data.d.ts +3 -0
- package/dist/graph/functions/create-graph-from-data.d.ts.map +1 -0
- package/dist/graph/functions/create-graph-from-data.js +12 -0
- package/dist/graph/functions/create-graph-from-data.js.map +1 -0
- package/dist/graph/graph-normalization.d.ts +10 -0
- package/dist/graph/graph-normalization.d.ts.map +1 -0
- package/dist/graph/graph-normalization.js +65 -0
- package/dist/graph/graph-normalization.js.map +1 -0
- package/dist/graph/graph.d.ts +62 -155
- package/dist/graph/graph.d.ts.map +1 -1
- package/dist/graph/graph.js +11 -300
- package/dist/graph/graph.js.map +1 -1
- package/dist/graph/node.d.ts +6 -6
- package/dist/graph/node.d.ts.map +1 -1
- package/dist/graph/node.js +2 -2
- package/dist/graph/node.js.map +1 -1
- package/dist/graph-data/arrow-graph-data-builder.d.ts +21 -0
- package/dist/graph-data/arrow-graph-data-builder.d.ts.map +1 -0
- package/dist/graph-data/arrow-graph-data-builder.js +105 -0
- package/dist/graph-data/arrow-graph-data-builder.js.map +1 -0
- package/dist/graph-data/graph-data-builder.d.ts +6 -0
- package/dist/graph-data/graph-data-builder.d.ts.map +1 -0
- package/dist/graph-data/graph-data-builder.js +1 -0
- package/dist/graph-data/graph-data-builder.js.map +1 -0
- package/dist/graph-data/graph-data.d.ts +40 -0
- package/dist/graph-data/graph-data.d.ts.map +1 -0
- package/dist/graph-data/graph-data.js +11 -0
- package/dist/graph-data/graph-data.js.map +1 -0
- package/dist/graph-data/plain-graph-data-builder.d.ts +20 -0
- package/dist/graph-data/plain-graph-data-builder.d.ts.map +1 -0
- package/dist/graph-data/plain-graph-data-builder.js +105 -0
- package/dist/graph-data/plain-graph-data-builder.js.map +1 -0
- package/dist/graph-style-schema.cdn.js +1 -1
- package/dist/graph-style-schema.json +1 -1
- package/dist/index.cjs +6905 -4576
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +14 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -11
- package/dist/index.js.map +1 -1
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer.d.ts.map +1 -1
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer.js +1 -2
- package/dist/layers/common-layers/flow-path-layer/flow-path-layer.js.map +1 -1
- package/dist/layers/common-layers/grid-layer/grid-layer.d.ts +83 -0
- package/dist/layers/common-layers/grid-layer/grid-layer.d.ts.map +1 -0
- package/dist/layers/common-layers/grid-layer/grid-layer.js +133 -0
- package/dist/layers/common-layers/grid-layer/grid-layer.js.map +1 -0
- package/dist/layers/edge-attachment-helper.d.ts.map +1 -1
- package/dist/layers/edge-attachment-helper.js +1 -2
- package/dist/layers/edge-attachment-helper.js.map +1 -1
- package/dist/layers/graph-layer.d.ts +68 -11
- package/dist/layers/graph-layer.d.ts.map +1 -1
- package/dist/layers/graph-layer.js +435 -50
- package/dist/layers/graph-layer.js.map +1 -1
- package/dist/layouts/d3-dag/collapsable-d3-dag-layout.d.ts +24 -0
- package/dist/layouts/d3-dag/collapsable-d3-dag-layout.d.ts.map +1 -0
- package/dist/layouts/d3-dag/collapsable-d3-dag-layout.js +251 -0
- package/dist/layouts/d3-dag/collapsable-d3-dag-layout.js.map +1 -0
- package/dist/layouts/d3-dag/d3-dag-layout.d.ts +46 -61
- package/dist/layouts/d3-dag/d3-dag-layout.d.ts.map +1 -1
- package/dist/layouts/d3-dag/d3-dag-layout.js +85 -270
- package/dist/layouts/d3-dag/d3-dag-layout.js.map +1 -1
- package/dist/layouts/d3-force/d3-force-layout.d.ts +20 -8
- package/dist/layouts/d3-force/d3-force-layout.d.ts.map +1 -1
- package/dist/layouts/d3-force/d3-force-layout.js +39 -20
- package/dist/layouts/d3-force/d3-force-layout.js.map +1 -1
- package/dist/layouts/experimental/force-multi-graph-layout.d.ts +19 -15
- package/dist/layouts/experimental/force-multi-graph-layout.d.ts.map +1 -1
- package/dist/layouts/experimental/force-multi-graph-layout.js +47 -38
- package/dist/layouts/experimental/force-multi-graph-layout.js.map +1 -1
- package/dist/layouts/experimental/hive-plot-layout.d.ts +18 -15
- package/dist/layouts/experimental/hive-plot-layout.d.ts.map +1 -1
- package/dist/layouts/experimental/hive-plot-layout.js +33 -34
- package/dist/layouts/experimental/hive-plot-layout.js.map +1 -1
- package/dist/layouts/experimental/radial-layout.d.ts +12 -7
- package/dist/layouts/experimental/radial-layout.d.ts.map +1 -1
- package/dist/layouts/experimental/radial-layout.js +31 -14
- package/dist/layouts/experimental/radial-layout.js.map +1 -1
- package/dist/layouts/gpu-force/gpu-force-layout.d.ts +11 -8
- package/dist/layouts/gpu-force/gpu-force-layout.d.ts.map +1 -1
- package/dist/layouts/gpu-force/gpu-force-layout.js +59 -56
- package/dist/layouts/gpu-force/gpu-force-layout.js.map +1 -1
- package/dist/layouts/simple-layout.d.ts +8 -25
- package/dist/layouts/simple-layout.d.ts.map +1 -1
- package/dist/layouts/simple-layout.js +13 -17
- package/dist/layouts/simple-layout.js.map +1 -1
- package/dist/loaders/dot-graph-loader.d.ts +25 -0
- package/dist/loaders/dot-graph-loader.d.ts.map +1 -0
- package/dist/loaders/dot-graph-loader.js +668 -0
- package/dist/loaders/dot-graph-loader.js.map +1 -0
- package/dist/loaders/json-graph-loader.d.ts +6 -0
- package/dist/loaders/json-graph-loader.d.ts.map +1 -0
- package/dist/loaders/json-graph-loader.js +31 -0
- package/dist/loaders/json-graph-loader.js.map +1 -0
- package/dist/loaders/{edge-parsers.d.ts → parsers/edge-parsers.d.ts} +1 -1
- package/dist/loaders/parsers/edge-parsers.d.ts.map +1 -0
- package/dist/loaders/{edge-parsers.js → parsers/edge-parsers.js} +1 -1
- package/dist/loaders/parsers/edge-parsers.js.map +1 -0
- package/dist/loaders/{node-parsers.d.ts → parsers/node-parsers.d.ts} +1 -1
- package/dist/loaders/parsers/node-parsers.d.ts.map +1 -0
- package/dist/loaders/{node-parsers.js → parsers/node-parsers.js} +1 -1
- package/dist/loaders/parsers/node-parsers.js.map +1 -0
- package/dist/loaders/parsers/parse-json-graph.d.ts +29 -0
- package/dist/loaders/parsers/parse-json-graph.d.ts.map +1 -0
- package/dist/loaders/parsers/parse-json-graph.js +78 -0
- package/dist/loaders/parsers/parse-json-graph.js.map +1 -0
- package/dist/style/graph-style-engine.d.ts +4 -2
- package/dist/style/graph-style-engine.d.ts.map +1 -1
- package/dist/style/graph-style-engine.js +3 -2
- package/dist/style/graph-style-engine.js.map +1 -1
- package/dist/style/{style-engine.d.ts → stylesheet-engine.d.ts} +3 -3
- package/dist/style/stylesheet-engine.d.ts.map +1 -0
- package/dist/style/{style-engine.js → stylesheet-engine.js} +1 -1
- package/dist/style/stylesheet-engine.js.map +1 -0
- package/dist/utils/collapsed-chains.d.ts +8 -8
- package/dist/utils/collapsed-chains.d.ts.map +1 -1
- package/dist/utils/collapsed-chains.js +1 -6
- package/dist/utils/collapsed-chains.js.map +1 -1
- package/dist/utils/rank-grid.d.ts +30 -0
- package/dist/utils/rank-grid.d.ts.map +1 -0
- package/dist/utils/rank-grid.js +306 -0
- package/dist/utils/rank-grid.js.map +1 -0
- package/package.json +4 -8
- package/src/_disabled/arrow-graph-data.ts.disabled +18 -0
- package/src/_disabled/columnar-graph-data-builder.ts.disabled +250 -0
- package/src/_disabled/graph-runtime-layout.ts.disabled +29 -0
- package/src/core/graph-engine.ts +201 -84
- package/src/core/graph-layout.ts +52 -29
- package/src/core/interaction-manager.ts +20 -20
- package/src/graph/arrow-graph.ts +648 -0
- package/src/graph/classic-graph.ts +447 -0
- package/src/graph/edge.ts +7 -7
- package/src/graph/functions/arrow-utils.ts +72 -0
- package/src/graph/functions/convert-arrow-graph-to-classic-graph.ts.disabled +47 -0
- package/src/graph/functions/convert-plain-graph-to-arrow-graph.ts.disabled +119 -0
- package/src/graph/functions/create-graph-from-data.ts +16 -0
- package/src/graph/functions/create-plain-graph-from-data.ts.disabled +176 -0
- package/src/graph/graph-normalization.ts +87 -0
- package/src/graph/graph.ts +68 -339
- package/src/graph/node.ts +9 -9
- package/src/graph/tabular-graph.ts.disabled +761 -0
- package/src/graph-data/arrow-graph-data-builder.ts +165 -0
- package/src/graph-data/graph-data-builder.ts +7 -0
- package/src/graph-data/graph-data.ts +57 -0
- package/src/graph-data/plain-graph-data-builder.ts +132 -0
- package/src/index.ts +53 -13
- package/src/layers/common-layers/flow-path-layer/flow-path-layer.ts +1 -2
- package/src/layers/common-layers/grid-layer/grid-layer.ts +237 -0
- package/src/layers/edge-attachment-helper.ts +22 -16
- package/src/layers/graph-layer.ts +642 -62
- package/src/layouts/d3-dag/collapsable-d3-dag-layout.ts +330 -0
- package/src/layouts/d3-dag/d3-dag-layout.ts +166 -396
- package/src/layouts/d3-force/d3-force-layout.ts +52 -30
- package/src/layouts/experimental/force-multi-graph-layout.ts +55 -49
- package/src/layouts/experimental/hive-plot-layout.ts +41 -42
- package/src/layouts/experimental/radial-layout.ts +39 -20
- package/src/layouts/gpu-force/gpu-force-layout.ts +72 -70
- package/src/layouts/simple-layout.ts +20 -44
- package/src/loaders/{create-graph.ts → deprecated/create-graph.ts.disabled} +6 -6
- package/src/loaders/deprecated/json-classic-graph-loader.ts.disabled +33 -0
- package/src/loaders/{simple-json-graph-loader.ts → deprecated/simple-json-graph-loader.ts.disabled} +3 -3
- package/src/loaders/{table-graph-loader.ts → deprecated/table-graph-loader.ts.disabled} +8 -8
- package/src/loaders/dot-graph-loader.ts +860 -0
- package/src/loaders/json-graph-loader.ts +48 -0
- package/src/loaders/parsers/create-graph-data.ts.disabled +45 -0
- package/src/loaders/{edge-parsers.ts → parsers/edge-parsers.ts} +2 -2
- package/src/loaders/{node-parsers.ts → parsers/node-parsers.ts} +2 -2
- package/src/loaders/parsers/parse-json-graph.ts +134 -0
- package/src/style/graph-style-engine.ts +5 -2
- package/src/style/{style-engine.ts → stylesheet-engine.ts} +3 -3
- package/src/utils/collapsed-chains.ts +11 -17
- package/src/utils/rank-grid.ts +426 -0
- package/dist/loaders/create-graph.d.ts +0 -12
- package/dist/loaders/create-graph.d.ts.map +0 -1
- package/dist/loaders/create-graph.js +0 -38
- package/dist/loaders/create-graph.js.map +0 -1
- package/dist/loaders/edge-parsers.d.ts.map +0 -1
- package/dist/loaders/edge-parsers.js.map +0 -1
- package/dist/loaders/json-loader.d.ts +0 -7
- package/dist/loaders/json-loader.d.ts.map +0 -1
- package/dist/loaders/json-loader.js +0 -16
- package/dist/loaders/json-loader.js.map +0 -1
- package/dist/loaders/node-parsers.d.ts.map +0 -1
- package/dist/loaders/node-parsers.js.map +0 -1
- package/dist/loaders/simple-json-graph-loader.d.ts +0 -11
- package/dist/loaders/simple-json-graph-loader.d.ts.map +0 -1
- package/dist/loaders/simple-json-graph-loader.js +0 -20
- package/dist/loaders/simple-json-graph-loader.js.map +0 -1
- package/dist/loaders/table-graph-loader.d.ts +0 -16
- package/dist/loaders/table-graph-loader.d.ts.map +0 -1
- package/dist/loaders/table-graph-loader.js +0 -91
- package/dist/loaders/table-graph-loader.js.map +0 -1
- package/dist/style/style-engine.d.ts.map +0 -1
- package/dist/style/style-engine.js.map +0 -1
- package/dist/widgets/long-press-button.d.ts +0 -12
- package/dist/widgets/long-press-button.d.ts.map +0 -1
- package/dist/widgets/long-press-button.js +0 -31
- package/dist/widgets/long-press-button.js.map +0 -1
- package/dist/widgets/view-control-widget.d.ts +0 -77
- package/dist/widgets/view-control-widget.d.ts.map +0 -1
- package/dist/widgets/view-control-widget.js +0 -197
- package/dist/widgets/view-control-widget.js.map +0 -1
- package/src/loaders/json-loader.ts +0 -19
- package/src/widgets/long-press-button.tsx +0 -50
- package/src/widgets/view-control-widget.tsx +0 -339
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import * as arrow from 'apache-arrow';
|
|
6
|
+
|
|
7
|
+
import type {NodeState, EdgeState} from '../core/constants';
|
|
8
|
+
import type {ArrowGraphData} from '../graph-data/graph-data';
|
|
9
|
+
import {PlainGraphDataBuilder} from '../graph-data/plain-graph-data-builder';
|
|
10
|
+
import type {GraphProps, NodeInterface, EdgeInterface} from './graph';
|
|
11
|
+
import {Graph} from './graph';
|
|
12
|
+
import {ClassicGraph} from './classic-graph';
|
|
13
|
+
import {cloneRecord, normalizeEdgeState, normalizeNodeState} from './graph-normalization';
|
|
14
|
+
|
|
15
|
+
import {getVectorLength,
|
|
16
|
+
getVectorValue,
|
|
17
|
+
getColumnVector,
|
|
18
|
+
parseDataRecord,
|
|
19
|
+
coerceIdentifier
|
|
20
|
+
} from './functions/arrow-utils'
|
|
21
|
+
|
|
22
|
+
type NodeOverride = {
|
|
23
|
+
state?: NodeState;
|
|
24
|
+
selectable?: boolean;
|
|
25
|
+
highlightConnectedEdges?: boolean;
|
|
26
|
+
data?: Record<string, unknown>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type EdgeOverride = {
|
|
30
|
+
state?: EdgeState;
|
|
31
|
+
directed?: boolean;
|
|
32
|
+
data?: Record<string, unknown>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type NodeVectors = {
|
|
36
|
+
id: arrow.Vector | null;
|
|
37
|
+
state: arrow.Vector | null;
|
|
38
|
+
selectable: arrow.Vector | null;
|
|
39
|
+
highlightConnectedEdges: arrow.Vector | null;
|
|
40
|
+
data: arrow.Vector | null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type EdgeVectors = {
|
|
44
|
+
id: arrow.Vector | null;
|
|
45
|
+
sourceId: arrow.Vector | null;
|
|
46
|
+
targetId: arrow.Vector | null;
|
|
47
|
+
directed: arrow.Vector | null;
|
|
48
|
+
state: arrow.Vector | null;
|
|
49
|
+
data: arrow.Vector | null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type ArrowGraphProps = GraphProps & {
|
|
53
|
+
data: ArrowGraphData;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class ArrowGraph extends Graph<ArrowGraphProps> {
|
|
57
|
+
private readonly nodeTable: arrow.Table;
|
|
58
|
+
private readonly edgeTable: arrow.Table;
|
|
59
|
+
|
|
60
|
+
private readonly nodeVectors: NodeVectors;
|
|
61
|
+
private readonly edgeVectors: EdgeVectors;
|
|
62
|
+
|
|
63
|
+
private readonly nodeOverrides: NodeOverride[];
|
|
64
|
+
private readonly edgeOverrides: EdgeOverride[];
|
|
65
|
+
|
|
66
|
+
private readonly nodeDataCache: Array<Record<string, unknown> | null>;
|
|
67
|
+
private readonly edgeDataCache: Array<Record<string, unknown> | null>;
|
|
68
|
+
|
|
69
|
+
private readonly nodeEdgeIndices: number[][];
|
|
70
|
+
private nodeIndices: WeakMap<NodeInterface, number> = new WeakMap();
|
|
71
|
+
private edgeIndices: WeakMap<EdgeInterface, number> = new WeakMap();
|
|
72
|
+
|
|
73
|
+
private readonly nodes: ArrowGraphNode[];
|
|
74
|
+
private readonly edges: ArrowGraphEdge[];
|
|
75
|
+
private readonly nodeMap: Map<string | number, ArrowGraphNode> = new Map();
|
|
76
|
+
|
|
77
|
+
private readonly _version: number;
|
|
78
|
+
|
|
79
|
+
constructor(props: ArrowGraphProps) {
|
|
80
|
+
super(props);
|
|
81
|
+
|
|
82
|
+
this._version = props.data.version;
|
|
83
|
+
this.nodeTable = props.data.nodes;
|
|
84
|
+
this.edgeTable = props.data.edges;
|
|
85
|
+
|
|
86
|
+
this.nodeVectors = this.extractNodeVectors();
|
|
87
|
+
this.edgeVectors = this.extractEdgeVectors();
|
|
88
|
+
this.assertRequiredColumns();
|
|
89
|
+
|
|
90
|
+
const nodeCount = getVectorLength(this.nodeVectors.id);
|
|
91
|
+
const edgeCount = getVectorLength(this.edgeVectors.id);
|
|
92
|
+
|
|
93
|
+
this.nodeOverrides = new Array(nodeCount);
|
|
94
|
+
this.edgeOverrides = new Array(edgeCount);
|
|
95
|
+
this.nodeDataCache = new Array(nodeCount).fill(null);
|
|
96
|
+
this.edgeDataCache = new Array(edgeCount).fill(null);
|
|
97
|
+
this.nodeEdgeIndices = Array.from({length: nodeCount}, () => []);
|
|
98
|
+
|
|
99
|
+
this.nodes = this.initializeNodes(nodeCount);
|
|
100
|
+
this.edges = this.initializeEdges(edgeCount);
|
|
101
|
+
|
|
102
|
+
this.registerNodes();
|
|
103
|
+
this.registerEdges();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get version(): number {
|
|
107
|
+
return this._version;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getNodes(): Iterable<NodeInterface> {
|
|
111
|
+
return this.nodes;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getEdges(): Iterable<EdgeInterface> {
|
|
115
|
+
return this.edges;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
findNode(id: string | number): NodeInterface | undefined {
|
|
119
|
+
return this.nodeMap.get(id) ?? this.nodeMap.get(String(id));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
findNodeById(id: string | number): NodeInterface | undefined {
|
|
123
|
+
return this.findNode(id);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
destroy(): void {
|
|
127
|
+
this.nodeMap.clear();
|
|
128
|
+
this.nodeIndices = new WeakMap();
|
|
129
|
+
this.edgeIndices = new WeakMap();
|
|
130
|
+
this.nodeEdgeIndices.length = 0;
|
|
131
|
+
this.nodes.length = 0;
|
|
132
|
+
this.edges.length = 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getNodeIdByIndex(index: number): string | number {
|
|
136
|
+
const value = getVectorValue(this.nodeVectors.id, index);
|
|
137
|
+
if (typeof value === 'undefined') {
|
|
138
|
+
throw new Error('Arrow graph requires an id column for nodes.');
|
|
139
|
+
}
|
|
140
|
+
return coerceIdentifier(value);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getNodeStateByIndex(index: number): NodeState {
|
|
144
|
+
const override = this.nodeOverrides[index]?.state;
|
|
145
|
+
if (override) {
|
|
146
|
+
return override;
|
|
147
|
+
}
|
|
148
|
+
const value = getVectorValue(this.nodeVectors.state, index);
|
|
149
|
+
return normalizeNodeState(typeof value === 'string' ? (value as NodeState) : undefined);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
setNodeStateByIndex(index: number, state: NodeState): void {
|
|
153
|
+
const override = (this.nodeOverrides[index] ??= {});
|
|
154
|
+
override.state = state;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
isNodeSelectableByIndex(index: number): boolean {
|
|
158
|
+
const override = this.nodeOverrides[index]?.selectable;
|
|
159
|
+
if (typeof override === 'boolean') {
|
|
160
|
+
return override;
|
|
161
|
+
}
|
|
162
|
+
const value = getVectorValue(this.nodeVectors.selectable, index);
|
|
163
|
+
return coerceBoolean(value, false);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
shouldHighlightConnectedEdgesByIndex(index: number): boolean {
|
|
167
|
+
const override = this.nodeOverrides[index]?.highlightConnectedEdges;
|
|
168
|
+
if (typeof override === 'boolean') {
|
|
169
|
+
return override;
|
|
170
|
+
}
|
|
171
|
+
const value = getVectorValue(this.nodeVectors.highlightConnectedEdges, index);
|
|
172
|
+
return coerceBoolean(value, false);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getNodeDegreeByIndex(index: number): number {
|
|
176
|
+
return this.nodeEdgeIndices[index]?.length ?? 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
getNodeInDegreeByIndex(index: number): number {
|
|
180
|
+
const id = this.getNodeIdByIndex(index);
|
|
181
|
+
return (this.nodeEdgeIndices[index] ?? []).reduce((count, edgeIndex) => {
|
|
182
|
+
if (this.getEdgeTargetIdByIndex(edgeIndex) === id && this.isEdgeDirectedByIndex(edgeIndex)) {
|
|
183
|
+
return count + 1;
|
|
184
|
+
}
|
|
185
|
+
return count;
|
|
186
|
+
}, 0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
getNodeOutDegreeByIndex(index: number): number {
|
|
190
|
+
const id = this.getNodeIdByIndex(index);
|
|
191
|
+
return (this.nodeEdgeIndices[index] ?? []).reduce((count, edgeIndex) => {
|
|
192
|
+
if (this.getEdgeSourceIdByIndex(edgeIndex) === id && this.isEdgeDirectedByIndex(edgeIndex)) {
|
|
193
|
+
return count + 1;
|
|
194
|
+
}
|
|
195
|
+
return count;
|
|
196
|
+
}, 0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getNodeSiblingIdsByIndex(index: number): (string | number)[] {
|
|
200
|
+
const id = this.getNodeIdByIndex(index);
|
|
201
|
+
const edges = this.nodeEdgeIndices[index] ?? [];
|
|
202
|
+
const siblings: (string | number)[] = [];
|
|
203
|
+
for (const edgeIndex of edges) {
|
|
204
|
+
const source = this.getEdgeSourceIdByIndex(edgeIndex);
|
|
205
|
+
const target = this.getEdgeTargetIdByIndex(edgeIndex);
|
|
206
|
+
if (source === id) {
|
|
207
|
+
siblings.push(target);
|
|
208
|
+
} else {
|
|
209
|
+
siblings.push(source);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return siblings;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
getNodeConnectedEdgesByIndex(index: number): EdgeInterface[] {
|
|
216
|
+
return (this.nodeEdgeIndices[index] ?? [])
|
|
217
|
+
.map((edgeIndex) => this.edges[edgeIndex])
|
|
218
|
+
.filter((edge): edge is ArrowGraphEdge => Boolean(edge));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
setNodeDataByIndex(index: number, data: Record<string, unknown>): void {
|
|
222
|
+
const override = (this.nodeOverrides[index] ??= {});
|
|
223
|
+
override.data = {...data};
|
|
224
|
+
this.nodeDataCache[index] = {...data};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
setNodeDataPropertyByIndex(index: number, key: string, value: unknown): void {
|
|
228
|
+
const data = this.getNodeDataInternal(index);
|
|
229
|
+
data[key] = value;
|
|
230
|
+
const override = (this.nodeOverrides[index] ??= {});
|
|
231
|
+
override.data = {...data};
|
|
232
|
+
this.nodeDataCache[index] = {...data};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
getNodeDataByIndex(index: number): Record<string, unknown> {
|
|
236
|
+
return cloneRecord(this.getNodeDataInternal(index));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
getNodePropertyValueByIndex(index: number, key: string): unknown {
|
|
240
|
+
const data = this.getNodeDataInternal(index);
|
|
241
|
+
if (key in data) {
|
|
242
|
+
return data[key];
|
|
243
|
+
}
|
|
244
|
+
const accessors = this.nodeVectors.data;
|
|
245
|
+
if (!accessors) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
registerEdgeForNode(node: NodeInterface, edge: EdgeInterface): void {
|
|
252
|
+
const nodeIndex = this.nodeIndices.get(node);
|
|
253
|
+
const edgeIndex = this.edgeIndices.get(edge);
|
|
254
|
+
if (nodeIndex === undefined || edgeIndex === undefined) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const edges = this.nodeEdgeIndices[nodeIndex];
|
|
258
|
+
if (!edges.includes(edgeIndex)) {
|
|
259
|
+
edges.push(edgeIndex);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
unregisterEdgeForNode(node: NodeInterface, edge: EdgeInterface): void {
|
|
264
|
+
const nodeIndex = this.nodeIndices.get(node);
|
|
265
|
+
const edgeIndex = this.edgeIndices.get(edge);
|
|
266
|
+
if (nodeIndex === undefined || edgeIndex === undefined) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const edges = this.nodeEdgeIndices[nodeIndex];
|
|
270
|
+
const next = edges.filter((candidate) => candidate !== edgeIndex);
|
|
271
|
+
this.nodeEdgeIndices[nodeIndex] = next;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getEdgeIdByIndex(index: number): string | number {
|
|
275
|
+
const value = getVectorValue(this.edgeVectors.id, index);
|
|
276
|
+
if (typeof value === 'undefined') {
|
|
277
|
+
throw new Error('Arrow graph requires an id column for edges.');
|
|
278
|
+
}
|
|
279
|
+
return coerceIdentifier(value);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
getEdgeStateByIndex(index: number): EdgeState {
|
|
283
|
+
const override = this.edgeOverrides[index]?.state;
|
|
284
|
+
if (override) {
|
|
285
|
+
return override;
|
|
286
|
+
}
|
|
287
|
+
const value = getVectorValue(this.edgeVectors.state, index);
|
|
288
|
+
return normalizeEdgeState(typeof value === 'string' ? (value as EdgeState) : undefined);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
setEdgeStateByIndex(index: number, state: EdgeState): void {
|
|
292
|
+
const override = (this.edgeOverrides[index] ??= {});
|
|
293
|
+
override.state = state;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
isEdgeDirectedByIndex(index: number): boolean {
|
|
297
|
+
const override = this.edgeOverrides[index]?.directed;
|
|
298
|
+
if (typeof override === 'boolean') {
|
|
299
|
+
return override;
|
|
300
|
+
}
|
|
301
|
+
const value = getVectorValue(this.edgeVectors.directed, index);
|
|
302
|
+
return coerceBoolean(value, false);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
getEdgeSourceIdByIndex(index: number): string | number {
|
|
306
|
+
const value = getVectorValue(this.edgeVectors.sourceId, index);
|
|
307
|
+
if (typeof value === 'undefined') {
|
|
308
|
+
throw new Error('Arrow graph requires a sourceId column.');
|
|
309
|
+
}
|
|
310
|
+
return coerceIdentifier(value);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
getEdgeTargetIdByIndex(index: number): string | number {
|
|
314
|
+
const value = getVectorValue(this.edgeVectors.targetId, index);
|
|
315
|
+
if (typeof value === 'undefined') {
|
|
316
|
+
throw new Error('Arrow graph requires a targetId column.');
|
|
317
|
+
}
|
|
318
|
+
return coerceIdentifier(value);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
getEdgePropertyValueByIndex(index: number, key: string): unknown {
|
|
322
|
+
const data = this.getEdgeDataInternal(index);
|
|
323
|
+
return data[key];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
setEdgeDataByIndex(index: number, data: Record<string, unknown>): void {
|
|
327
|
+
const override = (this.edgeOverrides[index] ??= {});
|
|
328
|
+
override.data = {...data};
|
|
329
|
+
this.edgeDataCache[index] = {...data};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
setEdgeDataPropertyByIndex(index: number, key: string, value: unknown): void {
|
|
333
|
+
const data = this.getEdgeDataInternal(index);
|
|
334
|
+
data[key] = value;
|
|
335
|
+
const override = (this.edgeOverrides[index] ??= {});
|
|
336
|
+
override.data = {...data};
|
|
337
|
+
this.edgeDataCache[index] = {...data};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
getEdgeDataByIndex(index: number): Record<string, unknown> {
|
|
341
|
+
return cloneRecord(this.getEdgeDataInternal(index));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
getEdgeConnectedNodesByIndex(index: number): NodeInterface[] {
|
|
345
|
+
return this.edges[index]?.getConnectedNodes() ?? [];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
toClassicGraph(): ClassicGraph {
|
|
349
|
+
const builder = new PlainGraphDataBuilder({version: this._version});
|
|
350
|
+
|
|
351
|
+
const nodeCount = getVectorLength(this.nodeVectors.id);
|
|
352
|
+
for (let index = 0; index < nodeCount; index++) {
|
|
353
|
+
builder.addNode({
|
|
354
|
+
id: this.getNodeIdByIndex(index),
|
|
355
|
+
state: this.getNodeStateByIndex(index),
|
|
356
|
+
selectable: this.isNodeSelectableByIndex(index),
|
|
357
|
+
highlightConnectedEdges: this.shouldHighlightConnectedEdgesByIndex(index),
|
|
358
|
+
attributes: this.getNodeDataByIndex(index)
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const edgeCount = getVectorLength(this.edgeVectors.id);
|
|
363
|
+
for (let index = 0; index < edgeCount; index++) {
|
|
364
|
+
builder.addEdge({
|
|
365
|
+
id: this.getEdgeIdByIndex(index),
|
|
366
|
+
sourceId: this.getEdgeSourceIdByIndex(index),
|
|
367
|
+
targetId: this.getEdgeTargetIdByIndex(index),
|
|
368
|
+
directed: this.isEdgeDirectedByIndex(index),
|
|
369
|
+
state: this.getEdgeStateByIndex(index),
|
|
370
|
+
attributes: this.getEdgeDataByIndex(index)
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return new ClassicGraph({data: builder.build()});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private getNodeDataInternal(index: number): Record<string, unknown> {
|
|
378
|
+
const override = this.nodeOverrides[index]?.data;
|
|
379
|
+
if (override) {
|
|
380
|
+
return override;
|
|
381
|
+
}
|
|
382
|
+
const cached = this.nodeDataCache[index];
|
|
383
|
+
if (cached) {
|
|
384
|
+
return cached;
|
|
385
|
+
}
|
|
386
|
+
const value = getVectorValue(this.nodeVectors.data, index);
|
|
387
|
+
const data = parseDataRecord(value);
|
|
388
|
+
this.nodeDataCache[index] = data;
|
|
389
|
+
return data;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private getEdgeDataInternal(index: number): Record<string, unknown> {
|
|
393
|
+
const override = this.edgeOverrides[index]?.data;
|
|
394
|
+
if (override) {
|
|
395
|
+
return override;
|
|
396
|
+
}
|
|
397
|
+
const cached = this.edgeDataCache[index];
|
|
398
|
+
if (cached) {
|
|
399
|
+
return cached;
|
|
400
|
+
}
|
|
401
|
+
const value = getVectorValue(this.edgeVectors.data, index);
|
|
402
|
+
const data = parseDataRecord(value);
|
|
403
|
+
this.edgeDataCache[index] = data;
|
|
404
|
+
return data;
|
|
405
|
+
}
|
|
406
|
+
private extractNodeVectors(): NodeVectors {
|
|
407
|
+
return {
|
|
408
|
+
id: getColumnVector(this.nodeTable, 'id'),
|
|
409
|
+
state: getColumnVector(this.nodeTable, 'state'),
|
|
410
|
+
selectable: getColumnVector(this.nodeTable, 'selectable'),
|
|
411
|
+
highlightConnectedEdges: getColumnVector(this.nodeTable, 'highlightConnectedEdges'),
|
|
412
|
+
data: getColumnVector(this.nodeTable, 'data')
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private extractEdgeVectors(): EdgeVectors {
|
|
417
|
+
return {
|
|
418
|
+
id: getColumnVector(this.edgeTable, 'id'),
|
|
419
|
+
sourceId: getColumnVector(this.edgeTable, 'sourceId'),
|
|
420
|
+
targetId: getColumnVector(this.edgeTable, 'targetId'),
|
|
421
|
+
directed: getColumnVector(this.edgeTable, 'directed'),
|
|
422
|
+
state: getColumnVector(this.edgeTable, 'state'),
|
|
423
|
+
data: getColumnVector(this.edgeTable, 'data')
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private assertRequiredColumns(): void {
|
|
428
|
+
if (!this.nodeVectors.id) {
|
|
429
|
+
throw new Error('Arrow graph requires an "id" column for nodes.');
|
|
430
|
+
}
|
|
431
|
+
if (!this.edgeVectors.id || !this.edgeVectors.sourceId || !this.edgeVectors.targetId) {
|
|
432
|
+
throw new Error('Arrow graph requires "id", "sourceId", and "targetId" columns for edges.');
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private initializeNodes(count: number): ArrowGraphNode[] {
|
|
437
|
+
return Array.from({length: count}, (_, index) => new ArrowGraphNode(this, index));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private initializeEdges(count: number): ArrowGraphEdge[] {
|
|
441
|
+
return Array.from({length: count}, (_, index) => new ArrowGraphEdge(this, index));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
private registerNodes(): void {
|
|
445
|
+
for (let index = 0; index < this.nodes.length; index++) {
|
|
446
|
+
const node = this.nodes[index];
|
|
447
|
+
const id = this.getNodeIdByIndex(index);
|
|
448
|
+
registerNodeVariants(this.nodeMap, id, node);
|
|
449
|
+
this.nodeIndices.set(node, index);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private registerEdges(): void {
|
|
454
|
+
for (let index = 0; index < this.edges.length; index++) {
|
|
455
|
+
const edge = this.edges[index];
|
|
456
|
+
this.edgeIndices.set(edge, index);
|
|
457
|
+
|
|
458
|
+
const source = this.findNodeById(this.getEdgeSourceIdByIndex(index));
|
|
459
|
+
if (source) {
|
|
460
|
+
edge.addNode(source);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const target = this.findNodeById(this.getEdgeTargetIdByIndex(index));
|
|
464
|
+
if (target) {
|
|
465
|
+
edge.addNode(target);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
class ArrowGraphNode implements NodeInterface {
|
|
472
|
+
public readonly isNode = true;
|
|
473
|
+
|
|
474
|
+
get id(): string | number {
|
|
475
|
+
return this.getId();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
constructor(private readonly graph: ArrowGraph, private readonly index: number) {}
|
|
479
|
+
|
|
480
|
+
getId(): string | number {
|
|
481
|
+
return this.graph.getNodeIdByIndex(this.index);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
getDegree(): number {
|
|
485
|
+
return this.graph.getNodeDegreeByIndex(this.index);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
getInDegree(): number {
|
|
489
|
+
return this.graph.getNodeInDegreeByIndex(this.index);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
getOutDegree(): number {
|
|
493
|
+
return this.graph.getNodeOutDegreeByIndex(this.index);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
getSiblingIds(): (string | number)[] {
|
|
497
|
+
return this.graph.getNodeSiblingIdsByIndex(this.index);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
getConnectedEdges(): EdgeInterface[] {
|
|
501
|
+
return this.graph.getNodeConnectedEdgesByIndex(this.index);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
addConnectedEdges(edge: EdgeInterface | EdgeInterface[]): void {
|
|
505
|
+
const edges = Array.isArray(edge) ? edge : [edge];
|
|
506
|
+
for (const candidate of edges) {
|
|
507
|
+
candidate.addNode(this);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
removeConnectedEdges(edge: EdgeInterface | EdgeInterface[]): void {
|
|
512
|
+
const edges = Array.isArray(edge) ? edge : [edge];
|
|
513
|
+
for (const candidate of edges) {
|
|
514
|
+
candidate.removeNode(this);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
clearConnectedEdges(): void {
|
|
519
|
+
const edges = this.getConnectedEdges();
|
|
520
|
+
for (const edge of edges) {
|
|
521
|
+
edge.removeNode(this);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
getPropertyValue(key: string): unknown {
|
|
526
|
+
return this.graph.getNodePropertyValueByIndex(this.index, key);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
setData(data: Record<string, unknown>): void {
|
|
530
|
+
this.graph.setNodeDataByIndex(this.index, data);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
setDataProperty(key: string, value: unknown): void {
|
|
534
|
+
this.graph.setNodeDataPropertyByIndex(this.index, key, value);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
setState(state: NodeState): void {
|
|
538
|
+
this.graph.setNodeStateByIndex(this.index, state);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
getState(): NodeState {
|
|
542
|
+
return this.graph.getNodeStateByIndex(this.index);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
isSelectable(): boolean {
|
|
546
|
+
return this.graph.isNodeSelectableByIndex(this.index);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
shouldHighlightConnectedEdges(): boolean {
|
|
550
|
+
return this.graph.shouldHighlightConnectedEdgesByIndex(this.index);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
class ArrowGraphEdge implements EdgeInterface {
|
|
555
|
+
public readonly isEdge = true;
|
|
556
|
+
private readonly connectedNodes: Map<string | number, NodeInterface> = new Map();
|
|
557
|
+
|
|
558
|
+
get id(): string | number {
|
|
559
|
+
return this.getId();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
constructor(private readonly graph: ArrowGraph, private readonly index: number) {}
|
|
563
|
+
|
|
564
|
+
getId(): string | number {
|
|
565
|
+
return this.graph.getEdgeIdByIndex(this.index);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
isDirected(): boolean {
|
|
569
|
+
return this.graph.isEdgeDirectedByIndex(this.index);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
getSourceNodeId(): string | number {
|
|
573
|
+
return this.graph.getEdgeSourceIdByIndex(this.index);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
getTargetNodeId(): string | number {
|
|
577
|
+
return this.graph.getEdgeTargetIdByIndex(this.index);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
getConnectedNodes(): NodeInterface[] {
|
|
581
|
+
return [...this.connectedNodes.values()];
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
addNode(node: NodeInterface): void {
|
|
585
|
+
this.connectedNodes.set(node.getId(), node);
|
|
586
|
+
this.graph.registerEdgeForNode(node, this);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
removeNode(node: NodeInterface): void {
|
|
590
|
+
this.connectedNodes.delete(node.getId());
|
|
591
|
+
this.graph.unregisterEdgeForNode(node, this);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
getPropertyValue(key: string): unknown {
|
|
595
|
+
return this.graph.getEdgePropertyValueByIndex(this.index, key);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
setData(data: Record<string, unknown>): void {
|
|
599
|
+
this.graph.setEdgeDataByIndex(this.index, data);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
setDataProperty(key: string, value: unknown): void {
|
|
603
|
+
this.graph.setEdgeDataPropertyByIndex(this.index, key, value);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
setState(state: EdgeState): void {
|
|
607
|
+
this.graph.setEdgeStateByIndex(this.index, state);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
getState(): EdgeState {
|
|
611
|
+
return this.graph.getEdgeStateByIndex(this.index);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function registerNodeVariants(
|
|
616
|
+
map: Map<string | number, ArrowGraphNode>,
|
|
617
|
+
id: string | number,
|
|
618
|
+
node: ArrowGraphNode
|
|
619
|
+
): void {
|
|
620
|
+
map.set(id, node);
|
|
621
|
+
if (typeof id === 'string') {
|
|
622
|
+
const numeric = Number(id);
|
|
623
|
+
if (!Number.isNaN(numeric) && id.trim() !== '') {
|
|
624
|
+
map.set(numeric, node);
|
|
625
|
+
}
|
|
626
|
+
} else {
|
|
627
|
+
map.set(String(id), node);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function coerceBoolean(value: unknown, fallback: boolean): boolean {
|
|
632
|
+
if (typeof value === 'boolean') {
|
|
633
|
+
return value;
|
|
634
|
+
}
|
|
635
|
+
if (typeof value === 'number') {
|
|
636
|
+
return value !== 0;
|
|
637
|
+
}
|
|
638
|
+
if (typeof value === 'string') {
|
|
639
|
+
const normalized = value.trim().toLowerCase();
|
|
640
|
+
if (normalized === 'true' || normalized === '1') {
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
if (normalized === 'false' || normalized === '0' || normalized === '') {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return fallback;
|
|
648
|
+
}
|