@statelyai/graph 0.4.0 → 0.5.0

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 (45) hide show
  1. package/dist/{algorithms-CnTmuX9t.mjs → algorithms-DldwenLt.mjs} +1 -1
  2. package/dist/algorithms.d.mts +1 -1
  3. package/dist/algorithms.mjs +1 -1
  4. package/dist/{converter-C5DlzzHs.mjs → converter-B5CUD0r9.mjs} +2 -2
  5. package/dist/formats/adjacency-list/index.d.mts +1 -1
  6. package/dist/formats/adjacency-list/index.mjs +1 -1
  7. package/dist/formats/converter/index.d.mts +2 -2
  8. package/dist/formats/converter/index.mjs +1 -1
  9. package/dist/formats/cytoscape/index.d.mts +1 -1
  10. package/dist/formats/cytoscape/index.mjs +1 -1
  11. package/dist/formats/d3/index.d.mts +1 -1
  12. package/dist/formats/d3/index.mjs +1 -1
  13. package/dist/formats/dot/index.d.mts +1 -1
  14. package/dist/formats/dot/index.mjs +1 -1
  15. package/dist/formats/edge-list/index.d.mts +1 -1
  16. package/dist/formats/edge-list/index.mjs +1 -1
  17. package/dist/formats/elk/index.d.mts +61 -0
  18. package/dist/formats/elk/index.mjs +176 -0
  19. package/dist/formats/gexf/index.d.mts +1 -1
  20. package/dist/formats/gexf/index.mjs +1 -1
  21. package/dist/formats/gml/index.d.mts +1 -1
  22. package/dist/formats/gml/index.mjs +1 -1
  23. package/dist/formats/graphml/index.d.mts +1 -1
  24. package/dist/formats/graphml/index.mjs +1 -1
  25. package/dist/formats/jgf/index.d.mts +1 -1
  26. package/dist/formats/jgf/index.mjs +1 -1
  27. package/dist/formats/mermaid/index.d.mts +46 -33
  28. package/dist/formats/mermaid/index.mjs +315 -31
  29. package/dist/formats/tgf/index.d.mts +1 -1
  30. package/dist/formats/tgf/index.mjs +1 -1
  31. package/dist/formats/xyflow/index.d.mts +1 -1
  32. package/dist/index.d.mts +1 -1
  33. package/dist/index.mjs +3 -3
  34. package/dist/queries.d.mts +1 -1
  35. package/dist/queries.mjs +1 -1
  36. package/dist/schemas.d.mts +37 -4
  37. package/dist/schemas.mjs +26 -5
  38. package/dist/{types-Bq_fmLwW.d.mts → types-FBZCrmnG.d.mts} +6 -6
  39. package/package.json +7 -1
  40. package/schemas/edge.schema.json +32 -1
  41. package/schemas/graph.schema.json +114 -4
  42. package/schemas/node.schema.json +45 -2
  43. /package/dist/{adjacency-list-Bv4tfiM3.mjs → adjacency-list-fldj-QAL.mjs} +0 -0
  44. /package/dist/{edge-list-R1SUbHwe.mjs → edge-list-Br05wXMg.mjs} +0 -0
  45. /package/dist/{indexing-DitHphT7.mjs → indexing-DyfgLuzw.mjs} +0 -0
@@ -1,4 +1,4 @@
1
- import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdge, o as invalidateIndex, r as indexAddNode, t as getIndex } from "./indexing-DitHphT7.mjs";
1
+ import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdge, o as invalidateIndex, r as indexAddNode, t as getIndex } from "./indexing-DyfgLuzw.mjs";
2
2
 
3
3
  //#region src/graph.ts
4
4
  function resolveNode(config) {
@@ -1,4 +1,4 @@
1
- import { C as TraversalOptions, _ as MSTOptions, b as PathOptions, c as Graph, h as GraphPath, p as GraphNode, t as AllPairsShortestPathsOptions, x as SinglePathOptions } from "./types-Bq_fmLwW.mjs";
1
+ import { C as TraversalOptions, _ as MSTOptions, b as PathOptions, c as Graph, h as GraphPath, p as GraphNode, t as AllPairsShortestPathsOptions, x as SinglePathOptions } from "./types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/algorithms.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { C as isAcyclic, E as joinPaths, S as hasPath, T as isTree, _ as getShortestPaths, a as genPreorders, b as getStronglyConnectedComponents, c as getAllPairsShortestPaths, d as getMinimumSpanningTree, f as getPostorder, g as getShortestPath, h as getPreorders, i as genPostorders, l as getConnectedComponents, m as getPreorder, n as dfs, o as genShortestPaths, p as getPostorders, r as genCycles, s as genSimplePaths, t as bfs, u as getCycles, v as getSimplePath, w as isConnected, x as getTopologicalSort, y as getSimplePaths } from "./algorithms-CnTmuX9t.mjs";
1
+ import { C as isAcyclic, E as joinPaths, S as hasPath, T as isTree, _ as getShortestPaths, a as genPreorders, b as getStronglyConnectedComponents, c as getAllPairsShortestPaths, d as getMinimumSpanningTree, f as getPostorder, g as getShortestPath, h as getPreorders, i as genPostorders, l as getConnectedComponents, m as getPreorder, n as dfs, o as genShortestPaths, p as getPostorders, r as genCycles, s as genSimplePaths, t as bfs, u as getCycles, v as getSimplePath, w as isConnected, x as getTopologicalSort, y as getSimplePaths } from "./algorithms-DldwenLt.mjs";
2
2
 
3
3
  export { bfs, dfs, genCycles, genPostorders, genPreorders, genShortestPaths, genSimplePaths, getAllPairsShortestPaths, getConnectedComponents, getCycles, getMinimumSpanningTree, getPostorder, getPostorders, getPreorder, getPreorders, getShortestPath, getShortestPaths, getSimplePath, getSimplePaths, getStronglyConnectedComponents, getTopologicalSort, hasPath, isAcyclic, isConnected, isTree, joinPaths };
@@ -1,5 +1,5 @@
1
- import { n as toAdjacencyList, t as fromAdjacencyList } from "./adjacency-list-Bv4tfiM3.mjs";
2
- import { n as toEdgeList, t as fromEdgeList } from "./edge-list-R1SUbHwe.mjs";
1
+ import { n as toAdjacencyList, t as fromAdjacencyList } from "./adjacency-list-fldj-QAL.mjs";
2
+ import { n as toEdgeList, t as fromEdgeList } from "./edge-list-Br05wXMg.mjs";
3
3
 
4
4
  //#region src/formats/converter/index.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { c as Graph } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/adjacency-list/index.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { n as toAdjacencyList, t as fromAdjacencyList } from "../../adjacency-list-Bv4tfiM3.mjs";
1
+ import { n as toAdjacencyList, t as fromAdjacencyList } from "../../adjacency-list-fldj-QAL.mjs";
2
2
 
3
3
  export { fromAdjacencyList, toAdjacencyList };
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/converter/index.d.ts
4
4
 
@@ -18,7 +18,7 @@ import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs"
18
18
  * const graph = yamlConverter.from(yaml);
19
19
  * ```
20
20
  */
21
- declare function createFormatConverter<TSerial>(to: (graph: Graph) => TSerial, from: (input: TSerial) => Graph): GraphFormatConverter<TSerial>;
21
+ declare function createFormatConverter<TSerial, N = any, E = any, G = any>(to: (graph: Graph<N, E, G>) => TSerial, from: (input: TSerial) => Graph<N, E, G>): GraphFormatConverter<TSerial, N, E, G>;
22
22
  /**
23
23
  * Bidirectional converter for adjacency-list format (`Record<string, string[]>`).
24
24
  *
@@ -1,3 +1,3 @@
1
- import { n as createFormatConverter, r as edgeListConverter, t as adjacencyListConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter, r as edgeListConverter, t as adjacencyListConverter } from "../../converter-B5CUD0r9.mjs";
2
2
 
3
3
  export { adjacencyListConverter, createFormatConverter, edgeListConverter };
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/cytoscape/index.d.ts
4
4
  interface CytoscapeNode {
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter } from "../../converter-B5CUD0r9.mjs";
2
2
 
3
3
  //#region src/formats/cytoscape/index.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/d3/index.d.ts
4
4
  interface D3Node {
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter } from "../../converter-B5CUD0r9.mjs";
2
2
 
3
3
  //#region src/formats/d3/index.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/dot/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter } from "../../converter-B5CUD0r9.mjs";
2
2
  import parse from "dotparser";
3
3
 
4
4
  //#region src/formats/dot/index.ts
@@ -1,4 +1,4 @@
1
- import { c as Graph } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/edge-list/index.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { n as toEdgeList, t as fromEdgeList } from "../../edge-list-R1SUbHwe.mjs";
1
+ import { n as toEdgeList, t as fromEdgeList } from "../../edge-list-Br05wXMg.mjs";
2
2
 
3
3
  export { fromEdgeList, toEdgeList };
@@ -0,0 +1,61 @@
1
+ import { D as VisualGraphFormatConverter, T as VisualGraph } from "../../types-FBZCrmnG.mjs";
2
+ import { ElkEdge, ElkEdgeSection, ElkExtendedEdge, ElkGraphElement, ElkLabel, ElkNode, ElkNode as ElkNode$1, ElkPoint, ElkPort, ElkPrimitiveEdge, ElkShape, LayoutOptions } from "elkjs/lib/elk-api";
3
+
4
+ //#region src/formats/elk/index.d.ts
5
+
6
+ /**
7
+ * Converts a visual graph to ELK JSON format.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { createVisualGraph } from '@statelyai/graph';
12
+ * import { toELK } from '@statelyai/graph/elk';
13
+ *
14
+ * const graph = createVisualGraph({
15
+ * nodes: [
16
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
17
+ * { id: 'b', x: 200, y: 0, width: 100, height: 50 },
18
+ * ],
19
+ * edges: [{ id: 'e1', sourceId: 'a', targetId: 'b' }],
20
+ * });
21
+ *
22
+ * const elk = toELK(graph);
23
+ * // { id: '', children: [...], edges: [...] }
24
+ * ```
25
+ */
26
+ declare function toELK(graph: VisualGraph): ElkNode$1;
27
+ /**
28
+ * Parses an ELK JSON node into a visual graph.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { fromELK } from '@statelyai/graph/elk';
33
+ *
34
+ * const graph = fromELK({
35
+ * id: 'root',
36
+ * children: [
37
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
38
+ * { id: 'b', x: 200, y: 0, width: 100, height: 50 },
39
+ * ],
40
+ * edges: [{ id: 'e1', sources: ['a'], targets: ['b'] }],
41
+ * });
42
+ *
43
+ * graph.nodes; // [{id: 'a', x: 0, y: 0, ...}, {id: 'b', x: 200, ...}]
44
+ * graph.edges; // [{sourceId: 'a', targetId: 'b', ...}]
45
+ * ```
46
+ */
47
+ declare function fromELK(elkRoot: ElkNode$1): VisualGraph;
48
+ /**
49
+ * Bidirectional converter for ELK JSON format.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * import { elkConverter } from '@statelyai/graph/elk';
54
+ *
55
+ * const elk = elkConverter.to(graph);
56
+ * const roundTripped = elkConverter.from(elk);
57
+ * ```
58
+ */
59
+ declare const elkConverter: VisualGraphFormatConverter<ElkNode$1>;
60
+ //#endregion
61
+ export { type ElkEdge, type ElkEdgeSection, type ElkExtendedEdge, type ElkGraphElement, type ElkLabel, type ElkNode, type ElkPoint, type ElkPort, type ElkPrimitiveEdge, type ElkShape, type LayoutOptions, elkConverter, fromELK, toELK };
@@ -0,0 +1,176 @@
1
+ import { getChildren } from "../../queries.mjs";
2
+
3
+ //#region src/formats/elk/index.ts
4
+ const DIRECTION_TO_ELK = {
5
+ down: "DOWN",
6
+ up: "UP",
7
+ right: "RIGHT",
8
+ left: "LEFT"
9
+ };
10
+ const ELK_TO_DIRECTION = {
11
+ DOWN: "down",
12
+ UP: "up",
13
+ RIGHT: "right",
14
+ LEFT: "left"
15
+ };
16
+ function convertEdge(edge) {
17
+ const elkEdge = {
18
+ id: edge.id,
19
+ sources: [edge.sourceId],
20
+ targets: [edge.targetId]
21
+ };
22
+ if (edge.label) elkEdge.labels = [{ text: edge.label }];
23
+ return elkEdge;
24
+ }
25
+ function convertNode(graph, node) {
26
+ const elkNode = {
27
+ id: node.id,
28
+ x: node.x,
29
+ y: node.y,
30
+ width: node.width,
31
+ height: node.height
32
+ };
33
+ if (node.label) elkNode.labels = [{ text: node.label }];
34
+ const children = getChildren(graph, node.id);
35
+ if (children.length > 0) {
36
+ elkNode.children = children.map((child) => convertNode(graph, child));
37
+ const descendantIds = /* @__PURE__ */ new Set();
38
+ collectDescendants(graph, node.id, descendantIds);
39
+ const innerEdges = graph.edges.filter((e) => descendantIds.has(e.sourceId) && descendantIds.has(e.targetId));
40
+ if (innerEdges.length > 0) elkNode.edges = innerEdges.map(convertEdge);
41
+ }
42
+ return elkNode;
43
+ }
44
+ function collectDescendants(graph, nodeId, set) {
45
+ const children = getChildren(graph, nodeId);
46
+ for (const child of children) {
47
+ set.add(child.id);
48
+ collectDescendants(graph, child.id, set);
49
+ }
50
+ }
51
+ /**
52
+ * Converts a visual graph to ELK JSON format.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { createVisualGraph } from '@statelyai/graph';
57
+ * import { toELK } from '@statelyai/graph/elk';
58
+ *
59
+ * const graph = createVisualGraph({
60
+ * nodes: [
61
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
62
+ * { id: 'b', x: 200, y: 0, width: 100, height: 50 },
63
+ * ],
64
+ * edges: [{ id: 'e1', sourceId: 'a', targetId: 'b' }],
65
+ * });
66
+ *
67
+ * const elk = toELK(graph);
68
+ * // { id: '', children: [...], edges: [...] }
69
+ * ```
70
+ */
71
+ function toELK(graph) {
72
+ const root = { id: graph.id };
73
+ const elkDir = DIRECTION_TO_ELK[graph.direction];
74
+ if (elkDir) root.layoutOptions = { "elk.direction": elkDir };
75
+ const roots = getChildren(graph, null);
76
+ if (roots.length > 0) root.children = roots.map((node) => convertNode(graph, node));
77
+ const allInnerEdgeIds = /* @__PURE__ */ new Set();
78
+ for (const node of graph.nodes) if (getChildren(graph, node.id).length > 0) {
79
+ const descendantIds = /* @__PURE__ */ new Set();
80
+ collectDescendants(graph, node.id, descendantIds);
81
+ for (const edge of graph.edges) if (descendantIds.has(edge.sourceId) && descendantIds.has(edge.targetId)) allInnerEdgeIds.add(edge.id);
82
+ }
83
+ const rootEdges = graph.edges.filter((e) => !allInnerEdgeIds.has(e.id));
84
+ if (rootEdges.length > 0) root.edges = rootEdges.map(convertEdge);
85
+ return root;
86
+ }
87
+ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx) {
88
+ if (elkNode.children) for (const child of elkNode.children) {
89
+ const label = child.labels?.[0]?.text ?? "";
90
+ const node = {
91
+ type: "node",
92
+ id: child.id,
93
+ parentId,
94
+ initialNodeId: null,
95
+ label,
96
+ data: void 0,
97
+ x: child.x ?? 0,
98
+ y: child.y ?? 0,
99
+ width: child.width ?? 0,
100
+ height: child.height ?? 0
101
+ };
102
+ nodes.push(node);
103
+ flattenElkNodes(child, child.id, nodes, edges, edgeIdx);
104
+ }
105
+ if (elkNode.edges) for (const elkEdge of elkNode.edges) for (const source of elkEdge.sources) for (const target of elkEdge.targets) {
106
+ const edge = {
107
+ type: "edge",
108
+ id: elkEdge.id ?? `e${edgeIdx.value++}`,
109
+ sourceId: source,
110
+ targetId: target,
111
+ label: elkEdge.labels?.[0]?.text ?? "",
112
+ data: void 0,
113
+ x: 0,
114
+ y: 0,
115
+ width: 0,
116
+ height: 0
117
+ };
118
+ edges.push(edge);
119
+ }
120
+ }
121
+ /**
122
+ * Parses an ELK JSON node into a visual graph.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * import { fromELK } from '@statelyai/graph/elk';
127
+ *
128
+ * const graph = fromELK({
129
+ * id: 'root',
130
+ * children: [
131
+ * { id: 'a', x: 0, y: 0, width: 100, height: 50 },
132
+ * { id: 'b', x: 200, y: 0, width: 100, height: 50 },
133
+ * ],
134
+ * edges: [{ id: 'e1', sources: ['a'], targets: ['b'] }],
135
+ * });
136
+ *
137
+ * graph.nodes; // [{id: 'a', x: 0, y: 0, ...}, {id: 'b', x: 200, ...}]
138
+ * graph.edges; // [{sourceId: 'a', targetId: 'b', ...}]
139
+ * ```
140
+ */
141
+ function fromELK(elkRoot) {
142
+ const nodes = [];
143
+ const edges = [];
144
+ flattenElkNodes(elkRoot, null, nodes, edges, { value: 0 });
145
+ const seenEdges = /* @__PURE__ */ new Map();
146
+ for (const edge of edges) if (!seenEdges.has(edge.id)) seenEdges.set(edge.id, edge);
147
+ const elkDir = elkRoot.layoutOptions?.["elk.direction"];
148
+ const direction = (elkDir ? ELK_TO_DIRECTION[elkDir] : void 0) ?? "down";
149
+ return {
150
+ id: elkRoot.id,
151
+ type: "directed",
152
+ initialNodeId: null,
153
+ nodes,
154
+ edges: [...seenEdges.values()],
155
+ data: void 0,
156
+ direction
157
+ };
158
+ }
159
+ /**
160
+ * Bidirectional converter for ELK JSON format.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * import { elkConverter } from '@statelyai/graph/elk';
165
+ *
166
+ * const elk = elkConverter.to(graph);
167
+ * const roundTripped = elkConverter.from(elk);
168
+ * ```
169
+ */
170
+ const elkConverter = {
171
+ to: toELK,
172
+ from: fromELK
173
+ };
174
+
175
+ //#endregion
176
+ export { elkConverter, fromELK, toELK };
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/gexf/index.d.ts
4
4
  declare function toGEXF(graph: Graph): string;
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter } from "../../converter-B5CUD0r9.mjs";
2
2
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
3
3
 
4
4
  //#region src/formats/gexf/index.ts
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/gml/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter } from "../../converter-B5CUD0r9.mjs";
2
2
 
3
3
  //#region src/formats/gml/index.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/graphml/index.d.ts
4
4
  declare function toGraphML(graph: Graph): string;
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter } from "../../converter-B5CUD0r9.mjs";
2
2
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
3
3
 
4
4
  //#region src/formats/graphml/index.ts
@@ -1,4 +1,4 @@
1
- import { c as Graph, f as GraphFormatConverter } from "../../types-Bq_fmLwW.mjs";
1
+ import { c as Graph, f as GraphFormatConverter } from "../../types-FBZCrmnG.mjs";
2
2
 
3
3
  //#region src/formats/jgf/index.d.ts
4
4
  interface JGFNode {
@@ -1,4 +1,4 @@
1
- import { n as createFormatConverter } from "../../converter-C5DlzzHs.mjs";
1
+ import { n as createFormatConverter } from "../../converter-B5CUD0r9.mjs";
2
2
 
3
3
  //#region src/formats/jgf/index.ts
4
4
  /**