@loaders.gl/graphs 4.5.3 → 5.0.0-alpha.10
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/LICENSE +14 -3
- package/README.md +122 -10
- package/dist/dot-loader-types.d.ts +40 -3
- package/dist/dot-loader-types.d.ts.map +1 -1
- package/dist/dot-loader-types.js +8 -2
- package/dist/dot-loader-types.js.map +1 -1
- package/dist/dot-loader.cjs +1449 -108
- package/dist/dot-loader.cjs.map +3 -4
- package/dist/dot-loader.d.ts +11 -6
- package/dist/dot-loader.d.ts.map +1 -1
- package/dist/dot-loader.js +3 -3
- package/dist/dot-loader.js.map +1 -1
- package/dist/dot-types.d.ts +6 -1
- package/dist/dot-types.d.ts.map +1 -1
- package/dist/gexf-loader-types.d.ts +53 -0
- package/dist/gexf-loader-types.d.ts.map +1 -0
- package/dist/gexf-loader-types.js +26 -0
- package/dist/gexf-loader-types.js.map +1 -0
- package/dist/gexf-loader.cjs +1304 -0
- package/dist/gexf-loader.cjs.map +6 -0
- package/dist/gexf-loader.d.ts +24 -0
- package/dist/gexf-loader.d.ts.map +1 -0
- package/dist/gexf-loader.js +13 -0
- package/dist/gexf-loader.js.map +1 -0
- package/dist/gexf-types.d.ts +16 -0
- package/dist/gexf-types.d.ts.map +1 -0
- package/dist/gexf-types.js +5 -0
- package/dist/gexf-types.js.map +1 -0
- package/dist/graph-types.d.ts +28 -0
- package/dist/graph-types.d.ts.map +1 -1
- package/dist/graphml-loader-types.d.ts +39 -3
- package/dist/graphml-loader-types.d.ts.map +1 -1
- package/dist/graphml-loader-types.js +8 -2
- package/dist/graphml-loader-types.js.map +1 -1
- package/dist/graphml-loader.cjs +1006 -61
- package/dist/graphml-loader.cjs.map +3 -4
- package/dist/graphml-loader.d.ts +11 -6
- package/dist/graphml-loader.d.ts.map +1 -1
- package/dist/graphml-loader.js +3 -3
- package/dist/graphml-loader.js.map +1 -1
- package/dist/index.cjs +1452 -470
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/build-graph-tables.d.ts +4 -0
- package/dist/lib/build-graph-tables.d.ts.map +1 -0
- package/dist/lib/build-graph-tables.js +143 -0
- package/dist/lib/build-graph-tables.js.map +1 -0
- package/dist/lib/parse-dot.d.ts +3 -1
- package/dist/lib/parse-dot.d.ts.map +1 -1
- package/dist/lib/parse-dot.js +41 -24
- package/dist/lib/parse-dot.js.map +1 -1
- package/dist/lib/parse-gexf.d.ts +5 -0
- package/dist/lib/parse-gexf.d.ts.map +1 -0
- package/dist/lib/parse-gexf.js +380 -0
- package/dist/lib/parse-gexf.js.map +1 -0
- package/dist/lib/parse-graphml.d.ts +2 -1
- package/dist/lib/parse-graphml.d.ts.map +1 -1
- package/dist/lib/parse-graphml.js +50 -27
- package/dist/lib/parse-graphml.js.map +1 -1
- package/package.json +15 -6
- package/src/dot-loader-types.ts +21 -4
- package/src/dot-loader.ts +7 -4
- package/src/dot-types.ts +7 -1
- package/src/gexf-loader-types.ts +41 -0
- package/src/gexf-loader.ts +17 -0
- package/src/gexf-types.ts +21 -0
- package/src/graph-types.ts +35 -0
- package/src/graphml-loader-types.ts +20 -4
- package/src/graphml-loader.ts +7 -4
- package/src/index.ts +11 -2
- package/src/lib/build-graph-tables.ts +208 -0
- package/src/lib/parse-dot.ts +47 -24
- package/src/lib/parse-gexf.ts +388 -0
- package/src/lib/parse-graphml.ts +86 -34
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res, err) => function __init() {
|
|
9
|
+
if (err) throw err[0];
|
|
10
|
+
try {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
throw err = [e], e;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
6
16
|
var __export = (target, all) => {
|
|
7
17
|
for (var name in all)
|
|
8
18
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -15,46 +25,458 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
25
|
}
|
|
16
26
|
return to;
|
|
17
27
|
};
|
|
28
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
29
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
30
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
31
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
32
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
33
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
34
|
+
mod
|
|
35
|
+
));
|
|
18
36
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
37
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
// src/graphml-loader-types.ts
|
|
39
|
+
async function preload() {
|
|
40
|
+
const { GraphMLLoaderWithParser: GraphMLLoaderWithParser2 } = await Promise.resolve().then(() => (init_graphml_loader(), graphml_loader_exports));
|
|
41
|
+
return GraphMLLoaderWithParser2;
|
|
42
|
+
}
|
|
43
|
+
var VERSION, GraphMLLoader;
|
|
44
|
+
var init_graphml_loader_types = __esm({
|
|
45
|
+
"src/graphml-loader-types.ts"() {
|
|
46
|
+
"use strict";
|
|
47
|
+
VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest";
|
|
48
|
+
GraphMLLoader = {
|
|
49
|
+
name: "GraphML",
|
|
50
|
+
id: "graphml",
|
|
51
|
+
module: "graphs",
|
|
52
|
+
version: VERSION,
|
|
53
|
+
extensions: ["graphml"],
|
|
54
|
+
mimeTypes: ["application/graphml+xml"],
|
|
55
|
+
text: true,
|
|
56
|
+
worker: false,
|
|
57
|
+
dataType: null,
|
|
58
|
+
batchType: null,
|
|
59
|
+
options: { graphml: { shape: "arrow-table" } },
|
|
60
|
+
preload
|
|
61
|
+
};
|
|
62
|
+
}
|
|
25
63
|
});
|
|
26
|
-
module.exports = __toCommonJS(dist_exports);
|
|
27
64
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
65
|
+
// ../schema-utils/src/lib/schema/convert-arrow-schema.ts
|
|
66
|
+
function convertArrowToSchema(arrowSchema) {
|
|
67
|
+
return serializeArrowSchema(arrowSchema);
|
|
68
|
+
}
|
|
69
|
+
function serializeArrowSchema(arrowSchema) {
|
|
70
|
+
return {
|
|
71
|
+
fields: arrowSchema.fields.map((arrowField) => serializeArrowField(arrowField)),
|
|
72
|
+
metadata: serializeArrowMetadata(arrowSchema.metadata)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function serializeArrowMetadata(arrowMetadata) {
|
|
76
|
+
return Object.fromEntries(arrowMetadata);
|
|
77
|
+
}
|
|
78
|
+
function serializeArrowField(field) {
|
|
79
|
+
return {
|
|
80
|
+
name: field.name,
|
|
81
|
+
type: serializeArrowType(field.type),
|
|
82
|
+
nullable: field.nullable,
|
|
83
|
+
metadata: serializeArrowMetadata(field.metadata)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function serializeArrowType(arrowType) {
|
|
87
|
+
if (isArrowViewType(arrowType, "BinaryView")) {
|
|
88
|
+
return "binary-view";
|
|
89
|
+
}
|
|
90
|
+
if (isArrowViewType(arrowType, "Utf8View")) {
|
|
91
|
+
return "utf8-view";
|
|
92
|
+
}
|
|
93
|
+
if (isOptionalArrowType(arrowType, "LargeList")) {
|
|
94
|
+
const largeListType = arrowType;
|
|
95
|
+
return {
|
|
96
|
+
type: "large-list",
|
|
97
|
+
children: [serializeArrowField(largeListType.valueField)]
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
switch (arrowType.constructor) {
|
|
101
|
+
case arrow.Null:
|
|
102
|
+
return "null";
|
|
103
|
+
case arrow.Binary:
|
|
104
|
+
return "binary";
|
|
105
|
+
case arrow.FixedSizeBinary:
|
|
106
|
+
return {
|
|
107
|
+
type: "fixed-size-binary",
|
|
108
|
+
byteWidth: arrowType.byteWidth
|
|
109
|
+
};
|
|
110
|
+
case arrow.Bool:
|
|
111
|
+
return "bool";
|
|
112
|
+
case arrow.Int:
|
|
113
|
+
const intType = arrowType;
|
|
114
|
+
return `${intType.isSigned ? "" : "u"}int${intType.bitWidth}`;
|
|
115
|
+
case arrow.Int8:
|
|
116
|
+
return "int8";
|
|
117
|
+
case arrow.Int16:
|
|
118
|
+
return "int16";
|
|
119
|
+
case arrow.Int32:
|
|
120
|
+
return "int32";
|
|
121
|
+
case arrow.Int64:
|
|
122
|
+
return "int64";
|
|
123
|
+
case arrow.Uint8:
|
|
124
|
+
return "uint8";
|
|
125
|
+
case arrow.Uint16:
|
|
126
|
+
return "uint16";
|
|
127
|
+
case arrow.Uint32:
|
|
128
|
+
return "uint32";
|
|
129
|
+
case arrow.Uint64:
|
|
130
|
+
return "uint64";
|
|
131
|
+
case arrow.Float:
|
|
132
|
+
const precision = arrowType.precision;
|
|
133
|
+
switch (precision) {
|
|
134
|
+
case arrow.Precision.HALF:
|
|
135
|
+
return "float16";
|
|
136
|
+
case arrow.Precision.SINGLE:
|
|
137
|
+
return "float32";
|
|
138
|
+
case arrow.Precision.DOUBLE:
|
|
139
|
+
return "float64";
|
|
140
|
+
default:
|
|
141
|
+
return "float16";
|
|
142
|
+
}
|
|
143
|
+
case arrow.Float16:
|
|
144
|
+
return "float16";
|
|
145
|
+
case arrow.Float32:
|
|
146
|
+
return "float32";
|
|
147
|
+
case arrow.Float64:
|
|
148
|
+
return "float64";
|
|
149
|
+
case arrow.Utf8:
|
|
150
|
+
return "utf8";
|
|
151
|
+
case arrow.Decimal:
|
|
152
|
+
const decimal = arrowType;
|
|
153
|
+
return {
|
|
154
|
+
type: "decimal",
|
|
155
|
+
bitWidth: decimal.bitWidth,
|
|
156
|
+
precision: decimal.precision,
|
|
157
|
+
scale: decimal.scale
|
|
158
|
+
};
|
|
159
|
+
case arrow.Date_:
|
|
160
|
+
const dateUnit = arrowType.unit;
|
|
161
|
+
return dateUnit === arrow.DateUnit.DAY ? "date-day" : "date-millisecond";
|
|
162
|
+
case arrow.DateDay:
|
|
163
|
+
return "date-day";
|
|
164
|
+
case arrow.DateMillisecond:
|
|
165
|
+
return "date-millisecond";
|
|
166
|
+
case arrow.Time:
|
|
167
|
+
const timeUnit = arrowType.unit;
|
|
168
|
+
switch (timeUnit) {
|
|
169
|
+
case arrow.TimeUnit.SECOND:
|
|
170
|
+
return "time-second";
|
|
171
|
+
case arrow.TimeUnit.MILLISECOND:
|
|
172
|
+
return "time-millisecond";
|
|
173
|
+
case arrow.TimeUnit.MICROSECOND:
|
|
174
|
+
return "time-microsecond";
|
|
175
|
+
case arrow.TimeUnit.NANOSECOND:
|
|
176
|
+
return "time-nanosecond";
|
|
177
|
+
default:
|
|
178
|
+
return "time-second";
|
|
179
|
+
}
|
|
180
|
+
case arrow.TimeMillisecond:
|
|
181
|
+
return "time-millisecond";
|
|
182
|
+
case arrow.TimeSecond:
|
|
183
|
+
return "time-second";
|
|
184
|
+
case arrow.TimeMicrosecond:
|
|
185
|
+
return "time-microsecond";
|
|
186
|
+
case arrow.TimeNanosecond:
|
|
187
|
+
return "time-nanosecond";
|
|
188
|
+
case arrow.Timestamp:
|
|
189
|
+
const timeStampUnit = arrowType.unit;
|
|
190
|
+
switch (timeStampUnit) {
|
|
191
|
+
case arrow.TimeUnit.SECOND:
|
|
192
|
+
return "timestamp-second";
|
|
193
|
+
case arrow.TimeUnit.MILLISECOND:
|
|
194
|
+
return "timestamp-millisecond";
|
|
195
|
+
case arrow.TimeUnit.MICROSECOND:
|
|
196
|
+
return "timestamp-microsecond";
|
|
197
|
+
case arrow.TimeUnit.NANOSECOND:
|
|
198
|
+
return "timestamp-nanosecond";
|
|
199
|
+
default:
|
|
200
|
+
return "timestamp-second";
|
|
201
|
+
}
|
|
202
|
+
case arrow.TimestampSecond:
|
|
203
|
+
return "timestamp-second";
|
|
204
|
+
case arrow.TimestampMillisecond:
|
|
205
|
+
return "timestamp-millisecond";
|
|
206
|
+
case arrow.TimestampMicrosecond:
|
|
207
|
+
return "timestamp-microsecond";
|
|
208
|
+
case arrow.TimestampNanosecond:
|
|
209
|
+
return "timestamp-nanosecond";
|
|
210
|
+
case arrow.Interval:
|
|
211
|
+
const intervalUnit = arrowType.unit;
|
|
212
|
+
switch (intervalUnit) {
|
|
213
|
+
case arrow.IntervalUnit.DAY_TIME:
|
|
214
|
+
return "interval-daytime";
|
|
215
|
+
case arrow.IntervalUnit.YEAR_MONTH:
|
|
216
|
+
return "interval-yearmonth";
|
|
217
|
+
default:
|
|
218
|
+
return "interval-daytime";
|
|
219
|
+
}
|
|
220
|
+
case arrow.IntervalDayTime:
|
|
221
|
+
return "interval-daytime";
|
|
222
|
+
case arrow.IntervalYearMonth:
|
|
223
|
+
return "interval-yearmonth";
|
|
224
|
+
case arrow.Map_:
|
|
225
|
+
const mapType = arrowType;
|
|
226
|
+
return {
|
|
227
|
+
type: "map",
|
|
228
|
+
keysSorted: mapType.keysSorted,
|
|
229
|
+
children: mapType.children.map((arrowField) => serializeArrowField(arrowField))
|
|
230
|
+
};
|
|
231
|
+
case arrow.List:
|
|
232
|
+
const listType = arrowType;
|
|
233
|
+
const listField = listType.valueField;
|
|
234
|
+
return {
|
|
235
|
+
type: "list",
|
|
236
|
+
children: [serializeArrowField(listField)]
|
|
237
|
+
};
|
|
238
|
+
case arrow.FixedSizeList:
|
|
239
|
+
const fixedSizeList = arrowType;
|
|
240
|
+
return {
|
|
241
|
+
type: "fixed-size-list",
|
|
242
|
+
listSize: fixedSizeList.listSize,
|
|
243
|
+
children: [serializeArrowField(fixedSizeList.children[0])]
|
|
244
|
+
};
|
|
245
|
+
case arrow.Struct:
|
|
246
|
+
const structType = arrowType;
|
|
247
|
+
return {
|
|
248
|
+
type: "struct",
|
|
249
|
+
children: structType.children.map((arrowField) => serializeArrowField(arrowField))
|
|
250
|
+
};
|
|
251
|
+
case arrow.SparseUnion:
|
|
252
|
+
case arrow.DenseUnion:
|
|
253
|
+
const unionType = arrowType;
|
|
254
|
+
return {
|
|
255
|
+
type: unionType instanceof arrow.DenseUnion ? "dense-union" : "sparse-union",
|
|
256
|
+
typeIds: unionType.typeIds,
|
|
257
|
+
children: unionType.children.map((arrowField) => serializeArrowField(arrowField)),
|
|
258
|
+
typeIdToChildIndex: { ...unionType.typeIdToChildIndex }
|
|
259
|
+
};
|
|
260
|
+
case arrow.Dictionary:
|
|
261
|
+
const dictionaryType = arrowType;
|
|
262
|
+
return {
|
|
263
|
+
type: "dictionary",
|
|
264
|
+
id: dictionaryType.id,
|
|
265
|
+
indices: serializeArrowDictionaryKeyType(dictionaryType.indices),
|
|
266
|
+
dictionary: serializeArrowType(dictionaryType.dictionary),
|
|
267
|
+
isOrdered: dictionaryType.isOrdered
|
|
268
|
+
};
|
|
269
|
+
default:
|
|
270
|
+
throw new Error(`arrow type not supported: ${arrowType.constructor.name}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function getOptionalArrowConstructor(typeName) {
|
|
274
|
+
return arrow[typeName];
|
|
275
|
+
}
|
|
276
|
+
function isArrowViewType(arrowType, viewTypeName) {
|
|
277
|
+
const constructor = getOptionalArrowConstructor(viewTypeName);
|
|
278
|
+
if (constructor && arrowType instanceof constructor) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
const guards = arrow.DataType;
|
|
282
|
+
const guard = viewTypeName === "BinaryView" ? guards.isBinaryView : guards.isUtf8View;
|
|
283
|
+
return Boolean(guard == null ? void 0 : guard(arrowType));
|
|
284
|
+
}
|
|
285
|
+
function isOptionalArrowType(arrowType, typeName) {
|
|
286
|
+
const constructor = getOptionalArrowConstructor(typeName);
|
|
287
|
+
return Boolean(constructor && arrowType instanceof constructor);
|
|
288
|
+
}
|
|
289
|
+
function serializeArrowDictionaryKeyType(arrowType) {
|
|
290
|
+
if (arrowType instanceof arrow.Int) {
|
|
291
|
+
const prefix = arrowType.isSigned ? "int" : "uint";
|
|
292
|
+
const keyType = `${prefix}${arrowType.bitWidth}`;
|
|
293
|
+
if (isSchemaDictionaryKeyType(keyType)) {
|
|
294
|
+
return keyType;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
switch (arrowType.constructor) {
|
|
298
|
+
case arrow.Int8:
|
|
299
|
+
return "int8";
|
|
300
|
+
case arrow.Int16:
|
|
301
|
+
return "int16";
|
|
302
|
+
case arrow.Int32:
|
|
303
|
+
return "int32";
|
|
304
|
+
case arrow.Uint8:
|
|
305
|
+
return "uint8";
|
|
306
|
+
case arrow.Uint16:
|
|
307
|
+
return "uint16";
|
|
308
|
+
case arrow.Uint32:
|
|
309
|
+
return "uint32";
|
|
310
|
+
default:
|
|
311
|
+
throw new Error(`arrow dictionary index type not supported: ${arrowType.constructor.name}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function isSchemaDictionaryKeyType(keyType) {
|
|
315
|
+
return ["int8", "int16", "int32", "uint8", "uint16", "uint32"].includes(keyType);
|
|
316
|
+
}
|
|
317
|
+
var arrow;
|
|
318
|
+
var init_convert_arrow_schema = __esm({
|
|
319
|
+
"../schema-utils/src/lib/schema/convert-arrow-schema.ts"() {
|
|
320
|
+
"use strict";
|
|
321
|
+
arrow = __toESM(require("apache-arrow"), 1);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// ../schema-utils/src/index.ts
|
|
326
|
+
var init_src = __esm({
|
|
327
|
+
"../schema-utils/src/index.ts"() {
|
|
328
|
+
"use strict";
|
|
329
|
+
init_convert_arrow_schema();
|
|
330
|
+
}
|
|
331
|
+
});
|
|
43
332
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
333
|
+
// src/lib/build-graph-tables.ts
|
|
334
|
+
function buildGraphTables(nodes, edges, shape, declarations) {
|
|
335
|
+
if (shape === "plain-graph-data") {
|
|
336
|
+
return { shape, nodes: Array.from(nodes), edges: Array.from(edges) };
|
|
337
|
+
}
|
|
338
|
+
if (shape !== "arrow-table" && shape !== "object-row-table") {
|
|
339
|
+
throw new Error(`Unsupported graph shape: ${shape}`);
|
|
340
|
+
}
|
|
341
|
+
const nodeTable = buildGraphTable(nodes, false, shape, declarations == null ? void 0 : declarations.nodes);
|
|
342
|
+
const edgeTable = buildGraphTable(edges, true, shape, declarations == null ? void 0 : declarations.edges);
|
|
343
|
+
return {
|
|
344
|
+
shape: "tables",
|
|
345
|
+
tables: [
|
|
346
|
+
{ name: "nodes", table: nodeTable },
|
|
347
|
+
{ name: "edges", table: edgeTable }
|
|
348
|
+
]
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function buildGraphTable(records, isEdge, shape, declarations) {
|
|
352
|
+
const columnNames = isEdge ? ["id", "sourceId", "targetId", "directed", "label", "attributes"] : ["id", "label", "attributes"];
|
|
353
|
+
const columns = new Map(columnNames.map((name) => [name, []]));
|
|
354
|
+
for (const record of records) {
|
|
355
|
+
for (const name of columnNames) {
|
|
356
|
+
const value = record[name];
|
|
357
|
+
columns.get(name).push(
|
|
358
|
+
name === "id" || name === "sourceId" || name === "targetId" ? String(value) : value ?? null
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (shape === "object-row-table") {
|
|
363
|
+
return {
|
|
364
|
+
shape,
|
|
365
|
+
data: columns.get("id").map(
|
|
366
|
+
(_, index) => Object.fromEntries(columnNames.map((name) => [name, columns.get(name)[index]]))
|
|
367
|
+
)
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
const vectors = /* @__PURE__ */ Object.create(null);
|
|
371
|
+
for (const [name, values] of columns) {
|
|
372
|
+
const type = name === "attributes" ? inferAttributeType(values, declarations) : name === "directed" ? new import_apache_arrow.Bool() : new import_apache_arrow.Utf8();
|
|
373
|
+
vectors[name] = (0, import_apache_arrow.vectorFromArray)(
|
|
374
|
+
values.map((value) => normalizeValue(value, type)),
|
|
375
|
+
type
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
const data = new import_apache_arrow.Table(vectors);
|
|
379
|
+
return { shape, schema: convertArrowToSchema(data.schema), data };
|
|
380
|
+
}
|
|
381
|
+
function inferAttributeType(values, declarations) {
|
|
382
|
+
const names = new Set(declarations == null ? void 0 : declarations.keys());
|
|
383
|
+
for (const value of values) {
|
|
384
|
+
if (value && typeof value === "object") {
|
|
385
|
+
for (const name of Object.keys(value)) {
|
|
386
|
+
names.add(name);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return new import_apache_arrow.Struct(
|
|
391
|
+
Array.from(names, (name) => {
|
|
392
|
+
const fieldValues = values.map(
|
|
393
|
+
(value) => (value == null ? void 0 : value[name]) ?? null
|
|
394
|
+
);
|
|
395
|
+
return new import_apache_arrow.Field(name, inferValueType(fieldValues, declarations == null ? void 0 : declarations.get(name)), true);
|
|
396
|
+
})
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
function inferValueType(values, declaration) {
|
|
400
|
+
const present = values.filter((value) => value !== null && value !== void 0);
|
|
401
|
+
if (declaration && typeof declaration === "object") {
|
|
402
|
+
if (!present.every((value) => Array.isArray(value))) return new import_apache_arrow.Utf8();
|
|
403
|
+
return new import_apache_arrow.List(
|
|
404
|
+
new import_apache_arrow.Field(
|
|
405
|
+
"item",
|
|
406
|
+
inferValueType(
|
|
407
|
+
present.flatMap((value) => value),
|
|
408
|
+
declaration.list
|
|
409
|
+
),
|
|
410
|
+
true
|
|
411
|
+
)
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
if (declaration) {
|
|
415
|
+
const declaredTypes = {
|
|
416
|
+
boolean: new import_apache_arrow.Bool(),
|
|
417
|
+
int: new import_apache_arrow.Int32(),
|
|
418
|
+
long: new import_apache_arrow.Int64(),
|
|
419
|
+
float: new import_apache_arrow.Float32(),
|
|
420
|
+
double: new import_apache_arrow.Float64(),
|
|
421
|
+
string: new import_apache_arrow.Utf8()
|
|
422
|
+
};
|
|
423
|
+
const valid = present.every((value) => {
|
|
424
|
+
switch (declaration) {
|
|
425
|
+
case "boolean":
|
|
426
|
+
return typeof value === "boolean";
|
|
427
|
+
case "int":
|
|
428
|
+
return typeof value === "number" && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647;
|
|
429
|
+
case "long":
|
|
430
|
+
return typeof value === "bigint" && value >= -(2n ** 63n) && value < 2n ** 63n;
|
|
431
|
+
case "float":
|
|
432
|
+
case "double":
|
|
433
|
+
return typeof value === "number";
|
|
434
|
+
case "string":
|
|
435
|
+
return typeof value === "string";
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
return valid ? declaredTypes[declaration] : new import_apache_arrow.Utf8();
|
|
439
|
+
}
|
|
440
|
+
if (present.length && present.every((value) => typeof value === "boolean")) return new import_apache_arrow.Bool();
|
|
441
|
+
if (present.length && present.every((value) => typeof value === "number")) return new import_apache_arrow.Float64();
|
|
442
|
+
if (present.length && present.every((value) => Array.isArray(value))) {
|
|
443
|
+
return new import_apache_arrow.List(
|
|
444
|
+
new import_apache_arrow.Field("item", inferValueType(present.flatMap((value) => value)), true)
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
if (present.length && present.every((value) => typeof value === "object" && !Array.isArray(value))) {
|
|
448
|
+
return inferAttributeType(present);
|
|
449
|
+
}
|
|
450
|
+
return new import_apache_arrow.Utf8();
|
|
451
|
+
}
|
|
452
|
+
function normalizeValue(value, type) {
|
|
453
|
+
if (value === null || value === void 0) return null;
|
|
454
|
+
if (type instanceof import_apache_arrow.Struct) {
|
|
455
|
+
return Object.fromEntries(
|
|
456
|
+
type.children.map((field) => [
|
|
457
|
+
field.name,
|
|
458
|
+
normalizeValue(value[field.name], field.type)
|
|
459
|
+
])
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
if (type instanceof import_apache_arrow.List)
|
|
463
|
+
return value.map((item) => normalizeValue(item, type.valueType));
|
|
464
|
+
if (type instanceof import_apache_arrow.Utf8 && typeof value !== "string") {
|
|
465
|
+
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
466
|
+
}
|
|
467
|
+
return value;
|
|
468
|
+
}
|
|
469
|
+
var import_apache_arrow;
|
|
470
|
+
var init_build_graph_tables = __esm({
|
|
471
|
+
"src/lib/build-graph-tables.ts"() {
|
|
472
|
+
"use strict";
|
|
473
|
+
import_apache_arrow = require("apache-arrow");
|
|
474
|
+
init_src();
|
|
475
|
+
}
|
|
56
476
|
});
|
|
57
|
-
|
|
477
|
+
|
|
478
|
+
// src/lib/parse-graphml.ts
|
|
479
|
+
function parseGraphML(graphml, shape = "plain-graph-data") {
|
|
58
480
|
const xmlText = decodeGraphML(graphml);
|
|
59
481
|
const document = graphmlParser.parse(xmlText);
|
|
60
482
|
const graphmlRoot = getGraphMLRoot(document);
|
|
@@ -65,17 +487,38 @@ function parseGraphML(graphml) {
|
|
|
65
487
|
if (!graphElement) {
|
|
66
488
|
throw new Error("GraphML document does not contain a <graph> element.");
|
|
67
489
|
}
|
|
68
|
-
const keyDefinitions = collectKeyDefinitions(
|
|
490
|
+
const keyDefinitions = collectKeyDefinitions(
|
|
491
|
+
graphmlRoot,
|
|
492
|
+
graphElement,
|
|
493
|
+
shape !== "plain-graph-data"
|
|
494
|
+
);
|
|
69
495
|
const defaultDirected = parseEdgeDefault(graphElement[`${XML_ATTRIBUTE_PREFIX}edgedefault`]);
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
496
|
+
const declarations = { nodes: /* @__PURE__ */ new Map(), edges: /* @__PURE__ */ new Map() };
|
|
497
|
+
for (const definition of keyDefinitions.values()) {
|
|
498
|
+
if (definition.domain === "all" || definition.domain === "node")
|
|
499
|
+
declarations.nodes.set(definition.name, definition.type);
|
|
500
|
+
if (definition.domain === "all" || definition.domain === "edge")
|
|
501
|
+
declarations.edges.set(definition.name, definition.type);
|
|
502
|
+
}
|
|
503
|
+
return buildGraphTables(
|
|
504
|
+
iterateNodes(graphElement.node, keyDefinitions),
|
|
505
|
+
iterateEdges(graphElement.edge, keyDefinitions, defaultDirected),
|
|
506
|
+
shape,
|
|
507
|
+
declarations
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
function* iterateNodes(nodes, definitions) {
|
|
511
|
+
for (const node of normalizeArray(nodes)) {
|
|
512
|
+
const record = parseNode(node, definitions);
|
|
513
|
+
if (record) yield record;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function* iterateEdges(edges, definitions, directed) {
|
|
517
|
+
let index = 0;
|
|
518
|
+
for (const edge of normalizeArray(edges)) {
|
|
519
|
+
const record = parseEdge(edge, index++, definitions, directed);
|
|
520
|
+
if (record) yield record;
|
|
521
|
+
}
|
|
79
522
|
}
|
|
80
523
|
function decodeGraphML(graphml) {
|
|
81
524
|
if (typeof graphml === "string") {
|
|
@@ -98,16 +541,21 @@ function getGraphMLRoot(document) {
|
|
|
98
541
|
}
|
|
99
542
|
function getGraphElement(graphmlRoot) {
|
|
100
543
|
const graph = graphmlRoot.graph;
|
|
544
|
+
if (typeof graph === "string" && !graph.trim()) {
|
|
545
|
+
return {};
|
|
546
|
+
}
|
|
101
547
|
if (!graph) {
|
|
102
548
|
return null;
|
|
103
549
|
}
|
|
104
550
|
if (Array.isArray(graph)) {
|
|
105
|
-
const firstGraph = graph.find(
|
|
106
|
-
|
|
551
|
+
const firstGraph = graph.find(
|
|
552
|
+
(entry) => isObject(entry) || typeof entry === "string" && !entry.trim()
|
|
553
|
+
);
|
|
554
|
+
return isObject(firstGraph) ? firstGraph : typeof firstGraph === "string" ? {} : null;
|
|
107
555
|
}
|
|
108
556
|
return isObject(graph) ? graph : null;
|
|
109
557
|
}
|
|
110
|
-
function collectKeyDefinitions(graphmlRoot, graphElement) {
|
|
558
|
+
function collectKeyDefinitions(graphmlRoot, graphElement, lossless) {
|
|
111
559
|
const keys = /* @__PURE__ */ new Map();
|
|
112
560
|
for (const candidate of [
|
|
113
561
|
...normalizeArray(graphmlRoot.key),
|
|
@@ -120,8 +568,8 @@ function collectKeyDefinitions(graphmlRoot, graphElement) {
|
|
|
120
568
|
const name = String(candidate[`${XML_ATTRIBUTE_PREFIX}attr.name`] ?? id).trim();
|
|
121
569
|
const type = normalizeType(candidate[`${XML_ATTRIBUTE_PREFIX}attr.type`]);
|
|
122
570
|
const defaultNode = candidate.default ?? null;
|
|
123
|
-
const defaultValue = defaultNode !== null ? castDataValue(defaultNode, type) : void 0;
|
|
124
|
-
keys.set(id, { id, domain, name, type, defaultValue });
|
|
571
|
+
const defaultValue = defaultNode !== null ? castDataValue(defaultNode, type, lossless) : void 0;
|
|
572
|
+
keys.set(id, { id, domain, name, type, defaultValue, lossless });
|
|
125
573
|
}
|
|
126
574
|
}
|
|
127
575
|
}
|
|
@@ -199,12 +647,12 @@ function assignAttributeFromDataEntry(entry, keyDefinitions, attributes) {
|
|
|
199
647
|
}
|
|
200
648
|
const definition = keyDefinitions.get(keyId);
|
|
201
649
|
const attributeName = (definition == null ? void 0 : definition.name) ?? keyId;
|
|
202
|
-
const value = castDataValue(entry, (definition == null ? void 0 : definition.type) ?? "string");
|
|
650
|
+
const value = (definition == null ? void 0 : definition.type) === "boolean" && extractTextContent(entry) === void 0 ? false : castDataValue(entry, (definition == null ? void 0 : definition.type) ?? "string", definition == null ? void 0 : definition.lossless);
|
|
203
651
|
if (value !== void 0) {
|
|
204
652
|
attributes[attributeName] = value;
|
|
205
653
|
}
|
|
206
654
|
}
|
|
207
|
-
function castDataValue(value, type) {
|
|
655
|
+
function castDataValue(value, type, lossless = false) {
|
|
208
656
|
if (value === null || typeof value === "undefined") {
|
|
209
657
|
return void 0;
|
|
210
658
|
}
|
|
@@ -215,6 +663,9 @@ function castDataValue(value, type) {
|
|
|
215
663
|
if (type === "boolean") {
|
|
216
664
|
return parseBoolean(text);
|
|
217
665
|
}
|
|
666
|
+
if (type === "long" && lossless) {
|
|
667
|
+
return /^[+-]?\d+$/.test(text.trim()) ? BigInt(text.trim()) : text;
|
|
668
|
+
}
|
|
218
669
|
if (type === "int" || type === "long") {
|
|
219
670
|
const parsed = Number.parseInt(text, 10);
|
|
220
671
|
return Number.isNaN(parsed) ? text : parsed;
|
|
@@ -237,12 +688,14 @@ function extractTextContent(value) {
|
|
|
237
688
|
}
|
|
238
689
|
if (isObject(value)) {
|
|
239
690
|
const text = value[XML_TEXT_KEY];
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const nonAttributeEntries = Object.entries(value).filter(([key]) => !key.startsWith(XML_ATTRIBUTE_PREFIX));
|
|
691
|
+
const nonAttributeEntries = Object.entries(value).filter(
|
|
692
|
+
([key]) => !key.startsWith(XML_ATTRIBUTE_PREFIX) && key !== XML_TEXT_KEY
|
|
693
|
+
);
|
|
244
694
|
if (nonAttributeEntries.length === 0) {
|
|
245
|
-
return void 0;
|
|
695
|
+
return typeof text === "string" ? text : void 0;
|
|
696
|
+
}
|
|
697
|
+
if (typeof text === "string" && text.trim()) {
|
|
698
|
+
nonAttributeEntries.push([XML_TEXT_KEY, text]);
|
|
246
699
|
}
|
|
247
700
|
return JSON.stringify(Object.fromEntries(nonAttributeEntries));
|
|
248
701
|
}
|
|
@@ -303,51 +756,116 @@ function parseBoolean(value) {
|
|
|
303
756
|
if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "n") {
|
|
304
757
|
return false;
|
|
305
758
|
}
|
|
306
|
-
return Boolean(
|
|
759
|
+
return Boolean(normalized);
|
|
307
760
|
}
|
|
761
|
+
var import_fast_xml_parser, XML_ATTRIBUTE_PREFIX, XML_TEXT_KEY, graphmlParser;
|
|
762
|
+
var init_parse_graphml = __esm({
|
|
763
|
+
"src/lib/parse-graphml.ts"() {
|
|
764
|
+
"use strict";
|
|
765
|
+
import_fast_xml_parser = require("fast-xml-parser");
|
|
766
|
+
init_build_graph_tables();
|
|
767
|
+
XML_ATTRIBUTE_PREFIX = "@_";
|
|
768
|
+
XML_TEXT_KEY = "#text";
|
|
769
|
+
graphmlParser = new import_fast_xml_parser.XMLParser({
|
|
770
|
+
ignoreAttributes: false,
|
|
771
|
+
attributeNamePrefix: XML_ATTRIBUTE_PREFIX,
|
|
772
|
+
textNodeName: XML_TEXT_KEY,
|
|
773
|
+
trimValues: false,
|
|
774
|
+
parseAttributeValue: false,
|
|
775
|
+
parseTagValue: false,
|
|
776
|
+
removeNSPrefix: true
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
});
|
|
308
780
|
|
|
309
|
-
//
|
|
310
|
-
var
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
781
|
+
// src/graphml-loader.ts
|
|
782
|
+
var graphml_loader_exports = {};
|
|
783
|
+
__export(graphml_loader_exports, {
|
|
784
|
+
GraphMLLoaderWithParser: () => GraphMLLoaderWithParser
|
|
785
|
+
});
|
|
786
|
+
var GraphMLLoaderWithParser;
|
|
787
|
+
var init_graphml_loader = __esm({
|
|
788
|
+
"src/graphml-loader.ts"() {
|
|
789
|
+
"use strict";
|
|
790
|
+
init_graphml_loader_types();
|
|
791
|
+
init_parse_graphml();
|
|
792
|
+
GraphMLLoaderWithParser = {
|
|
793
|
+
...GraphMLLoader,
|
|
794
|
+
parse: async (data, options) => {
|
|
795
|
+
var _a;
|
|
796
|
+
return parseGraphML(data, ((_a = options == null ? void 0 : options.graphml) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
797
|
+
},
|
|
798
|
+
parseSync: (data, options) => {
|
|
799
|
+
var _a;
|
|
800
|
+
return parseGraphML(data, ((_a = options == null ? void 0 : options.graphml) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
801
|
+
},
|
|
802
|
+
parseTextSync: (data, options) => {
|
|
803
|
+
var _a;
|
|
804
|
+
return parseGraphML(data, ((_a = options == null ? void 0 : options.graphml) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
});
|
|
316
809
|
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
810
|
+
// src/dot-loader-types.ts
|
|
811
|
+
async function preload3() {
|
|
812
|
+
const { DOTLoaderWithParser: DOTLoaderWithParser2 } = await Promise.resolve().then(() => (init_dot_loader(), dot_loader_exports));
|
|
813
|
+
return DOTLoaderWithParser2;
|
|
814
|
+
}
|
|
815
|
+
var VERSION3, DOTLoader;
|
|
816
|
+
var init_dot_loader_types = __esm({
|
|
817
|
+
"src/dot-loader-types.ts"() {
|
|
818
|
+
"use strict";
|
|
819
|
+
VERSION3 = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest";
|
|
820
|
+
DOTLoader = {
|
|
821
|
+
name: "DOT",
|
|
822
|
+
id: "dot",
|
|
823
|
+
module: "graphs",
|
|
824
|
+
version: VERSION3,
|
|
825
|
+
extensions: ["dot", "gv"],
|
|
826
|
+
mimeTypes: ["text/vnd.graphviz", "text/x-graphviz", "application/vnd.graphviz"],
|
|
827
|
+
text: true,
|
|
828
|
+
worker: false,
|
|
829
|
+
dataType: null,
|
|
830
|
+
batchType: null,
|
|
831
|
+
options: { dot: { shape: "arrow-table" } },
|
|
832
|
+
preload: preload3
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
});
|
|
332
836
|
|
|
333
|
-
//
|
|
334
|
-
function parseDOT(input) {
|
|
837
|
+
// src/lib/parse-dot.ts
|
|
838
|
+
function parseDOT(input, shape = "plain-graph-data") {
|
|
335
839
|
const parsed = parseDOTDocument(input);
|
|
336
|
-
const subgraphs = Array.from(parsed.subgraphs.values(), (subgraph) => ({
|
|
337
|
-
...subgraph,
|
|
338
|
-
attributes: { ...subgraph.attributes }
|
|
339
|
-
}));
|
|
340
840
|
return {
|
|
341
|
-
|
|
342
|
-
|
|
841
|
+
...buildGraphTables(iterateNodes2(parsed), iterateEdges2(parsed), shape),
|
|
842
|
+
metadata: {
|
|
843
|
+
id: parsed.id,
|
|
844
|
+
directed: parsed.directed,
|
|
845
|
+
strict: parsed.strict,
|
|
846
|
+
attributes: { ...parsed.graphAttributes },
|
|
847
|
+
subgraphs: Array.from(parsed.subgraphs.values(), (subgraph) => ({
|
|
848
|
+
...subgraph,
|
|
849
|
+
attributes: { ...subgraph.attributes }
|
|
850
|
+
}))
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
function* iterateNodes2(parsed) {
|
|
855
|
+
for (const node of parsed.nodes.values()) {
|
|
856
|
+
yield {
|
|
343
857
|
id: node.id,
|
|
344
858
|
...typeof node.attributes.label === "string" ? { label: node.attributes.label } : {},
|
|
345
859
|
attributes: {
|
|
346
860
|
...node.attributes,
|
|
347
861
|
...node.subgraphs.size ? { subgraphs: Array.from(node.subgraphs, (id) => describeSubgraph(id, parsed.subgraphs)) } : {}
|
|
348
862
|
}
|
|
349
|
-
}
|
|
350
|
-
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function* iterateEdges2(parsed) {
|
|
867
|
+
for (const edge of parsed.edges) {
|
|
868
|
+
yield {
|
|
351
869
|
id: edge.id,
|
|
352
870
|
sourceId: edge.sourceId,
|
|
353
871
|
targetId: edge.targetId,
|
|
@@ -357,15 +875,8 @@ function parseDOT(input) {
|
|
|
357
875
|
...edge.attributes,
|
|
358
876
|
...edge.subgraphs.length ? { subgraphs: edge.subgraphs.map((id) => describeSubgraph(id, parsed.subgraphs)) } : {}
|
|
359
877
|
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
id: parsed.id,
|
|
363
|
-
directed: parsed.directed,
|
|
364
|
-
strict: parsed.strict,
|
|
365
|
-
attributes: { ...parsed.graphAttributes },
|
|
366
|
-
subgraphs
|
|
367
|
-
}
|
|
368
|
-
};
|
|
878
|
+
};
|
|
879
|
+
}
|
|
369
880
|
}
|
|
370
881
|
function describeSubgraph(id, descriptors) {
|
|
371
882
|
const subgraph = descriptors.get(id);
|
|
@@ -375,373 +886,10 @@ function describeSubgraph(id, descriptors) {
|
|
|
375
886
|
parentId: subgraph.parentId
|
|
376
887
|
};
|
|
377
888
|
}
|
|
378
|
-
var DOTParser = class {
|
|
379
|
-
/** Tokens for DOT parsing. */
|
|
380
|
-
tokens;
|
|
381
|
-
/** Position for DOT parsing. */
|
|
382
|
-
position = 0;
|
|
383
|
-
/** Accumulated graph records and attributes. */
|
|
384
|
-
result = {
|
|
385
|
-
directed: false,
|
|
386
|
-
strict: false,
|
|
387
|
-
graphAttributes: /* @__PURE__ */ Object.create(null),
|
|
388
|
-
nodes: /* @__PURE__ */ new Map(),
|
|
389
|
-
edges: [],
|
|
390
|
-
subgraphs: /* @__PURE__ */ new Map()
|
|
391
|
-
};
|
|
392
|
-
/** Scopes for DOT parsing. */
|
|
393
|
-
scopes = [
|
|
394
|
-
{
|
|
395
|
-
nodeDefaults: /* @__PURE__ */ Object.create(null),
|
|
396
|
-
edgeDefaults: /* @__PURE__ */ Object.create(null),
|
|
397
|
-
graphAttributes: /* @__PURE__ */ Object.create(null)
|
|
398
|
-
}
|
|
399
|
-
];
|
|
400
|
-
/** Subgraph counter for DOT parsing. */
|
|
401
|
-
subgraphCounter = 0;
|
|
402
|
-
/** Edge counter for DOT parsing. */
|
|
403
|
-
edgeCounter = 0;
|
|
404
|
-
/** Edge lookup used to coalesce parallel edges in strict graphs. */
|
|
405
|
-
strictEdges = /* @__PURE__ */ new Map();
|
|
406
|
-
/** Persistent attributes and defaults for reopened named subgraphs. */
|
|
407
|
-
subgraphScopes = /* @__PURE__ */ new Map();
|
|
408
|
-
/** Explicit names reserved so anonymous subgraph IDs cannot collide with them. */
|
|
409
|
-
namedSubgraphIds = /* @__PURE__ */ new Set();
|
|
410
|
-
/** Initializes the token stream. */
|
|
411
|
-
constructor(tokens) {
|
|
412
|
-
this.tokens = tokens;
|
|
413
|
-
for (let index = 0; index < tokens.length - 1; index++) {
|
|
414
|
-
if (isKeyword(tokens[index], "subgraph") && isIdentifierLike(tokens[index + 1])) {
|
|
415
|
-
this.namedSubgraphIds.add(tokens[index + 1].value);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
/** Parses one complete DOT document. */
|
|
420
|
-
parse() {
|
|
421
|
-
this.parseGraph();
|
|
422
|
-
this.consumeOptionalSemicolon();
|
|
423
|
-
if (this.peek()) {
|
|
424
|
-
throw new Error("Unexpected content after DOT graph.");
|
|
425
|
-
}
|
|
426
|
-
return this.result;
|
|
427
|
-
}
|
|
428
|
-
/** Reads graph identity, declaration, and statements. */
|
|
429
|
-
parseGraph() {
|
|
430
|
-
const strictToken = this.peek();
|
|
431
|
-
if (strictToken && isKeyword(strictToken, "strict")) {
|
|
432
|
-
this.consume();
|
|
433
|
-
this.result.strict = true;
|
|
434
|
-
}
|
|
435
|
-
const typeToken = this.peek();
|
|
436
|
-
if (!typeToken || !isGraphType(typeToken)) {
|
|
437
|
-
throw new Error("DOT graph must start with graph or digraph keyword.");
|
|
438
|
-
}
|
|
439
|
-
this.consume();
|
|
440
|
-
this.result.directed = isKeyword(typeToken, "digraph");
|
|
441
|
-
const idToken = this.peek();
|
|
442
|
-
if (idToken && isIdentifierLike(idToken)) {
|
|
443
|
-
this.result.id = parseIdentifierValue(this.consume());
|
|
444
|
-
}
|
|
445
|
-
this.expect("lbrace");
|
|
446
|
-
while (!this.match("rbrace")) {
|
|
447
|
-
if (!this.parseStatement()) {
|
|
448
|
-
throw new Error("Expected closing brace in DOT graph.");
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
this.result.graphAttributes = { ...this.scopes[0].graphAttributes };
|
|
452
|
-
}
|
|
453
|
-
/** Dispatches a graph statement. */
|
|
454
|
-
parseStatement() {
|
|
455
|
-
if (this.consumeSemicolonIfPresent()) {
|
|
456
|
-
return true;
|
|
457
|
-
}
|
|
458
|
-
const token = this.peek();
|
|
459
|
-
if (!token) {
|
|
460
|
-
return false;
|
|
461
|
-
}
|
|
462
|
-
if (this.tryParseSubgraphStatement(token)) {
|
|
463
|
-
return true;
|
|
464
|
-
}
|
|
465
|
-
if (this.tryParseKeywordStatement(token)) {
|
|
466
|
-
return true;
|
|
467
|
-
}
|
|
468
|
-
if (this.tryParseAssignmentStatement(token)) {
|
|
469
|
-
return true;
|
|
470
|
-
}
|
|
471
|
-
if (isIdentifierLike(token)) {
|
|
472
|
-
this.parseNodeOrEdgeStatement();
|
|
473
|
-
this.consumeOptionalSemicolon();
|
|
474
|
-
return true;
|
|
475
|
-
}
|
|
476
|
-
throw new Error(`Unexpected token: ${token.value}`);
|
|
477
|
-
}
|
|
478
|
-
/** Consumes an empty statement. */
|
|
479
|
-
consumeSemicolonIfPresent() {
|
|
480
|
-
const next = this.peek();
|
|
481
|
-
if ((next == null ? void 0 : next.type) === "semicolon") {
|
|
482
|
-
this.consume();
|
|
483
|
-
return true;
|
|
484
|
-
}
|
|
485
|
-
return false;
|
|
486
|
-
}
|
|
487
|
-
/** Reads a standalone subgraph statement. */
|
|
488
|
-
tryParseSubgraphStatement(token) {
|
|
489
|
-
if (token.type !== "lbrace" && !isKeyword(token, "subgraph")) {
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
this.parseSubgraph();
|
|
493
|
-
return true;
|
|
494
|
-
}
|
|
495
|
-
/** Applies graph, node, or edge defaults. */
|
|
496
|
-
tryParseKeywordStatement(token) {
|
|
497
|
-
if (!(isKeyword(token, "graph") || isKeyword(token, "node") || isKeyword(token, "edge"))) {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
this.consume();
|
|
501
|
-
const explicitAttributes = this.parseAttributeList();
|
|
502
|
-
if (isKeyword(token, "graph")) {
|
|
503
|
-
Object.assign(this.currentScope().graphAttributes, explicitAttributes);
|
|
504
|
-
} else if (isKeyword(token, "node")) {
|
|
505
|
-
Object.assign(this.currentScope().nodeDefaults, explicitAttributes);
|
|
506
|
-
} else {
|
|
507
|
-
Object.assign(this.currentScope().edgeDefaults, explicitAttributes);
|
|
508
|
-
}
|
|
509
|
-
this.consumeOptionalSemicolon();
|
|
510
|
-
return true;
|
|
511
|
-
}
|
|
512
|
-
/** Reads a graph attribute assignment. */
|
|
513
|
-
tryParseAssignmentStatement(token) {
|
|
514
|
-
if (!isIdentifierLike(token)) {
|
|
515
|
-
return false;
|
|
516
|
-
}
|
|
517
|
-
const next = this.peek(1);
|
|
518
|
-
if ((next == null ? void 0 : next.type) !== "equals") {
|
|
519
|
-
return false;
|
|
520
|
-
}
|
|
521
|
-
const key = parseIdentifierValue(this.consume());
|
|
522
|
-
this.consume();
|
|
523
|
-
const valueToken = this.consume();
|
|
524
|
-
const value = parseAttributeValue(valueToken);
|
|
525
|
-
this.currentScope().graphAttributes[key] = value;
|
|
526
|
-
this.consumeOptionalSemicolon();
|
|
527
|
-
return true;
|
|
528
|
-
}
|
|
529
|
-
/** Reads a node statement or chained edges. */
|
|
530
|
-
parseNodeOrEdgeStatement() {
|
|
531
|
-
const first = parseIdentifierValue(this.consume());
|
|
532
|
-
const references = [first];
|
|
533
|
-
const operators = [];
|
|
534
|
-
let operatorToken = this.match("arrow");
|
|
535
|
-
while (operatorToken) {
|
|
536
|
-
operators.push(operatorToken.value);
|
|
537
|
-
const referenceToken = this.consume();
|
|
538
|
-
if (!isIdentifierLike(referenceToken)) {
|
|
539
|
-
throw new Error("Expected node identifier in edge statement.");
|
|
540
|
-
}
|
|
541
|
-
references.push(parseIdentifierValue(referenceToken));
|
|
542
|
-
operatorToken = this.match("arrow");
|
|
543
|
-
}
|
|
544
|
-
if (operators.length === 0) {
|
|
545
|
-
const explicitAttributes2 = this.parseAttributeList();
|
|
546
|
-
this.addNode(first, explicitAttributes2);
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
const explicitAttributes = this.parseAttributeList();
|
|
550
|
-
this.addEdgeChain(references, operators, explicitAttributes);
|
|
551
|
-
}
|
|
552
|
-
/** Merges explicit attributes into a node. */
|
|
553
|
-
addNode(id, explicitAttributes) {
|
|
554
|
-
const membership = this.getCurrentSubgraphChain();
|
|
555
|
-
const node = this.ensureNode(id, membership);
|
|
556
|
-
node.attributes = { ...node.attributes, ...explicitAttributes };
|
|
557
|
-
}
|
|
558
|
-
/** Expands an edge chain into node and edge records. */
|
|
559
|
-
addEdgeChain(nodes, operators, explicitAttributes) {
|
|
560
|
-
const membership = this.getCurrentSubgraphChain();
|
|
561
|
-
const defaults = this.currentScope().edgeDefaults;
|
|
562
|
-
const attributes = { ...defaults, ...explicitAttributes };
|
|
563
|
-
for (let index = 0; index < nodes.length - 1; index++) {
|
|
564
|
-
const sourceId = nodes[index];
|
|
565
|
-
const targetId = nodes[index + 1];
|
|
566
|
-
const operator = operators[index];
|
|
567
|
-
const directed = operator === "->";
|
|
568
|
-
if (directed !== this.result.directed) {
|
|
569
|
-
throw new Error("DOT edge operator must match graph or digraph declaration.");
|
|
570
|
-
}
|
|
571
|
-
const edgeId = deriveEdgeId(attributes, sourceId, targetId, ++this.edgeCounter);
|
|
572
|
-
const edgeAttributes = { ...attributes };
|
|
573
|
-
this.ensureNode(sourceId, membership);
|
|
574
|
-
this.ensureNode(targetId, membership);
|
|
575
|
-
const strictKey = JSON.stringify(directed ? [sourceId, targetId] : [sourceId, targetId].sort());
|
|
576
|
-
const existingEdge = this.result.strict ? this.strictEdges.get(strictKey) : void 0;
|
|
577
|
-
if (existingEdge) {
|
|
578
|
-
existingEdge.attributes = { ...existingEdge.attributes, ...explicitAttributes };
|
|
579
|
-
existingEdge.directed = deriveDirectedFlag(existingEdge.attributes, directed);
|
|
580
|
-
existingEdge.subgraphs = Array.from(/* @__PURE__ */ new Set([...existingEdge.subgraphs, ...membership]));
|
|
581
|
-
continue;
|
|
582
|
-
}
|
|
583
|
-
const directedOverride = deriveDirectedFlag(edgeAttributes, directed);
|
|
584
|
-
const edge = {
|
|
585
|
-
id: edgeId,
|
|
586
|
-
sourceId,
|
|
587
|
-
targetId,
|
|
588
|
-
directed: directedOverride,
|
|
589
|
-
attributes: edgeAttributes,
|
|
590
|
-
subgraphs: membership
|
|
591
|
-
};
|
|
592
|
-
this.result.edges.push(edge);
|
|
593
|
-
if (this.result.strict) {
|
|
594
|
-
this.strictEdges.set(strictKey, edge);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
/** Reads a nested scope with inherited defaults. */
|
|
599
|
-
parseSubgraph() {
|
|
600
|
-
let idToken = this.peek();
|
|
601
|
-
let subgraphId;
|
|
602
|
-
if (idToken && isKeyword(idToken, "subgraph")) {
|
|
603
|
-
this.consume();
|
|
604
|
-
idToken = this.peek();
|
|
605
|
-
}
|
|
606
|
-
if (idToken && isIdentifierLike(idToken)) {
|
|
607
|
-
subgraphId = parseIdentifierValue(this.consume());
|
|
608
|
-
} else {
|
|
609
|
-
do {
|
|
610
|
-
subgraphId = `subgraph_${++this.subgraphCounter}`;
|
|
611
|
-
} while (this.namedSubgraphIds.has(subgraphId));
|
|
612
|
-
}
|
|
613
|
-
this.expect("lbrace");
|
|
614
|
-
const parentId = this.findCurrentSubgraphId();
|
|
615
|
-
let context = this.subgraphScopes.get(subgraphId);
|
|
616
|
-
if (!context) {
|
|
617
|
-
context = {
|
|
618
|
-
id: subgraphId,
|
|
619
|
-
nodeDefaults: Object.assign(/* @__PURE__ */ Object.create(null), this.currentScope().nodeDefaults),
|
|
620
|
-
edgeDefaults: Object.assign(/* @__PURE__ */ Object.create(null), this.currentScope().edgeDefaults),
|
|
621
|
-
graphAttributes: /* @__PURE__ */ Object.create(null)
|
|
622
|
-
};
|
|
623
|
-
this.subgraphScopes.set(subgraphId, context);
|
|
624
|
-
this.result.subgraphs.set(subgraphId, {
|
|
625
|
-
id: subgraphId,
|
|
626
|
-
attributes: context.graphAttributes,
|
|
627
|
-
parentId
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
this.scopes.push(context);
|
|
631
|
-
let shouldContinue = true;
|
|
632
|
-
while (shouldContinue) {
|
|
633
|
-
if (this.match("rbrace")) {
|
|
634
|
-
break;
|
|
635
|
-
}
|
|
636
|
-
shouldContinue = this.parseStatement();
|
|
637
|
-
if (!shouldContinue) {
|
|
638
|
-
throw new Error("Expected closing brace in DOT subgraph.");
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
this.scopes.pop();
|
|
642
|
-
}
|
|
643
|
-
/** Reads repeated attribute lists. */
|
|
644
|
-
parseAttributeList() {
|
|
645
|
-
var _a, _b;
|
|
646
|
-
const attributes = /* @__PURE__ */ Object.create(null);
|
|
647
|
-
while (this.match("lbrack")) {
|
|
648
|
-
while (!this.match("rbrack")) {
|
|
649
|
-
const keyToken = this.consume();
|
|
650
|
-
if (!isIdentifierLike(keyToken)) {
|
|
651
|
-
throw new Error("Expected attribute name.");
|
|
652
|
-
}
|
|
653
|
-
const key = parseIdentifierValue(keyToken);
|
|
654
|
-
let value = true;
|
|
655
|
-
if (this.match("equals")) {
|
|
656
|
-
const valueToken = this.consume();
|
|
657
|
-
value = parseAttributeValue(valueToken);
|
|
658
|
-
}
|
|
659
|
-
attributes[key] = value;
|
|
660
|
-
if (((_a = this.peek()) == null ? void 0 : _a.type) === "comma" || ((_b = this.peek()) == null ? void 0 : _b.type) === "semicolon") {
|
|
661
|
-
this.consume();
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
return attributes;
|
|
666
|
-
}
|
|
667
|
-
/** Creates an implicit node and records its subgraph memberships. */
|
|
668
|
-
ensureNode(id, membership) {
|
|
669
|
-
let node = this.result.nodes.get(id);
|
|
670
|
-
if (!node) {
|
|
671
|
-
const defaults = this.currentScope().nodeDefaults;
|
|
672
|
-
node = { id, attributes: { ...defaults }, subgraphs: /* @__PURE__ */ new Set() };
|
|
673
|
-
this.result.nodes.set(id, node);
|
|
674
|
-
}
|
|
675
|
-
membership.forEach((subgraphId) => node.subgraphs.add(subgraphId));
|
|
676
|
-
return node;
|
|
677
|
-
}
|
|
678
|
-
/** Lists enclosing subgraphs. */
|
|
679
|
-
getCurrentSubgraphChain() {
|
|
680
|
-
const chain = [];
|
|
681
|
-
for (const scope of this.scopes) {
|
|
682
|
-
if (scope.id) {
|
|
683
|
-
chain.push(scope.id);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
return chain;
|
|
687
|
-
}
|
|
688
|
-
/** Finds the nearest enclosing named or generated subgraph. */
|
|
689
|
-
findCurrentSubgraphId() {
|
|
690
|
-
for (let index = this.scopes.length - 1; index >= 0; index--) {
|
|
691
|
-
const scope = this.scopes[index];
|
|
692
|
-
if (scope.id) {
|
|
693
|
-
return scope.id;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
return void 0;
|
|
697
|
-
}
|
|
698
|
-
/** Returns the active attribute scope. */
|
|
699
|
-
currentScope() {
|
|
700
|
-
return this.scopes[this.scopes.length - 1];
|
|
701
|
-
}
|
|
702
|
-
/** Consumes an optional statement terminator. */
|
|
703
|
-
consumeOptionalSemicolon() {
|
|
704
|
-
const next = this.peek();
|
|
705
|
-
if ((next == null ? void 0 : next.type) === "semicolon") {
|
|
706
|
-
this.consume();
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
/** Requires the next token to have the requested type. */
|
|
710
|
-
expect(type) {
|
|
711
|
-
const token = this.consume();
|
|
712
|
-
if (!token || token.type !== type) {
|
|
713
|
-
throw new Error(`Expected token ${type}.`);
|
|
714
|
-
}
|
|
715
|
-
return token;
|
|
716
|
-
}
|
|
717
|
-
/** Consumes the next token or reports truncated input. */
|
|
718
|
-
consume() {
|
|
719
|
-
const token = this.tokens[this.position];
|
|
720
|
-
if (!token) {
|
|
721
|
-
throw new Error("Unexpected end of DOT input.");
|
|
722
|
-
}
|
|
723
|
-
this.position++;
|
|
724
|
-
return token;
|
|
725
|
-
}
|
|
726
|
-
/** Consumes a token when its type matches. */
|
|
727
|
-
match(type) {
|
|
728
|
-
const token = this.peek();
|
|
729
|
-
if (token && token.type === type) {
|
|
730
|
-
this.position++;
|
|
731
|
-
return token;
|
|
732
|
-
}
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
/** Inspects a token without consuming it. */
|
|
736
|
-
peek(offset = 0) {
|
|
737
|
-
return this.tokens[this.position + offset] ?? null;
|
|
738
|
-
}
|
|
739
|
-
};
|
|
740
889
|
function parseDOTDocument(input) {
|
|
741
|
-
const
|
|
742
|
-
return
|
|
890
|
+
const parser2 = new DOTParser(tokenize(input));
|
|
891
|
+
return parser2.parse();
|
|
743
892
|
}
|
|
744
|
-
var IDENTIFIER_TERMINATORS = /* @__PURE__ */ new Set(["{", "}", "[", "]", "=", ";", ",", '"', "<", "#"]);
|
|
745
893
|
function tokenize(input) {
|
|
746
894
|
const tokens = [];
|
|
747
895
|
let index = 0;
|
|
@@ -880,6 +1028,10 @@ function readQuotedString(input, startIndex) {
|
|
|
880
1028
|
function readEscapedCharacter(input, startIndex) {
|
|
881
1029
|
const next = input[startIndex];
|
|
882
1030
|
switch (next) {
|
|
1031
|
+
case "\n":
|
|
1032
|
+
return { value: "", nextIndex: startIndex + 1 };
|
|
1033
|
+
case "\r":
|
|
1034
|
+
return { value: "", nextIndex: startIndex + (input[startIndex + 1] === "\n" ? 2 : 1) };
|
|
883
1035
|
case "n":
|
|
884
1036
|
case "l":
|
|
885
1037
|
case "L":
|
|
@@ -896,7 +1048,7 @@ function readEscapedCharacter(input, startIndex) {
|
|
|
896
1048
|
if (typeof next === "undefined") {
|
|
897
1049
|
throw new Error("Unterminated escape sequence in DOT source.");
|
|
898
1050
|
}
|
|
899
|
-
return { value: next
|
|
1051
|
+
return { value: `\\${next}`, nextIndex: startIndex + 1 };
|
|
900
1052
|
}
|
|
901
1053
|
}
|
|
902
1054
|
}
|
|
@@ -1011,12 +1163,842 @@ function deriveDirectedFlag(attributes, defaultDirected) {
|
|
|
1011
1163
|
}
|
|
1012
1164
|
return defaultDirected;
|
|
1013
1165
|
}
|
|
1166
|
+
var DOTParser, IDENTIFIER_TERMINATORS;
|
|
1167
|
+
var init_parse_dot = __esm({
|
|
1168
|
+
"src/lib/parse-dot.ts"() {
|
|
1169
|
+
"use strict";
|
|
1170
|
+
init_build_graph_tables();
|
|
1171
|
+
DOTParser = class {
|
|
1172
|
+
/** Tokens for DOT parsing. */
|
|
1173
|
+
tokens;
|
|
1174
|
+
/** Position for DOT parsing. */
|
|
1175
|
+
position = 0;
|
|
1176
|
+
/** Accumulated graph records and attributes. */
|
|
1177
|
+
result = {
|
|
1178
|
+
directed: false,
|
|
1179
|
+
strict: false,
|
|
1180
|
+
graphAttributes: /* @__PURE__ */ Object.create(null),
|
|
1181
|
+
nodes: /* @__PURE__ */ new Map(),
|
|
1182
|
+
edges: [],
|
|
1183
|
+
subgraphs: /* @__PURE__ */ new Map()
|
|
1184
|
+
};
|
|
1185
|
+
/** Scopes for DOT parsing. */
|
|
1186
|
+
scopes = [
|
|
1187
|
+
{
|
|
1188
|
+
nodeDefaults: /* @__PURE__ */ Object.create(null),
|
|
1189
|
+
edgeDefaults: /* @__PURE__ */ Object.create(null),
|
|
1190
|
+
graphAttributes: /* @__PURE__ */ Object.create(null)
|
|
1191
|
+
}
|
|
1192
|
+
];
|
|
1193
|
+
/** Subgraph counter for DOT parsing. */
|
|
1194
|
+
subgraphCounter = 0;
|
|
1195
|
+
/** Edge counter for DOT parsing. */
|
|
1196
|
+
edgeCounter = 0;
|
|
1197
|
+
/** Edge lookup used to coalesce parallel edges in strict graphs. */
|
|
1198
|
+
strictEdges = /* @__PURE__ */ new Map();
|
|
1199
|
+
/** Persistent attributes and defaults for reopened named subgraphs. */
|
|
1200
|
+
subgraphScopes = /* @__PURE__ */ new Map();
|
|
1201
|
+
/** Explicit names reserved so anonymous subgraph IDs cannot collide with them. */
|
|
1202
|
+
namedSubgraphIds = /* @__PURE__ */ new Set();
|
|
1203
|
+
/** Initializes the token stream. */
|
|
1204
|
+
constructor(tokens) {
|
|
1205
|
+
this.tokens = tokens;
|
|
1206
|
+
for (let index = 0; index < tokens.length - 1; index++) {
|
|
1207
|
+
if (isKeyword(tokens[index], "subgraph") && isIdentifierLike(tokens[index + 1])) {
|
|
1208
|
+
this.namedSubgraphIds.add(tokens[index + 1].value);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
/** Parses one complete DOT document. */
|
|
1213
|
+
parse() {
|
|
1214
|
+
this.parseGraph();
|
|
1215
|
+
this.consumeOptionalSemicolon();
|
|
1216
|
+
if (this.peek()) {
|
|
1217
|
+
throw new Error("Unexpected content after DOT graph.");
|
|
1218
|
+
}
|
|
1219
|
+
return this.result;
|
|
1220
|
+
}
|
|
1221
|
+
/** Reads graph identity, declaration, and statements. */
|
|
1222
|
+
parseGraph() {
|
|
1223
|
+
const strictToken = this.peek();
|
|
1224
|
+
if (strictToken && isKeyword(strictToken, "strict")) {
|
|
1225
|
+
this.consume();
|
|
1226
|
+
this.result.strict = true;
|
|
1227
|
+
}
|
|
1228
|
+
const typeToken = this.peek();
|
|
1229
|
+
if (!typeToken || !isGraphType(typeToken)) {
|
|
1230
|
+
throw new Error("DOT graph must start with graph or digraph keyword.");
|
|
1231
|
+
}
|
|
1232
|
+
this.consume();
|
|
1233
|
+
this.result.directed = isKeyword(typeToken, "digraph");
|
|
1234
|
+
const idToken = this.peek();
|
|
1235
|
+
if (idToken && isIdentifierLike(idToken)) {
|
|
1236
|
+
this.result.id = parseIdentifierValue(this.consume());
|
|
1237
|
+
}
|
|
1238
|
+
this.expect("lbrace");
|
|
1239
|
+
while (!this.match("rbrace")) {
|
|
1240
|
+
if (!this.parseStatement()) {
|
|
1241
|
+
throw new Error("Expected closing brace in DOT graph.");
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
this.result.graphAttributes = { ...this.scopes[0].graphAttributes };
|
|
1245
|
+
}
|
|
1246
|
+
/** Dispatches a graph statement. */
|
|
1247
|
+
parseStatement() {
|
|
1248
|
+
if (this.consumeSemicolonIfPresent()) {
|
|
1249
|
+
return true;
|
|
1250
|
+
}
|
|
1251
|
+
const token = this.peek();
|
|
1252
|
+
if (!token) {
|
|
1253
|
+
return false;
|
|
1254
|
+
}
|
|
1255
|
+
if (this.tryParseSubgraphStatement(token)) {
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
if (this.tryParseKeywordStatement(token)) {
|
|
1259
|
+
return true;
|
|
1260
|
+
}
|
|
1261
|
+
if (this.tryParseAssignmentStatement(token)) {
|
|
1262
|
+
return true;
|
|
1263
|
+
}
|
|
1264
|
+
if (isIdentifierLike(token)) {
|
|
1265
|
+
this.parseNodeOrEdgeStatement();
|
|
1266
|
+
this.consumeOptionalSemicolon();
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
throw new Error(`Unexpected token: ${token.value}`);
|
|
1270
|
+
}
|
|
1271
|
+
/** Consumes an empty statement. */
|
|
1272
|
+
consumeSemicolonIfPresent() {
|
|
1273
|
+
const next = this.peek();
|
|
1274
|
+
if ((next == null ? void 0 : next.type) === "semicolon") {
|
|
1275
|
+
this.consume();
|
|
1276
|
+
return true;
|
|
1277
|
+
}
|
|
1278
|
+
return false;
|
|
1279
|
+
}
|
|
1280
|
+
/** Reads a standalone subgraph statement. */
|
|
1281
|
+
tryParseSubgraphStatement(token) {
|
|
1282
|
+
if (token.type !== "lbrace" && !isKeyword(token, "subgraph")) {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
this.parseSubgraph();
|
|
1286
|
+
return true;
|
|
1287
|
+
}
|
|
1288
|
+
/** Applies graph, node, or edge defaults. */
|
|
1289
|
+
tryParseKeywordStatement(token) {
|
|
1290
|
+
if (!(isKeyword(token, "graph") || isKeyword(token, "node") || isKeyword(token, "edge"))) {
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
this.consume();
|
|
1294
|
+
const explicitAttributes = this.parseAttributeList();
|
|
1295
|
+
if (isKeyword(token, "graph")) {
|
|
1296
|
+
Object.assign(this.currentScope().graphAttributes, explicitAttributes);
|
|
1297
|
+
} else if (isKeyword(token, "node")) {
|
|
1298
|
+
Object.assign(this.currentScope().nodeDefaults, explicitAttributes);
|
|
1299
|
+
} else {
|
|
1300
|
+
Object.assign(this.currentScope().edgeDefaults, explicitAttributes);
|
|
1301
|
+
}
|
|
1302
|
+
this.consumeOptionalSemicolon();
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
/** Reads a graph attribute assignment. */
|
|
1306
|
+
tryParseAssignmentStatement(token) {
|
|
1307
|
+
if (!isIdentifierLike(token)) {
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
const next = this.peek(1);
|
|
1311
|
+
if ((next == null ? void 0 : next.type) !== "equals") {
|
|
1312
|
+
return false;
|
|
1313
|
+
}
|
|
1314
|
+
const key = parseIdentifierValue(this.consume());
|
|
1315
|
+
this.consume();
|
|
1316
|
+
const valueToken = this.consume();
|
|
1317
|
+
const value = parseAttributeValue(valueToken);
|
|
1318
|
+
this.currentScope().graphAttributes[key] = value;
|
|
1319
|
+
this.consumeOptionalSemicolon();
|
|
1320
|
+
return true;
|
|
1321
|
+
}
|
|
1322
|
+
/** Reads a node statement or chained edges. */
|
|
1323
|
+
parseNodeOrEdgeStatement() {
|
|
1324
|
+
const first = parseIdentifierValue(this.consume());
|
|
1325
|
+
const references = [first];
|
|
1326
|
+
const operators = [];
|
|
1327
|
+
let operatorToken = this.match("arrow");
|
|
1328
|
+
while (operatorToken) {
|
|
1329
|
+
operators.push(operatorToken.value);
|
|
1330
|
+
const referenceToken = this.consume();
|
|
1331
|
+
if (!isIdentifierLike(referenceToken)) {
|
|
1332
|
+
throw new Error("Expected node identifier in edge statement.");
|
|
1333
|
+
}
|
|
1334
|
+
references.push(parseIdentifierValue(referenceToken));
|
|
1335
|
+
operatorToken = this.match("arrow");
|
|
1336
|
+
}
|
|
1337
|
+
if (operators.length === 0) {
|
|
1338
|
+
const explicitAttributes2 = this.parseAttributeList();
|
|
1339
|
+
this.addNode(first, explicitAttributes2);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
const explicitAttributes = this.parseAttributeList();
|
|
1343
|
+
this.addEdgeChain(references, operators, explicitAttributes);
|
|
1344
|
+
}
|
|
1345
|
+
/** Merges explicit attributes into a node. */
|
|
1346
|
+
addNode(id, explicitAttributes) {
|
|
1347
|
+
const membership = this.getCurrentSubgraphChain();
|
|
1348
|
+
const node = this.ensureNode(id, membership);
|
|
1349
|
+
node.attributes = { ...node.attributes, ...explicitAttributes };
|
|
1350
|
+
}
|
|
1351
|
+
/** Expands an edge chain into node and edge records. */
|
|
1352
|
+
addEdgeChain(nodes, operators, explicitAttributes) {
|
|
1353
|
+
const membership = this.getCurrentSubgraphChain();
|
|
1354
|
+
const defaults = this.currentScope().edgeDefaults;
|
|
1355
|
+
const attributes = { ...defaults, ...explicitAttributes };
|
|
1356
|
+
for (let index = 0; index < nodes.length - 1; index++) {
|
|
1357
|
+
const sourceId = nodes[index];
|
|
1358
|
+
const targetId = nodes[index + 1];
|
|
1359
|
+
const operator = operators[index];
|
|
1360
|
+
const directed = operator === "->";
|
|
1361
|
+
if (directed !== this.result.directed) {
|
|
1362
|
+
throw new Error("DOT edge operator must match graph or digraph declaration.");
|
|
1363
|
+
}
|
|
1364
|
+
const edgeId = deriveEdgeId(attributes, sourceId, targetId, ++this.edgeCounter);
|
|
1365
|
+
const edgeAttributes = { ...attributes };
|
|
1366
|
+
this.ensureNode(sourceId, membership);
|
|
1367
|
+
this.ensureNode(targetId, membership);
|
|
1368
|
+
const strictKey = JSON.stringify(
|
|
1369
|
+
directed ? [sourceId, targetId] : [sourceId, targetId].sort()
|
|
1370
|
+
);
|
|
1371
|
+
const existingEdge = this.result.strict ? this.strictEdges.get(strictKey) : void 0;
|
|
1372
|
+
if (existingEdge) {
|
|
1373
|
+
existingEdge.attributes = { ...existingEdge.attributes, ...explicitAttributes };
|
|
1374
|
+
const explicitId = explicitAttributes.id ?? explicitAttributes.Id ?? explicitAttributes.ID;
|
|
1375
|
+
if (typeof explicitId === "string" || typeof explicitId === "number") {
|
|
1376
|
+
existingEdge.id = String(explicitId);
|
|
1377
|
+
}
|
|
1378
|
+
existingEdge.directed = deriveDirectedFlag(existingEdge.attributes, directed);
|
|
1379
|
+
existingEdge.subgraphs = Array.from(/* @__PURE__ */ new Set([...existingEdge.subgraphs, ...membership]));
|
|
1380
|
+
continue;
|
|
1381
|
+
}
|
|
1382
|
+
const directedOverride = deriveDirectedFlag(edgeAttributes, directed);
|
|
1383
|
+
const edge = {
|
|
1384
|
+
id: edgeId,
|
|
1385
|
+
sourceId,
|
|
1386
|
+
targetId,
|
|
1387
|
+
directed: directedOverride,
|
|
1388
|
+
attributes: edgeAttributes,
|
|
1389
|
+
subgraphs: membership
|
|
1390
|
+
};
|
|
1391
|
+
this.result.edges.push(edge);
|
|
1392
|
+
if (this.result.strict) {
|
|
1393
|
+
this.strictEdges.set(strictKey, edge);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
/** Reads a nested scope with inherited defaults. */
|
|
1398
|
+
parseSubgraph() {
|
|
1399
|
+
let idToken = this.peek();
|
|
1400
|
+
let subgraphId;
|
|
1401
|
+
if (idToken && isKeyword(idToken, "subgraph")) {
|
|
1402
|
+
this.consume();
|
|
1403
|
+
idToken = this.peek();
|
|
1404
|
+
}
|
|
1405
|
+
if (idToken && isIdentifierLike(idToken)) {
|
|
1406
|
+
subgraphId = parseIdentifierValue(this.consume());
|
|
1407
|
+
} else {
|
|
1408
|
+
do {
|
|
1409
|
+
subgraphId = `subgraph_${++this.subgraphCounter}`;
|
|
1410
|
+
} while (this.namedSubgraphIds.has(subgraphId));
|
|
1411
|
+
}
|
|
1412
|
+
this.expect("lbrace");
|
|
1413
|
+
const parentId = this.findCurrentSubgraphId();
|
|
1414
|
+
let context = this.subgraphScopes.get(subgraphId);
|
|
1415
|
+
if (!context) {
|
|
1416
|
+
context = {
|
|
1417
|
+
id: subgraphId,
|
|
1418
|
+
nodeDefaults: Object.assign(/* @__PURE__ */ Object.create(null), this.currentScope().nodeDefaults),
|
|
1419
|
+
edgeDefaults: Object.assign(/* @__PURE__ */ Object.create(null), this.currentScope().edgeDefaults),
|
|
1420
|
+
graphAttributes: /* @__PURE__ */ Object.create(null)
|
|
1421
|
+
};
|
|
1422
|
+
this.subgraphScopes.set(subgraphId, context);
|
|
1423
|
+
this.result.subgraphs.set(subgraphId, {
|
|
1424
|
+
id: subgraphId,
|
|
1425
|
+
attributes: context.graphAttributes,
|
|
1426
|
+
parentId
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
this.scopes.push(context);
|
|
1430
|
+
let shouldContinue = true;
|
|
1431
|
+
while (shouldContinue) {
|
|
1432
|
+
if (this.match("rbrace")) {
|
|
1433
|
+
break;
|
|
1434
|
+
}
|
|
1435
|
+
shouldContinue = this.parseStatement();
|
|
1436
|
+
if (!shouldContinue) {
|
|
1437
|
+
throw new Error("Expected closing brace in DOT subgraph.");
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
this.scopes.pop();
|
|
1441
|
+
}
|
|
1442
|
+
/** Reads repeated attribute lists. */
|
|
1443
|
+
parseAttributeList() {
|
|
1444
|
+
var _a, _b;
|
|
1445
|
+
const attributes = /* @__PURE__ */ Object.create(null);
|
|
1446
|
+
while (this.match("lbrack")) {
|
|
1447
|
+
while (!this.match("rbrack")) {
|
|
1448
|
+
const keyToken = this.consume();
|
|
1449
|
+
if (!isIdentifierLike(keyToken)) {
|
|
1450
|
+
throw new Error("Expected attribute name.");
|
|
1451
|
+
}
|
|
1452
|
+
const key = parseIdentifierValue(keyToken);
|
|
1453
|
+
let value = true;
|
|
1454
|
+
if (this.match("equals")) {
|
|
1455
|
+
const valueToken = this.consume();
|
|
1456
|
+
value = parseAttributeValue(valueToken);
|
|
1457
|
+
}
|
|
1458
|
+
attributes[key] = value;
|
|
1459
|
+
if (((_a = this.peek()) == null ? void 0 : _a.type) === "comma" || ((_b = this.peek()) == null ? void 0 : _b.type) === "semicolon") {
|
|
1460
|
+
this.consume();
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
return attributes;
|
|
1465
|
+
}
|
|
1466
|
+
/** Creates an implicit node and records its subgraph memberships. */
|
|
1467
|
+
ensureNode(id, membership) {
|
|
1468
|
+
let node = this.result.nodes.get(id);
|
|
1469
|
+
if (!node) {
|
|
1470
|
+
const defaults = this.currentScope().nodeDefaults;
|
|
1471
|
+
node = { id, attributes: { ...defaults }, subgraphs: /* @__PURE__ */ new Set() };
|
|
1472
|
+
this.result.nodes.set(id, node);
|
|
1473
|
+
}
|
|
1474
|
+
membership.forEach((subgraphId) => node.subgraphs.add(subgraphId));
|
|
1475
|
+
return node;
|
|
1476
|
+
}
|
|
1477
|
+
/** Lists enclosing subgraphs. */
|
|
1478
|
+
getCurrentSubgraphChain() {
|
|
1479
|
+
const chain = [];
|
|
1480
|
+
for (const scope of this.scopes) {
|
|
1481
|
+
if (scope.id) {
|
|
1482
|
+
chain.push(scope.id);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return chain;
|
|
1486
|
+
}
|
|
1487
|
+
/** Finds the nearest enclosing named or generated subgraph. */
|
|
1488
|
+
findCurrentSubgraphId() {
|
|
1489
|
+
for (let index = this.scopes.length - 1; index >= 0; index--) {
|
|
1490
|
+
const scope = this.scopes[index];
|
|
1491
|
+
if (scope.id) {
|
|
1492
|
+
return scope.id;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return void 0;
|
|
1496
|
+
}
|
|
1497
|
+
/** Returns the active attribute scope. */
|
|
1498
|
+
currentScope() {
|
|
1499
|
+
return this.scopes[this.scopes.length - 1];
|
|
1500
|
+
}
|
|
1501
|
+
/** Consumes an optional statement terminator. */
|
|
1502
|
+
consumeOptionalSemicolon() {
|
|
1503
|
+
const next = this.peek();
|
|
1504
|
+
if ((next == null ? void 0 : next.type) === "semicolon") {
|
|
1505
|
+
this.consume();
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
/** Requires the next token to have the requested type. */
|
|
1509
|
+
expect(type) {
|
|
1510
|
+
const token = this.consume();
|
|
1511
|
+
if (!token || token.type !== type) {
|
|
1512
|
+
throw new Error(`Expected token ${type}.`);
|
|
1513
|
+
}
|
|
1514
|
+
return token;
|
|
1515
|
+
}
|
|
1516
|
+
/** Consumes the next token or reports truncated input. */
|
|
1517
|
+
consume() {
|
|
1518
|
+
const token = this.tokens[this.position];
|
|
1519
|
+
if (!token) {
|
|
1520
|
+
throw new Error("Unexpected end of DOT input.");
|
|
1521
|
+
}
|
|
1522
|
+
this.position++;
|
|
1523
|
+
return token;
|
|
1524
|
+
}
|
|
1525
|
+
/** Consumes a token when its type matches. */
|
|
1526
|
+
match(type) {
|
|
1527
|
+
const token = this.peek();
|
|
1528
|
+
if (token && token.type === type) {
|
|
1529
|
+
this.position++;
|
|
1530
|
+
return token;
|
|
1531
|
+
}
|
|
1532
|
+
return null;
|
|
1533
|
+
}
|
|
1534
|
+
/** Inspects a token without consuming it. */
|
|
1535
|
+
peek(offset = 0) {
|
|
1536
|
+
return this.tokens[this.position + offset] ?? null;
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1539
|
+
IDENTIFIER_TERMINATORS = /* @__PURE__ */ new Set(["{", "}", "[", "]", "=", ";", ",", '"', "<", "#"]);
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
|
|
1543
|
+
// src/dot-loader.ts
|
|
1544
|
+
var dot_loader_exports = {};
|
|
1545
|
+
__export(dot_loader_exports, {
|
|
1546
|
+
DOTLoaderWithParser: () => DOTLoaderWithParser
|
|
1547
|
+
});
|
|
1548
|
+
var DOTLoaderWithParser;
|
|
1549
|
+
var init_dot_loader = __esm({
|
|
1550
|
+
"src/dot-loader.ts"() {
|
|
1551
|
+
"use strict";
|
|
1552
|
+
init_dot_loader_types();
|
|
1553
|
+
init_parse_dot();
|
|
1554
|
+
DOTLoaderWithParser = {
|
|
1555
|
+
...DOTLoader,
|
|
1556
|
+
parse: async (data, options) => {
|
|
1557
|
+
var _a;
|
|
1558
|
+
return parseDOT(new TextDecoder().decode(data), ((_a = options == null ? void 0 : options.dot) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
1559
|
+
},
|
|
1560
|
+
parseSync: (data, options) => {
|
|
1561
|
+
var _a;
|
|
1562
|
+
return parseDOT(new TextDecoder().decode(data), ((_a = options == null ? void 0 : options.dot) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
1563
|
+
},
|
|
1564
|
+
parseTextSync: (data, options) => {
|
|
1565
|
+
var _a;
|
|
1566
|
+
return parseDOT(data, ((_a = options == null ? void 0 : options.dot) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
});
|
|
1571
|
+
|
|
1572
|
+
// src/gexf-loader-types.ts
|
|
1573
|
+
async function preload5() {
|
|
1574
|
+
const { GEXFLoaderWithParser: GEXFLoaderWithParser2 } = await Promise.resolve().then(() => (init_gexf_loader(), gexf_loader_exports));
|
|
1575
|
+
return GEXFLoaderWithParser2;
|
|
1576
|
+
}
|
|
1577
|
+
var VERSION5, GEXFLoader;
|
|
1578
|
+
var init_gexf_loader_types = __esm({
|
|
1579
|
+
"src/gexf-loader-types.ts"() {
|
|
1580
|
+
"use strict";
|
|
1581
|
+
VERSION5 = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest";
|
|
1582
|
+
GEXFLoader = {
|
|
1583
|
+
name: "GEXF",
|
|
1584
|
+
id: "gexf",
|
|
1585
|
+
module: "graphs",
|
|
1586
|
+
version: VERSION5,
|
|
1587
|
+
extensions: ["gexf"],
|
|
1588
|
+
mimeTypes: ["application/gexf+xml"],
|
|
1589
|
+
text: true,
|
|
1590
|
+
worker: false,
|
|
1591
|
+
dataType: null,
|
|
1592
|
+
batchType: null,
|
|
1593
|
+
options: { gexf: { shape: "arrow-table" } },
|
|
1594
|
+
preload: preload5
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1014
1598
|
|
|
1015
|
-
//
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1599
|
+
// src/lib/parse-gexf.ts
|
|
1600
|
+
function parseGEXF(input, shape = "arrow-table") {
|
|
1601
|
+
const text = typeof input === "string" ? input : new TextDecoder().decode(input);
|
|
1602
|
+
const validation = import_fast_xml_parser2.XMLValidator.validate(text);
|
|
1603
|
+
if (validation !== true) throw new Error(`Invalid GEXF XML: ${validation.err.msg}`);
|
|
1604
|
+
if (/<!DOCTYPE\s/i.test(text)) throw new Error("GEXF document types are unsupported.");
|
|
1605
|
+
const document = normalizeNamespaces(parser.parse(text), {});
|
|
1606
|
+
const root = getElement(document.gexf, "gexf");
|
|
1607
|
+
const version = root["@_version"];
|
|
1608
|
+
if (version && !["1.2", "1.2draft", "1.3"].includes(version)) {
|
|
1609
|
+
throw new Error(`Unsupported GEXF version: ${version}`);
|
|
1610
|
+
}
|
|
1611
|
+
const graph = getElement(root.graph, "graph");
|
|
1612
|
+
const directed = parseDirection(graph["@_defaultedgetype"] ?? "undirected");
|
|
1613
|
+
const definitions = {
|
|
1614
|
+
nodes: /* @__PURE__ */ new Map(),
|
|
1615
|
+
edges: /* @__PURE__ */ new Map()
|
|
1616
|
+
};
|
|
1617
|
+
for (const attributes of getElements(graph.attributes)) {
|
|
1618
|
+
const domain = attributes["@_class"];
|
|
1619
|
+
if (domain !== "node" && domain !== "edge")
|
|
1620
|
+
throw new Error("GEXF attributes require a node or edge class.");
|
|
1621
|
+
const scoped = domain === "node" ? definitions.nodes : definitions.edges;
|
|
1622
|
+
const names = new Set(Array.from(scoped.values(), (definition) => definition.name));
|
|
1623
|
+
for (const attribute of getElements(attributes.attribute)) {
|
|
1624
|
+
const identifier = requireAttribute(attribute, "id");
|
|
1625
|
+
const name = requireAttribute(attribute, "title");
|
|
1626
|
+
if (name === "gexf" || names.has(name) || scoped.has(identifier)) {
|
|
1627
|
+
throw new Error(`Duplicate or reserved GEXF attribute: ${name}`);
|
|
1628
|
+
}
|
|
1629
|
+
names.add(name);
|
|
1630
|
+
const type = parseAttributeType(requireAttribute(attribute, "type"));
|
|
1631
|
+
scoped.set(identifier, {
|
|
1632
|
+
name,
|
|
1633
|
+
type,
|
|
1634
|
+
defaultValue: attribute.default === void 0 ? void 0 : castValue(readTextContent(attribute.default), type)
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const nodeElements = getElements(getContainer(graph.nodes).node);
|
|
1639
|
+
const edgeElements = getElements(getContainer(graph.edges).edge);
|
|
1640
|
+
const nodeIdentifiers = /* @__PURE__ */ new Set();
|
|
1641
|
+
for (const node of nodeElements) {
|
|
1642
|
+
const identifier = requireAttribute(node, "id");
|
|
1643
|
+
if (nodeIdentifiers.has(identifier)) throw new Error(`Duplicate GEXF node ID: ${identifier}`);
|
|
1644
|
+
nodeIdentifiers.add(identifier);
|
|
1645
|
+
}
|
|
1646
|
+
const edgeIdentifiers = new Set(
|
|
1647
|
+
edgeElements.map((edge) => edge["@_id"]).filter((identifier) => typeof identifier === "string")
|
|
1648
|
+
);
|
|
1649
|
+
const seenEdges = /* @__PURE__ */ new Set();
|
|
1650
|
+
function* iterateNodes3() {
|
|
1651
|
+
for (const node of nodeElements) yield parseRecord(node, definitions.nodes);
|
|
1652
|
+
}
|
|
1653
|
+
function* iterateEdges3() {
|
|
1654
|
+
for (const [index, edge] of edgeElements.entries()) {
|
|
1655
|
+
const sourceId = requireAttribute(edge, "source");
|
|
1656
|
+
const targetId = requireAttribute(edge, "target");
|
|
1657
|
+
if (!nodeIdentifiers.has(sourceId) || !nodeIdentifiers.has(targetId)) {
|
|
1658
|
+
throw new Error("GEXF edge references an undeclared node.");
|
|
1659
|
+
}
|
|
1660
|
+
let identifier = edge["@_id"] === void 0 ? void 0 : requireAttribute(edge, "id");
|
|
1661
|
+
if (identifier === void 0) {
|
|
1662
|
+
identifier = `edge-${index}`;
|
|
1663
|
+
while (edgeIdentifiers.has(identifier)) identifier = `_${identifier}`;
|
|
1664
|
+
edgeIdentifiers.add(identifier);
|
|
1665
|
+
}
|
|
1666
|
+
if (seenEdges.has(identifier)) throw new Error(`Duplicate GEXF edge ID: ${identifier}`);
|
|
1667
|
+
seenEdges.add(identifier);
|
|
1668
|
+
yield {
|
|
1669
|
+
...parseRecord(edge, definitions.edges, identifier),
|
|
1670
|
+
sourceId,
|
|
1671
|
+
targetId,
|
|
1672
|
+
directed: edge["@_type"] === void 0 ? directed : parseDirection(edge["@_type"])
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const metadataAttributes = /* @__PURE__ */ Object.create(null);
|
|
1677
|
+
const meta = getContainer(root.meta);
|
|
1678
|
+
for (const name of ["creator", "description", "keywords"]) {
|
|
1679
|
+
if (meta[name] !== void 0) metadataAttributes[name] = readTextContent(meta[name]);
|
|
1680
|
+
}
|
|
1681
|
+
if (typeof meta["@_lastmodifieddate"] === "string")
|
|
1682
|
+
metadataAttributes.lastmodifieddate = meta["@_lastmodifieddate"];
|
|
1683
|
+
const declarations = {
|
|
1684
|
+
nodes: new Map(
|
|
1685
|
+
Array.from(definitions.nodes.values(), (definition) => [definition.name, definition.type])
|
|
1686
|
+
),
|
|
1687
|
+
edges: new Map(
|
|
1688
|
+
Array.from(definitions.edges.values(), (definition) => [definition.name, definition.type])
|
|
1689
|
+
)
|
|
1690
|
+
};
|
|
1691
|
+
return {
|
|
1692
|
+
...buildGraphTables(iterateNodes3(), iterateEdges3(), shape, declarations),
|
|
1693
|
+
metadata: { version, directed, attributes: metadataAttributes }
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
function normalizeNamespaces(element, inherited) {
|
|
1697
|
+
const namespaces = Object.assign(/* @__PURE__ */ Object.create(null), inherited);
|
|
1698
|
+
for (const [name, value] of Object.entries(element)) {
|
|
1699
|
+
if (name === "@_xmlns") namespaces[""] = String(value);
|
|
1700
|
+
else if (name.startsWith("@_xmlns:")) namespaces[name.slice(8)] = String(value);
|
|
1701
|
+
}
|
|
1702
|
+
const normalized = /* @__PURE__ */ Object.create(null);
|
|
1703
|
+
for (const [name, value] of Object.entries(element)) {
|
|
1704
|
+
if (name.startsWith("@_") || name === "#text" || name.startsWith("?")) {
|
|
1705
|
+
normalized[name] = value;
|
|
1706
|
+
continue;
|
|
1707
|
+
}
|
|
1708
|
+
const separator = name.indexOf(":");
|
|
1709
|
+
const prefix = separator < 0 ? "" : name.slice(0, separator);
|
|
1710
|
+
const localName = name.slice(separator + 1);
|
|
1711
|
+
const children = Array.isArray(value) ? value : [value];
|
|
1712
|
+
for (const child of children) {
|
|
1713
|
+
const object = typeof child === "object" && child !== null ? child : {};
|
|
1714
|
+
const namespace = object[prefix ? `@_xmlns:${prefix}` : "@_xmlns"] ?? namespaces[prefix];
|
|
1715
|
+
const match = typeof namespace === "string" ? GEXF_NAMESPACE.exec(namespace) : null;
|
|
1716
|
+
const key = match ? `${match[1] ? "viz:" : ""}${localName}` : namespace || prefix ? `foreign:${name}` : localName;
|
|
1717
|
+
const childValue = typeof child === "object" && child !== null ? match || !namespace && !prefix ? normalizeNamespaces(object, namespaces) : object : child;
|
|
1718
|
+
if (normalized[key] === void 0) normalized[key] = childValue;
|
|
1719
|
+
else
|
|
1720
|
+
normalized[key] = [
|
|
1721
|
+
...Array.isArray(normalized[key]) ? normalized[key] : [normalized[key]],
|
|
1722
|
+
childValue
|
|
1723
|
+
];
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
assertStaticElement(normalized);
|
|
1727
|
+
return normalized;
|
|
1728
|
+
}
|
|
1729
|
+
function assertStaticElement(element) {
|
|
1730
|
+
if (element["@_mode"] !== void 0 && element["@_mode"] !== "static" || TEMPORAL_ATTRIBUTES.some((name) => element[`@_${name}`] !== void 0) || element.spells !== void 0) {
|
|
1731
|
+
throw new Error("Dynamic GEXF graphs and temporal values are unsupported.");
|
|
1732
|
+
}
|
|
1733
|
+
if (element["@_pid"] !== void 0 || element.parents !== void 0 || element["@_id"] !== void 0 && (element.nodes !== void 0 || element.edges !== void 0)) {
|
|
1734
|
+
throw new Error("Hierarchical GEXF graphs are unsupported.");
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
function getElement(value, name) {
|
|
1738
|
+
if (value === "") return {};
|
|
1739
|
+
if (value && typeof value === "object" && !Array.isArray(value)) return value;
|
|
1740
|
+
throw new Error(`GEXF requires one <${name}> element.`);
|
|
1741
|
+
}
|
|
1742
|
+
function readTextContent(value) {
|
|
1743
|
+
if (typeof value === "string") return value;
|
|
1744
|
+
const text = getElement(value, "text")["#text"];
|
|
1745
|
+
return typeof text === "string" ? text : "";
|
|
1746
|
+
}
|
|
1747
|
+
function getContainer(value) {
|
|
1748
|
+
return value === void 0 ? {} : getElement(value, "container");
|
|
1749
|
+
}
|
|
1750
|
+
function getElements(value) {
|
|
1751
|
+
if (value === void 0) return [];
|
|
1752
|
+
return (Array.isArray(value) ? value : [value]).map((entry) => getElement(entry, "record"));
|
|
1753
|
+
}
|
|
1754
|
+
function requireAttribute(element, name) {
|
|
1755
|
+
const value = element[`@_${name}`];
|
|
1756
|
+
if (typeof value !== "string" || value === "")
|
|
1757
|
+
throw new Error(`GEXF requires a ${name} attribute.`);
|
|
1758
|
+
return value;
|
|
1759
|
+
}
|
|
1760
|
+
function parseDirection(value) {
|
|
1761
|
+
if (value !== "directed" && value !== "undirected")
|
|
1762
|
+
throw new Error(`Unsupported GEXF edge type: ${String(value)}`);
|
|
1763
|
+
return value === "directed";
|
|
1764
|
+
}
|
|
1765
|
+
function parseAttributeType(value) {
|
|
1766
|
+
if (value.startsWith("list") && !value.slice(4).startsWith("list")) {
|
|
1767
|
+
return { list: parseAttributeType(value.slice(4)) };
|
|
1768
|
+
}
|
|
1769
|
+
if (value === "integer" || value === "byte" || value === "short") return "int";
|
|
1770
|
+
if (value === "boolean" || value === "long" || value === "float" || value === "double")
|
|
1771
|
+
return value;
|
|
1772
|
+
if (["string", "anyURI", "date", "char", "bigdecimal", "biginteger"].includes(value))
|
|
1773
|
+
return "string";
|
|
1774
|
+
throw new Error(`Unsupported GEXF attribute type: ${value}`);
|
|
1775
|
+
}
|
|
1776
|
+
function castValue(text, type) {
|
|
1777
|
+
if (typeof type === "object") {
|
|
1778
|
+
return parseListValues(text).map((value) => castValue(value, type.list));
|
|
1779
|
+
}
|
|
1780
|
+
if (type === "string") return text;
|
|
1781
|
+
const trimmed = text.trim();
|
|
1782
|
+
if (type === "boolean") {
|
|
1783
|
+
if (trimmed === "true" || trimmed === "1") return true;
|
|
1784
|
+
if (trimmed === "false" || trimmed === "0") return false;
|
|
1785
|
+
return text;
|
|
1786
|
+
}
|
|
1787
|
+
if (type === "int" || type === "long") {
|
|
1788
|
+
if (!/^[+-]?\d+$/.test(trimmed)) return text;
|
|
1789
|
+
return type === "long" ? BigInt(trimmed) : Number.isSafeInteger(Number(trimmed)) ? Number(trimmed) : text;
|
|
1790
|
+
}
|
|
1791
|
+
if (trimmed === "INF") return Infinity;
|
|
1792
|
+
if (trimmed === "-INF") return -Infinity;
|
|
1793
|
+
if (trimmed === "NaN") return NaN;
|
|
1794
|
+
if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(trimmed)) return text;
|
|
1795
|
+
const number = Number(trimmed);
|
|
1796
|
+
const represented = type === "float" ? Math.fround(number) : number;
|
|
1797
|
+
const nonzero = /[1-9]/.test(trimmed.split(/[eE]/)[0]);
|
|
1798
|
+
return Number.isFinite(represented) && (represented !== 0 || !nonzero) ? number : text;
|
|
1799
|
+
}
|
|
1800
|
+
function parseListValues(text) {
|
|
1801
|
+
const trimmed = text.trim();
|
|
1802
|
+
const bracketed = trimmed.startsWith("[");
|
|
1803
|
+
if (bracketed !== trimmed.endsWith("]")) throw new Error("Invalid GEXF list brackets.");
|
|
1804
|
+
const contents = bracketed ? trimmed.slice(1, -1) : trimmed;
|
|
1805
|
+
if (!contents.trim()) return [];
|
|
1806
|
+
const values = [];
|
|
1807
|
+
let value = "";
|
|
1808
|
+
let quote = "";
|
|
1809
|
+
let quoted = false;
|
|
1810
|
+
let closed = false;
|
|
1811
|
+
for (let index = 0; index < contents.length; index++) {
|
|
1812
|
+
const character = contents[index];
|
|
1813
|
+
if (quote) {
|
|
1814
|
+
if (character === "\\" && (contents[index + 1] === quote || contents[index + 1] === "\\")) {
|
|
1815
|
+
value += contents[++index];
|
|
1816
|
+
} else if (character === quote) {
|
|
1817
|
+
quote = "";
|
|
1818
|
+
closed = true;
|
|
1819
|
+
} else value += character;
|
|
1820
|
+
} else if (character === "," || !bracketed && (character === "|" || character === ";")) {
|
|
1821
|
+
values.push(quoted ? value : value.trim());
|
|
1822
|
+
value = "";
|
|
1823
|
+
quoted = false;
|
|
1824
|
+
closed = false;
|
|
1825
|
+
} else if (closed) {
|
|
1826
|
+
if (character.trim()) throw new Error("Invalid GEXF list text after a quote.");
|
|
1827
|
+
} else if ((character === "'" || character === '"') && !value.trim()) {
|
|
1828
|
+
quote = character;
|
|
1829
|
+
quoted = true;
|
|
1830
|
+
value = "";
|
|
1831
|
+
} else value += character;
|
|
1832
|
+
}
|
|
1833
|
+
if (quote) throw new Error("Unterminated GEXF list quote.");
|
|
1834
|
+
values.push(quoted ? value : value.trim());
|
|
1835
|
+
return values;
|
|
1836
|
+
}
|
|
1837
|
+
function parseRecord(element, definitions, identifier) {
|
|
1838
|
+
const attributes = /* @__PURE__ */ Object.create(null);
|
|
1839
|
+
for (const definition of definitions.values()) {
|
|
1840
|
+
if (definition.defaultValue !== void 0)
|
|
1841
|
+
attributes[definition.name] = Array.isArray(definition.defaultValue) ? [...definition.defaultValue] : definition.defaultValue;
|
|
1842
|
+
}
|
|
1843
|
+
for (const attvalue of getElements(getContainer(element.attvalues).attvalue)) {
|
|
1844
|
+
const key = requireAttribute(attvalue, "for");
|
|
1845
|
+
const definition = definitions.get(key);
|
|
1846
|
+
if (!definition) throw new Error(`Undeclared GEXF attribute: ${key}`);
|
|
1847
|
+
attributes[definition.name] = castValue(requireValue(attvalue), definition.type);
|
|
1848
|
+
}
|
|
1849
|
+
const native = /* @__PURE__ */ Object.create(null);
|
|
1850
|
+
if (element["@_weight"] !== void 0)
|
|
1851
|
+
native.weight = castValue(String(element["@_weight"]), "double");
|
|
1852
|
+
if (element["@_kind"] !== void 0) native.kind = element["@_kind"];
|
|
1853
|
+
const visualization = /* @__PURE__ */ Object.create(null);
|
|
1854
|
+
for (const name of ["color", "position", "size", "shape", "thickness"]) {
|
|
1855
|
+
const value = element[`viz:${name}`];
|
|
1856
|
+
if (value === void 0) continue;
|
|
1857
|
+
const properties = /* @__PURE__ */ Object.create(null);
|
|
1858
|
+
for (const [key, property] of Object.entries(getElement(value, `viz:${name}`))) {
|
|
1859
|
+
if (key.startsWith("@_") && !key.startsWith("@_xmlns")) {
|
|
1860
|
+
const propertyName = key.slice(2);
|
|
1861
|
+
properties[propertyName] = ["r", "g", "b", "a", "x", "y", "z"].includes(propertyName) || propertyName === "value" && (name === "size" || name === "thickness") ? castValue(String(property), "double") : property;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
visualization[name] = properties;
|
|
1865
|
+
}
|
|
1866
|
+
if (Object.keys(visualization).length) native.viz = visualization;
|
|
1867
|
+
if (Object.keys(native).length) attributes.gexf = native;
|
|
1868
|
+
return {
|
|
1869
|
+
id: identifier ?? requireAttribute(element, "id"),
|
|
1870
|
+
label: element["@_label"],
|
|
1871
|
+
attributes
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
function requireValue(element) {
|
|
1875
|
+
const value = element["@_value"];
|
|
1876
|
+
if (typeof value !== "string") throw new Error("GEXF attvalue requires a value attribute.");
|
|
1877
|
+
return value;
|
|
1878
|
+
}
|
|
1879
|
+
var import_fast_xml_parser2, parser, GEXF_NAMESPACE, TEMPORAL_ATTRIBUTES;
|
|
1880
|
+
var init_parse_gexf = __esm({
|
|
1881
|
+
"src/lib/parse-gexf.ts"() {
|
|
1882
|
+
"use strict";
|
|
1883
|
+
import_fast_xml_parser2 = require("fast-xml-parser");
|
|
1884
|
+
init_build_graph_tables();
|
|
1885
|
+
parser = new import_fast_xml_parser2.XMLParser({
|
|
1886
|
+
ignoreAttributes: false,
|
|
1887
|
+
parseAttributeValue: false,
|
|
1888
|
+
parseTagValue: false,
|
|
1889
|
+
trimValues: false
|
|
1890
|
+
});
|
|
1891
|
+
GEXF_NAMESPACE = /^https?:\/\/(?:www\.)?gexf.net\/1\.(?:2(?:draft)?|3)(\/viz)?$/;
|
|
1892
|
+
TEMPORAL_ATTRIBUTES = [
|
|
1893
|
+
"start",
|
|
1894
|
+
"end",
|
|
1895
|
+
"startopen",
|
|
1896
|
+
"endopen",
|
|
1897
|
+
"timestamp",
|
|
1898
|
+
"timestamps",
|
|
1899
|
+
"intervals"
|
|
1900
|
+
];
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
|
|
1904
|
+
// src/gexf-loader.ts
|
|
1905
|
+
var gexf_loader_exports = {};
|
|
1906
|
+
__export(gexf_loader_exports, {
|
|
1907
|
+
GEXFLoaderWithParser: () => GEXFLoaderWithParser
|
|
1908
|
+
});
|
|
1909
|
+
var GEXFLoaderWithParser;
|
|
1910
|
+
var init_gexf_loader = __esm({
|
|
1911
|
+
"src/gexf-loader.ts"() {
|
|
1912
|
+
"use strict";
|
|
1913
|
+
init_gexf_loader_types();
|
|
1914
|
+
init_parse_gexf();
|
|
1915
|
+
GEXFLoaderWithParser = {
|
|
1916
|
+
...GEXFLoader,
|
|
1917
|
+
parse: async (data, options) => {
|
|
1918
|
+
var _a;
|
|
1919
|
+
return parseGEXF(data, ((_a = options == null ? void 0 : options.gexf) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
1920
|
+
},
|
|
1921
|
+
parseSync: (data, options) => {
|
|
1922
|
+
var _a;
|
|
1923
|
+
return parseGEXF(data, ((_a = options == null ? void 0 : options.gexf) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
1924
|
+
},
|
|
1925
|
+
parseTextSync: (data, options) => {
|
|
1926
|
+
var _a;
|
|
1927
|
+
return parseGEXF(data, ((_a = options == null ? void 0 : options.gexf) == null ? void 0 : _a.shape) ?? "arrow-table");
|
|
1928
|
+
}
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
// dist/index.js
|
|
1934
|
+
var index_exports = {};
|
|
1935
|
+
__export(index_exports, {
|
|
1936
|
+
DOTLoader: () => DOTLoader2,
|
|
1937
|
+
GEXFLoader: () => GEXFLoader2,
|
|
1938
|
+
GraphMLLoader: () => GraphMLLoader2
|
|
1939
|
+
});
|
|
1940
|
+
module.exports = __toCommonJS(index_exports);
|
|
1941
|
+
|
|
1942
|
+
// dist/graphml-loader-types.js
|
|
1943
|
+
var VERSION2 = true ? "5.0.0-alpha.10" : "latest";
|
|
1944
|
+
async function preload2() {
|
|
1945
|
+
const { GraphMLLoaderWithParser: GraphMLLoaderWithParser2 } = await Promise.resolve().then(() => (init_graphml_loader(), graphml_loader_exports));
|
|
1946
|
+
return GraphMLLoaderWithParser2;
|
|
1947
|
+
}
|
|
1948
|
+
var GraphMLLoader2 = {
|
|
1949
|
+
name: "GraphML",
|
|
1950
|
+
id: "graphml",
|
|
1951
|
+
module: "graphs",
|
|
1952
|
+
version: VERSION2,
|
|
1953
|
+
extensions: ["graphml"],
|
|
1954
|
+
mimeTypes: ["application/graphml+xml"],
|
|
1955
|
+
text: true,
|
|
1956
|
+
worker: false,
|
|
1957
|
+
dataType: null,
|
|
1958
|
+
batchType: null,
|
|
1959
|
+
options: { graphml: { shape: "arrow-table" } },
|
|
1960
|
+
preload: preload2
|
|
1961
|
+
};
|
|
1962
|
+
|
|
1963
|
+
// dist/dot-loader-types.js
|
|
1964
|
+
var VERSION4 = true ? "5.0.0-alpha.10" : "latest";
|
|
1965
|
+
async function preload4() {
|
|
1966
|
+
const { DOTLoaderWithParser: DOTLoaderWithParser2 } = await Promise.resolve().then(() => (init_dot_loader(), dot_loader_exports));
|
|
1967
|
+
return DOTLoaderWithParser2;
|
|
1968
|
+
}
|
|
1969
|
+
var DOTLoader2 = {
|
|
1970
|
+
name: "DOT",
|
|
1971
|
+
id: "dot",
|
|
1972
|
+
module: "graphs",
|
|
1973
|
+
version: VERSION4,
|
|
1974
|
+
extensions: ["dot", "gv"],
|
|
1975
|
+
mimeTypes: ["text/vnd.graphviz", "text/x-graphviz", "application/vnd.graphviz"],
|
|
1976
|
+
text: true,
|
|
1977
|
+
worker: false,
|
|
1978
|
+
dataType: null,
|
|
1979
|
+
batchType: null,
|
|
1980
|
+
options: { dot: { shape: "arrow-table" } },
|
|
1981
|
+
preload: preload4
|
|
1982
|
+
};
|
|
1983
|
+
|
|
1984
|
+
// dist/gexf-loader-types.js
|
|
1985
|
+
var VERSION6 = true ? "5.0.0-alpha.10" : "latest";
|
|
1986
|
+
async function preload6() {
|
|
1987
|
+
const { GEXFLoaderWithParser: GEXFLoaderWithParser2 } = await Promise.resolve().then(() => (init_gexf_loader(), gexf_loader_exports));
|
|
1988
|
+
return GEXFLoaderWithParser2;
|
|
1989
|
+
}
|
|
1990
|
+
var GEXFLoader2 = {
|
|
1991
|
+
name: "GEXF",
|
|
1992
|
+
id: "gexf",
|
|
1993
|
+
module: "graphs",
|
|
1994
|
+
version: VERSION6,
|
|
1995
|
+
extensions: ["gexf"],
|
|
1996
|
+
mimeTypes: ["application/gexf+xml"],
|
|
1997
|
+
text: true,
|
|
1998
|
+
worker: false,
|
|
1999
|
+
dataType: null,
|
|
2000
|
+
batchType: null,
|
|
2001
|
+
options: { gexf: { shape: "arrow-table" } },
|
|
2002
|
+
preload: preload6
|
|
1021
2003
|
};
|
|
1022
2004
|
//# sourceMappingURL=index.cjs.map
|