@kgdiff/core 0.1.0 → 0.3.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.
- package/README.md +13 -0
- package/dist/graph-json.d.ts +3 -2
- package/dist/graph-json.d.ts.map +1 -1
- package/dist/graph-json.js +7 -3
- package/dist/graph-json.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/normalize.d.ts +4 -1
- package/dist/normalize.d.ts.map +1 -1
- package/dist/normalize.js +21 -1
- package/dist/normalize.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
- package/schema/delta.v1.json +176 -0
- package/schema/diff.v1.json +566 -0
- package/schema/graph.v1.json +154 -0
- package/src/graph-json.ts +7 -3
- package/src/index.ts +8 -3
- package/src/normalize.ts +28 -1
- package/src/types.ts +9 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://cdn.jsdelivr.net/npm/@kgdiff/core/schema/graph.v1.json",
|
|
4
|
+
"title": "kgdiff graph, version 1",
|
|
5
|
+
"description": "A graph in kgdiff JSON, as graphToJSON and `kgdiff apply` write it. kgdiff also reads looser JSON (other field names, numeric ids, loose fields as attributes); this schema describes the canonical form. See https://github.com/iiwish/kgdiff/blob/main/docs/formats.md#kgdiff-json",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The URI of this schema, for editors."
|
|
11
|
+
},
|
|
12
|
+
"format": {
|
|
13
|
+
"const": "kgdiff.graph",
|
|
14
|
+
"description": "Format marker. Optional, together with version; it makes readers recognise the file for certain."
|
|
15
|
+
},
|
|
16
|
+
"version": {
|
|
17
|
+
"const": 1,
|
|
18
|
+
"description": "Version of the format (not of the graph: that is meta.version). Readers reject versions they do not support."
|
|
19
|
+
},
|
|
20
|
+
"meta": { "$ref": "#/$defs/meta" },
|
|
21
|
+
"nodes": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"items": { "$ref": "#/$defs/node" }
|
|
24
|
+
},
|
|
25
|
+
"edges": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": { "$ref": "#/$defs/edge" }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"required": ["nodes"],
|
|
31
|
+
"dependentRequired": {
|
|
32
|
+
"format": ["version"],
|
|
33
|
+
"version": ["format"]
|
|
34
|
+
},
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"$defs": {
|
|
37
|
+
"id": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"minLength": 1,
|
|
40
|
+
"description": "A node id: an IRI, a CURIE such as GO:0008150, or any non-empty string."
|
|
41
|
+
},
|
|
42
|
+
"ids": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": { "$ref": "#/$defs/id" }
|
|
45
|
+
},
|
|
46
|
+
"strings": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"items": { "type": "string" }
|
|
49
|
+
},
|
|
50
|
+
"attrs": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"description": "Any other values, by name: strings, numbers, booleans, null, arrays and objects."
|
|
53
|
+
},
|
|
54
|
+
"meta": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"description": "About the graph. Other keys are kept as they are.",
|
|
57
|
+
"properties": {
|
|
58
|
+
"name": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "Shown in headers and titles."
|
|
61
|
+
},
|
|
62
|
+
"version": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "The version of the graph, shown in headers and titles."
|
|
65
|
+
},
|
|
66
|
+
"source": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "Where the graph came from: a file name, a URL…"
|
|
69
|
+
},
|
|
70
|
+
"predicateLabels": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"additionalProperties": { "type": "string" },
|
|
73
|
+
"description": "Display names of relations, by predicate."
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"node": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"properties": {
|
|
80
|
+
"id": { "$ref": "#/$defs/id" },
|
|
81
|
+
"label": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"description": "The display name; defaults to the id. A changed label is a rename."
|
|
84
|
+
},
|
|
85
|
+
"type": {
|
|
86
|
+
"anyOf": [{ "type": "string" }, { "$ref": "#/$defs/strings" }],
|
|
87
|
+
"description": "A type, or several (class, category, rdf:type)."
|
|
88
|
+
},
|
|
89
|
+
"types": {
|
|
90
|
+
"$ref": "#/$defs/strings",
|
|
91
|
+
"description": "The types, as graphToJSON writes them. Use type or types, not both."
|
|
92
|
+
},
|
|
93
|
+
"aliases": {
|
|
94
|
+
"$ref": "#/$defs/strings",
|
|
95
|
+
"description": "Synonyms."
|
|
96
|
+
},
|
|
97
|
+
"attrs": { "$ref": "#/$defs/attrs" },
|
|
98
|
+
"deprecated": {
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"description": "True for an obsolete node that is still present."
|
|
101
|
+
},
|
|
102
|
+
"replaces": {
|
|
103
|
+
"$ref": "#/$defs/ids",
|
|
104
|
+
"description": "Ids of the nodes this one replaces in the other version: a strong merge hint."
|
|
105
|
+
},
|
|
106
|
+
"replacedBy": {
|
|
107
|
+
"$ref": "#/$defs/ids",
|
|
108
|
+
"description": "Ids of the nodes that replace this one: a strong merge hint."
|
|
109
|
+
},
|
|
110
|
+
"implicit": {
|
|
111
|
+
"type": "boolean",
|
|
112
|
+
"description": "Written by graphToJSON for a node created at an edge end the graph does not describe."
|
|
113
|
+
},
|
|
114
|
+
"position": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"properties": {
|
|
117
|
+
"x": { "type": "number" },
|
|
118
|
+
"y": { "type": "number" }
|
|
119
|
+
},
|
|
120
|
+
"required": ["x", "y"],
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"description": "A pinned layout position. Never part of the diff."
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"required": ["id"],
|
|
126
|
+
"dependentSchemas": {
|
|
127
|
+
"types": { "type": "object", "properties": { "type": false } }
|
|
128
|
+
},
|
|
129
|
+
"additionalProperties": false
|
|
130
|
+
},
|
|
131
|
+
"edge": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"properties": {
|
|
134
|
+
"id": {
|
|
135
|
+
"$ref": "#/$defs/id",
|
|
136
|
+
"description": "An explicit edge id, used for identity only when edges are identified by id."
|
|
137
|
+
},
|
|
138
|
+
"source": { "$ref": "#/$defs/id" },
|
|
139
|
+
"target": { "$ref": "#/$defs/id" },
|
|
140
|
+
"predicate": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"description": "The relation: an IRI, a CURIE or a plain name. Defaults to the empty string."
|
|
143
|
+
},
|
|
144
|
+
"label": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"description": "Display label of the relation; defaults to a readable form of the predicate."
|
|
147
|
+
},
|
|
148
|
+
"attrs": { "$ref": "#/$defs/attrs" }
|
|
149
|
+
},
|
|
150
|
+
"required": ["source", "target"],
|
|
151
|
+
"additionalProperties": false
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
package/src/graph-json.ts
CHANGED
|
@@ -35,8 +35,9 @@ export function edgeToJSON(
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Canonical JSON form of a graph:
|
|
39
|
-
* attribute keys sorted. `normalize(graphToJSON(g))` rebuilds
|
|
38
|
+
* Canonical JSON form of a graph: the `kgdiff.graph` version 1 marker, nodes sorted by id,
|
|
39
|
+
* edges by key, defaults omitted, attribute keys sorted. `normalize(graphToJSON(g))` rebuilds
|
|
40
|
+
* `g` (positions excepted).
|
|
40
41
|
*/
|
|
41
42
|
export function graphToJSON(graph: KGraph): GraphJSON {
|
|
42
43
|
const nodes = [...graph.nodes.values()]
|
|
@@ -45,7 +46,10 @@ export function graphToJSON(graph: KGraph): GraphJSON {
|
|
|
45
46
|
const edges = [...graph.edges.values()]
|
|
46
47
|
.sort((a, b) => compareStrings(a.key, b.key))
|
|
47
48
|
.map((edge) => edgeToJSON(edge));
|
|
48
|
-
|
|
49
|
+
const marker = { format: 'kgdiff.graph', version: 1 } as const;
|
|
50
|
+
return Object.keys(graph.meta).length
|
|
51
|
+
? { ...marker, meta: graph.meta, nodes, edges }
|
|
52
|
+
: { ...marker, nodes, edges };
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
/**
|
package/src/index.ts
CHANGED
|
@@ -68,9 +68,11 @@ export function resolveOptions(options?: DiffOptions): ResolvedDiffOptions {
|
|
|
68
68
|
*
|
|
69
69
|
* Labels default to ids; sets are sorted and de-duplicated; duplicate nodes are merged;
|
|
70
70
|
* parallel edges are keyed `#2`, `#3`… in content order; dangling endpoints follow
|
|
71
|
-
* `danglingEdges` (default `create`). Problems are reported in `issues`.
|
|
71
|
+
* `danglingEdges` (default `create`). Problems are reported in `issues`. The kgdiff JSON
|
|
72
|
+
* marker (`format: 'kgdiff.graph'`, `version: 1`) is optional.
|
|
72
73
|
*
|
|
73
|
-
* @throws KgdiffError `invalid-input` for malformed input, `dangling-edge` (policy `error`)
|
|
74
|
+
* @throws KgdiffError `invalid-input` for malformed input, `dangling-edge` (policy `error`),
|
|
75
|
+
* `unsupported-version` for another kgdiff format or graph version.
|
|
74
76
|
*/
|
|
75
77
|
export function normalize(input: GraphInput, options?: NormalizeOptions): KGraph {
|
|
76
78
|
return normalizeGraph(input, options);
|
|
@@ -129,7 +131,10 @@ export function hashGraph(graph: KGraph): string {
|
|
|
129
131
|
return hash(graph);
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Canonical JSON form of a graph: kgdiff JSON with its `kgdiff.graph` version 1 marker, sorted,
|
|
136
|
+
* defaults omitted. Accepted by `normalize`.
|
|
137
|
+
*/
|
|
133
138
|
export function graphToJSON(graph: KGraph): GraphJSON {
|
|
134
139
|
return toGraphJSON(graph);
|
|
135
140
|
}
|
package/src/normalize.ts
CHANGED
|
@@ -77,6 +77,29 @@ function fail(message: string, details?: Record<string, unknown>): never {
|
|
|
77
77
|
throw new KgdiffError('invalid-input', message, details);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Checks the optional kgdiff JSON marker. Only `kgdiff.*` formats are kgdiff's: a `format`
|
|
82
|
+
* field with any other value belongs to someone else and is ignored, like other unknown keys.
|
|
83
|
+
*/
|
|
84
|
+
function checkFormat(input: Readonly<Record<string, unknown>>): void {
|
|
85
|
+
const { format, version } = input;
|
|
86
|
+
if (typeof format !== 'string' || !format.startsWith('kgdiff.')) return;
|
|
87
|
+
if (format !== 'kgdiff.graph') {
|
|
88
|
+
throw new KgdiffError(
|
|
89
|
+
'unsupported-version',
|
|
90
|
+
`Expected a kgdiff graph (format "kgdiff.graph"), got format "${format}"`,
|
|
91
|
+
{ format },
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (version !== 1) {
|
|
95
|
+
throw new KgdiffError(
|
|
96
|
+
'unsupported-version',
|
|
97
|
+
`Unsupported graph version ${String(version)}; this reader supports version 1`,
|
|
98
|
+
{ version },
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
80
103
|
/** Ids are strings; finite numbers are accepted and stringified. Empty → undefined. */
|
|
81
104
|
function coerceId(value: unknown): Id | undefined {
|
|
82
105
|
if (typeof value === 'string') return value === '' ? undefined : value;
|
|
@@ -339,10 +362,14 @@ export function isKGraph(value: unknown): value is KGraph {
|
|
|
339
362
|
* field-wise. Both cases record a `duplicate-edge` issue.
|
|
340
363
|
* - Dangling endpoints (`danglingEdges`, default `create`): `create` adds an implicit node,
|
|
341
364
|
* `drop` discards the edge, `error` throws `KgdiffError('dangling-edge')`.
|
|
365
|
+
* - The kgdiff JSON marker is optional; a `kgdiff.*` format other than `kgdiff.graph`, or a
|
|
366
|
+
* version other than 1, is rejected.
|
|
342
367
|
*
|
|
343
|
-
* @throws KgdiffError `invalid-input` for malformed input, `dangling-edge` (policy `error`)
|
|
368
|
+
* @throws KgdiffError `invalid-input` for malformed input, `dangling-edge` (policy `error`),
|
|
369
|
+
* `unsupported-version` for another kgdiff format or graph version.
|
|
344
370
|
*/
|
|
345
371
|
export function normalize(input: GraphInput, options: NormalizeOptions = {}): KGraph {
|
|
372
|
+
if (isRecord(input)) checkFormat(input);
|
|
346
373
|
if (!isRecord(input) || !Array.isArray(input.nodes)) {
|
|
347
374
|
fail('normalize expects a graph of the form { nodes: [...], edges: [...] }');
|
|
348
375
|
}
|
package/src/types.ts
CHANGED
|
@@ -105,6 +105,12 @@ export interface GraphMeta {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export interface GraphInput {
|
|
108
|
+
/**
|
|
109
|
+
* Format marker of kgdiff JSON, as `graphToJSON` writes it. Optional; when present, `version`
|
|
110
|
+
* must be 1 (anything else is rejected with `unsupported-version`).
|
|
111
|
+
*/
|
|
112
|
+
readonly format?: 'kgdiff.graph';
|
|
113
|
+
readonly version?: 1;
|
|
108
114
|
readonly nodes: readonly NodeInput[];
|
|
109
115
|
readonly edges: readonly EdgeInput[];
|
|
110
116
|
readonly meta?: GraphMeta;
|
|
@@ -618,7 +624,10 @@ export interface DiffResult {
|
|
|
618
624
|
|
|
619
625
|
// ─── JSON transport ─────────────────────────────────────────────────────────
|
|
620
626
|
|
|
627
|
+
/** kgdiff JSON (docs/formats.md §kgdiff JSON), schema `schema/graph.v1.json`. */
|
|
621
628
|
export interface GraphJSON {
|
|
629
|
+
readonly format: 'kgdiff.graph';
|
|
630
|
+
readonly version: 1;
|
|
622
631
|
readonly meta?: GraphMeta;
|
|
623
632
|
readonly nodes: readonly NodeJSON[];
|
|
624
633
|
readonly edges: readonly EdgeJSON[];
|