@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
|
@@ -2,104 +2,395 @@ import chalk from "chalk";
|
|
|
2
2
|
// Deep import, not #pipeline: the barrel re-exports collectCommands, which
|
|
3
3
|
// imports every domain barrel — including this one — so #pipeline here forms
|
|
4
4
|
// a module cycle that evaluates this domain's stories as undefined at load.
|
|
5
|
-
import {
|
|
6
|
-
formatHeading,
|
|
7
|
-
formatSection,
|
|
8
|
-
} from "../../../pipeline/formatTerminal.js";
|
|
9
|
-
import compactUri from "../../shared/compactUri.js";
|
|
5
|
+
import { formatHeading } from "../../../pipeline/formatTerminal.js";
|
|
10
6
|
import type { Formatters } from "../../shared/formatters.js";
|
|
11
|
-
import {
|
|
12
|
-
|
|
7
|
+
import type {
|
|
8
|
+
OntologyClassFocus,
|
|
9
|
+
OntologyDetailed,
|
|
10
|
+
OntologyProperty,
|
|
11
|
+
} from "../../shared/types/index.js";
|
|
12
|
+
import type { ClassTreeNode } from "../helpers/buildClassTree.js";
|
|
13
|
+
import buildClassTree from "../helpers/buildClassTree.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Formatter input for `ontology show`: the one complete structure plus the
|
|
17
|
+
* projection controls resolved from the story params. `--full-uris` is
|
|
18
|
+
* already applied to `ontology` by the story's `toOutput`, so the renderers
|
|
19
|
+
* below never re-encode IRIs.
|
|
20
|
+
*/
|
|
21
|
+
export interface OntologyShowInput {
|
|
22
|
+
readonly ontology: OntologyDetailed;
|
|
23
|
+
/** Include datatype properties (attributes); default renders relations only. */
|
|
24
|
+
readonly showProperties: boolean;
|
|
25
|
+
}
|
|
13
26
|
|
|
14
27
|
/**
|
|
15
28
|
* Formatters for `pragma ontology show` output.
|
|
16
29
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
30
|
+
* All three modes are projections of the same {@link OntologyDetailed}
|
|
31
|
+
* structure — they may style differently, but every fact they print comes
|
|
32
|
+
* from that one shape, and the hierarchy is derived through the single
|
|
33
|
+
* shared {@link buildClassTree} helper so it can never diverge between
|
|
34
|
+
* output modes.
|
|
35
|
+
*
|
|
36
|
+
* - **plain**: chalk-styled header (name, metadata, counts), class
|
|
37
|
+
* hierarchy tree with instance counts and inline relations, constraints,
|
|
38
|
+
* and a next-command footer. Attributes render only with `--properties`.
|
|
39
|
+
* Renders the class deep dive when `focus` is present.
|
|
40
|
+
* - **llm**: the same content as condensed Markdown.
|
|
41
|
+
* - **json**: the complete {@link OntologyDetailed} structure itself
|
|
42
|
+
* (always everything — display flags never subset the canonical data).
|
|
20
43
|
*/
|
|
21
|
-
const formatters: Formatters<
|
|
22
|
-
plain(
|
|
23
|
-
|
|
44
|
+
const formatters: Formatters<OntologyShowInput> = {
|
|
45
|
+
plain({ ontology, showProperties }) {
|
|
46
|
+
if (ontology.focus) return renderFocusPlain(ontology.focus);
|
|
24
47
|
|
|
48
|
+
const lines: string[] = [];
|
|
25
49
|
lines.push(
|
|
26
|
-
formatHeading(
|
|
27
|
-
`${data.prefix}: ${compactUri(data.namespace, PREFIX_MAP)}`,
|
|
28
|
-
),
|
|
50
|
+
formatHeading(`Ontology ${ontology.prefix}: — ${ontology.namespace}`),
|
|
29
51
|
);
|
|
52
|
+
lines.push(chalk.dim(summaryLine(ontology)));
|
|
53
|
+
if (ontology.meta?.imports?.length) {
|
|
54
|
+
lines.push(chalk.dim(`imports: ${ontology.meta.imports.join(", ")}`));
|
|
55
|
+
}
|
|
30
56
|
|
|
31
|
-
if (
|
|
32
|
-
const classLines = data.classes
|
|
33
|
-
.map((c) => {
|
|
34
|
-
const sup = c.superclass
|
|
35
|
-
? chalk.dim(` extends ${compactUri(c.superclass, PREFIX_MAP)}`)
|
|
36
|
-
: "";
|
|
37
|
-
return ` ${chalk.bold(c.label)} ${chalk.dim(`(${compactUri(c.uri, PREFIX_MAP)})`)}${sup}`;
|
|
38
|
-
})
|
|
39
|
-
.join("\n");
|
|
57
|
+
if (ontology.classes.length > 0) {
|
|
40
58
|
lines.push("");
|
|
41
|
-
|
|
59
|
+
for (const root of buildClassTree(ontology.classes)) {
|
|
60
|
+
renderTreePlain(root, "", "", showProperties, lines);
|
|
61
|
+
}
|
|
42
62
|
}
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
.map((p) => {
|
|
47
|
-
const domain = p.domain
|
|
48
|
-
? chalk.dim(` domain: ${compactUri(p.domain, PREFIX_MAP)}`)
|
|
49
|
-
: "";
|
|
50
|
-
const range = p.range
|
|
51
|
-
? chalk.dim(` range: ${compactUri(p.range, PREFIX_MAP)}`)
|
|
52
|
-
: "";
|
|
53
|
-
return ` ${chalk.bold(p.label)} ${chalk.dim(`(${compactUri(p.uri, PREFIX_MAP)})`)} (${p.type})${domain}${range}`;
|
|
54
|
-
})
|
|
55
|
-
.join("\n");
|
|
64
|
+
const looseRelations = unattachedToShow(ontology, showProperties);
|
|
65
|
+
if (looseRelations.length > 0) {
|
|
56
66
|
lines.push("");
|
|
57
|
-
lines.push(
|
|
67
|
+
lines.push(
|
|
68
|
+
`${chalk.bold("Unattached:")} ${looseRelations
|
|
69
|
+
.map((p) => propLine(p, showProperties))
|
|
70
|
+
.join(", ")}`,
|
|
71
|
+
);
|
|
58
72
|
}
|
|
59
73
|
|
|
74
|
+
if (ontology.constraints && ontology.constraints.length > 0) {
|
|
75
|
+
lines.push("");
|
|
76
|
+
lines.push(chalk.bold("Constraints:"));
|
|
77
|
+
for (const c of ontology.constraints) {
|
|
78
|
+
const target = c.targetClass ? ` on ${c.targetClass}` : "";
|
|
79
|
+
lines.push(
|
|
80
|
+
` ${c.shape}${target} ${chalk.dim(
|
|
81
|
+
`(${c.propertyCount} property constraints)`,
|
|
82
|
+
)}`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
lines.push("");
|
|
88
|
+
lines.push(chalk.dim(footer(ontology)));
|
|
89
|
+
|
|
60
90
|
return lines.join("\n");
|
|
61
91
|
},
|
|
62
92
|
|
|
63
|
-
llm(
|
|
64
|
-
|
|
65
|
-
`## ${data.prefix}: ${compactUri(data.namespace, PREFIX_MAP)}`,
|
|
66
|
-
"",
|
|
67
|
-
];
|
|
93
|
+
llm({ ontology, showProperties }) {
|
|
94
|
+
if (ontology.focus) return renderFocusLlm(ontology.focus);
|
|
68
95
|
|
|
69
|
-
|
|
96
|
+
const lines: string[] = [];
|
|
97
|
+
lines.push(`## Ontology ${ontology.prefix}: — ${ontology.namespace}`);
|
|
98
|
+
lines.push(summaryLine(ontology));
|
|
99
|
+
if (ontology.meta?.imports?.length) {
|
|
100
|
+
lines.push(`imports: ${ontology.meta.imports.join(", ")}`);
|
|
101
|
+
}
|
|
102
|
+
lines.push("");
|
|
103
|
+
|
|
104
|
+
if (ontology.classes.length > 0) {
|
|
70
105
|
lines.push("### Classes");
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
: "";
|
|
75
|
-
lines.push(
|
|
76
|
-
`- \`${compactUri(c.uri, PREFIX_MAP)}\` — **${c.label}**${sup}`,
|
|
77
|
-
);
|
|
106
|
+
lines.push("```");
|
|
107
|
+
for (const root of buildClassTree(ontology.classes)) {
|
|
108
|
+
renderTreeLlm(root, 0, showProperties, lines);
|
|
78
109
|
}
|
|
79
|
-
lines.push("");
|
|
110
|
+
lines.push("```");
|
|
80
111
|
}
|
|
81
112
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
113
|
+
const looseRelations = unattachedToShow(ontology, showProperties);
|
|
114
|
+
if (looseRelations.length > 0) {
|
|
115
|
+
lines.push(
|
|
116
|
+
`Unattached: ${looseRelations
|
|
117
|
+
.map((p) => propLine(p, showProperties))
|
|
118
|
+
.join(", ")}`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (ontology.constraints && ontology.constraints.length > 0) {
|
|
123
|
+
lines.push("### Constraints");
|
|
124
|
+
for (const c of ontology.constraints) {
|
|
125
|
+
const target = c.targetClass ? ` targets ${c.targetClass}` : "";
|
|
126
|
+
lines.push(`- ${c.shape}${target} (${c.propertyCount} constraints)`);
|
|
94
127
|
}
|
|
95
128
|
}
|
|
96
129
|
|
|
130
|
+
lines.push("");
|
|
131
|
+
lines.push(footer(ontology));
|
|
132
|
+
|
|
97
133
|
return lines.join("\n");
|
|
98
134
|
},
|
|
99
135
|
|
|
100
|
-
json(
|
|
101
|
-
return JSON.stringify(
|
|
136
|
+
json({ ontology }) {
|
|
137
|
+
return JSON.stringify(ontology, null, 2);
|
|
102
138
|
},
|
|
103
139
|
};
|
|
104
140
|
|
|
105
141
|
export default formatters;
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Shared fragments
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
function allProperties(ontology: OntologyDetailed): OntologyProperty[] {
|
|
148
|
+
return [
|
|
149
|
+
...ontology.classes.flatMap((c) => c.properties),
|
|
150
|
+
...ontology.unattached,
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function summaryLine(ontology: OntologyDetailed): string {
|
|
155
|
+
const props = allProperties(ontology);
|
|
156
|
+
const relations = props.filter((p) => p.kind === "object").length;
|
|
157
|
+
const attributes = props.length - relations;
|
|
158
|
+
const parts = [
|
|
159
|
+
`${ontology.classes.length} classes`,
|
|
160
|
+
`${relations} relations`,
|
|
161
|
+
`${attributes} attributes`,
|
|
162
|
+
];
|
|
163
|
+
if (ontology.constraints?.length) {
|
|
164
|
+
parts.push(`${ontology.constraints.length} shapes`);
|
|
165
|
+
}
|
|
166
|
+
if (ontology.meta?.version) parts.push(`v${ontology.meta.version}`);
|
|
167
|
+
if (ontology.meta?.title) parts.push(ontology.meta.title);
|
|
168
|
+
return parts.join(" · ");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The next-step footer: the class deep dive, plus a runnable query seeded
|
|
173
|
+
* with a real class from this namespace so follow-up querying starts from
|
|
174
|
+
* copy-paste, not from reading SPARQL docs.
|
|
175
|
+
*/
|
|
176
|
+
function footer(ontology: OntologyDetailed): string {
|
|
177
|
+
const seed =
|
|
178
|
+
ontology.classes.find((c) => (c.instances ?? 0) > 0) ?? ontology.classes[0];
|
|
179
|
+
const query = seed
|
|
180
|
+
? ` · pragma graph query "SELECT ?s WHERE { ?s a ${sparqlTerm(seed.iri)} } LIMIT 10"`
|
|
181
|
+
: "";
|
|
182
|
+
return `Next: pragma ontology show ${ontology.prefix} --class <Class>${query}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Render an IRI as a valid SPARQL term: full URIs (present under
|
|
187
|
+
* `--full-uris`) need `<...>`; compact prefixed IRIs are usable as-is.
|
|
188
|
+
*/
|
|
189
|
+
function sparqlTerm(iri: string): string {
|
|
190
|
+
return iri.startsWith("http://") || iri.startsWith("https://")
|
|
191
|
+
? `<${iri}>`
|
|
192
|
+
: iri;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Properties a class renders inline: relations always, attributes on demand. */
|
|
196
|
+
function inlineProperties(
|
|
197
|
+
props: readonly OntologyProperty[],
|
|
198
|
+
showProperties: boolean,
|
|
199
|
+
): readonly OntologyProperty[] {
|
|
200
|
+
return showProperties ? props : props.filter((p) => p.kind === "object");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Unattached entries to display under the current verbosity. */
|
|
204
|
+
function unattachedToShow(
|
|
205
|
+
ontology: OntologyDetailed,
|
|
206
|
+
showProperties: boolean,
|
|
207
|
+
): readonly OntologyProperty[] {
|
|
208
|
+
return inlineProperties(ontology.unattached, showProperties);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** One class's inline property summary: `tier →ds:Tier, …(+2 more)`. */
|
|
212
|
+
function inlineProps(
|
|
213
|
+
props: readonly OntologyProperty[],
|
|
214
|
+
showProperties: boolean,
|
|
215
|
+
max = 4,
|
|
216
|
+
): string {
|
|
217
|
+
const shown = inlineProperties(props, showProperties);
|
|
218
|
+
if (shown.length === 0) return "";
|
|
219
|
+
const rendered = shown.slice(0, max).map((p) => propLine(p, showProperties));
|
|
220
|
+
const extra = shown.length - max;
|
|
221
|
+
return ` — ${rendered.join(", ")}${extra > 0 ? `, …(+${extra} more)` : ""}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function propLine(p: OntologyProperty, showProperties: boolean): string {
|
|
225
|
+
const range =
|
|
226
|
+
p.kind === "object" && p.range
|
|
227
|
+
? ` →${p.range}`
|
|
228
|
+
: showProperties && p.range
|
|
229
|
+
? `: ${p.range}`
|
|
230
|
+
: "";
|
|
231
|
+
return `${p.label}${range}`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function classSuffix(node: ClassTreeNode): string {
|
|
235
|
+
const count = node.cls.instances ? ` (${node.cls.instances})` : "";
|
|
236
|
+
const also =
|
|
237
|
+
node.alsoExtends.length > 0
|
|
238
|
+
? ` [also extends ${node.alsoExtends.join(", ")}]`
|
|
239
|
+
: "";
|
|
240
|
+
return `${count}${also}`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// Tree renderers (both derive from the same buildClassTree forest)
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
function renderTreePlain(
|
|
248
|
+
node: ClassTreeNode,
|
|
249
|
+
prefix: string,
|
|
250
|
+
childIndent: string,
|
|
251
|
+
showProperties: boolean,
|
|
252
|
+
lines: string[],
|
|
253
|
+
): void {
|
|
254
|
+
const suffix = chalk.dim(
|
|
255
|
+
`${classSuffix(node)}${inlineProps(node.cls.properties, showProperties)}`,
|
|
256
|
+
);
|
|
257
|
+
lines.push(`${prefix}${chalk.bold(node.cls.label)}${suffix}`);
|
|
258
|
+
|
|
259
|
+
node.children.forEach((child, i) => {
|
|
260
|
+
const last = i === node.children.length - 1;
|
|
261
|
+
renderTreePlain(
|
|
262
|
+
child,
|
|
263
|
+
`${childIndent}${last ? "└─ " : "├─ "}`,
|
|
264
|
+
`${childIndent}${last ? " " : "│ "}`,
|
|
265
|
+
showProperties,
|
|
266
|
+
lines,
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function renderTreeLlm(
|
|
272
|
+
node: ClassTreeNode,
|
|
273
|
+
depth: number,
|
|
274
|
+
showProperties: boolean,
|
|
275
|
+
lines: string[],
|
|
276
|
+
): void {
|
|
277
|
+
const pad = " ".repeat(depth);
|
|
278
|
+
lines.push(
|
|
279
|
+
`${pad}${node.cls.iri}${classSuffix(node)}${inlineProps(node.cls.properties, showProperties)}`,
|
|
280
|
+
);
|
|
281
|
+
for (const child of node.children) {
|
|
282
|
+
renderTreeLlm(child, depth + 1, showProperties, lines);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
// Focus (per-class deep dive) renderers — always full detail
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
function renderFocusPlain(f: OntologyClassFocus): string {
|
|
291
|
+
const lines: string[] = [];
|
|
292
|
+
|
|
293
|
+
lines.push(formatHeading(`${f.label} — ${f.iri}`));
|
|
294
|
+
if (f.comment) lines.push(f.comment);
|
|
295
|
+
const chain =
|
|
296
|
+
f.superChain.length > 0 ? f.superChain.join(" < ") : "(root class)";
|
|
297
|
+
lines.push(chalk.dim(`extends: ${chain} · instances: ${f.instances}`));
|
|
298
|
+
|
|
299
|
+
pushPropSection(lines, "Relations", relationsOf(f.directProperties));
|
|
300
|
+
pushPropSection(lines, "Attributes", attributesOf(f.directProperties));
|
|
301
|
+
pushPropSection(lines, "Inherited", f.inheritedProperties);
|
|
302
|
+
pushPropSection(lines, "Referenced by", f.referencedBy);
|
|
303
|
+
|
|
304
|
+
if (f.subclasses.length > 0) {
|
|
305
|
+
lines.push("");
|
|
306
|
+
lines.push(`${chalk.bold("Subclasses:")} ${f.subclasses.join(", ")}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (f.sampleInstances.length > 0) {
|
|
310
|
+
lines.push("");
|
|
311
|
+
lines.push(`${chalk.bold("Instances:")} ${f.sampleInstances.join(", ")}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (f.queries.length > 0) {
|
|
315
|
+
lines.push("");
|
|
316
|
+
lines.push(chalk.bold("Queries:"));
|
|
317
|
+
for (const q of f.queries) {
|
|
318
|
+
lines.push(chalk.dim(` # ${q.label}`));
|
|
319
|
+
lines.push(` pragma graph query "${q.sparql}"`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return lines.join("\n");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function renderFocusLlm(f: OntologyClassFocus): string {
|
|
327
|
+
const lines: string[] = [];
|
|
328
|
+
|
|
329
|
+
lines.push(`## ${f.iri} — ${f.label}`);
|
|
330
|
+
if (f.comment) lines.push(f.comment);
|
|
331
|
+
const chain =
|
|
332
|
+
f.superChain.length > 0 ? f.superChain.join(" < ") : "(root class)";
|
|
333
|
+
lines.push(`- extends: ${chain}`);
|
|
334
|
+
lines.push(`- instances: ${f.instances}`);
|
|
335
|
+
if (f.subclasses.length > 0) {
|
|
336
|
+
lines.push(`- subclasses: ${f.subclasses.join(", ")}`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const section = (title: string, props: readonly OntologyProperty[]) => {
|
|
340
|
+
if (props.length === 0) return;
|
|
341
|
+
lines.push(`### ${title}`);
|
|
342
|
+
for (const p of props) {
|
|
343
|
+
lines.push(`- ${fullPropLine(p)}`);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
section("Relations", relationsOf(f.directProperties));
|
|
347
|
+
section("Attributes", attributesOf(f.directProperties));
|
|
348
|
+
section("Inherited", f.inheritedProperties);
|
|
349
|
+
section("Referenced by", f.referencedBy);
|
|
350
|
+
|
|
351
|
+
if (f.sampleInstances.length > 0) {
|
|
352
|
+
lines.push("### Instances");
|
|
353
|
+
for (const iri of f.sampleInstances) lines.push(`- ${iri}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (f.queries.length > 0) {
|
|
357
|
+
lines.push("### Queries");
|
|
358
|
+
for (const q of f.queries) {
|
|
359
|
+
lines.push(`- ${q.label}: \`${q.sparql}\``);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return lines.join("\n");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function relationsOf(
|
|
367
|
+
props: readonly OntologyProperty[],
|
|
368
|
+
): readonly OntologyProperty[] {
|
|
369
|
+
return props.filter((p) => p.kind === "object");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function attributesOf(
|
|
373
|
+
props: readonly OntologyProperty[],
|
|
374
|
+
): readonly OntologyProperty[] {
|
|
375
|
+
return props.filter((p) => p.kind === "datatype");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function pushPropSection(
|
|
379
|
+
lines: string[],
|
|
380
|
+
title: string,
|
|
381
|
+
props: readonly OntologyProperty[],
|
|
382
|
+
): void {
|
|
383
|
+
if (props.length === 0) return;
|
|
384
|
+
lines.push("");
|
|
385
|
+
lines.push(chalk.bold(`${title}:`));
|
|
386
|
+
for (const p of props) {
|
|
387
|
+
lines.push(` ${fullPropLine(p)}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function fullPropLine(p: OntologyProperty): string {
|
|
392
|
+
const range = p.range ? ` → ${p.range}` : "";
|
|
393
|
+
const from = p.domain ? ` (on ${p.domain})` : "";
|
|
394
|
+
const functional = p.functional ? " [functional]" : "";
|
|
395
|
+
return `${p.iri} (${p.kind})${range}${from}${functional}`;
|
|
396
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure class-hierarchy builder shared by every `ontology show` projection.
|
|
3
|
+
*
|
|
4
|
+
* The {@link OntologyDetailed} data shape keeps classes as a flat,
|
|
5
|
+
* topologically ordered array with `subClassOf` edges (canonical for JSON
|
|
6
|
+
* and MCP consumers); the plain and llm renderers both derive their tree
|
|
7
|
+
* from this single implementation so the hierarchy can never diverge
|
|
8
|
+
* between output modes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { OntologyClass } from "../../shared/types/index.js";
|
|
12
|
+
|
|
13
|
+
/** A class positioned in the rendered hierarchy. */
|
|
14
|
+
export interface ClassTreeNode {
|
|
15
|
+
readonly cls: OntologyClass;
|
|
16
|
+
readonly children: readonly ClassTreeNode[];
|
|
17
|
+
/**
|
|
18
|
+
* Superclasses beyond the one used for tree placement (multiple
|
|
19
|
+
* inheritance) — renderers annotate these as "also extends".
|
|
20
|
+
*/
|
|
21
|
+
readonly alsoExtends: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Arrange classes into a forest.
|
|
26
|
+
*
|
|
27
|
+
* Roots are classes with no in-set parent; children sort alphabetically by
|
|
28
|
+
* label. A class with several in-set parents is placed under its first
|
|
29
|
+
* (alphabetically sorted) parent and carries the rest in `alsoExtends`.
|
|
30
|
+
* Cycle-safe: an edge that would revisit a placed class is ignored, so a
|
|
31
|
+
* cyclic ontology degrades to a flat listing rather than recursing.
|
|
32
|
+
*
|
|
33
|
+
* @param classes - Flat class array (any order; `subClassOf` holds compact
|
|
34
|
+
* or full IRIs — matching whatever `iri` uses).
|
|
35
|
+
* @returns Root nodes in alphabetical label order.
|
|
36
|
+
*/
|
|
37
|
+
export default function buildClassTree(
|
|
38
|
+
classes: readonly OntologyClass[],
|
|
39
|
+
): ClassTreeNode[] {
|
|
40
|
+
const byIri = new Map(classes.map((c) => [c.iri, c]));
|
|
41
|
+
const placed = new Set<string>();
|
|
42
|
+
|
|
43
|
+
const inSetParents = (cls: OntologyClass): string[] =>
|
|
44
|
+
cls.subClassOf.filter((parent) => byIri.has(parent));
|
|
45
|
+
|
|
46
|
+
const childrenOf = (parentIri: string): OntologyClass[] =>
|
|
47
|
+
classes
|
|
48
|
+
.filter((c) => inSetParents(c)[0] === parentIri)
|
|
49
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
50
|
+
|
|
51
|
+
const build = (cls: OntologyClass): ClassTreeNode => {
|
|
52
|
+
placed.add(cls.iri);
|
|
53
|
+
const parents = inSetParents(cls);
|
|
54
|
+
return {
|
|
55
|
+
cls,
|
|
56
|
+
alsoExtends: parents.slice(1),
|
|
57
|
+
children: childrenOf(cls.iri)
|
|
58
|
+
.filter((child) => !placed.has(child.iri))
|
|
59
|
+
.map(build),
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const roots = classes
|
|
64
|
+
.filter((c) => inSetParents(c).length === 0)
|
|
65
|
+
.sort((a, b) => a.label.localeCompare(b.label))
|
|
66
|
+
.map(build);
|
|
67
|
+
|
|
68
|
+
// Cycle fallback: anything unreachable from a root (e.g. mutually
|
|
69
|
+
// extending classes) is appended as an additional root.
|
|
70
|
+
const leftovers = classes
|
|
71
|
+
.filter((c) => !placed.has(c.iri))
|
|
72
|
+
.sort((a, b) => a.label.localeCompare(b.label))
|
|
73
|
+
.map(build);
|
|
74
|
+
|
|
75
|
+
return [...roots, ...leftovers];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Flatten a forest to pre-order (the shape's canonical class ordering). */
|
|
79
|
+
export function flattenClassTree(
|
|
80
|
+
nodes: readonly ClassTreeNode[],
|
|
81
|
+
): OntologyClass[] {
|
|
82
|
+
const out: OntologyClass[] = [];
|
|
83
|
+
const walk = (node: ClassTreeNode): void => {
|
|
84
|
+
out.push(node.cls);
|
|
85
|
+
for (const child of node.children) walk(child);
|
|
86
|
+
};
|
|
87
|
+
for (const node of nodes) walk(node);
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Store } from "@canonical/ke";
|
|
2
|
+
import { buildQuery } from "../../shared/buildQuery.js";
|
|
3
|
+
import extractLocalName from "../../shared/extractLocalName.js";
|
|
4
|
+
import { P } from "../../shared/prefixes.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Tab-completion candidates for the `--class` parameter of
|
|
8
|
+
* `ontology show`: the local names of every `owl:Class` in the store,
|
|
9
|
+
* filtered by the typed partial (case-insensitive prefix match).
|
|
10
|
+
*
|
|
11
|
+
* Completion runs before the prefix positional is resolvable from the
|
|
12
|
+
* completion context, so candidates span all namespaces — the operation
|
|
13
|
+
* scopes the actual lookup to the requested namespace. Results are capped
|
|
14
|
+
* at `limit` so a huge graph cannot flood the shell; an empty partial
|
|
15
|
+
* still completes (class sets are small and the query is one indexed
|
|
16
|
+
* type-scan).
|
|
17
|
+
*
|
|
18
|
+
* @note Queries ke store
|
|
19
|
+
*/
|
|
20
|
+
export default async function completeClassNames(
|
|
21
|
+
store: Store,
|
|
22
|
+
partial: string,
|
|
23
|
+
limit = 50,
|
|
24
|
+
): Promise<string[]> {
|
|
25
|
+
const result = await store.query(
|
|
26
|
+
buildQuery(`
|
|
27
|
+
SELECT ?class
|
|
28
|
+
WHERE { ?class a ${P.owl}Class }
|
|
29
|
+
`),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (result.type !== "select") return [];
|
|
33
|
+
|
|
34
|
+
const needle = partial.toLowerCase();
|
|
35
|
+
const names = new Set<string>();
|
|
36
|
+
for (const b of result.bindings) {
|
|
37
|
+
if (!b.class) continue;
|
|
38
|
+
const local = extractLocalName(b.class);
|
|
39
|
+
if (local.toLowerCase().startsWith(needle)) names.add(local);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return [...names].sort().slice(0, limit);
|
|
43
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expand every compact IRI in an {@link OntologyDetailed} structure back to
|
|
3
|
+
* its full URI using the structure's own `prefixes` map.
|
|
4
|
+
*
|
|
5
|
+
* The data shape keeps compact IRIs as its canonical encoding; `--full-uris`
|
|
6
|
+
* applies this pure transform once in the story's `toOutput`, so every
|
|
7
|
+
* projection (plain, llm, json, MCP condensed) inherits the expansion from
|
|
8
|
+
* the same place. Query hints are left untouched: their compact IRIs are
|
|
9
|
+
* resolvable by `pragma graph query` regardless of the display encoding.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
OntologyClass,
|
|
14
|
+
OntologyClassFocus,
|
|
15
|
+
OntologyDetailed,
|
|
16
|
+
OntologyProperty,
|
|
17
|
+
} from "../../shared/types/index.js";
|
|
18
|
+
|
|
19
|
+
export default function expandOntologyIris(
|
|
20
|
+
ontology: OntologyDetailed,
|
|
21
|
+
): OntologyDetailed {
|
|
22
|
+
const expand = (iri: string): string => {
|
|
23
|
+
const colon = iri.indexOf(":");
|
|
24
|
+
if (colon <= 0) return iri;
|
|
25
|
+
const namespace = ontology.prefixes[iri.slice(0, colon)];
|
|
26
|
+
return namespace === undefined
|
|
27
|
+
? iri
|
|
28
|
+
: `${namespace}${iri.slice(colon + 1)}`;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const expandProperty = (p: OntologyProperty): OntologyProperty => ({
|
|
32
|
+
...p,
|
|
33
|
+
iri: expand(p.iri),
|
|
34
|
+
...(p.domain ? { domain: expand(p.domain) } : {}),
|
|
35
|
+
...(p.range ? { range: expand(p.range) } : {}),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const expandClass = (c: OntologyClass): OntologyClass => ({
|
|
39
|
+
...c,
|
|
40
|
+
iri: expand(c.iri),
|
|
41
|
+
subClassOf: c.subClassOf.map(expand),
|
|
42
|
+
properties: c.properties.map(expandProperty),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const expandFocus = (f: OntologyClassFocus): OntologyClassFocus => ({
|
|
46
|
+
...f,
|
|
47
|
+
iri: expand(f.iri),
|
|
48
|
+
superChain: f.superChain.map(expand),
|
|
49
|
+
subclasses: f.subclasses.map(expand),
|
|
50
|
+
directProperties: f.directProperties.map(expandProperty),
|
|
51
|
+
inheritedProperties: f.inheritedProperties.map(expandProperty),
|
|
52
|
+
referencedBy: f.referencedBy.map(expandProperty),
|
|
53
|
+
sampleInstances: f.sampleInstances.map(expand),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...ontology,
|
|
58
|
+
...(ontology.meta?.imports
|
|
59
|
+
? {
|
|
60
|
+
meta: {
|
|
61
|
+
...ontology.meta,
|
|
62
|
+
imports: ontology.meta.imports.map(expand),
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
: {}),
|
|
66
|
+
classes: ontology.classes.map(expandClass),
|
|
67
|
+
unattached: ontology.unattached.map(expandProperty),
|
|
68
|
+
...(ontology.constraints
|
|
69
|
+
? {
|
|
70
|
+
constraints: ontology.constraints.map((c) => ({
|
|
71
|
+
...c,
|
|
72
|
+
shape: expand(c.shape),
|
|
73
|
+
...(c.targetClass ? { targetClass: expand(c.targetClass) } : {}),
|
|
74
|
+
})),
|
|
75
|
+
}
|
|
76
|
+
: {}),
|
|
77
|
+
...(ontology.focus ? { focus: expandFocus(ontology.focus) } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|