@canonical/pragma-cli 0.30.0 → 0.31.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 +64 -35
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/parseConfigValues.ts +2 -0
- package/src/config/readConfigLayers.test.ts +2 -0
- package/src/config/readConfigLayers.ts +3 -0
- package/src/config/types.ts +7 -0
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/stories.ts +9 -3
- package/src/domains/config/commands/channel.test.ts +16 -0
- package/src/domains/config/commands/channel.ts +14 -1
- package/src/domains/config/commands/framework.test.ts +14 -0
- package/src/domains/config/commands/framework.ts +13 -1
- package/src/domains/config/commands/tier.test.ts +7 -0
- package/src/domains/config/commands/tier.ts +14 -1
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +9 -6
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -12
- package/src/domains/doctor/operations/checks.test.ts +6 -4
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/serve.test.ts +12 -6
- package/src/domains/graphql/commands/serve.ts +16 -6
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/operations/compileSchema.test.ts +2 -2
- package/src/domains/graphql/operations/createSchemaServer.test.ts +2 -2
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +5 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +23 -3
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +49 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/stories.ts +9 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +358 -67
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.ts +53 -8
- package/src/domains/refs/operations/updateRefs.ts +0 -1
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +56 -20
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.ts +48 -0
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/localLoader.ts +2 -18
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +46 -8
- package/src/domains/shared/lookupMany.test.ts +15 -0
- package/src/domains/shared/lookupMany.ts +22 -6
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +17 -2
- package/src/domains/shared/stories/compileLookupCommand.ts +3 -1
- package/src/domains/shared/stories/compileLookupTool.ts +1 -1
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +49 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +46 -11
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +30 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +75 -1
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +305 -7
- package/src/domains/shared/stories/pack/collectPackStories.ts +90 -8
- package/src/domains/shared/stories/pack/compilePackCommands.ts +17 -7
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +607 -2
- package/src/domains/shared/stories/pack/compilePackStories.ts +544 -73
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +20 -8
- package/src/domains/shared/stories/pack/index.ts +14 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +293 -220
- package/src/domains/shared/stories/pack/types.ts +123 -3
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +502 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +475 -24
- package/src/domains/shared/stories/types.ts +9 -1
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +13 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +2 -2
- package/src/mcp/resources/pickPrimaryType.test.ts +5 -5
- package/src/mcp/tools/index.ts +18 -10
- package/src/mcp/tools/registerTools.test.ts +106 -30
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +34 -19
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +162 -19
- package/src/testing/cli.ts +30 -2
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +1 -4
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +4 -4
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +2 -2
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +6 -4
- package/src/testing/integration/executor-golden.test.ts +2 -2
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +119 -70
- package/src/testing/setupXdgIsolation.ts +16 -5
- package/src/testing/standardParityFixtures.ts +26 -83
- package/src/testing/store.ts +4 -4
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -11
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -14
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -89
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/stories.test.ts +0 -24
- package/src/domains/standard/stories.ts +0 -151
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -9
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -12
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/domains/tier/stories.test.ts +0 -22
- package/src/domains/tier/stories.ts +0 -52
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
/** @module Barrel for ontology helpers. */
|
|
2
2
|
|
|
3
3
|
export { default as extractLocalName } from "../../shared/extractLocalName.js";
|
|
4
|
+
export type { ClassTreeNode } from "./buildClassTree.js";
|
|
5
|
+
export {
|
|
6
|
+
default as buildClassTree,
|
|
7
|
+
flattenClassTree,
|
|
8
|
+
} from "./buildClassTree.js";
|
|
9
|
+
export { default as expandOntologyIris } from "./expandOntologyIris.js";
|
|
4
10
|
export { default as findNamespace } from "./findNamespace.js";
|
|
11
|
+
export type { RawOntologyClass } from "./queryClasses.js";
|
|
5
12
|
export { default as queryClasses } from "./queryClasses.js";
|
|
13
|
+
export type { RawOntologyProperty } from "./queryProperties.js";
|
|
6
14
|
export { default as queryProperties } from "./queryProperties.js";
|
|
15
|
+
export {
|
|
16
|
+
queryConstraints,
|
|
17
|
+
queryInstanceCounts,
|
|
18
|
+
queryOntologyMeta,
|
|
19
|
+
querySampleInstances,
|
|
20
|
+
} from "./queryTboxFacts.js";
|
|
7
21
|
export { default as resolvePrefix } from "./resolvePrefix.js";
|
|
@@ -2,49 +2,65 @@ import type { Store } from "@canonical/ke";
|
|
|
2
2
|
import { buildQuery } from "../../shared/buildQuery.js";
|
|
3
3
|
import extractLocalName from "../../shared/extractLocalName.js";
|
|
4
4
|
import { P } from "../../shared/prefixes.js";
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
/** A class row as stored — full URIs, all superclass edges preserved. */
|
|
7
|
+
export interface RawOntologyClass {
|
|
8
|
+
readonly uri: string;
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly comment?: string;
|
|
11
|
+
readonly subClassOf: readonly string[];
|
|
12
|
+
}
|
|
6
13
|
|
|
7
14
|
/**
|
|
8
|
-
* Queries all `owl:Class` definitions within a namespace
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* values, only the first superclass encountered is kept.
|
|
15
|
+
* Queries all `owl:Class` definitions within a namespace, including their
|
|
16
|
+
* `rdfs:comment` and **every** `rdfs:subClassOf` edge (multiple inheritance
|
|
17
|
+
* is preserved; downstream renderers decide tree placement).
|
|
12
18
|
*
|
|
13
19
|
* @note Queries ke store
|
|
14
20
|
*
|
|
15
21
|
* @param store - The ke store to query.
|
|
16
22
|
* @param namespace - The namespace URI to filter classes by.
|
|
17
|
-
* @returns An array of {@link
|
|
23
|
+
* @returns An array of {@link RawOntologyClass} entries, one per class.
|
|
18
24
|
*/
|
|
19
25
|
export default async function queryClasses(
|
|
20
26
|
store: Store,
|
|
21
27
|
namespace: string,
|
|
22
|
-
): Promise<
|
|
28
|
+
): Promise<RawOntologyClass[]> {
|
|
23
29
|
const result = await store.query(
|
|
24
30
|
buildQuery(`
|
|
25
|
-
SELECT ?class ?label ?superclass
|
|
31
|
+
SELECT ?class ?label ?comment ?superclass
|
|
26
32
|
WHERE {
|
|
27
33
|
?class a ${P.owl}Class .
|
|
28
34
|
FILTER(STRSTARTS(STR(?class), "${namespace}"))
|
|
29
35
|
OPTIONAL { ?class ${P.rdfs}label ?label }
|
|
36
|
+
OPTIONAL { ?class ${P.rdfs}comment ?comment }
|
|
30
37
|
OPTIONAL { ?class ${P.rdfs}subClassOf ?superclass }
|
|
31
38
|
}
|
|
32
|
-
ORDER BY ?class
|
|
39
|
+
ORDER BY ?class ?superclass
|
|
33
40
|
`),
|
|
34
41
|
);
|
|
35
42
|
|
|
36
43
|
if (result.type !== "select") return [];
|
|
37
44
|
|
|
38
|
-
const seen = new Map<
|
|
45
|
+
const seen = new Map<
|
|
46
|
+
string,
|
|
47
|
+
{ label: string; comment?: string; supers: Set<string> }
|
|
48
|
+
>();
|
|
39
49
|
for (const b of result.bindings) {
|
|
40
50
|
const uri = b.class ?? "";
|
|
41
|
-
|
|
42
|
-
seen.set(uri, {
|
|
43
|
-
uri,
|
|
51
|
+
const entry = seen.get(uri) ?? {
|
|
44
52
|
label: b.label ?? extractLocalName(uri),
|
|
45
|
-
...(b.
|
|
46
|
-
|
|
53
|
+
...(b.comment ? { comment: b.comment } : {}),
|
|
54
|
+
supers: new Set<string>(),
|
|
55
|
+
};
|
|
56
|
+
if (b.superclass) entry.supers.add(b.superclass);
|
|
57
|
+
seen.set(uri, entry);
|
|
47
58
|
}
|
|
48
59
|
|
|
49
|
-
return [...seen.
|
|
60
|
+
return [...seen.entries()].map(([uri, entry]) => ({
|
|
61
|
+
uri,
|
|
62
|
+
label: entry.label,
|
|
63
|
+
...(entry.comment ? { comment: entry.comment } : {}),
|
|
64
|
+
subClassOf: [...entry.supers].sort(),
|
|
65
|
+
}));
|
|
50
66
|
}
|
|
@@ -2,11 +2,20 @@ import type { Store } from "@canonical/ke";
|
|
|
2
2
|
import { buildQuery } from "../../shared/buildQuery.js";
|
|
3
3
|
import extractLocalName from "../../shared/extractLocalName.js";
|
|
4
4
|
import { P } from "../../shared/prefixes.js";
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
/** A property row as stored — full URIs. */
|
|
7
|
+
export interface RawOntologyProperty {
|
|
8
|
+
readonly uri: string;
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly kind: "object" | "datatype";
|
|
11
|
+
readonly domain?: string;
|
|
12
|
+
readonly range?: string;
|
|
13
|
+
readonly functional: boolean;
|
|
14
|
+
}
|
|
6
15
|
|
|
7
16
|
/**
|
|
8
17
|
* Queries all `owl:ObjectProperty` and `owl:DatatypeProperty` definitions
|
|
9
|
-
* within a namespace
|
|
18
|
+
* within a namespace, marking those also typed `owl:FunctionalProperty`.
|
|
10
19
|
*
|
|
11
20
|
* Deduplicates by property URI -- when a property has multiple `rdfs:domain`
|
|
12
21
|
* or `rdfs:range` values, only the first encountered is kept.
|
|
@@ -15,12 +24,12 @@ import type { OntologyProperty } from "../../shared/types/index.js";
|
|
|
15
24
|
*
|
|
16
25
|
* @param store - The ke store to query.
|
|
17
26
|
* @param namespace - The namespace URI to filter properties by.
|
|
18
|
-
* @returns An array of {@link
|
|
27
|
+
* @returns An array of {@link RawOntologyProperty} entries.
|
|
19
28
|
*/
|
|
20
29
|
export default async function queryProperties(
|
|
21
30
|
store: Store,
|
|
22
31
|
namespace: string,
|
|
23
|
-
): Promise<
|
|
32
|
+
): Promise<RawOntologyProperty[]> {
|
|
24
33
|
const result = await store.query(
|
|
25
34
|
buildQuery(`
|
|
26
35
|
SELECT ?prop ?label ?domain ?range ?propType
|
|
@@ -38,7 +47,9 @@ export default async function queryProperties(
|
|
|
38
47
|
|
|
39
48
|
if (result.type !== "select") return [];
|
|
40
49
|
|
|
41
|
-
const
|
|
50
|
+
const functional = await queryFunctionalProperties(store, namespace);
|
|
51
|
+
|
|
52
|
+
const seen = new Map<string, RawOntologyProperty>();
|
|
42
53
|
for (const b of result.bindings) {
|
|
43
54
|
const uri = b.prop ?? "";
|
|
44
55
|
if (seen.has(uri)) continue;
|
|
@@ -47,11 +58,33 @@ export default async function queryProperties(
|
|
|
47
58
|
label: b.label ?? extractLocalName(uri),
|
|
48
59
|
...(b.domain ? { domain: b.domain } : {}),
|
|
49
60
|
...(b.range ? { range: b.range } : {}),
|
|
50
|
-
|
|
61
|
+
kind: b.propType?.includes("ObjectProperty")
|
|
51
62
|
? ("object" as const)
|
|
52
63
|
: ("datatype" as const),
|
|
64
|
+
functional: functional.has(uri),
|
|
53
65
|
});
|
|
54
66
|
}
|
|
55
67
|
|
|
56
68
|
return [...seen.values()];
|
|
57
69
|
}
|
|
70
|
+
|
|
71
|
+
/** URIs within the namespace typed `owl:FunctionalProperty`. */
|
|
72
|
+
async function queryFunctionalProperties(
|
|
73
|
+
store: Store,
|
|
74
|
+
namespace: string,
|
|
75
|
+
): Promise<Set<string>> {
|
|
76
|
+
const result = await store.query(
|
|
77
|
+
buildQuery(`
|
|
78
|
+
SELECT ?prop
|
|
79
|
+
WHERE {
|
|
80
|
+
?prop a ${P.owl}FunctionalProperty .
|
|
81
|
+
FILTER(STRSTARTS(STR(?prop), "${namespace}"))
|
|
82
|
+
}
|
|
83
|
+
`),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (result.type !== "select") return new Set();
|
|
87
|
+
return new Set(
|
|
88
|
+
result.bindings.map((b) => b.prop).filter((p): p is string => Boolean(p)),
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supplementary TBox queries for `ontology show` — instance counts, SHACL
|
|
3
|
+
* shape summaries, the owl:Ontology header, and per-class sample instances.
|
|
4
|
+
*
|
|
5
|
+
* Kept together because each is a single small SELECT feeding the one
|
|
6
|
+
* {@link OntologyDetailed} structure the show operation assembles.
|
|
7
|
+
*
|
|
8
|
+
* @note Every function queries the ke store.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Store } from "@canonical/ke";
|
|
12
|
+
import { buildQuery } from "../../shared/buildQuery.js";
|
|
13
|
+
import { P } from "../../shared/prefixes.js";
|
|
14
|
+
|
|
15
|
+
const SH = "http://www.w3.org/ns/shacl#";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Count instances per class within a namespace.
|
|
19
|
+
*
|
|
20
|
+
* One `GROUP BY ?type` query for the whole namespace — never per class.
|
|
21
|
+
*
|
|
22
|
+
* @returns Map from full class URI to instance count.
|
|
23
|
+
*/
|
|
24
|
+
export async function queryInstanceCounts(
|
|
25
|
+
store: Store,
|
|
26
|
+
namespace: string,
|
|
27
|
+
): Promise<ReadonlyMap<string, number>> {
|
|
28
|
+
const result = await store.query(
|
|
29
|
+
buildQuery(`
|
|
30
|
+
SELECT ?type (COUNT(?s) AS ?n)
|
|
31
|
+
WHERE {
|
|
32
|
+
?s a ?type .
|
|
33
|
+
FILTER(STRSTARTS(STR(?type), "${namespace}"))
|
|
34
|
+
}
|
|
35
|
+
GROUP BY ?type
|
|
36
|
+
`),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (result.type !== "select") return new Map();
|
|
40
|
+
|
|
41
|
+
const counts = new Map<string, number>();
|
|
42
|
+
for (const b of result.bindings) {
|
|
43
|
+
if (b.type && b.n) counts.set(b.type, Number(b.n));
|
|
44
|
+
}
|
|
45
|
+
return counts;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A SHACL node-shape summary row — full URIs. */
|
|
49
|
+
export interface RawConstraint {
|
|
50
|
+
readonly shape: string;
|
|
51
|
+
readonly targetClass?: string;
|
|
52
|
+
readonly propertyCount: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Summarize `sh:NodeShape`s whose shape or target class lives in the
|
|
57
|
+
* namespace: target class and number of property constraints.
|
|
58
|
+
*/
|
|
59
|
+
export async function queryConstraints(
|
|
60
|
+
store: Store,
|
|
61
|
+
namespace: string,
|
|
62
|
+
): Promise<RawConstraint[]> {
|
|
63
|
+
const result = await store.query(
|
|
64
|
+
buildQuery(`
|
|
65
|
+
SELECT ?shape ?target (COUNT(?p) AS ?n)
|
|
66
|
+
WHERE {
|
|
67
|
+
?shape a <${SH}NodeShape> .
|
|
68
|
+
OPTIONAL { ?shape <${SH}targetClass> ?target }
|
|
69
|
+
OPTIONAL { ?shape <${SH}property> ?p }
|
|
70
|
+
FILTER(
|
|
71
|
+
STRSTARTS(STR(?shape), "${namespace}") ||
|
|
72
|
+
STRSTARTS(STR(?target), "${namespace}")
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
GROUP BY ?shape ?target
|
|
76
|
+
ORDER BY ?shape
|
|
77
|
+
`),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (result.type !== "select") return [];
|
|
81
|
+
|
|
82
|
+
return result.bindings
|
|
83
|
+
.filter((b) => Boolean(b.shape))
|
|
84
|
+
.map((b) => ({
|
|
85
|
+
shape: b.shape as string,
|
|
86
|
+
...(b.target ? { targetClass: b.target } : {}),
|
|
87
|
+
propertyCount: Number(b.n ?? 0),
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** owl:Ontology header row — full URIs. */
|
|
92
|
+
export interface RawOntologyMeta {
|
|
93
|
+
readonly title?: string;
|
|
94
|
+
readonly version?: string;
|
|
95
|
+
readonly imports: readonly string[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Read the `owl:Ontology` header declared in a namespace, when present:
|
|
100
|
+
* title (rdfs:label), version (owl:versionInfo), and owl:imports.
|
|
101
|
+
*/
|
|
102
|
+
export async function queryOntologyMeta(
|
|
103
|
+
store: Store,
|
|
104
|
+
namespace: string,
|
|
105
|
+
): Promise<RawOntologyMeta | undefined> {
|
|
106
|
+
const result = await store.query(
|
|
107
|
+
buildQuery(`
|
|
108
|
+
SELECT ?ont ?title ?version ?import
|
|
109
|
+
WHERE {
|
|
110
|
+
?ont a ${P.owl}Ontology .
|
|
111
|
+
FILTER(STRSTARTS(STR(?ont), "${namespace}"))
|
|
112
|
+
OPTIONAL { ?ont ${P.rdfs}label ?title }
|
|
113
|
+
OPTIONAL { ?ont ${P.owl}versionInfo ?version }
|
|
114
|
+
OPTIONAL { ?ont ${P.owl}imports ?import }
|
|
115
|
+
}
|
|
116
|
+
ORDER BY ?ont
|
|
117
|
+
`),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (result.type !== "select" || result.bindings.length === 0) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const first = result.bindings[0] as (typeof result.bindings)[number];
|
|
125
|
+
const imports = [
|
|
126
|
+
...new Set(
|
|
127
|
+
result.bindings
|
|
128
|
+
.map((b) => b.import)
|
|
129
|
+
.filter((i): i is string => Boolean(i)),
|
|
130
|
+
),
|
|
131
|
+
].sort();
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
...(first.title ? { title: first.title } : {}),
|
|
135
|
+
...(first.version ? { version: first.version } : {}),
|
|
136
|
+
imports,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Fetch up to `limit` instance URIs of a class, as concrete entry points
|
|
142
|
+
* for `graph inspect` and domain lookups.
|
|
143
|
+
*/
|
|
144
|
+
export async function querySampleInstances(
|
|
145
|
+
store: Store,
|
|
146
|
+
classUri: string,
|
|
147
|
+
limit = 3,
|
|
148
|
+
): Promise<string[]> {
|
|
149
|
+
const result = await store.query(
|
|
150
|
+
buildQuery(`
|
|
151
|
+
SELECT ?s
|
|
152
|
+
WHERE { ?s a <${classUri}> }
|
|
153
|
+
ORDER BY ?s
|
|
154
|
+
LIMIT ${limit}
|
|
155
|
+
`),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
if (result.type !== "select") return [];
|
|
159
|
+
return result.bindings.map((b) => b.s).filter((s): s is string => Boolean(s));
|
|
160
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Store } from "@canonical/ke";
|
|
2
2
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
3
3
|
import { createTestStore, DS_ALL_TTL } from "#testing";
|
|
4
|
-
import {
|
|
4
|
+
import { DEFAULT_PREFIX_MAP } from "../../shared/prefixes.js";
|
|
5
5
|
import listOntologies from "./listOntologies.js";
|
|
6
6
|
|
|
7
7
|
let store: Store;
|
|
@@ -22,12 +22,36 @@ describe("listOntologies", () => {
|
|
|
22
22
|
|
|
23
23
|
const ds = result.find((o) => o.prefix === "ds");
|
|
24
24
|
expect(ds).toBeDefined();
|
|
25
|
-
expect(ds?.namespace).toBe(
|
|
25
|
+
expect(ds?.namespace).toBe(DEFAULT_PREFIX_MAP.ds);
|
|
26
26
|
expect(ds?.classCount).toBeGreaterThan(0);
|
|
27
|
-
expect(ds?.
|
|
27
|
+
expect(ds?.relationCount).toBeGreaterThan(0);
|
|
28
|
+
expect(ds?.attributeCount).toBeGreaterThan(0);
|
|
28
29
|
expect(ds?.anatomyCount).toBeGreaterThan(0);
|
|
29
30
|
});
|
|
30
31
|
|
|
32
|
+
it("splits relations from attributes", async () => {
|
|
33
|
+
const result = await listOntologies(store);
|
|
34
|
+
const ds = result.find((o) => o.prefix === "ds");
|
|
35
|
+
// ds:tier is an object property; ds:name is a datatype property.
|
|
36
|
+
expect(ds?.relationCount).toBeGreaterThan(0);
|
|
37
|
+
expect(ds?.attributeCount).toBeGreaterThan(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("carries the owl:Ontology header and SHACL shape count", async () => {
|
|
41
|
+
const result = await listOntologies(store);
|
|
42
|
+
const ds = result.find((o) => o.prefix === "ds");
|
|
43
|
+
expect(ds?.title).toBe("Design System Ontology");
|
|
44
|
+
expect(ds?.version).toBe("0.1.0-test");
|
|
45
|
+
expect(ds?.shapeCount).toBeGreaterThan(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("omits header fields for namespaces without an owl:Ontology node", async () => {
|
|
49
|
+
const result = await listOntologies(store);
|
|
50
|
+
const cs = result.find((o) => o.prefix === "cs");
|
|
51
|
+
expect(cs?.title).toBeUndefined();
|
|
52
|
+
expect(cs?.version).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
|
|
31
55
|
it("includes cs namespace", async () => {
|
|
32
56
|
const result = await listOntologies(store);
|
|
33
57
|
const cs = result.find((o) => o.prefix === "cs");
|
|
@@ -36,6 +60,59 @@ describe("listOntologies", () => {
|
|
|
36
60
|
expect(cs?.anatomyCount).toBe(0);
|
|
37
61
|
});
|
|
38
62
|
|
|
63
|
+
it("lists namespaces without a registered prefix", async () => {
|
|
64
|
+
const scoped = await createTestStore({
|
|
65
|
+
ttl: `@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
66
|
+
<http://unprefixed.example.org/ontology#Widget> a owl:Class .
|
|
67
|
+
<http://unprefixed.example.org/ontology#partOf> a owl:ObjectProperty .`,
|
|
68
|
+
});
|
|
69
|
+
try {
|
|
70
|
+
const result = await listOntologies(scoped.store);
|
|
71
|
+
const anon = result.find(
|
|
72
|
+
(o) => o.namespace === "http://unprefixed.example.org/ontology#",
|
|
73
|
+
);
|
|
74
|
+
expect(anon).toBeDefined();
|
|
75
|
+
expect(anon?.prefix).toBe("");
|
|
76
|
+
expect(anon?.classCount).toBe(1);
|
|
77
|
+
expect(anon?.relationCount).toBe(1);
|
|
78
|
+
} finally {
|
|
79
|
+
scoped.cleanup();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("derives namespaces from short path-only URIs", async () => {
|
|
84
|
+
const scoped = await createTestStore({
|
|
85
|
+
ttl: `@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
86
|
+
<http://a/b> a owl:Class .`,
|
|
87
|
+
});
|
|
88
|
+
try {
|
|
89
|
+
const result = await listOntologies(scoped.store);
|
|
90
|
+
expect(result.find((o) => o.namespace === "http://a/")?.classCount).toBe(
|
|
91
|
+
1,
|
|
92
|
+
);
|
|
93
|
+
} finally {
|
|
94
|
+
scoped.cleanup();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("lists a namespace that only contributes SHACL shapes", async () => {
|
|
99
|
+
const scoped = await createTestStore({
|
|
100
|
+
ttl: `@prefix sh: <http://www.w3.org/ns/shacl#> .
|
|
101
|
+
<http://shapes.example.org/ontology#OnlyShape> a sh:NodeShape .`,
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
const result = await listOntologies(scoped.store);
|
|
105
|
+
const shapeOnly = result.find(
|
|
106
|
+
(o) => o.namespace === "http://shapes.example.org/ontology#",
|
|
107
|
+
);
|
|
108
|
+
expect(shapeOnly).toBeDefined();
|
|
109
|
+
expect(shapeOnly?.shapeCount).toBe(1);
|
|
110
|
+
expect(shapeOnly?.classCount).toBe(0);
|
|
111
|
+
} finally {
|
|
112
|
+
scoped.cleanup();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
39
116
|
it("returns sorted by prefix", async () => {
|
|
40
117
|
const result = await listOntologies(store);
|
|
41
118
|
const prefixes = result.map((o) => o.prefix);
|
|
@@ -4,17 +4,51 @@ import { P } from "../../shared/prefixes.js";
|
|
|
4
4
|
import type { OntologySummary } from "../../shared/types/index.js";
|
|
5
5
|
import findNamespace from "../helpers/findNamespace.js";
|
|
6
6
|
|
|
7
|
+
const SH = "http://www.w3.org/ns/shacl#";
|
|
8
|
+
|
|
7
9
|
/** Check whether a URI belongs to the anatomy namespace. */
|
|
8
10
|
function isAnatomyTerm(uri: string): boolean {
|
|
9
11
|
return uri.toLowerCase().includes("anatomy");
|
|
10
12
|
}
|
|
11
13
|
|
|
14
|
+
/** Per-namespace URI accumulator. */
|
|
15
|
+
function add(
|
|
16
|
+
map: Map<string, Set<string>>,
|
|
17
|
+
namespace: string,
|
|
18
|
+
uri: string,
|
|
19
|
+
): void {
|
|
20
|
+
const set = map.get(namespace) ?? new Set();
|
|
21
|
+
set.add(uri);
|
|
22
|
+
map.set(namespace, set);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Derive a namespace from a URI when no registered prefix matches: cut
|
|
27
|
+
* after the last `#`, else after the last `/`. Keeps ontologies loaded
|
|
28
|
+
* without a declared prefix (e.g. a package that has not yet published
|
|
29
|
+
* its `pragma.prefixes`) visible in the listing instead of silently
|
|
30
|
+
* dropping their whole TBox.
|
|
31
|
+
*/
|
|
32
|
+
function deriveNamespace(uri: string): string | undefined {
|
|
33
|
+
const hash = uri.lastIndexOf("#");
|
|
34
|
+
if (hash > 0) return uri.slice(0, hash + 1);
|
|
35
|
+
// Scheme-agnostic: any slash after the `scheme://` authority separator
|
|
36
|
+
// marks a path segment; a URI with no path (http://host) has none.
|
|
37
|
+
const authority = uri.indexOf("://");
|
|
38
|
+
if (authority < 0) return undefined;
|
|
39
|
+
const slash = uri.lastIndexOf("/");
|
|
40
|
+
return slash > authority + 2 ? uri.slice(0, slash + 1) : undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
12
43
|
/**
|
|
13
|
-
* List all loaded ontology namespaces with their
|
|
44
|
+
* List all loaded ontology namespaces with their header metadata (title,
|
|
45
|
+
* version) and counts: classes, relations (object properties), attributes
|
|
46
|
+
* (datatype properties), SHACL shapes, and anatomy terms.
|
|
14
47
|
*
|
|
15
48
|
* Discovers namespaces by querying for `owl:Class` and
|
|
16
49
|
* `owl:ObjectProperty`/`owl:DatatypeProperty` definitions, then maps
|
|
17
|
-
* namespace URIs back to prefixes via `store.prefixes`.
|
|
50
|
+
* namespace URIs back to prefixes via `store.prefixes`. Four whole-store
|
|
51
|
+
* queries regardless of how many namespaces are loaded.
|
|
18
52
|
*
|
|
19
53
|
* @param store - The ke store to query.
|
|
20
54
|
* @returns An array of {@link OntologySummary} sorted alphabetically by prefix.
|
|
@@ -41,25 +75,47 @@ export default async function listOntologies(
|
|
|
41
75
|
`),
|
|
42
76
|
);
|
|
43
77
|
|
|
78
|
+
const shapeResult = await store.query(
|
|
79
|
+
buildQuery(`
|
|
80
|
+
SELECT ?shape ?target
|
|
81
|
+
WHERE {
|
|
82
|
+
?shape a <${SH}NodeShape> .
|
|
83
|
+
OPTIONAL { ?shape <${SH}targetClass> ?target }
|
|
84
|
+
}
|
|
85
|
+
`),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const metaResult = await store.query(
|
|
89
|
+
buildQuery(`
|
|
90
|
+
SELECT ?ont ?title ?version
|
|
91
|
+
WHERE {
|
|
92
|
+
?ont a ${P.owl}Ontology .
|
|
93
|
+
OPTIONAL { ?ont ${P.rdfs}label ?title }
|
|
94
|
+
OPTIONAL { ?ont ${P.owl}versionInfo ?version }
|
|
95
|
+
}
|
|
96
|
+
ORDER BY ?ont
|
|
97
|
+
`),
|
|
98
|
+
);
|
|
99
|
+
|
|
44
100
|
const prefixEntries = Object.entries(store.prefixes);
|
|
101
|
+
// Everything below is keyed by namespace URI so ontologies without a
|
|
102
|
+
// registered prefix still surface in the listing.
|
|
103
|
+
const nsOf = (uri: string): string | undefined =>
|
|
104
|
+
findNamespace(uri, prefixEntries)?.namespace ?? deriveNamespace(uri);
|
|
45
105
|
const classCounts = new Map<string, Set<string>>();
|
|
46
|
-
const
|
|
106
|
+
const relationCounts = new Map<string, Set<string>>();
|
|
107
|
+
const attributeCounts = new Map<string, Set<string>>();
|
|
108
|
+
const shapeCounts = new Map<string, Set<string>>();
|
|
47
109
|
const anatomyCounts = new Map<string, Set<string>>();
|
|
110
|
+
const meta = new Map<string, { title?: string; version?: string }>();
|
|
48
111
|
|
|
49
112
|
if (classResult.type === "select") {
|
|
50
113
|
for (const b of classResult.bindings) {
|
|
51
114
|
const uri = b.class ?? "";
|
|
52
|
-
const ns =
|
|
115
|
+
const ns = nsOf(uri);
|
|
53
116
|
if (ns) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
classCounts.set(ns.prefix, set);
|
|
57
|
-
|
|
58
|
-
if (isAnatomyTerm(uri)) {
|
|
59
|
-
const anatomySet = anatomyCounts.get(ns.prefix) ?? new Set();
|
|
60
|
-
anatomySet.add(uri);
|
|
61
|
-
anatomyCounts.set(ns.prefix, anatomySet);
|
|
62
|
-
}
|
|
117
|
+
add(classCounts, ns, uri);
|
|
118
|
+
if (isAnatomyTerm(uri)) add(anatomyCounts, ns, uri);
|
|
63
119
|
}
|
|
64
120
|
}
|
|
65
121
|
}
|
|
@@ -67,35 +123,75 @@ export default async function listOntologies(
|
|
|
67
123
|
if (propResult.type === "select") {
|
|
68
124
|
for (const b of propResult.bindings) {
|
|
69
125
|
const uri = b.prop ?? "";
|
|
70
|
-
const ns =
|
|
126
|
+
const ns = nsOf(uri);
|
|
71
127
|
if (ns) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (isAnatomyTerm(uri))
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
128
|
+
const kind = b.propType?.includes("ObjectProperty")
|
|
129
|
+
? relationCounts
|
|
130
|
+
: attributeCounts;
|
|
131
|
+
add(kind, ns, uri);
|
|
132
|
+
if (isAnatomyTerm(uri)) add(anatomyCounts, ns, uri);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (shapeResult.type === "select") {
|
|
138
|
+
for (const b of shapeResult.bindings) {
|
|
139
|
+
// Same attribution as `ontology show`: a shape counts for the
|
|
140
|
+
// namespace of the shape itself or of its target class.
|
|
141
|
+
const ns = b.shape ? nsOf(b.shape) : undefined;
|
|
142
|
+
const targetNs = b.target ? nsOf(b.target) : undefined;
|
|
143
|
+
const owner = ns ?? targetNs;
|
|
144
|
+
if (owner && b.shape) add(shapeCounts, owner, b.shape);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (metaResult.type === "select") {
|
|
149
|
+
for (const b of metaResult.bindings) {
|
|
150
|
+
const ns = b.ont ? nsOf(b.ont) : undefined;
|
|
151
|
+
if (ns && !meta.has(ns)) {
|
|
152
|
+
meta.set(ns, {
|
|
153
|
+
...(b.title ? { title: b.title } : {}),
|
|
154
|
+
...(b.version ? { version: b.version } : {}),
|
|
155
|
+
});
|
|
81
156
|
}
|
|
82
157
|
}
|
|
83
158
|
}
|
|
84
159
|
|
|
85
|
-
|
|
160
|
+
// A namespace that only contributes SHACL shapes still surfaces; one
|
|
161
|
+
// that only declares an owl:Ontology header (no terms at all) does not —
|
|
162
|
+
// an empty TBox is not a listable ontology.
|
|
163
|
+
const allNamespaces = new Set([
|
|
86
164
|
...classCounts.keys(),
|
|
87
|
-
...
|
|
165
|
+
...relationCounts.keys(),
|
|
166
|
+
...attributeCounts.keys(),
|
|
88
167
|
...anatomyCounts.keys(),
|
|
168
|
+
...shapeCounts.keys(),
|
|
89
169
|
]);
|
|
90
170
|
|
|
91
|
-
|
|
92
|
-
const
|
|
171
|
+
const summaries = [...allNamespaces].map((namespace) => {
|
|
172
|
+
const prefix = prefixEntries.find(([, ns]) => ns === namespace)?.[0] ?? "";
|
|
173
|
+
const header = meta.get(namespace);
|
|
93
174
|
return {
|
|
94
175
|
prefix,
|
|
95
176
|
namespace,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
177
|
+
...(header?.title ? { title: header.title } : {}),
|
|
178
|
+
...(header?.version ? { version: header.version } : {}),
|
|
179
|
+
classCount: classCounts.get(namespace)?.size ?? 0,
|
|
180
|
+
relationCount: relationCounts.get(namespace)?.size ?? 0,
|
|
181
|
+
attributeCount: attributeCounts.get(namespace)?.size ?? 0,
|
|
182
|
+
shapeCount: shapeCounts.get(namespace)?.size ?? 0,
|
|
183
|
+
anatomyCount: anatomyCounts.get(namespace)?.size ?? 0,
|
|
99
184
|
};
|
|
100
185
|
});
|
|
186
|
+
|
|
187
|
+
// Prefixed namespaces first (alphabetical), unprefixed after by URI.
|
|
188
|
+
return summaries.sort((a, b) =>
|
|
189
|
+
a.prefix && b.prefix
|
|
190
|
+
? a.prefix.localeCompare(b.prefix)
|
|
191
|
+
: a.prefix
|
|
192
|
+
? -1
|
|
193
|
+
: b.prefix
|
|
194
|
+
? 1
|
|
195
|
+
: a.namespace.localeCompare(b.namespace),
|
|
196
|
+
);
|
|
101
197
|
}
|