@canonical/pragma-cli 0.32.0 → 0.34.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 +72 -0
- package/dist/pragma +0 -0
- package/package.json +19 -15
- package/pragma.conf.ts +664 -0
- package/src/bin.ts +15 -6
- package/src/capabilities/block.parity.test.ts +250 -0
- package/src/capabilities/capabilities/capabilities.render.ts +3 -2
- package/src/capabilities/capabilities/capabilities.test.ts +10 -1
- package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
- package/src/capabilities/capabilities/catalog.ts +20 -8
- package/src/capabilities/capabilities/hints.ts +21 -36
- package/src/capabilities/capabilities/types.ts +2 -0
- package/src/capabilities/colophon/collectColophon.ts +34 -19
- package/src/capabilities/colophon/colophon.render.test.ts +31 -4
- package/src/capabilities/colophon/colophon.render.ts +13 -4
- package/src/capabilities/colophon/colophon.test.ts +3 -3
- package/src/capabilities/colophon/colophon.verb.ts +11 -4
- package/src/capabilities/colophon/markdownTerminal.ts +33 -14
- package/src/capabilities/colophon/types.ts +16 -7
- package/src/capabilities/config/collectConfigShow.test.ts +57 -0
- package/src/capabilities/config/collectConfigShow.ts +41 -4
- package/src/capabilities/config/fields.ts +10 -108
- package/src/capabilities/config/set.test.ts +6 -6
- package/src/capabilities/config/set.verb.ts +12 -8
- package/src/capabilities/config/show.render.test.ts +137 -0
- package/src/capabilities/config/show.render.ts +57 -16
- package/src/capabilities/config/show.verb.ts +9 -8
- package/src/capabilities/config/types.ts +8 -0
- package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
- package/src/capabilities/create/byteEquality.test.ts +82 -101
- package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
- package/src/capabilities/create/constants.ts +75 -0
- package/src/capabilities/create/create.mcp.test.ts +2 -2
- package/src/capabilities/create/create.test.ts +192 -16
- package/src/capabilities/create/create.verb.ts +208 -79
- package/src/capabilities/create/index.ts +2 -10
- package/src/capabilities/create/pickGenerator.ts +34 -28
- package/src/capabilities/create/templates.embedded.generated.ts +4 -29
- package/src/capabilities/create/types.ts +10 -6
- package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
- package/src/capabilities/distribution.test.ts +120 -0
- package/src/capabilities/distribution.ts +59 -0
- package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
- package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
- package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
- package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
- package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
- package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
- package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
- package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
- package/src/capabilities/doctor/doctor.render.test.ts +170 -0
- package/src/capabilities/doctor/doctor.render.ts +117 -34
- package/src/capabilities/doctor/doctor.test.ts +68 -9
- package/src/capabilities/doctor/doctor.verb.ts +6 -2
- package/src/capabilities/doctor/runChecks.ts +33 -9
- package/src/capabilities/doctor/types.ts +16 -1
- package/src/capabilities/emptyRecovery.test.ts +32 -15
- package/src/capabilities/graph/inspect.verb.ts +3 -2
- package/src/capabilities/graph/query.verb.ts +3 -2
- package/src/capabilities/graph/runQuery.ts +8 -5
- package/src/capabilities/index.ts +22 -18
- package/src/capabilities/info/collectInfo.ts +10 -6
- package/src/capabilities/info/info.render.ts +3 -2
- package/src/capabilities/info/info.verb.ts +3 -2
- package/src/capabilities/lazy.test.ts +93 -2
- package/src/capabilities/ontology/index.ts +10 -3
- package/src/capabilities/ontology/ontology.test.ts +157 -5
- package/src/capabilities/ontology/verbs.ts +203 -104
- package/src/capabilities/prompt/prompt.render.test.ts +94 -0
- package/src/capabilities/prompt/prompt.render.ts +23 -6
- package/src/capabilities/prompt/prompt.test.ts +68 -1
- package/src/capabilities/prompt/verbs.ts +24 -11
- package/src/capabilities/reference.test.ts +32 -0
- package/src/capabilities/resources/provider.ts +58 -25
- package/src/capabilities/resources/resources.test.ts +176 -21
- package/src/capabilities/scopeTypeSync.test.ts +37 -0
- package/src/capabilities/setup/index.ts +7 -1
- package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
- package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
- package/src/capabilities/setup/operations/setupLsp.ts +56 -12
- package/src/capabilities/setup/operations/setupMcp.ts +216 -39
- package/src/capabilities/setup/operations/setupSkills.ts +10 -4
- package/src/capabilities/setup/setup.render.test.ts +109 -0
- package/src/capabilities/setup/setup.render.ts +61 -14
- package/src/capabilities/setup/setup.test.ts +352 -1
- package/src/capabilities/setup/setup.verb.ts +68 -9
- package/src/capabilities/setup/shell.ts +29 -3
- package/src/capabilities/setup/types.ts +59 -2
- package/src/capabilities/shared/bands.ts +16 -0
- package/src/capabilities/skill/discover.ts +11 -5
- package/src/capabilities/skill/render.ts +4 -5
- package/src/capabilities/skill/verbs.ts +7 -7
- package/src/capabilities/sources/collectStatus.ts +69 -49
- package/src/capabilities/sources/index.ts +1 -1
- package/src/capabilities/sources/installSkills.ts +2 -2
- package/src/capabilities/sources/runUpdate.ts +146 -98
- package/src/capabilities/sources/sources.test.ts +264 -84
- package/src/capabilities/sources/status.render.test.ts +94 -0
- package/src/capabilities/sources/status.render.ts +55 -33
- package/src/capabilities/sources/status.verb.ts +15 -8
- package/src/capabilities/sources/types.ts +18 -25
- package/src/capabilities/sources/update.render.ts +9 -2
- package/src/capabilities/sources/update.verb.ts +12 -23
- package/src/capabilities/surface.test.ts +65 -17
- package/src/capabilities/tier.parity.test.ts +144 -0
- package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
- package/src/constants.test.ts +9 -7
- package/src/constants.ts +49 -23
- package/src/identity.test.ts +333 -0
- package/src/index.ts +2 -2
- package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
- package/src/kernel/completion/complete.test.ts +3 -5
- package/src/kernel/completion/emitScripts.test.ts +1 -1
- package/src/kernel/completion/emitScripts.ts +4 -2
- package/src/kernel/completion/entitySource.test.ts +185 -41
- package/src/kernel/completion/entitySource.ts +131 -113
- package/src/kernel/completion/model.test.ts +3 -3
- package/src/kernel/completion/model.ts +11 -1
- package/src/kernel/completion/parse.test.ts +2 -4
- package/src/kernel/completion/safety.test.ts +82 -36
- package/src/kernel/completion/shellDrive.test.ts +592 -0
- package/src/kernel/completion/templates/fish.ts +28 -6
- package/src/kernel/completion/types.ts +3 -3
- package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
- package/src/kernel/config/defaults.test.ts +85 -0
- package/src/kernel/config/defaults.ts +12 -23
- package/src/kernel/config/evaluateProjectConfig.ts +2 -3
- package/src/kernel/config/findProjectConfig.ts +4 -3
- package/src/kernel/config/firstRun.ts +12 -5
- package/src/kernel/config/globalConfig.ts +2 -2
- package/src/kernel/config/index.ts +4 -5
- package/src/kernel/config/paths.ts +17 -17
- package/src/kernel/config/readConfig.test.ts +242 -6
- package/src/kernel/config/readConfig.ts +8 -8
- package/src/kernel/config/schema.test.ts +50 -0
- package/src/kernel/config/schema.ts +97 -10
- package/src/kernel/config/types.ts +73 -19
- package/src/kernel/copy.test.ts +453 -0
- package/src/kernel/error/PragmaError.test.ts +5 -5
- package/src/kernel/error/constants.ts +3 -2
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +22 -45
- package/src/kernel/error/recovery.ts +22 -27
- package/src/kernel/error/renderError.ts +1 -1
- package/src/kernel/error/types.ts +5 -4
- package/src/kernel/packs/collect.test.ts +195 -11
- package/src/kernel/packs/collect.ts +213 -58
- package/src/kernel/packs/compile.test.ts +163 -7
- package/src/kernel/packs/compile.ts +15 -12
- package/src/kernel/packs/disclosure.test.ts +5 -2
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
- package/src/kernel/packs/graphqlSource.test.ts +6 -3
- package/src/kernel/packs/iri.ts +5 -7
- package/src/kernel/packs/renderPack.ts +5 -3
- package/src/kernel/packs/resolveEntity.ts +21 -17
- package/src/kernel/packs/runBodies.ts +15 -14
- package/src/kernel/packs/schema.ts +96 -10
- package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
- package/src/kernel/packs/sparql/runSelect.ts +73 -34
- package/src/kernel/packs/toolDescription.test.ts +10 -2
- package/src/kernel/packs/types.ts +39 -4
- package/src/kernel/packs/uniqueness.ts +4 -4
- package/src/kernel/project/cli/buildProgram.test.ts +60 -11
- package/src/kernel/project/cli/buildProgram.ts +11 -0
- package/src/kernel/project/cli/dispatch.test.ts +67 -0
- package/src/kernel/project/cli/dispatch.ts +34 -12
- package/src/kernel/project/cli/globalFlags.test.ts +12 -4
- package/src/kernel/project/cli/globalFlags.ts +14 -14
- package/src/kernel/project/cli/helpFormat.ts +1 -1
- package/src/kernel/project/cli/rootHelp.ts +109 -107
- package/src/kernel/project/mcp/buildServer.ts +11 -6
- package/src/kernel/project/mcp/instructions.ts +12 -7
- package/src/kernel/project/mcp/mcpError.ts +52 -0
- package/src/kernel/project/mcp/parity.test.ts +39 -1
- package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
- package/src/kernel/project/mcp/prompts/provider.ts +51 -7
- package/src/kernel/project/mcp/prompts/source.ts +68 -67
- package/src/kernel/project/mcp/registerVerb.ts +25 -6
- package/src/kernel/project/mcp/serve.ts +11 -3
- package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
- package/src/kernel/render/disclosure.ts +4 -2
- package/src/kernel/render/formatters.ts +5 -4
- package/src/kernel/render/prefixes.test.ts +36 -0
- package/src/kernel/render/prefixes.ts +40 -13
- package/src/kernel/render/renderers.test.ts +35 -1
- package/src/kernel/render/renderers.ts +25 -7
- package/src/kernel/render/style.test.ts +31 -0
- package/src/kernel/render/style.ts +74 -0
- package/src/kernel/runtime/graphpack/build.ts +57 -3
- package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
- package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
- package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +37 -11
- package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
- package/src/kernel/runtime/graphpack/hash.ts +3 -2
- package/src/kernel/runtime/graphpack/index.ts +1 -1
- package/src/kernel/runtime/graphpack/manifest.ts +114 -12
- package/src/kernel/runtime/graphpack/read.ts +32 -22
- package/src/kernel/runtime/graphpack/schemas.ts +74 -0
- package/src/kernel/runtime/graphpack/stories.ts +84 -0
- package/src/kernel/runtime/graphpack/types.ts +72 -45
- package/src/kernel/runtime/loadSession.ts +4 -12
- package/src/kernel/runtime/paths.ts +64 -15
- package/src/kernel/runtime/probe.test.ts +8 -4
- package/src/kernel/runtime/readEntity.ts +6 -5
- package/src/kernel/runtime/refs/gitOps.ts +1 -19
- package/src/kernel/runtime/refs/parseRef.ts +7 -7
- package/src/kernel/runtime/refs/resolve.ts +141 -65
- package/src/kernel/runtime/resolveSources.test.ts +63 -24
- package/src/kernel/runtime/resolveSources.ts +36 -25
- package/src/kernel/runtime/storeReadiness.ts +62 -0
- package/src/kernel/runtime/storeless.test.ts +1 -1
- package/src/kernel/runtime/types.ts +1 -1
- package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
- package/src/kernel/spec/emitReference.test.ts +196 -0
- package/src/kernel/spec/emitReference.ts +624 -0
- package/src/kernel/spec/emitSurface.ts +13 -7
- package/src/kernel/spec/index.ts +2 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.ts +8 -0
- package/src/kernel/spec/types.ts +29 -9
- package/src/kernel/spec/validate.ts +2 -1
- package/src/kernel/vocabulary.test.ts +164 -0
- package/src/kernel/vocabulary.ts +146 -0
- package/src/testing/PARITY_GAPS.ts +9 -3
- package/src/testing/behavioral/README.md +3 -3
- package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
- package/src/testing/behavioral/completion.test.ts +21 -8
- package/src/testing/behavioral/disclosure.test.ts +12 -0
- package/src/testing/behavioral/docExamples.test.ts +283 -0
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
- package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
- package/src/testing/behavioral/harness.foundation.test.ts +14 -5
- package/src/testing/behavioral/journeys.cli.test.ts +116 -73
- package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
- package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
- package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
- package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
- package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
- package/src/testing/behavioral/liveReadSurface.ts +4 -3
- package/src/testing/behavioral/mcpSurface.test.ts +10 -5
- package/src/testing/behavioral/staleCommands.test.ts +86 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
- package/src/testing/eval/cases/assembly.ts +16 -9
- package/src/testing/eval/cases/effectful.ts +31 -16
- package/src/testing/eval/cases/readNouns.ts +23 -18
- package/src/testing/eval/cases/stable.ts +5 -1
- package/src/testing/eval/eval.test.ts +7 -1
- package/src/testing/fixtures/fixtureCapability.ts +201 -1
- package/src/testing/fixtures/graph/canonical.ts +6 -3
- package/src/testing/fixtures/graph/defaultPack.ts +179 -0
- package/src/testing/fixtures/graph/owlExport.ts +97 -0
- package/src/testing/helpers/extractSnippets.ts +29 -0
- package/src/testing/helpers/fixtureGraph.ts +33 -16
- package/src/testing/helpers/golden.ts +1 -1
- package/src/testing/helpers/packRuntime.ts +2 -3
- package/src/testing/helpers/projectCli.ts +2 -1
- package/src/testing/helpers/projectMcp.ts +6 -0
- package/src/testing/perf/budgets.test.ts +4 -0
- package/src/testing/perf/budgets.ts +22 -7
- package/src/testing/perf/globalSetup.ts +121 -6
- package/src/types.ts +2 -2
- package/src/capabilities/block/blockList.verb.ts +0 -150
- package/src/capabilities/block/index.ts +0 -25
- package/src/capabilities/block/pack.ts +0 -155
- package/src/capabilities/block/parity.test.ts +0 -180
- package/src/capabilities/block/tierChain.ts +0 -78
- package/src/capabilities/colophon/pragmaColophon.ts +0 -66
- package/src/capabilities/config/field.test.ts +0 -188
- package/src/capabilities/modifier/index.ts +0 -15
- package/src/capabilities/modifier/pack.ts +0 -64
- package/src/capabilities/standard/index.ts +0 -18
- package/src/capabilities/standard/pack.ts +0 -128
- package/src/capabilities/tier/index.ts +0 -19
- package/src/capabilities/tier/lookup.render.ts +0 -34
- package/src/capabilities/tier/lookup.test.ts +0 -68
- package/src/capabilities/tier/lookup.verb.ts +0 -44
- package/src/capabilities/tier/pack.ts +0 -28
- package/src/capabilities/tier/runLookup.ts +0 -59
- package/src/capabilities/token/addConfig.render.ts +0 -27
- package/src/capabilities/token/addConfig.test.ts +0 -62
- package/src/capabilities/token/addConfig.verb.ts +0 -40
- package/src/capabilities/token/index.ts +0 -19
- package/src/capabilities/token/pack.ts +0 -58
- package/src/capabilities/token/runAddConfig.ts +0 -95
- package/src/kernel/completion/bashDrive.test.ts +0 -73
- package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
- package/src/kernel/runtime/lock.ts +0 -90
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Build the storeless entity index (`index.json`) from a populated store.
|
|
3
3
|
*
|
|
4
|
-
* A lean port of the v1 `buildGraphIndex` (#856):
|
|
5
|
-
* types
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A lean port of the v1 `buildGraphIndex` (#856): a few bulk SPARQL queries —
|
|
5
|
+
* types (which also yield the instance counts), labels, descriptions and
|
|
6
|
+
* alternative names — joined in memory, rather than a query per subject. Only
|
|
7
|
+
* the last names a DOMAIN property, and it is read from the distribution's
|
|
8
|
+
* declaration rather than spelled out here; see {@link resolveAltNamePredicate} for
|
|
9
|
+
* the pack that binds no such prefix.
|
|
10
|
+
*
|
|
11
|
+
* Only typed named-node subjects are indexed (untyped subjects and blank-node
|
|
12
|
+
* subjects are reachable via `graph query` but never listed). Each entity
|
|
8
13
|
* carries the FROZEN `{ name, type }` minimum plus the enrichment fields
|
|
9
14
|
* (`uri`, `prefixed`, `types`, `label`, `box`) the completion tier and reads
|
|
10
15
|
* use. The output is pure JSON — no store handle survives — so the completion
|
|
11
16
|
* tier reads it without ever booting oxigraph.
|
|
17
|
+
*
|
|
18
|
+
* Real-data correctness (things a single-typed toy graph never exercises):
|
|
19
|
+
* - Blank-node `rdf:type` objects (SHACL shapes, RDF lists) are ignored — a
|
|
20
|
+
* `_:b0` is never a domain class and must not become a `type:"_:b0"` key.
|
|
21
|
+
* - The abox primary type is the LEXICALLY-SMALLEST domain class, a
|
|
22
|
+
* deterministic tie-break: SPARQL returns a subject's types in unspecified
|
|
23
|
+
* order, so a multi-domain individual would otherwise get an arbitrary
|
|
24
|
+
* completion key that flips across builds/engines.
|
|
25
|
+
* - Multilingual labels prefer untagged / `@en`, then the lexically smallest —
|
|
26
|
+
* never the store's arbitrary first row.
|
|
27
|
+
* - An OWL-punned subject (a class/property IRI ALSO asserted as a domain
|
|
28
|
+
* individual) emits BOTH its tbox and abox facet, so its abox membership
|
|
29
|
+
* stays completable instead of vanishing behind the tbox facet.
|
|
12
30
|
*/
|
|
13
31
|
|
|
14
32
|
import { compactUri } from "../../render/compactUri.js";
|
|
33
|
+
import { VOCABULARY } from "../../vocabulary.js";
|
|
15
34
|
import type { PackIndex, PackIndexEntity } from "./types.js";
|
|
16
35
|
|
|
17
36
|
type Store = import("@canonical/ke").Store;
|
|
@@ -62,62 +81,143 @@ const DESCRIPTION_PREDICATES = [
|
|
|
62
81
|
"http://schema.org/description",
|
|
63
82
|
];
|
|
64
83
|
|
|
65
|
-
/**
|
|
66
|
-
* Alternative-name predicates: `ds:name`, the property `tier lookup` (and other
|
|
67
|
-
* `ds:name`-addressed families) match on. Projected into `altNames` so the
|
|
68
|
-
* storeless name-completion sources can offer those tokens exactly, without a
|
|
69
|
-
* store boot. Distinct from the display `label` — a subject may carry both.
|
|
70
|
-
*/
|
|
71
|
-
const ALT_NAME_PREDICATES = ["https://ds.canonical.com/name"];
|
|
72
|
-
|
|
73
84
|
const isStdVocab = (uri: string): boolean =>
|
|
74
85
|
STD_VOCAB_PREFIXES.some((ns) => uri.startsWith(ns));
|
|
75
86
|
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
/** One classification facet: which box a subject occupies + its primary type. */
|
|
88
|
+
interface Facet {
|
|
89
|
+
readonly box: "tbox" | "abox";
|
|
90
|
+
readonly primary: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Classify a subject's full types into its facets — one per box it occupies.
|
|
95
|
+
*
|
|
96
|
+
* A plain individual or a plain class yields ONE facet. An OWL-punned subject
|
|
97
|
+
* (a class/property IRI ALSO asserted as a domain individual) yields BOTH its
|
|
98
|
+
* tbox and abox facet (A8), so its abox membership stays completable rather than
|
|
99
|
+
* disappearing behind the tbox one. The abox primary is the lexically-smallest
|
|
100
|
+
* domain class — a deterministic tie-break (A3), not the store's first row.
|
|
101
|
+
*
|
|
102
|
+
* @param fullTypes - The subject's asserted `rdf:type` values (blank nodes
|
|
103
|
+
* already dropped), in store order.
|
|
104
|
+
* @returns The subject's facets (empty when it has no usable type).
|
|
105
|
+
*/
|
|
106
|
+
function classify(fullTypes: readonly string[]): Facet[] {
|
|
107
|
+
if (fullTypes.length === 0) return [];
|
|
108
|
+
const facets: Facet[] = [];
|
|
81
109
|
const classMeta = fullTypes.find((t) => CLASS_METATYPES.has(t));
|
|
82
|
-
if (classMeta) return { box: "tbox", primary: classMeta };
|
|
83
110
|
const propMeta = fullTypes.find((t) => PROPERTY_METATYPES.has(t));
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
if (classMeta !== undefined) facets.push({ box: "tbox", primary: classMeta });
|
|
112
|
+
else if (propMeta !== undefined)
|
|
113
|
+
facets.push({ box: "tbox", primary: propMeta });
|
|
114
|
+
|
|
115
|
+
// A subject's domain classes: its own types minus the structural std vocab
|
|
116
|
+
// and the `owl:NamedIndividual` typing artifact. Sorted, so the abox primary
|
|
117
|
+
// (the smallest) is stable across builds/engines regardless of store order.
|
|
118
|
+
const domainClasses = fullTypes
|
|
119
|
+
.filter((t) => t !== NAMED_INDIVIDUAL && !isStdVocab(t))
|
|
120
|
+
.sort();
|
|
121
|
+
const primaryDomain = domainClasses.at(0);
|
|
122
|
+
if (primaryDomain !== undefined) {
|
|
123
|
+
facets.push({ box: "abox", primary: primaryDomain });
|
|
124
|
+
} else if (facets.length === 0) {
|
|
125
|
+
// No schema facet and no domain class — a bare individual (only
|
|
126
|
+
// `owl:NamedIndividual`, or only std-vocab types). Keep it indexed under a
|
|
127
|
+
// deterministic primary rather than dropping it silently.
|
|
128
|
+
const fallback =
|
|
129
|
+
fullTypes
|
|
130
|
+
.filter((t) => t !== NAMED_INDIVIDUAL)
|
|
131
|
+
.sort()
|
|
132
|
+
.at(0) ?? [...fullTypes].sort().at(0);
|
|
133
|
+
if (fallback !== undefined) facets.push({ box: "abox", primary: fallback });
|
|
134
|
+
}
|
|
135
|
+
return facets;
|
|
91
136
|
}
|
|
92
137
|
|
|
93
138
|
/** A `VALUES ?p { … }` body from a predicate list. */
|
|
94
139
|
const valuesList = (uris: readonly string[]): string =>
|
|
95
140
|
uris.map((uri) => `<${uri}>`).join(" ");
|
|
96
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Expand the declared alternative-name property against a pack's prefix map.
|
|
144
|
+
*
|
|
145
|
+
* That property is what a family is addressed by, so the storeless completion
|
|
146
|
+
* sources can offer those tokens exactly and a bespoke lookup can match them,
|
|
147
|
+
* without a store boot. Distinct from the display `label`: a subject may carry
|
|
148
|
+
* both.
|
|
149
|
+
*
|
|
150
|
+
* The distribution DECLARES it as a prefixed name; this resolves it against the
|
|
151
|
+
* PACK's own prefixes, which are not the distribution's. A pack that binds no
|
|
152
|
+
* such prefix simply carries no alternative names — the normal case for a
|
|
153
|
+
* third-party pack outside the distribution's namespace. Expanding through
|
|
154
|
+
* `packs/iri.ts` instead would THROW on exactly that pack and take its whole
|
|
155
|
+
* build down, so this degrades and the caller skips the query.
|
|
156
|
+
*
|
|
157
|
+
* The split needs no guard: `kernel/vocabulary.ts` has already held the term to
|
|
158
|
+
* `prefix:local`, so the colon is there and both sides are non-empty.
|
|
159
|
+
*
|
|
160
|
+
* @param prefixes - The store's merged prefix map.
|
|
161
|
+
* @returns The full IRI to collect, or `undefined` when the prefix is unbound.
|
|
162
|
+
*/
|
|
163
|
+
function resolveAltNamePredicate(
|
|
164
|
+
prefixes: Readonly<Record<string, string>>,
|
|
165
|
+
): string | undefined {
|
|
166
|
+
const colon = VOCABULARY.altName.indexOf(":");
|
|
167
|
+
const namespace = prefixes[VOCABULARY.altName.slice(0, colon)];
|
|
168
|
+
return namespace === undefined
|
|
169
|
+
? undefined
|
|
170
|
+
: `${namespace}${VOCABULARY.altName.slice(colon + 1)}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Language rank for label selection: untagged (0) < `@en` (1) < any other (2). */
|
|
174
|
+
function rankLanguage(language: string): number {
|
|
175
|
+
if (language === "") return 0;
|
|
176
|
+
const lower = language.toLowerCase();
|
|
177
|
+
return lower === "en" || lower.startsWith("en-") ? 1 : 2;
|
|
178
|
+
}
|
|
179
|
+
|
|
97
180
|
/**
|
|
98
181
|
* Per subject, keep the object whose predicate ranks HIGHEST (lowest index) in
|
|
99
182
|
* `preference`. The `?p` binding is projected so the winner is chosen by declared
|
|
100
183
|
* preference order: SPARQL returns solutions in an unspecified order, so keeping
|
|
101
184
|
* the first row per subject would be store-order-dependent (an entity carrying
|
|
102
185
|
* both `rdfs:label` and `skos:prefLabel` could index either), not ranked.
|
|
186
|
+
*
|
|
187
|
+
* Ties within a predicate (a subject with several `rdfs:label` values — the
|
|
188
|
+
* multilingual case) are broken deterministically: untagged / `@en` first, then
|
|
189
|
+
* the lexically smallest value. So `"Button"@en`/`"Bouton"@fr` always resolves
|
|
190
|
+
* to `Button`, never whichever row the store happened to return first (A7).
|
|
103
191
|
*/
|
|
104
192
|
function preferredBySubject(
|
|
105
193
|
result: import("@canonical/ke").SelectResult,
|
|
106
194
|
valueVar: string,
|
|
107
195
|
preference: readonly string[],
|
|
108
196
|
): Map<string, string> {
|
|
109
|
-
const best = new Map<
|
|
197
|
+
const best = new Map<
|
|
198
|
+
string,
|
|
199
|
+
{ value: string; rank: number; langRank: number }
|
|
200
|
+
>();
|
|
110
201
|
for (const term of result.termBindings) {
|
|
111
202
|
if (term.s?.termType !== "NamedNode") continue;
|
|
112
|
-
const
|
|
203
|
+
const valueTerm = term[valueVar];
|
|
113
204
|
const predicate = term.p?.value;
|
|
114
|
-
if (
|
|
205
|
+
if (valueTerm === undefined || predicate === undefined) continue;
|
|
206
|
+
const value = valueTerm.value;
|
|
207
|
+
if (!value) continue;
|
|
115
208
|
const rank = preference.indexOf(predicate);
|
|
116
209
|
if (rank === -1) continue;
|
|
210
|
+
const language =
|
|
211
|
+
valueTerm.termType === "Literal" ? (valueTerm.language ?? "") : "";
|
|
212
|
+
const langRank = rankLanguage(language);
|
|
117
213
|
const current = best.get(term.s.value);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
214
|
+
const better =
|
|
215
|
+
current === undefined ||
|
|
216
|
+
rank < current.rank ||
|
|
217
|
+
(rank === current.rank &&
|
|
218
|
+
(langRank < current.langRank ||
|
|
219
|
+
(langRank === current.langRank && value < current.value)));
|
|
220
|
+
if (better) best.set(term.s.value, { value, rank, langRank });
|
|
121
221
|
}
|
|
122
222
|
const picked = new Map<string, string>();
|
|
123
223
|
for (const [subject, entry] of best) picked.set(subject, entry.value);
|
|
@@ -159,6 +259,7 @@ export async function buildIndex(
|
|
|
159
259
|
prefixes: Readonly<Record<string, string>>,
|
|
160
260
|
contentHash: string,
|
|
161
261
|
): Promise<PackIndex> {
|
|
262
|
+
const altNameUri = resolveAltNamePredicate(prefixes);
|
|
162
263
|
const [typesResult, labelResult, descResult, altNameResult] =
|
|
163
264
|
await Promise.all([
|
|
164
265
|
store.query(
|
|
@@ -170,17 +271,24 @@ export async function buildIndex(
|
|
|
170
271
|
store.query(
|
|
171
272
|
`SELECT ?s ?p ?desc WHERE { ?s ?p ?desc . VALUES ?p { ${valuesList(DESCRIPTION_PREDICATES)} } }` as never,
|
|
172
273
|
) as Promise<import("@canonical/ke").SelectResult>,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
274
|
+
altNameUri === undefined
|
|
275
|
+
? undefined
|
|
276
|
+
: (store.query(
|
|
277
|
+
`SELECT ?s ?p ?alt WHERE { ?s ?p ?alt . VALUES ?p { <${altNameUri}> } }` as never,
|
|
278
|
+
) as Promise<import("@canonical/ke").SelectResult>),
|
|
176
279
|
]);
|
|
177
280
|
|
|
178
281
|
const typesBySubject = new Map<string, string[]>();
|
|
179
282
|
const instanceCountByType: Record<string, number> = {};
|
|
180
283
|
for (const term of typesResult.termBindings) {
|
|
181
284
|
if (term.s?.termType !== "NamedNode") continue;
|
|
285
|
+
const typeTerm = term.type;
|
|
286
|
+
// Ignore blank-node `rdf:type` objects (SHACL shapes / RDF lists): a `_:b0`
|
|
287
|
+
// is never a domain class, so it must not become a garbage primary type or
|
|
288
|
+
// inflate the per-type instance counts (A6).
|
|
289
|
+
if (typeTerm === undefined || typeTerm.termType === "BlankNode") continue;
|
|
182
290
|
const subject = term.s.value;
|
|
183
|
-
const typeUri =
|
|
291
|
+
const typeUri = typeTerm.value;
|
|
184
292
|
const bucket = typesBySubject.get(subject) ?? [];
|
|
185
293
|
bucket.push(typeUri);
|
|
186
294
|
typesBySubject.set(subject, bucket);
|
|
@@ -197,27 +305,42 @@ export async function buildIndex(
|
|
|
197
305
|
"desc",
|
|
198
306
|
DESCRIPTION_PREDICATES,
|
|
199
307
|
);
|
|
200
|
-
const altNamesBySubject =
|
|
308
|
+
const altNamesBySubject =
|
|
309
|
+
altNameResult === undefined
|
|
310
|
+
? new Map<string, string[]>()
|
|
311
|
+
: allBySubject(altNameResult, "alt");
|
|
201
312
|
|
|
202
313
|
const entities: PackIndexEntity[] = [];
|
|
203
314
|
for (const [subject, fullTypes] of typesBySubject) {
|
|
204
|
-
const
|
|
205
|
-
if (
|
|
315
|
+
const facets = classify(fullTypes);
|
|
316
|
+
if (facets.length === 0) continue;
|
|
206
317
|
const prefixed = compactUri(subject, prefixes);
|
|
207
318
|
const altNames = altNamesBySubject.get(subject);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
319
|
+
const typesCompact = fullTypes.map((t) => compactUri(t, prefixes));
|
|
320
|
+
const label = labelBySubject.get(subject) ?? null;
|
|
321
|
+
const description = descBySubject.get(subject) ?? null;
|
|
322
|
+
// A punned subject emits one entity per facet (tbox class + abox individual).
|
|
323
|
+
for (const facet of facets) {
|
|
324
|
+
entities.push({
|
|
325
|
+
name: prefixed,
|
|
326
|
+
type: compactUri(facet.primary, prefixes),
|
|
327
|
+
uri: subject,
|
|
328
|
+
prefixed,
|
|
329
|
+
types: typesCompact,
|
|
330
|
+
label,
|
|
331
|
+
...(altNames && altNames.length > 0 ? { altNames } : {}),
|
|
332
|
+
box: facet.box,
|
|
333
|
+
description,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
219
336
|
}
|
|
220
|
-
|
|
337
|
+
// Sort by name, then a deterministic tie-break (box, then type) so a punned
|
|
338
|
+
// subject's two facets keep a stable order across builds.
|
|
339
|
+
entities.sort((a, b) => {
|
|
340
|
+
if (a.name !== b.name) return a.name < b.name ? -1 : 1;
|
|
341
|
+
if (a.box !== b.box) return a.box === "tbox" ? -1 : 1;
|
|
342
|
+
return a.type < b.type ? -1 : a.type > b.type ? 1 : 0;
|
|
343
|
+
});
|
|
221
344
|
|
|
222
345
|
return {
|
|
223
346
|
version: 2,
|