@_linked/server 3.13.0 → 3.14.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/CHANGELOG.md +33 -0
- package/lib/esm/backend.d.ts +23 -5
- package/lib/esm/backend.js +116 -25
- package/lib/esm/backend.js.map +1 -1
- package/lib/esm/shapes/LincdAPI.d.ts +1 -1
- package/lib/esm/shapes/LincdAPI.js +43 -7
- package/lib/esm/shapes/LincdAPI.js.map +1 -1
- package/lib/esm/shapes/LinkedServer.d.ts +50 -37
- package/lib/esm/shapes/LinkedServer.js +121 -76
- package/lib/esm/shapes/LinkedServer.js.map +1 -1
- package/lib/esm/shapes/quadstores/RemoteDataset.d.ts +35 -6
- package/lib/esm/shapes/quadstores/RemoteDataset.js +77 -12
- package/lib/esm/shapes/quadstores/RemoteDataset.js.map +1 -1
- package/lib/esm/utils/adminChannel.d.ts +22 -51
- package/lib/esm/utils/adminChannel.js +48 -36
- package/lib/esm/utils/adminChannel.js.map +1 -1
- package/lib/esm/utils/effectiveRoutes.d.ts +6 -0
- package/lib/esm/utils/effectiveRoutes.js +2 -1
- package/lib/esm/utils/effectiveRoutes.js.map +1 -1
- package/lib/esm/utils/forwardedTransactions.d.ts +46 -0
- package/lib/esm/utils/forwardedTransactions.js +155 -0
- package/lib/esm/utils/forwardedTransactions.js.map +1 -0
- package/lib/esm/utils/graphTargets.d.ts +37 -20
- package/lib/esm/utils/graphTargets.js +61 -82
- package/lib/esm/utils/graphTargets.js.map +1 -1
- package/lib/esm/utils/ontologySync.d.ts +40 -23
- package/lib/esm/utils/ontologySync.js +59 -37
- package/lib/esm/utils/ontologySync.js.map +1 -1
- package/lib/esm/utils/queryPlane.d.ts +13 -0
- package/lib/esm/utils/queryPlane.js +49 -2
- package/lib/esm/utils/queryPlane.js.map +1 -1
- package/lib/esm/utils/reloadSource.js +1 -1
- package/lib/esm/utils/reloadSource.js.map +1 -1
- package/lib/esm/utils/serverTransactions.d.ts +22 -0
- package/lib/esm/utils/serverTransactions.js +81 -0
- package/lib/esm/utils/serverTransactions.js.map +1 -0
- package/lib/esm/utils/syncShapes.d.ts +120 -8
- package/lib/esm/utils/syncShapes.js +151 -32
- package/lib/esm/utils/syncShapes.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +134 -24
|
@@ -11,12 +11,11 @@
|
|
|
11
11
|
* `.from(graphs.allOntologies)` reaches the backend as the portable name, and the backend's
|
|
12
12
|
* store expands it under this setting.
|
|
13
13
|
*
|
|
14
|
-
* The syncs
|
|
15
|
-
*
|
|
14
|
+
* The syncs follow the storage routing (core's `syncTargets`); {@link syncTargetsLine} names the
|
|
15
|
+
* datasets they write into when that is more than the default dataset.
|
|
16
16
|
*/
|
|
17
17
|
import { GRAPH_TARGET_ENV, graphTargetOptionsFromEnv, setGraphTargets, } from '@_linked/core/utils/graphTargets';
|
|
18
18
|
import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
|
|
19
|
-
import { getAllShapeClasses } from '@_linked/core/utils/ShapeClass';
|
|
20
19
|
/**
|
|
21
20
|
* Read `LINKED_ONTOLOGY_GRAPH` / `LINKED_SHAPE_GRAPH` from `env` (checked: an invalid value throws,
|
|
22
21
|
* naming the variable) and set them as the process's graph targets (core's `setGraphTargets`;
|
|
@@ -27,6 +26,29 @@ export function applyGraphTargetsFromEnv(env) {
|
|
|
27
26
|
setGraphTargets(fromEnv);
|
|
28
27
|
return fromEnv;
|
|
29
28
|
}
|
|
29
|
+
/** The variable that says who writes the app's shape and ontology graphs. */
|
|
30
|
+
export const GRAPH_WRITER_ENV = 'LINKED_GRAPH_WRITER';
|
|
31
|
+
/**
|
|
32
|
+
* `LINKED_GRAPH_WRITER` from `env`: unset or empty is `'app'`; any value other than `app` or
|
|
33
|
+
* `host` throws, naming the variable (the boot stops on it).
|
|
34
|
+
*/
|
|
35
|
+
export function graphWriterFromEnv(env) {
|
|
36
|
+
var _a;
|
|
37
|
+
const value = (_a = env[GRAPH_WRITER_ENV]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
38
|
+
if (!value || value === 'app')
|
|
39
|
+
return 'app';
|
|
40
|
+
if (value === 'host')
|
|
41
|
+
return 'host';
|
|
42
|
+
throw new Error(`${GRAPH_WRITER_ENV} must be 'app' or 'host' (who writes the app's shape and ontology graphs), not ${JSON.stringify(value)}`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether this process writes its shape and ontology graphs: the one check every write path of the
|
|
46
|
+
* syncs goes through (the boot's ontology and shape syncs). False
|
|
47
|
+
* under `LINKED_GRAPH_WRITER=host`.
|
|
48
|
+
*/
|
|
49
|
+
export function appWritesGraphs(env = process.env) {
|
|
50
|
+
return graphWriterFromEnv(env) === 'app';
|
|
51
|
+
}
|
|
30
52
|
/** The env variables as set (unset and empty ones left out), for `GET /admin/routes`. */
|
|
31
53
|
export function graphTargetEnv(env) {
|
|
32
54
|
const out = {};
|
|
@@ -37,91 +59,48 @@ export function graphTargetEnv(env) {
|
|
|
37
59
|
}
|
|
38
60
|
return out;
|
|
39
61
|
}
|
|
40
|
-
const SH = 'http://www.w3.org/ns/shacl#';
|
|
41
|
-
const RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
|
|
42
|
-
const RDFS = 'http://www.w3.org/2000/01/rdf-schema#';
|
|
43
|
-
const OWL = 'http://www.w3.org/2002/07/owl#';
|
|
44
|
-
/** The classes of the shape shapes: what the shape sync writes. */
|
|
45
|
-
export const SHAPE_SHAPE_CLASSES = [`${SH}NodeShape`, `${SH}PropertyShape`];
|
|
46
|
-
/**
|
|
47
|
-
* The classes of the ontology term shapes (the class and property shapes core, `@_linked/rdfs`
|
|
48
|
-
* and `@_linked/owl` register): what the ontology sync writes and the hierarchy reads.
|
|
49
|
-
*/
|
|
50
|
-
export const ONTOLOGY_TERM_CLASSES = [
|
|
51
|
-
`${RDFS}Class`,
|
|
52
|
-
`${OWL}Class`,
|
|
53
|
-
`${OWL}Restriction`,
|
|
54
|
-
`${RDF}Property`,
|
|
55
|
-
`${OWL}ObjectProperty`,
|
|
56
|
-
`${OWL}DatatypeProperty`,
|
|
57
|
-
`${OWL}AnnotationProperty`,
|
|
58
|
-
];
|
|
59
62
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const label = typeof id === 'string' && id ? id : typeof name === 'string' && name ? name : undefined;
|
|
68
|
-
return label ? `${store} "${label}"` : store;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* The class the instances of `shapeClass` carry, when it is watched, else undefined: the
|
|
72
|
-
* `targetClass` of the shape itself or, when it declares none of its own, of the nearest
|
|
73
|
-
* super-shape that does. A sub-shape with its own target class is not its parent's class: the
|
|
74
|
-
* syncs' data of the parent's class never reads through it.
|
|
75
|
-
*/
|
|
76
|
-
function watchedClassOf(shapeClass, watched) {
|
|
77
|
-
var _a;
|
|
78
|
-
for (let current = shapeClass; typeof current === 'function' && current !== Function.prototype; current = Object.getPrototypeOf(current)) {
|
|
79
|
-
if (!Object.prototype.hasOwnProperty.call(current, 'targetClass'))
|
|
80
|
-
continue;
|
|
81
|
-
const id = (_a = current.targetClass) === null || _a === void 0 ? void 0 : _a.id;
|
|
82
|
-
return typeof id === 'string' && watched.has(id) ? id : undefined;
|
|
83
|
-
}
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* One warning when routing pins a shape a boot sync that runs writes (the shape shapes
|
|
88
|
-
* `NodeShape` / `PropertyShape` for the shape sync, an ontology term shape such as `rdfs:Class` /
|
|
89
|
-
* `owl:ObjectProperty` for the ontology sync) to a dataset other than the default one: the syncs
|
|
90
|
-
* write into the default dataset only, so reads of those shapes on the pinned dataset miss what
|
|
91
|
-
* they wrote. Names each such shape and its dataset; a sub-shape counts under the target class it
|
|
92
|
-
* declares, or its parent's when it declares none.
|
|
93
|
-
* Undefined when there is nothing to warn about (no sync runs, or no default dataset). Reads the
|
|
63
|
+
* One log line naming the datasets the boot syncs that run write into, per graph kind, when the
|
|
64
|
+
* routing sends any graph beyond the default dataset (core's `graphSyncTargets`: a package's
|
|
65
|
+
* shape graph goes wherever its shapes or the shape shapes route, its ontology graph also
|
|
66
|
+
* wherever shapes using its classes or the term shapes route, the app's graph wherever shapes
|
|
67
|
+
* declaring `contains`, or their sub-shapes, route). Undefined when every graph goes to the default dataset only (the
|
|
68
|
+
* syncs' own lines say enough), when no sync runs or without a default dataset. `core` is
|
|
69
|
+
* `@_linked/core/shapes/syncTargets` (server only, imported when the boot runs). Reads the
|
|
94
70
|
* registered shapes outside any shape catalog (run it at boot, never inside `withShapeCatalog`).
|
|
95
71
|
*/
|
|
96
|
-
export function
|
|
97
|
-
var _a;
|
|
72
|
+
export function syncTargetsLine(syncs, core) {
|
|
98
73
|
const defaultDataset = LinkedStorage.getDefaultDataset();
|
|
99
|
-
if (!defaultDataset)
|
|
74
|
+
if (!defaultDataset || (!syncs.shapeSync && !syncs.ontologySync))
|
|
100
75
|
return undefined;
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
]
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
const { datasetLabel } = core;
|
|
77
|
+
const targets = core.graphSyncTargets();
|
|
78
|
+
const beyondDefault = (lists) => [...lists].some((list) => list.some((ds) => ds !== defaultDataset));
|
|
79
|
+
const kinds = [];
|
|
80
|
+
const perDataset = (byPackage) => {
|
|
81
|
+
const all = [...byPackage.keys()];
|
|
82
|
+
return targets.datasets
|
|
83
|
+
.map((ds) => {
|
|
84
|
+
const names = all.filter((name) => { var _a; return (_a = byPackage.get(name)) === null || _a === void 0 ? void 0 : _a.includes(ds); }).sort();
|
|
85
|
+
if (!names.length)
|
|
86
|
+
return undefined;
|
|
87
|
+
return `${datasetLabel(ds)} (${names.length === all.length ? 'every package' : names.join(', ')})`;
|
|
88
|
+
})
|
|
89
|
+
.filter((part) => !!part)
|
|
90
|
+
.join(', ');
|
|
91
|
+
};
|
|
92
|
+
let routed = false;
|
|
93
|
+
if (syncs.shapeSync) {
|
|
94
|
+
routed || (routed = beyondDefault(targets.shapes.values()) || beyondDefault([targets.appShapes]));
|
|
95
|
+
kinds.push(`shape graphs → ${perDataset(targets.shapes)}`);
|
|
96
|
+
kinds.push(`app shape graph → ${targets.appShapes.map(datasetLabel).join(', ')}`);
|
|
97
|
+
}
|
|
98
|
+
if (syncs.ontologySync) {
|
|
99
|
+
routed || (routed = beyondDefault(targets.ontologies.values()));
|
|
100
|
+
kinds.push(`ontology graphs → ${perDataset(targets.ontologies)}`);
|
|
118
101
|
}
|
|
119
|
-
if (
|
|
102
|
+
if (!routed)
|
|
120
103
|
return undefined;
|
|
121
|
-
|
|
122
|
-
const writers = syncs.ontologySync && syncs.shapeSync ? 'ontology and shape syncs write' : `${syncs.shapeSync ? 'shape' : 'ontology'} sync writes`;
|
|
123
|
-
return (`[LinkedServer] Routing pins shapes the boot syncs write to a dataset other than the default ` +
|
|
124
|
-
`one: ${parts.join('; ')}. The ${writers} into the default dataset (${datasetName(defaultDataset)}) ` +
|
|
125
|
-
`only, so reads of these shapes on the pinned dataset do not see what the boot wrote.`);
|
|
104
|
+
return `[LinkedServer] The boot syncs follow the routing: ${kinds.join('; ')}.`;
|
|
126
105
|
}
|
|
127
106
|
//# sourceMappingURL=graphTargets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphTargets.js","sourceRoot":"","sources":["../../../src/utils/graphTargets.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where the boot syncs keep the registered packages' ontology data and shapes: core's graph\n * targets (`ontologyGraph`, `shapeGraph`; `@_linked/core/utils/graphTargets`), one global setting\n * of the process. `LINKED_ONTOLOGY_GRAPH` and `LINKED_SHAPE_GRAPH` each take `'package'` (the\n * default, one graph per package), `'default'` (the default graph) or the absolute IRI of one\n * shared graph.\n *\n * The server reads them once at boot, checks them (an invalid value stops the boot, naming the\n * variable) and calls core's `setGraphTargets` before the stores are initialised and before the\n * syncs. No store takes a setting of its own, and the frontend needs none: its\n * `.from(graphs.allOntologies)` reaches the backend as the portable name, and the backend's\n * store expands it under this setting.\n *\n * The syncs write into the default dataset only. {@link syncPinWarning} names the shapes whose\n * routing sends their reads elsewhere, when the boot runs those syncs.\n */\nimport {\n GRAPH_TARGET_ENV,\n graphTargetOptionsFromEnv,\n setGraphTargets,\n type GraphTargets,\n} from '@_linked/core/utils/graphTargets';\nimport { LinkedStorage } from '@_linked/core/utils/LinkedStorage';\nimport { getAllShapeClasses } from '@_linked/core/utils/ShapeClass';\nimport type { IDataset } from '@_linked/core/interfaces/IDataset';\n\n/**\n * Read `LINKED_ONTOLOGY_GRAPH` / `LINKED_SHAPE_GRAPH` from `env` (checked: an invalid value throws,\n * naming the variable) and set them as the process's graph targets (core's `setGraphTargets`;\n * unset ones are `'package'`). Returns the settings the env gave.\n */\nexport function applyGraphTargetsFromEnv(env: Record<string, string | undefined>): Partial<GraphTargets> {\n const fromEnv = graphTargetOptionsFromEnv(env);\n setGraphTargets(fromEnv);\n return fromEnv;\n}\n\n/** The env variables as set (unset and empty ones left out), for `GET /admin/routes`. */\nexport function graphTargetEnv(env: Record<string, string | undefined>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const name of [GRAPH_TARGET_ENV.ontologyGraph, GRAPH_TARGET_ENV.shapeGraph]) {\n const value = env[name];\n if (value) out[name] = value;\n }\n return out;\n}\n\nconst SH = 'http://www.w3.org/ns/shacl#';\nconst RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';\nconst RDFS = 'http://www.w3.org/2000/01/rdf-schema#';\nconst OWL = 'http://www.w3.org/2002/07/owl#';\n\n/** The classes of the shape shapes: what the shape sync writes. */\nexport const SHAPE_SHAPE_CLASSES: readonly string[] = [`${SH}NodeShape`, `${SH}PropertyShape`];\n\n/**\n * The classes of the ontology term shapes (the class and property shapes core, `@_linked/rdfs`\n * and `@_linked/owl` register): what the ontology sync writes and the hierarchy reads.\n */\nexport const ONTOLOGY_TERM_CLASSES: readonly string[] = [\n `${RDFS}Class`,\n `${OWL}Class`,\n `${OWL}Restriction`,\n `${RDF}Property`,\n `${OWL}ObjectProperty`,\n `${OWL}DatatypeProperty`,\n `${OWL}AnnotationProperty`,\n];\n\n/**\n * A dataset as a log line tells it apart: its store class, and its `id` when it has one, else the\n * dataset name a SPARQL store keeps from its endpoint (`FusekiStore`'s `dataset`).\n */\nfunction datasetName(dataset: IDataset): string {\n const { id, dataset: name } = dataset as { id?: unknown; dataset?: unknown };\n const store = dataset?.constructor?.name || 'unknown';\n const label = typeof id === 'string' && id ? id : typeof name === 'string' && name ? name : undefined;\n return label ? `${store} \"${label}\"` : store;\n}\n\n/**\n * The class the instances of `shapeClass` carry, when it is watched, else undefined: the\n * `targetClass` of the shape itself or, when it declares none of its own, of the nearest\n * super-shape that does. A sub-shape with its own target class is not its parent's class: the\n * syncs' data of the parent's class never reads through it.\n */\nfunction watchedClassOf(shapeClass: Function, watched: ReadonlySet<string>): string | undefined {\n for (let current: unknown = shapeClass; typeof current === 'function' && current !== Function.prototype; current = Object.getPrototypeOf(current)) {\n if (!Object.prototype.hasOwnProperty.call(current, 'targetClass')) continue;\n const id = (current as { targetClass?: { id?: unknown } }).targetClass?.id;\n return typeof id === 'string' && watched.has(id) ? id : undefined;\n }\n return undefined;\n}\n\n/** Which boot syncs run: only the shapes a running sync writes are worth a warning. */\nexport interface BootSyncs {\n /** The shape sync runs (`shapeSyncOn`): it writes the shape shapes. */\n shapeSync: boolean;\n /** The ontology sync runs (`bootSyncsOntologies`): it writes the ontology term shapes. */\n ontologySync: boolean;\n}\n\n/**\n * One warning when routing pins a shape a boot sync that runs writes (the shape shapes\n * `NodeShape` / `PropertyShape` for the shape sync, an ontology term shape such as `rdfs:Class` /\n * `owl:ObjectProperty` for the ontology sync) to a dataset other than the default one: the syncs\n * write into the default dataset only, so reads of those shapes on the pinned dataset miss what\n * they wrote. Names each such shape and its dataset; a sub-shape counts under the target class it\n * declares, or its parent's when it declares none.\n * Undefined when there is nothing to warn about (no sync runs, or no default dataset). Reads the\n * registered shapes outside any shape catalog (run it at boot, never inside `withShapeCatalog`).\n */\nexport function syncPinWarning(syncs: BootSyncs): string | undefined {\n const defaultDataset = LinkedStorage.getDefaultDataset();\n if (!defaultDataset) return undefined;\n const watched = new Set([\n ...(syncs.shapeSync ? SHAPE_SHAPE_CLASSES : []),\n ...(syncs.ontologySync ? ONTOLOGY_TERM_CLASSES : []),\n ]);\n if (watched.size === 0) return undefined;\n const pinned = new Map<IDataset, string[]>();\n for (const [iri, shapeClass] of getAllShapeClasses()) {\n const targetClass = watchedClassOf(shapeClass, watched);\n if (!targetClass) continue;\n const dataset = LinkedStorage.getDatasetForShapeClass(shapeClass);\n if (!dataset || dataset === defaultDataset) continue;\n const list = pinned.get(dataset) ?? [];\n list.push(`${shapeClass.name || iri} (${targetClass})`);\n pinned.set(dataset, list);\n }\n if (pinned.size === 0) return undefined;\n const parts = [...pinned].map(([dataset, shapes]) => `${shapes.sort().join(', ')} → ${datasetName(dataset)}`);\n const writers =\n syncs.ontologySync && syncs.shapeSync ? 'ontology and shape syncs write' : `${syncs.shapeSync ? 'shape' : 'ontology'} sync writes`;\n return (\n `[LinkedServer] Routing pins shapes the boot syncs write to a dataset other than the default ` +\n `one: ${parts.join('; ')}. The ${writers} into the default dataset (${datasetName(defaultDataset)}) ` +\n `only, so reads of these shapes on the pinned dataset do not see what the boot wrote.`\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graphTargets.js","sourceRoot":"","sources":["../../../src/utils/graphTargets.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where the boot syncs keep the registered packages' ontology data and shapes: core's graph\n * targets (`ontologyGraph`, `shapeGraph`; `@_linked/core/utils/graphTargets`), one global setting\n * of the process. `LINKED_ONTOLOGY_GRAPH` and `LINKED_SHAPE_GRAPH` each take `'package'` (the\n * default, one graph per package), `'default'` (the default graph) or the absolute IRI of one\n * shared graph.\n *\n * The server reads them once at boot, checks them (an invalid value stops the boot, naming the\n * variable) and calls core's `setGraphTargets` before the stores are initialised and before the\n * syncs. No store takes a setting of its own, and the frontend needs none: its\n * `.from(graphs.allOntologies)` reaches the backend as the portable name, and the backend's\n * store expands it under this setting.\n *\n * The syncs follow the storage routing (core's `syncTargets`); {@link syncTargetsLine} names the\n * datasets they write into when that is more than the default dataset.\n */\nimport {\n GRAPH_TARGET_ENV,\n graphTargetOptionsFromEnv,\n setGraphTargets,\n type GraphTargets,\n} from '@_linked/core/utils/graphTargets';\nimport { LinkedStorage } from '@_linked/core/utils/LinkedStorage';\nimport type { GraphSyncTargets } from '@_linked/core/shapes/syncTargets';\nimport type { IDataset } from '@_linked/core/interfaces/IDataset';\n\n/**\n * Read `LINKED_ONTOLOGY_GRAPH` / `LINKED_SHAPE_GRAPH` from `env` (checked: an invalid value throws,\n * naming the variable) and set them as the process's graph targets (core's `setGraphTargets`;\n * unset ones are `'package'`). Returns the settings the env gave.\n */\nexport function applyGraphTargetsFromEnv(env: Record<string, string | undefined>): Partial<GraphTargets> {\n const fromEnv = graphTargetOptionsFromEnv(env);\n setGraphTargets(fromEnv);\n return fromEnv;\n}\n\n/** The variable that says who writes the app's shape and ontology graphs. */\nexport const GRAPH_WRITER_ENV = 'LINKED_GRAPH_WRITER';\n\n/**\n * Who writes the app's shape and ontology graphs: `'app'` (the default) the app itself, at boot;\n * `'host'` a host that runs it (Create Now), which reads the\n * content through `POST /admin/graph-content` and writes it itself, so the app writes none.\n */\nexport type GraphWriter = 'app' | 'host';\n\n/**\n * `LINKED_GRAPH_WRITER` from `env`: unset or empty is `'app'`; any value other than `app` or\n * `host` throws, naming the variable (the boot stops on it).\n */\nexport function graphWriterFromEnv(env: Record<string, string | undefined>): GraphWriter {\n const value = env[GRAPH_WRITER_ENV]?.trim();\n if (!value || value === 'app') return 'app';\n if (value === 'host') return 'host';\n throw new Error(\n `${GRAPH_WRITER_ENV} must be 'app' or 'host' (who writes the app's shape and ontology graphs), not ${JSON.stringify(value)}`,\n );\n}\n\n/**\n * Whether this process writes its shape and ontology graphs: the one check every write path of the\n * syncs goes through (the boot's ontology and shape syncs). False\n * under `LINKED_GRAPH_WRITER=host`.\n */\nexport function appWritesGraphs(env: Record<string, string | undefined> = process.env): boolean {\n return graphWriterFromEnv(env) === 'app';\n}\n\n/** The env variables as set (unset and empty ones left out), for `GET /admin/routes`. */\nexport function graphTargetEnv(env: Record<string, string | undefined>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const name of [GRAPH_TARGET_ENV.ontologyGraph, GRAPH_TARGET_ENV.shapeGraph]) {\n const value = env[name];\n if (value) out[name] = value;\n }\n return out;\n}\n\n/** Which boot syncs run: only the graphs a running sync writes are named. */\nexport interface BootSyncs {\n /** The shape sync runs (`shapeSyncOn`): it writes the shape graphs and the app's shape graph. */\n shapeSync: boolean;\n /** The ontology sync runs (the same switch as the shape sync): it writes the ontology graphs. */\n ontologySync: boolean;\n}\n\n/**\n * One log line naming the datasets the boot syncs that run write into, per graph kind, when the\n * routing sends any graph beyond the default dataset (core's `graphSyncTargets`: a package's\n * shape graph goes wherever its shapes or the shape shapes route, its ontology graph also\n * wherever shapes using its classes or the term shapes route, the app's graph wherever shapes\n * declaring `contains`, or their sub-shapes, route). Undefined when every graph goes to the default dataset only (the\n * syncs' own lines say enough), when no sync runs or without a default dataset. `core` is\n * `@_linked/core/shapes/syncTargets` (server only, imported when the boot runs). Reads the\n * registered shapes outside any shape catalog (run it at boot, never inside `withShapeCatalog`).\n */\nexport function syncTargetsLine(\n syncs: BootSyncs,\n core: {\n graphSyncTargets: () => GraphSyncTargets;\n datasetLabel: (dataset: IDataset) => string;\n },\n): string | undefined {\n const defaultDataset = LinkedStorage.getDefaultDataset();\n if (!defaultDataset || (!syncs.shapeSync && !syncs.ontologySync)) return undefined;\n const { datasetLabel } = core;\n const targets = core.graphSyncTargets();\n const beyondDefault = (lists: Iterable<IDataset[]>) =>\n [...lists].some((list) => list.some((ds) => ds !== defaultDataset));\n const kinds: string[] = [];\n const perDataset = (byPackage: Map<string, IDataset[]>): string => {\n const all = [...byPackage.keys()];\n return targets.datasets\n .map((ds) => {\n const names = all.filter((name) => byPackage.get(name)?.includes(ds)).sort();\n if (!names.length) return undefined;\n return `${datasetLabel(ds)} (${names.length === all.length ? 'every package' : names.join(', ')})`;\n })\n .filter((part): part is string => !!part)\n .join(', ');\n };\n let routed = false;\n if (syncs.shapeSync) {\n routed ||= beyondDefault(targets.shapes.values()) || beyondDefault([targets.appShapes]);\n kinds.push(`shape graphs → ${perDataset(targets.shapes)}`);\n kinds.push(`app shape graph → ${targets.appShapes.map(datasetLabel).join(', ')}`);\n }\n if (syncs.ontologySync) {\n routed ||= beyondDefault(targets.ontologies.values());\n kinds.push(`ontology graphs → ${perDataset(targets.ontologies)}`);\n }\n if (!routed) return undefined;\n return `[LinkedServer] The boot syncs follow the routing: ${kinds.join('; ')}.`;\n}\n"]}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ontology sync and the shape check, as the server runs them before its shape sync
|
|
3
|
-
*
|
|
2
|
+
* Ontology sync and the shape check, as the server runs them before its shape sync at boot. The
|
|
3
|
+
* registration and the shape check also run when the host reloads packages for
|
|
4
|
+
* `POST /admin/graph-content`.
|
|
4
5
|
*
|
|
5
|
-
* - The ontology sync (core `
|
|
6
|
-
*
|
|
6
|
+
* - The ontology sync (core `syncOntologiesRouted` at boot; `syncOntologies` into one
|
|
7
|
+
* given dataset otherwise) writes each registered package's ontology data into its own named
|
|
8
|
+
* graph of every dataset the routing needs it in (`{package IRI}/graph/ontology`, with a
|
|
7
9
|
* record in the graph registry that marks it as a hierarchy source), skipping a package whose
|
|
8
10
|
* stored hash matches, so the store's RDFS view infers the class hierarchy. A store without
|
|
9
11
|
* graph queries (it answers `unsupported-query-kind`, or has no `graphQuery`) gets core's
|
|
@@ -25,7 +27,7 @@
|
|
|
25
27
|
* this server depends on has all of them. Kept apart from `LinkedServer` so the order and the failure handling can be tested without
|
|
26
28
|
* booting a server.
|
|
27
29
|
*/
|
|
28
|
-
import { type BootOrphanScope } from './syncShapes.js';
|
|
30
|
+
import { type BootOrphanScope, type RoutedGraphReport, type RoutedSyncReport } from './syncShapes.js';
|
|
29
31
|
/** The subset of core's ontology and shape-check API this module calls. */
|
|
30
32
|
export interface OntologyCore {
|
|
31
33
|
syncOntologies(ds: unknown, options: {
|
|
@@ -36,6 +38,20 @@ export interface OntologyCore {
|
|
|
36
38
|
pruned: string[];
|
|
37
39
|
target?: string;
|
|
38
40
|
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Core's routed ontology sync (`@_linked/core/shapes/syncTargets`): every package's ontology
|
|
43
|
+
* graph into each dataset the routing needs it in. Optional so a test double can leave it out.
|
|
44
|
+
*/
|
|
45
|
+
syncOntologiesRouted?(options: {
|
|
46
|
+
orphanScope: BootOrphanScope;
|
|
47
|
+
}): Promise<RoutedSyncReport<{
|
|
48
|
+
written: string[];
|
|
49
|
+
skipped: string[];
|
|
50
|
+
pruned: string[];
|
|
51
|
+
target?: string;
|
|
52
|
+
}>>;
|
|
53
|
+
/** Every dataset the router knows (where a routed sync may write). Optional for test doubles. */
|
|
54
|
+
routedDatasets?(): unknown[];
|
|
39
55
|
checkShapeHierarchy(options: {
|
|
40
56
|
dependencies?: Record<string, string[]>;
|
|
41
57
|
}): Promise<Array<{
|
|
@@ -69,7 +85,7 @@ export interface ShapeCheckSummary {
|
|
|
69
85
|
warnings: number;
|
|
70
86
|
failed?: string;
|
|
71
87
|
}
|
|
72
|
-
/** What the ontology sync and the shape check did
|
|
88
|
+
/** What the ontology sync and the shape check did. */
|
|
73
89
|
export interface OntologyPhaseResult {
|
|
74
90
|
/**
|
|
75
91
|
* Packages whose ontology graph (or, on a store without graph queries, hierarchy) was written,
|
|
@@ -81,6 +97,12 @@ export interface OntologyPhaseResult {
|
|
|
81
97
|
skipped: string[];
|
|
82
98
|
graphs?: string[];
|
|
83
99
|
droppedGraphs?: string[];
|
|
100
|
+
/** A routed sync: the datasets it wrote into, the default one first. */
|
|
101
|
+
datasets?: string[];
|
|
102
|
+
/** A routed sync: package ontology graphs dropped from the default dataset (routing sends them elsewhere). */
|
|
103
|
+
prunedGraphs?: RoutedGraphReport[];
|
|
104
|
+
/** A routed sync: package ontology graphs left in another dataset routing no longer sends them to. */
|
|
105
|
+
leftovers?: RoutedGraphReport[];
|
|
84
106
|
};
|
|
85
107
|
/**
|
|
86
108
|
* The shape check's findings, counted by severity, and the message of the error the check
|
|
@@ -91,16 +113,12 @@ export interface OntologyPhaseResult {
|
|
|
91
113
|
failure?: Error;
|
|
92
114
|
}
|
|
93
115
|
/**
|
|
94
|
-
* Whether the
|
|
95
|
-
* sets `syncShapesOnBoot: false
|
|
96
|
-
*
|
|
97
|
-
|
|
98
|
-
export declare function shapeSyncOn(env: Record<string, string | undefined>, configFlag: unknown): boolean;
|
|
99
|
-
/**
|
|
100
|
-
* Whether the boot syncs ontologies: when shape sync is on ({@link shapeSyncOn}) and the
|
|
101
|
-
* environment does not opt out with `LINKED_SYNC_ONTOLOGIES_ON_BOOT=false`.
|
|
116
|
+
* Whether the boot syncs the shapes and the ontologies into app data: off only when `linked.config`
|
|
117
|
+
* sets `syncShapesOnBoot: false`. One switch for both syncs. It applies only to an app that writes
|
|
118
|
+
* its own graphs (`appWritesGraphs`, utils/graphTargets): under `LINKED_GRAPH_WRITER=host` the boot
|
|
119
|
+
* syncs nothing whatever it says.
|
|
102
120
|
*/
|
|
103
|
-
export declare function
|
|
121
|
+
export declare function shapeSyncOn(configFlag: unknown): boolean;
|
|
104
122
|
/**
|
|
105
123
|
* Import core's ontology sync, shape check, hierarchy loader and installed packages'
|
|
106
124
|
* registration.
|
|
@@ -129,9 +147,15 @@ export declare function sourceModuleImporter(vite: {
|
|
|
129
147
|
* returned as `shapeCheck.failed`. Logs one line for the sync, one summary line for
|
|
130
148
|
* the check and one line per finding.
|
|
131
149
|
*/
|
|
132
|
-
export declare function runOntologyPhase(ds: unknown, { core, sync, orphanScope, dependencies, packageDirs, importModule, log, warn, }: {
|
|
150
|
+
export declare function runOntologyPhase(ds: unknown, { core, sync, routed, orphanScope, dependencies, packageDirs, importModule, log, warn, }: {
|
|
133
151
|
core: OntologyCore;
|
|
134
152
|
sync: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Follow the routing (core's `syncOntologiesRouted`): `ds` is then the default dataset, and each
|
|
155
|
+
* package's graph goes to every dataset the routing needs it in. Otherwise the sync writes into
|
|
156
|
+
* `ds` only. The server's boot runs routed.
|
|
157
|
+
*/
|
|
158
|
+
routed?: boolean;
|
|
135
159
|
orphanScope: BootOrphanScope;
|
|
136
160
|
dependencies?: Record<string, string[]>;
|
|
137
161
|
/** Every linked package directory the server discovered (transitively). */
|
|
@@ -156,10 +180,3 @@ export declare function graphsWrittenAndDropped(ops: Array<{
|
|
|
156
180
|
graphs: string[];
|
|
157
181
|
droppedGraphs: string[];
|
|
158
182
|
};
|
|
159
|
-
/**
|
|
160
|
-
* The host's re-sync: the ontology phase, then the shape sync, which runs also when the ontology
|
|
161
|
-
* sync failed. The ontology sync's failure is then thrown, so the route answers with it, as it
|
|
162
|
-
* does for a failed shape sync. A shape check that threw fails nothing: the answer reports it as
|
|
163
|
-
* `shapeCheck.failed`.
|
|
164
|
-
*/
|
|
165
|
-
export declare function syncOntologiesThenShapes<T extends object>(ontologyPhase: () => Promise<OntologyPhaseResult>, shapeSync: () => Promise<T>): Promise<T & Pick<OntologyPhaseResult, 'ontologies' | 'shapeCheck'>>;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ontology sync and the shape check, as the server runs them before its shape sync
|
|
3
|
-
*
|
|
2
|
+
* Ontology sync and the shape check, as the server runs them before its shape sync at boot. The
|
|
3
|
+
* registration and the shape check also run when the host reloads packages for
|
|
4
|
+
* `POST /admin/graph-content`.
|
|
4
5
|
*
|
|
5
|
-
* - The ontology sync (core `
|
|
6
|
-
*
|
|
6
|
+
* - The ontology sync (core `syncOntologiesRouted` at boot; `syncOntologies` into one
|
|
7
|
+
* given dataset otherwise) writes each registered package's ontology data into its own named
|
|
8
|
+
* graph of every dataset the routing needs it in (`{package IRI}/graph/ontology`, with a
|
|
7
9
|
* record in the graph registry that marks it as a hierarchy source), skipping a package whose
|
|
8
10
|
* stored hash matches, so the store's RDFS view infers the class hierarchy. A store without
|
|
9
11
|
* graph queries (it answers `unsupported-query-kind`, or has no `graphQuery`) gets core's
|
|
@@ -27,19 +29,13 @@
|
|
|
27
29
|
*/
|
|
28
30
|
import { writtenAndUnchanged } from './syncShapes.js';
|
|
29
31
|
/**
|
|
30
|
-
* Whether the
|
|
31
|
-
* sets `syncShapesOnBoot: false
|
|
32
|
-
*
|
|
32
|
+
* Whether the boot syncs the shapes and the ontologies into app data: off only when `linked.config`
|
|
33
|
+
* sets `syncShapesOnBoot: false`. One switch for both syncs. It applies only to an app that writes
|
|
34
|
+
* its own graphs (`appWritesGraphs`, utils/graphTargets): under `LINKED_GRAPH_WRITER=host` the boot
|
|
35
|
+
* syncs nothing whatever it says.
|
|
33
36
|
*/
|
|
34
|
-
export function shapeSyncOn(
|
|
35
|
-
return configFlag !== false
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Whether the boot syncs ontologies: when shape sync is on ({@link shapeSyncOn}) and the
|
|
39
|
-
* environment does not opt out with `LINKED_SYNC_ONTOLOGIES_ON_BOOT=false`.
|
|
40
|
-
*/
|
|
41
|
-
export function bootSyncsOntologies(env, configFlag) {
|
|
42
|
-
return shapeSyncOn(env, configFlag) && env.LINKED_SYNC_ONTOLOGIES_ON_BOOT !== 'false';
|
|
37
|
+
export function shapeSyncOn(configFlag) {
|
|
38
|
+
return configFlag !== false;
|
|
43
39
|
}
|
|
44
40
|
/**
|
|
45
41
|
* Import core's ontology sync, shape check, hierarchy loader and installed packages'
|
|
@@ -51,8 +47,12 @@ export async function loadOntologyCore() {
|
|
|
51
47
|
const hierarchy = await import('@_linked/core/ontology/hierarchy');
|
|
52
48
|
const registerInstalled = await import('@_linked/core/ontology/registerInstalled');
|
|
53
49
|
const dispatch = await import('@_linked/core/queries/queryDispatch');
|
|
50
|
+
const targets = await import('@_linked/core/shapes/syncTargets');
|
|
51
|
+
const storage = await import('@_linked/core/utils/LinkedStorage');
|
|
54
52
|
return {
|
|
55
53
|
syncOntologies: (ds, options) => sync.syncOntologies(ds, options),
|
|
54
|
+
syncOntologiesRouted: (options) => targets.syncOntologiesRouted(options),
|
|
55
|
+
routedDatasets: () => [...storage.LinkedStorage.getDatasets()],
|
|
56
56
|
checkShapeHierarchy: (options) => check.checkShapeHierarchy(options),
|
|
57
57
|
loadOntologyHierarchy: () => hierarchy.loadOntologyHierarchy(),
|
|
58
58
|
registerInstalledOntologies: (packageDirs, options) => registerInstalled.registerInstalledOntologies(packageDirs, options),
|
|
@@ -90,8 +90,8 @@ const list = (names) => (names.length ? names.join(', ') : 'none');
|
|
|
90
90
|
* returned as `shapeCheck.failed`. Logs one line for the sync, one summary line for
|
|
91
91
|
* the check and one line per finding.
|
|
92
92
|
*/
|
|
93
|
-
export async function runOntologyPhase(ds, { core, sync, orphanScope, dependencies, packageDirs = [], importModule, log = console.log, warn = console.warn, }) {
|
|
94
|
-
var _a;
|
|
93
|
+
export async function runOntologyPhase(ds, { core, sync, routed = false, orphanScope, dependencies, packageDirs = [], importModule, log = console.log, warn = console.warn, }) {
|
|
94
|
+
var _a, _b, _c, _d;
|
|
95
95
|
const result = {
|
|
96
96
|
ontologies: { written: [], skipped: [], graphs: [], droppedGraphs: [] },
|
|
97
97
|
shapeCheck: { errors: 0, warnings: 0 },
|
|
@@ -112,16 +112,44 @@ export async function runOntologyPhase(ds, { core, sync, orphanScope, dependenci
|
|
|
112
112
|
// Every graph query that succeeded, in the order the sync sent them.
|
|
113
113
|
const ops = [];
|
|
114
114
|
const pending = [];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
if (routed && !core.syncOntologiesRouted) {
|
|
116
|
+
throw new Error('@_linked/core has no syncOntologiesRouted: this server needs the core it was built with');
|
|
117
|
+
}
|
|
118
|
+
const observed = routed ? (_b = (_a = core.routedDatasets) === null || _a === void 0 ? void 0 : _a.call(core)) !== null && _b !== void 0 ? _b : [ds] : [ds];
|
|
119
|
+
const stops = observed.map((target) => {
|
|
120
|
+
var _a;
|
|
121
|
+
return (_a = core.observeGraphQueries) === null || _a === void 0 ? void 0 : _a.call(core, target, (query, settled) => {
|
|
122
|
+
const op = { kind: query.kind, graph: query.graph };
|
|
123
|
+
ops.push(op);
|
|
124
|
+
pending.push(settled.then(() => (op.ok = true), () => undefined));
|
|
125
|
+
});
|
|
119
126
|
});
|
|
127
|
+
const stop = () => stops.forEach((s) => s === null || s === void 0 ? void 0 : s());
|
|
120
128
|
try {
|
|
121
|
-
|
|
129
|
+
let sync;
|
|
130
|
+
let routedRun;
|
|
131
|
+
if (routed) {
|
|
132
|
+
routedRun = await core.syncOntologiesRouted({ orphanScope });
|
|
133
|
+
const runs = routedRun.targets.map((t) => t.result);
|
|
134
|
+
const written = [...new Set(runs.flatMap((r) => r.written))].sort();
|
|
135
|
+
sync = {
|
|
136
|
+
written,
|
|
137
|
+
skipped: [...new Set(runs.flatMap((r) => r.skipped))].filter((n) => !written.includes(n)).sort(),
|
|
138
|
+
pruned: [...new Set(runs.flatMap((r) => r.pruned))].sort(),
|
|
139
|
+
target: (_c = runs[0]) === null || _c === void 0 ? void 0 : _c.target,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
sync = await core.syncOntologies(ds, { orphanScope });
|
|
144
|
+
}
|
|
122
145
|
await Promise.all(pending);
|
|
123
146
|
const { graphs, droppedGraphs } = graphsWrittenAndDropped(ops.filter((op) => op.ok));
|
|
124
147
|
result.ontologies = { written: sync.written, skipped: sync.skipped, graphs, droppedGraphs };
|
|
148
|
+
if (routedRun) {
|
|
149
|
+
result.ontologies.datasets = routedRun.targets.map((t) => t.dataset);
|
|
150
|
+
result.ontologies.prunedGraphs = routedRun.pruned;
|
|
151
|
+
result.ontologies.leftovers = routedRun.leftovers;
|
|
152
|
+
}
|
|
125
153
|
hierarchyLoaded = true;
|
|
126
154
|
// Core says where it wrote (the global `ontologyGraph`). A store without graph queries makes
|
|
127
155
|
// the sync throw in the 'package' and shared modes, reported below as a failed sync.
|
|
@@ -134,7 +162,14 @@ export async function runOntologyPhase(ds, { core, sync, orphanScope, dependenci
|
|
|
134
162
|
writtenAndUnchanged(sync.written, sync.skipped) +
|
|
135
163
|
(orphanScope === 'all' ? `; pruned ${list(sync.pruned)}` : '') +
|
|
136
164
|
(graphs.length ? `; graphs written ${list(graphs)}` : '') +
|
|
137
|
-
(droppedGraphs.length ? `; graphs dropped ${list(droppedGraphs)}` : '')
|
|
165
|
+
(droppedGraphs.length ? `; graphs dropped ${list(droppedGraphs)}` : '') +
|
|
166
|
+
(routedRun && routedRun.targets.length > 1 ? `; datasets ${list(routedRun.targets.map((t) => t.dataset))}` : ''));
|
|
167
|
+
for (const { dataset, package: name, graph } of (_d = routedRun === null || routedRun === void 0 ? void 0 : routedRun.pruned) !== null && _d !== void 0 ? _d : []) {
|
|
168
|
+
log(`[LinkedServer] ontology sync: dropped <${graph}> (${name}) from ${dataset}: routing sends its ontology elsewhere`);
|
|
169
|
+
}
|
|
170
|
+
if (routedRun === null || routedRun === void 0 ? void 0 : routedRun.failed.length) {
|
|
171
|
+
throw new Error(`the ontology sync failed in ${routedRun.failed.map((f) => `${f.dataset}: ${f.error}`).join('; ')}`);
|
|
172
|
+
}
|
|
138
173
|
}
|
|
139
174
|
catch (err) {
|
|
140
175
|
result.failure = asError(err);
|
|
@@ -192,17 +227,4 @@ export function graphsWrittenAndDropped(ops) {
|
|
|
192
227
|
}
|
|
193
228
|
return { graphs: graphs.sort(), droppedGraphs: droppedGraphs.sort() };
|
|
194
229
|
}
|
|
195
|
-
/**
|
|
196
|
-
* The host's re-sync: the ontology phase, then the shape sync, which runs also when the ontology
|
|
197
|
-
* sync failed. The ontology sync's failure is then thrown, so the route answers with it, as it
|
|
198
|
-
* does for a failed shape sync. A shape check that threw fails nothing: the answer reports it as
|
|
199
|
-
* `shapeCheck.failed`.
|
|
200
|
-
*/
|
|
201
|
-
export async function syncOntologiesThenShapes(ontologyPhase, shapeSync) {
|
|
202
|
-
const { ontologies, shapeCheck, failure } = await ontologyPhase();
|
|
203
|
-
const shapes = await shapeSync();
|
|
204
|
-
if (failure)
|
|
205
|
-
throw failure;
|
|
206
|
-
return { ...shapes, ontologies, shapeCheck };
|
|
207
|
-
}
|
|
208
230
|
//# sourceMappingURL=ontologySync.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ontologySync.js","sourceRoot":"","sources":["../../../src/utils/ontologySync.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ontology sync and the shape check, as the server runs them before its shape sync: at boot and\n * when the host asks for a re-sync (`POST /admin/sync-shapes`).\n *\n * - The ontology sync (core `syncOntologies`) writes each registered package's ontology data into\n * its own named graph of the app's default dataset (`{package IRI}/graph/ontology`, with a\n * record in the graph registry that marks it as a hierarchy source), skipping a package whose\n * stored hash matches, so the store's RDFS view infers the class hierarchy. A store without\n * graph queries (it answers `unsupported-query-kind`, or has no `graphQuery`) gets core's\n * earlier sync of the `rdfs:subClassOf` triples into the default graph instead. The graphs the\n * sync wrote or dropped are observed on core's query dispatch and reported by name.\n * - The shape check (core `checkShapeHierarchy`) compares the registered shapes with that\n * hierarchy. A running app is never stopped by it: its findings are logged and counted.\n * - The in-memory class hierarchy (core `loadOntologyHierarchy`) is loaded also when the sync\n * is off; the sync loads it itself when it runs.\n * - Before either, every linked package directory the server discovered registers its ontologies\n * (core `registerInstalledOntologies`): a package the app only deep-imports shape files from\n * never evaluates its register modules, so its data would be missing from both. A source\n * package without built register modules (a workspace package in dev) registers through\n * `importModule` (Vite's `ssrLoadModule`), when the caller passes one.\n *\n * The four core modules (`ontology/syncOntologies`, `shapes/checkShapeHierarchy`,\n * `ontology/hierarchy`, `ontology/registerInstalled`) are server only and not in core's index, so\n * {@link loadOntologyCore} imports them by their fixed subpaths when the phase runs; the core\n * this server depends on has all of them. Kept apart from `LinkedServer` so the order and the failure handling can be tested without\n * booting a server.\n */\nimport {writtenAndUnchanged, type BootOrphanScope} from './syncShapes.js';\n\n/** The subset of core's ontology and shape-check API this module calls. */\nexport interface OntologyCore {\n syncOntologies(\n ds: unknown,\n options: {orphanScope: BootOrphanScope},\n ): Promise<{written: string[]; skipped: string[]; pruned: string[]; target?: string}>;\n checkShapeHierarchy(options: {dependencies?: Record<string, string[]>}): Promise<\n Array<{kind: string; severity: 'error' | 'warning'; shape?: string; iri?: string; message: string}>\n >;\n /** Loads the in-memory class hierarchy. Optional so a test double can leave it out. */\n loadOntologyHierarchy?: () => Promise<unknown>;\n /**\n * Calls `onGraph` for every graph query on `ds` that succeeds until the returned function is\n * called (core's `subscribeQueryDispatch`). Optional so a test double can leave it out.\n */\n observeGraphQueries?: (\n ds: unknown,\n onGraph: (query: {kind: string; graph: string}, settled: Promise<unknown>) => void,\n ) => () => void;\n /**\n * Registers the ontologies of the installed packages in the given directories. Optional so a\n * test double can leave it out.\n */\n registerInstalledOntologies?: (\n packageDirs: string[],\n options?: {importModule?: (file: string) => Promise<unknown>},\n ) => Promise<string[]>;\n}\n\n/** The shape check's findings by severity; `failed` holds the message of a check that threw. */\nexport interface ShapeCheckSummary {\n errors: number;\n warnings: number;\n failed?: string;\n}\n\n/** What the ontology sync and the shape check did, as `POST /admin/sync-shapes` reports it. */\nexport interface OntologyPhaseResult {\n /**\n * Packages whose ontology graph (or, on a store without graph queries, hierarchy) was written,\n * packages left as they were (unchanged or unreadable), and the graphs the sync wrote and\n * dropped (empty on a store without graph queries).\n */\n ontologies: {written: string[]; skipped: string[]; graphs?: string[]; droppedGraphs?: string[]};\n /**\n * The shape check's findings, counted by severity, and the message of the error the check\n * threw, if it did (`failed`). A thrown check is reported here and never fails the phase.\n */\n shapeCheck: ShapeCheckSummary;\n /** The error the ontology sync threw; the phase went on without it. */\n failure?: Error;\n}\n\n/**\n * Whether the server syncs shapes (and so ontologies) into its app data: off when `linked.config`\n * sets `syncShapesOnBoot: false` or the environment sets `LINKED_SYNC_SHAPES_ON_BOOT=false`. One\n * switch for both syncs, at boot and on the host's request.\n */\nexport function shapeSyncOn(\n env: Record<string, string | undefined>,\n configFlag: unknown,\n): boolean {\n return configFlag !== false && env.LINKED_SYNC_SHAPES_ON_BOOT !== 'false';\n}\n\n/**\n * Whether the boot syncs ontologies: when shape sync is on ({@link shapeSyncOn}) and the\n * environment does not opt out with `LINKED_SYNC_ONTOLOGIES_ON_BOOT=false`.\n */\nexport function bootSyncsOntologies(\n env: Record<string, string | undefined>,\n configFlag?: unknown,\n): boolean {\n return shapeSyncOn(env, configFlag) && env.LINKED_SYNC_ONTOLOGIES_ON_BOOT !== 'false';\n}\n\n/**\n * Import core's ontology sync, shape check, hierarchy loader and installed packages'\n * registration.\n */\nexport async function loadOntologyCore(): Promise<OntologyCore> {\n const sync = await import('@_linked/core/ontology/syncOntologies');\n const check = await import('@_linked/core/shapes/checkShapeHierarchy');\n const hierarchy = await import('@_linked/core/ontology/hierarchy');\n const registerInstalled = await import('@_linked/core/ontology/registerInstalled');\n const dispatch = await import('@_linked/core/queries/queryDispatch');\n return {\n syncOntologies: (ds, options) =>\n sync.syncOntologies(ds as Parameters<typeof sync.syncOntologies>[0], options),\n checkShapeHierarchy: (options) => check.checkShapeHierarchy(options),\n loadOntologyHierarchy: () => hierarchy.loadOntologyHierarchy(),\n registerInstalledOntologies: (packageDirs, options) =>\n registerInstalled.registerInstalledOntologies(packageDirs, options),\n observeGraphQueries: (ds, onGraph) =>\n dispatch.subscribeQueryDispatch((event) => {\n if (event.kind === 'graph' && event.target === ds) onGraph(event.query, event.result);\n }),\n };\n}\n\n/**\n * The package directories whose ontologies register before the sync: every linked package\n * discovered in the app's dependency tree, then the app's own workspace packages (an ontology\n * package in `packages/` that no package imports is one, so the dependency walk never reaches it).\n * Order kept, duplicates dropped.\n */\nexport function ontologyPackageDirs(\n linkedPackageDirs: Iterable<string>,\n workspacePackageDirs: Iterable<string>,\n): string[] {\n return [...new Set([...linkedPackageDirs, ...workspacePackageDirs])];\n}\n\n/**\n * In dev, the loader for source register modules: Vite's `ssrLoadModule`, so a workspace package\n * that was never built registers on the core the SSR graph uses. Undefined without Vite (a built\n * app), where source packages are skipped.\n */\nexport function sourceModuleImporter(\n vite: {ssrLoadModule?: (url: string) => Promise<unknown>} | undefined | null,\n): ((file: string) => Promise<unknown>) | undefined {\n const ssrLoadModule = vite?.ssrLoadModule;\n return typeof ssrLoadModule === 'function' ? (file) => ssrLoadModule.call(vite, file) : undefined;\n}\n\nconst asError = (err: unknown): Error => (err instanceof Error ? err : new Error(String(err)));\nconst list = (names: string[]) => (names.length ? names.join(', ') : 'none');\n\n/**\n * Register the ontologies of the installed linked packages in `packageDirs` (package roots), sync\n * the registered ontologies into `ds` (when `sync` is set and there is a dataset), then run the\n * shape check. Never throws: a failed registration is logged and the phase goes on; a failed sync\n * is logged and returned as `failure`, and the check still runs; a check that throws is logged and\n * returned as `shapeCheck.failed`. Logs one line for the sync, one summary line for\n * the check and one line per finding.\n */\nexport async function runOntologyPhase(\n ds: unknown,\n {\n core,\n sync,\n orphanScope,\n dependencies,\n packageDirs = [],\n importModule,\n log = console.log,\n warn = console.warn,\n }: {\n core: OntologyCore;\n sync: boolean;\n orphanScope: BootOrphanScope;\n dependencies?: Record<string, string[]>;\n /** Every linked package directory the server discovered (transitively). */\n packageDirs?: string[];\n /**\n * Imports a source register module (`src/ontologies/*.register.ts`) of a package directory\n * without built ones; in dev, Vite's `ssrLoadModule`.\n */\n importModule?: (file: string) => Promise<unknown>;\n log?: (message: string) => void;\n warn?: (message: string, err?: unknown) => void;\n },\n): Promise<OntologyPhaseResult> {\n const result: OntologyPhaseResult = {\n ontologies: {written: [], skipped: [], graphs: [], droppedGraphs: []},\n shapeCheck: {errors: 0, warnings: 0},\n };\n\n if (core.registerInstalledOntologies && packageDirs.length) {\n try {\n if (importModule) await core.registerInstalledOntologies(packageDirs, {importModule});\n else await core.registerInstalledOntologies(packageDirs);\n } catch (err) {\n warn(\"[LinkedServer] registering the installed packages' ontologies failed:\", err);\n }\n }\n\n let hierarchyLoaded = false;\n if (sync && ds) {\n // Every graph query that succeeded, in the order the sync sent them.\n const ops: Array<{kind: string; graph: string; ok?: boolean}> = [];\n const pending: Promise<unknown>[] = [];\n const stop = core.observeGraphQueries?.(ds, (query, settled) => {\n const op: {kind: string; graph: string; ok?: boolean} = {kind: query.kind, graph: query.graph};\n ops.push(op);\n pending.push(\n settled.then(\n () => (op.ok = true),\n () => undefined,\n ),\n );\n });\n try {\n const sync = await core.syncOntologies(ds, {orphanScope});\n await Promise.all(pending);\n const {graphs, droppedGraphs} = graphsWrittenAndDropped(ops.filter((op) => op.ok));\n result.ontologies = {written: sync.written, skipped: sync.skipped, graphs, droppedGraphs};\n hierarchyLoaded = true;\n // Core says where it wrote (the global `ontologyGraph`). A store without graph queries makes\n // the sync throw in the 'package' and shared modes, reported below as a failed sync.\n const where =\n sync.target === 'default'\n ? \" (default graph: ontologyGraph is 'default')\"\n : sync.target && sync.target !== 'package'\n ? ` (one shared graph <${sync.target}>)`\n : '';\n log(\n `[LinkedServer] ontology sync${where}: ` +\n writtenAndUnchanged(sync.written, sync.skipped) +\n (orphanScope === 'all' ? `; pruned ${list(sync.pruned)}` : '') +\n (graphs.length ? `; graphs written ${list(graphs)}` : '') +\n (droppedGraphs.length ? `; graphs dropped ${list(droppedGraphs)}` : ''),\n );\n } catch (err) {\n result.failure = asError(err);\n warn('[LinkedServer] ontology sync failed:', err);\n } finally {\n stop?.();\n }\n }\n if (!hierarchyLoaded && core.loadOntologyHierarchy) {\n try {\n await core.loadOntologyHierarchy();\n } catch (err) {\n warn('[LinkedServer] loading the class hierarchy failed:', err);\n }\n }\n\n try {\n const findings = await core.checkShapeHierarchy({dependencies});\n const errors = findings.filter((f) => f.severity === 'error').length;\n result.shapeCheck = {errors, warnings: findings.length - errors};\n if (findings.length) {\n log(`[LinkedServer] shape check: ${errors} error(s), ${findings.length - errors} warning(s)`);\n }\n for (const f of findings) warn(`[LinkedServer] shape check ${f.severity} (${f.kind}): ${f.message}`);\n } catch (err) {\n result.shapeCheck = {errors: 0, warnings: 0, failed: asError(err).message};\n warn('[LinkedServer] shape check failed:', err);\n }\n return result;\n}\n\n/**\n * The graphs a sync left written and those it left dropped, from its successful graph queries in\n * order. A graph the run created and then dropped (the inference probe's temporary graphs) is in\n * neither list.\n */\nexport function graphsWrittenAndDropped(ops: Array<{kind: string; graph: string}>): {\n graphs: string[];\n droppedGraphs: string[];\n} {\n const byGraph = new Map<string, string[]>();\n for (const {kind, graph} of ops) {\n const kinds = byGraph.get(graph) ?? [];\n kinds.push(kind);\n byGraph.set(graph, kinds);\n }\n const graphs: string[] = [];\n const droppedGraphs: string[] = [];\n for (const [graph, kinds] of byGraph) {\n const last = kinds[kinds.length - 1];\n if (last !== 'graph_drop') graphs.push(graph);\n else if (kinds[0] !== 'graph_create') droppedGraphs.push(graph);\n }\n return {graphs: graphs.sort(), droppedGraphs: droppedGraphs.sort()};\n}\n\n/**\n * The host's re-sync: the ontology phase, then the shape sync, which runs also when the ontology\n * sync failed. The ontology sync's failure is then thrown, so the route answers with it, as it\n * does for a failed shape sync. A shape check that threw fails nothing: the answer reports it as\n * `shapeCheck.failed`.\n */\nexport async function syncOntologiesThenShapes<T extends object>(\n ontologyPhase: () => Promise<OntologyPhaseResult>,\n shapeSync: () => Promise<T>,\n): Promise<T & Pick<OntologyPhaseResult, 'ontologies' | 'shapeCheck'>> {\n const {ontologies, shapeCheck, failure} = await ontologyPhase();\n const shapes = await shapeSync();\n if (failure) throw failure;\n return {...shapes, ontologies, shapeCheck};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ontologySync.js","sourceRoot":"","sources":["../../../src/utils/ontologySync.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ontology sync and the shape check, as the server runs them before its shape sync at boot. The\n * registration and the shape check also run when the host reloads packages for\n * `POST /admin/graph-content`.\n *\n * - The ontology sync (core `syncOntologiesRouted` at boot; `syncOntologies` into one\n * given dataset otherwise) writes each registered package's ontology data into its own named\n * graph of every dataset the routing needs it in (`{package IRI}/graph/ontology`, with a\n * record in the graph registry that marks it as a hierarchy source), skipping a package whose\n * stored hash matches, so the store's RDFS view infers the class hierarchy. A store without\n * graph queries (it answers `unsupported-query-kind`, or has no `graphQuery`) gets core's\n * earlier sync of the `rdfs:subClassOf` triples into the default graph instead. The graphs the\n * sync wrote or dropped are observed on core's query dispatch and reported by name.\n * - The shape check (core `checkShapeHierarchy`) compares the registered shapes with that\n * hierarchy. A running app is never stopped by it: its findings are logged and counted.\n * - The in-memory class hierarchy (core `loadOntologyHierarchy`) is loaded also when the sync\n * is off; the sync loads it itself when it runs.\n * - Before either, every linked package directory the server discovered registers its ontologies\n * (core `registerInstalledOntologies`): a package the app only deep-imports shape files from\n * never evaluates its register modules, so its data would be missing from both. A source\n * package without built register modules (a workspace package in dev) registers through\n * `importModule` (Vite's `ssrLoadModule`), when the caller passes one.\n *\n * The four core modules (`ontology/syncOntologies`, `shapes/checkShapeHierarchy`,\n * `ontology/hierarchy`, `ontology/registerInstalled`) are server only and not in core's index, so\n * {@link loadOntologyCore} imports them by their fixed subpaths when the phase runs; the core\n * this server depends on has all of them. Kept apart from `LinkedServer` so the order and the failure handling can be tested without\n * booting a server.\n */\nimport {writtenAndUnchanged, type BootOrphanScope, type RoutedGraphReport, type RoutedSyncReport} from './syncShapes.js';\n\n/** The subset of core's ontology and shape-check API this module calls. */\nexport interface OntologyCore {\n syncOntologies(\n ds: unknown,\n options: {orphanScope: BootOrphanScope},\n ): Promise<{written: string[]; skipped: string[]; pruned: string[]; target?: string}>;\n /**\n * Core's routed ontology sync (`@_linked/core/shapes/syncTargets`): every package's ontology\n * graph into each dataset the routing needs it in. Optional so a test double can leave it out.\n */\n syncOntologiesRouted?(options: {orphanScope: BootOrphanScope}): Promise<\n RoutedSyncReport<{written: string[]; skipped: string[]; pruned: string[]; target?: string}>\n >;\n /** Every dataset the router knows (where a routed sync may write). Optional for test doubles. */\n routedDatasets?(): unknown[];\n checkShapeHierarchy(options: {dependencies?: Record<string, string[]>}): Promise<\n Array<{kind: string; severity: 'error' | 'warning'; shape?: string; iri?: string; message: string}>\n >;\n /** Loads the in-memory class hierarchy. Optional so a test double can leave it out. */\n loadOntologyHierarchy?: () => Promise<unknown>;\n /**\n * Calls `onGraph` for every graph query on `ds` that succeeds until the returned function is\n * called (core's `subscribeQueryDispatch`). Optional so a test double can leave it out.\n */\n observeGraphQueries?: (\n ds: unknown,\n onGraph: (query: {kind: string; graph: string}, settled: Promise<unknown>) => void,\n ) => () => void;\n /**\n * Registers the ontologies of the installed packages in the given directories. Optional so a\n * test double can leave it out.\n */\n registerInstalledOntologies?: (\n packageDirs: string[],\n options?: {importModule?: (file: string) => Promise<unknown>},\n ) => Promise<string[]>;\n}\n\n/** The shape check's findings by severity; `failed` holds the message of a check that threw. */\nexport interface ShapeCheckSummary {\n errors: number;\n warnings: number;\n failed?: string;\n}\n\n/** What the ontology sync and the shape check did. */\nexport interface OntologyPhaseResult {\n /**\n * Packages whose ontology graph (or, on a store without graph queries, hierarchy) was written,\n * packages left as they were (unchanged or unreadable), and the graphs the sync wrote and\n * dropped (empty on a store without graph queries).\n */\n ontologies: {\n written: string[];\n skipped: string[];\n graphs?: string[];\n droppedGraphs?: string[];\n /** A routed sync: the datasets it wrote into, the default one first. */\n datasets?: string[];\n /** A routed sync: package ontology graphs dropped from the default dataset (routing sends them elsewhere). */\n prunedGraphs?: RoutedGraphReport[];\n /** A routed sync: package ontology graphs left in another dataset routing no longer sends them to. */\n leftovers?: RoutedGraphReport[];\n };\n /**\n * The shape check's findings, counted by severity, and the message of the error the check\n * threw, if it did (`failed`). A thrown check is reported here and never fails the phase.\n */\n shapeCheck: ShapeCheckSummary;\n /** The error the ontology sync threw; the phase went on without it. */\n failure?: Error;\n}\n\n/**\n * Whether the boot syncs the shapes and the ontologies into app data: off only when `linked.config`\n * sets `syncShapesOnBoot: false`. One switch for both syncs. It applies only to an app that writes\n * its own graphs (`appWritesGraphs`, utils/graphTargets): under `LINKED_GRAPH_WRITER=host` the boot\n * syncs nothing whatever it says.\n */\nexport function shapeSyncOn(configFlag: unknown): boolean {\n return configFlag !== false;\n}\n\n/**\n * Import core's ontology sync, shape check, hierarchy loader and installed packages'\n * registration.\n */\nexport async function loadOntologyCore(): Promise<OntologyCore> {\n const sync = await import('@_linked/core/ontology/syncOntologies');\n const check = await import('@_linked/core/shapes/checkShapeHierarchy');\n const hierarchy = await import('@_linked/core/ontology/hierarchy');\n const registerInstalled = await import('@_linked/core/ontology/registerInstalled');\n const dispatch = await import('@_linked/core/queries/queryDispatch');\n const targets = await import('@_linked/core/shapes/syncTargets');\n const storage = await import('@_linked/core/utils/LinkedStorage');\n return {\n syncOntologies: (ds, options) =>\n sync.syncOntologies(ds as Parameters<typeof sync.syncOntologies>[0], options),\n syncOntologiesRouted: (options) => targets.syncOntologiesRouted(options),\n routedDatasets: () => [...storage.LinkedStorage.getDatasets()],\n checkShapeHierarchy: (options) => check.checkShapeHierarchy(options),\n loadOntologyHierarchy: () => hierarchy.loadOntologyHierarchy(),\n registerInstalledOntologies: (packageDirs, options) =>\n registerInstalled.registerInstalledOntologies(packageDirs, options),\n observeGraphQueries: (ds, onGraph) =>\n dispatch.subscribeQueryDispatch((event) => {\n if (event.kind === 'graph' && event.target === ds) onGraph(event.query, event.result);\n }),\n };\n}\n\n/**\n * The package directories whose ontologies register before the sync: every linked package\n * discovered in the app's dependency tree, then the app's own workspace packages (an ontology\n * package in `packages/` that no package imports is one, so the dependency walk never reaches it).\n * Order kept, duplicates dropped.\n */\nexport function ontologyPackageDirs(\n linkedPackageDirs: Iterable<string>,\n workspacePackageDirs: Iterable<string>,\n): string[] {\n return [...new Set([...linkedPackageDirs, ...workspacePackageDirs])];\n}\n\n/**\n * In dev, the loader for source register modules: Vite's `ssrLoadModule`, so a workspace package\n * that was never built registers on the core the SSR graph uses. Undefined without Vite (a built\n * app), where source packages are skipped.\n */\nexport function sourceModuleImporter(\n vite: {ssrLoadModule?: (url: string) => Promise<unknown>} | undefined | null,\n): ((file: string) => Promise<unknown>) | undefined {\n const ssrLoadModule = vite?.ssrLoadModule;\n return typeof ssrLoadModule === 'function' ? (file) => ssrLoadModule.call(vite, file) : undefined;\n}\n\nconst asError = (err: unknown): Error => (err instanceof Error ? err : new Error(String(err)));\nconst list = (names: string[]) => (names.length ? names.join(', ') : 'none');\n\n/**\n * Register the ontologies of the installed linked packages in `packageDirs` (package roots), sync\n * the registered ontologies into `ds` (when `sync` is set and there is a dataset), then run the\n * shape check. Never throws: a failed registration is logged and the phase goes on; a failed sync\n * is logged and returned as `failure`, and the check still runs; a check that throws is logged and\n * returned as `shapeCheck.failed`. Logs one line for the sync, one summary line for\n * the check and one line per finding.\n */\nexport async function runOntologyPhase(\n ds: unknown,\n {\n core,\n sync,\n routed = false,\n orphanScope,\n dependencies,\n packageDirs = [],\n importModule,\n log = console.log,\n warn = console.warn,\n }: {\n core: OntologyCore;\n sync: boolean;\n /**\n * Follow the routing (core's `syncOntologiesRouted`): `ds` is then the default dataset, and each\n * package's graph goes to every dataset the routing needs it in. Otherwise the sync writes into\n * `ds` only. The server's boot runs routed.\n */\n routed?: boolean;\n orphanScope: BootOrphanScope;\n dependencies?: Record<string, string[]>;\n /** Every linked package directory the server discovered (transitively). */\n packageDirs?: string[];\n /**\n * Imports a source register module (`src/ontologies/*.register.ts`) of a package directory\n * without built ones; in dev, Vite's `ssrLoadModule`.\n */\n importModule?: (file: string) => Promise<unknown>;\n log?: (message: string) => void;\n warn?: (message: string, err?: unknown) => void;\n },\n): Promise<OntologyPhaseResult> {\n const result: OntologyPhaseResult = {\n ontologies: {written: [], skipped: [], graphs: [], droppedGraphs: []},\n shapeCheck: {errors: 0, warnings: 0},\n };\n\n if (core.registerInstalledOntologies && packageDirs.length) {\n try {\n if (importModule) await core.registerInstalledOntologies(packageDirs, {importModule});\n else await core.registerInstalledOntologies(packageDirs);\n } catch (err) {\n warn(\"[LinkedServer] registering the installed packages' ontologies failed:\", err);\n }\n }\n\n let hierarchyLoaded = false;\n if (sync && ds) {\n // Every graph query that succeeded, in the order the sync sent them.\n const ops: Array<{kind: string; graph: string; ok?: boolean}> = [];\n const pending: Promise<unknown>[] = [];\n if (routed && !core.syncOntologiesRouted) {\n throw new Error('@_linked/core has no syncOntologiesRouted: this server needs the core it was built with');\n }\n const observed = routed ? core.routedDatasets?.() ?? [ds] : [ds];\n const stops = observed.map((target) =>\n core.observeGraphQueries?.(target, (query, settled) => {\n const op: {kind: string; graph: string; ok?: boolean} = {kind: query.kind, graph: query.graph};\n ops.push(op);\n pending.push(\n settled.then(\n () => (op.ok = true),\n () => undefined,\n ),\n );\n }),\n );\n const stop = () => stops.forEach((s) => s?.());\n try {\n let sync: {written: string[]; skipped: string[]; pruned: string[]; target?: string};\n let routedRun: RoutedSyncReport<typeof sync> | undefined;\n if (routed) {\n routedRun = await core.syncOntologiesRouted!({orphanScope});\n const runs = routedRun.targets.map((t) => t.result);\n const written = [...new Set(runs.flatMap((r) => r.written))].sort();\n sync = {\n written,\n skipped: [...new Set(runs.flatMap((r) => r.skipped))].filter((n) => !written.includes(n)).sort(),\n pruned: [...new Set(runs.flatMap((r) => r.pruned))].sort(),\n target: runs[0]?.target,\n };\n } else {\n sync = await core.syncOntologies(ds, {orphanScope});\n }\n await Promise.all(pending);\n const {graphs, droppedGraphs} = graphsWrittenAndDropped(ops.filter((op) => op.ok));\n result.ontologies = {written: sync.written, skipped: sync.skipped, graphs, droppedGraphs};\n if (routedRun) {\n result.ontologies.datasets = routedRun.targets.map((t) => t.dataset);\n result.ontologies.prunedGraphs = routedRun.pruned;\n result.ontologies.leftovers = routedRun.leftovers;\n }\n hierarchyLoaded = true;\n // Core says where it wrote (the global `ontologyGraph`). A store without graph queries makes\n // the sync throw in the 'package' and shared modes, reported below as a failed sync.\n const where =\n sync.target === 'default'\n ? \" (default graph: ontologyGraph is 'default')\"\n : sync.target && sync.target !== 'package'\n ? ` (one shared graph <${sync.target}>)`\n : '';\n log(\n `[LinkedServer] ontology sync${where}: ` +\n writtenAndUnchanged(sync.written, sync.skipped) +\n (orphanScope === 'all' ? `; pruned ${list(sync.pruned)}` : '') +\n (graphs.length ? `; graphs written ${list(graphs)}` : '') +\n (droppedGraphs.length ? `; graphs dropped ${list(droppedGraphs)}` : '') +\n (routedRun && routedRun.targets.length > 1 ? `; datasets ${list(routedRun.targets.map((t) => t.dataset))}` : ''),\n );\n for (const {dataset, package: name, graph} of routedRun?.pruned ?? []) {\n log(`[LinkedServer] ontology sync: dropped <${graph}> (${name}) from ${dataset}: routing sends its ontology elsewhere`);\n }\n if (routedRun?.failed.length) {\n throw new Error(\n `the ontology sync failed in ${routedRun.failed.map((f) => `${f.dataset}: ${f.error}`).join('; ')}`,\n );\n }\n } catch (err) {\n result.failure = asError(err);\n warn('[LinkedServer] ontology sync failed:', err);\n } finally {\n stop?.();\n }\n }\n if (!hierarchyLoaded && core.loadOntologyHierarchy) {\n try {\n await core.loadOntologyHierarchy();\n } catch (err) {\n warn('[LinkedServer] loading the class hierarchy failed:', err);\n }\n }\n\n try {\n const findings = await core.checkShapeHierarchy({dependencies});\n const errors = findings.filter((f) => f.severity === 'error').length;\n result.shapeCheck = {errors, warnings: findings.length - errors};\n if (findings.length) {\n log(`[LinkedServer] shape check: ${errors} error(s), ${findings.length - errors} warning(s)`);\n }\n for (const f of findings) warn(`[LinkedServer] shape check ${f.severity} (${f.kind}): ${f.message}`);\n } catch (err) {\n result.shapeCheck = {errors: 0, warnings: 0, failed: asError(err).message};\n warn('[LinkedServer] shape check failed:', err);\n }\n return result;\n}\n\n/**\n * The graphs a sync left written and those it left dropped, from its successful graph queries in\n * order. A graph the run created and then dropped (the inference probe's temporary graphs) is in\n * neither list.\n */\nexport function graphsWrittenAndDropped(ops: Array<{kind: string; graph: string}>): {\n graphs: string[];\n droppedGraphs: string[];\n} {\n const byGraph = new Map<string, string[]>();\n for (const {kind, graph} of ops) {\n const kinds = byGraph.get(graph) ?? [];\n kinds.push(kind);\n byGraph.set(graph, kinds);\n }\n const graphs: string[] = [];\n const droppedGraphs: string[] = [];\n for (const [graph, kinds] of byGraph) {\n const last = kinds[kinds.length - 1];\n if (last !== 'graph_drop') graphs.push(graph);\n else if (kinds[0] !== 'graph_create') droppedGraphs.push(graph);\n }\n return {graphs: graphs.sort(), droppedGraphs: droppedGraphs.sort()};\n}\n"]}
|