@canonical/pragma-cli 0.27.1-experimental.0 → 0.30.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 +67 -15
- package/dist/pragma +0 -0
- package/package.json +16 -17
- package/src/config/defaultConfig.test.ts +33 -0
- package/src/config/defaultConfig.ts +59 -0
- package/src/config/defaults.json +17 -0
- package/src/config/findProjectConfigPath.test.ts +53 -0
- package/src/config/findProjectConfigPath.ts +36 -0
- package/src/config/index.ts +17 -2
- package/src/config/mergeConfigFileUpdate.ts +58 -0
- package/src/config/parseConfigValues.ts +215 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -117
- package/src/config/readConfigLayers.test.ts +107 -0
- package/src/config/readConfigLayers.ts +115 -0
- package/src/config/resolveGlobalConfigPath.ts +13 -0
- package/src/config/resolveWriteConfigPath.test.ts +53 -0
- package/src/config/resolveWriteConfigPath.ts +31 -0
- package/src/config/types.ts +88 -2
- package/src/config/writeConfig.ts +25 -43
- package/src/constants.ts +12 -1
- package/src/domains/block/commands/index.ts +2 -1
- package/src/domains/block/commands/list.ts +5 -90
- package/src/domains/block/commands/lookup.ts +8 -167
- package/src/domains/block/commands/sample.ts +55 -0
- package/src/domains/block/formatters/index.ts +2 -1
- package/src/domains/block/formatters/sample.ts +21 -0
- package/src/domains/block/index.ts +3 -3
- package/src/domains/block/mcp/specs.ts +36 -168
- package/src/domains/block/operations/index.ts +2 -1
- package/src/domains/block/operations/lookup.ts +9 -0
- package/src/domains/block/operations/sample.ts +41 -0
- package/src/domains/block/orchestration/blockEmptyError.test.ts +26 -0
- package/src/domains/block/orchestration/resolveBlockLookup.ts +11 -3
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +254 -0
- package/src/domains/config/commands/channel.test.ts +14 -3
- package/src/domains/config/commands/channel.ts +11 -7
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +105 -0
- package/src/domains/config/commands/framework.ts +91 -0
- package/src/domains/config/commands/index.ts +2 -0
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +13 -2
- package/src/domains/config/commands/tier.ts +11 -7
- package/src/domains/config/commands/trace.test.ts +165 -0
- package/src/domains/config/commands/trace.ts +83 -0
- package/src/domains/config/formatters/channel.test.ts +20 -6
- package/src/domains/config/formatters/channel.ts +14 -11
- package/src/domains/config/formatters/framework.ts +37 -0
- package/src/domains/config/formatters/index.ts +2 -0
- package/src/domains/config/formatters/show.test.ts +27 -0
- package/src/domains/config/formatters/show.ts +27 -4
- package/src/domains/config/formatters/tier.test.ts +26 -10
- package/src/domains/config/formatters/tier.ts +19 -11
- package/src/domains/config/formatters/trace.ts +28 -0
- package/src/domains/config/index.ts +15 -2
- package/src/domains/config/mcp/specs.ts +33 -53
- package/src/domains/config/operations/index.ts +1 -0
- package/src/domains/config/operations/show.test.ts +18 -0
- package/src/domains/config/operations/show.ts +7 -1
- package/src/domains/config/operations/types.ts +7 -0
- package/src/domains/config/operations/validateFramework.ts +24 -0
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +3 -0
- package/src/domains/config/tasks/setChannel.ts +13 -22
- package/src/domains/config/tasks/setFramework.ts +21 -0
- package/src/domains/config/tasks/setTier.ts +10 -23
- package/src/domains/config/tasks/setTrace.ts +20 -0
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- package/src/domains/create/commands/component.ts +5 -5
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkKeStore.ts +5 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +130 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -48
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
- package/src/domains/doctor/operations/checks/index.ts +1 -0
- package/src/domains/doctor/operations/checks.test.ts +162 -8
- package/src/domains/doctor/operations/runChecks.test.ts +85 -4
- package/src/domains/doctor/operations/runChecks.ts +50 -12
- package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graphql/commands/build.test.ts +196 -0
- package/src/domains/graphql/commands/build.ts +117 -0
- package/src/domains/graphql/commands/check.test.ts +141 -0
- package/src/domains/graphql/commands/check.ts +81 -0
- package/src/domains/graphql/commands/index.ts +5 -0
- package/src/domains/graphql/commands/serve.test.ts +40 -0
- package/src/domains/graphql/commands/serve.ts +131 -0
- package/src/domains/graphql/constants.ts +10 -0
- package/src/domains/graphql/formatters/index.ts +3 -0
- package/src/domains/graphql/formatters/report.ts +78 -0
- package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
- package/src/domains/graphql/helpers/gatherSources.ts +58 -0
- package/src/domains/graphql/helpers/index.ts +6 -0
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +44 -0
- package/src/domains/graphql/helpers/parsePrefixes.ts +38 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +56 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +45 -0
- package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
- package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
- package/src/domains/graphql/index.ts +24 -0
- package/src/domains/graphql/operations/compileSchema.test.ts +95 -0
- package/src/domains/graphql/operations/compileSchema.ts +107 -0
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- package/src/domains/graphql/operations/index.ts +12 -0
- package/src/domains/graphql/types.ts +29 -0
- package/src/domains/info/operations/collectInfo.test.ts +16 -5
- package/src/domains/info/operations/collectInfo.ts +16 -19
- package/src/domains/llm/capabilities.test.ts +91 -0
- package/src/domains/llm/commands/capabilities.ts +28 -0
- package/src/domains/llm/commands/index.ts +1 -0
- package/src/domains/llm/data/commandReference.ts +90 -18
- package/src/domains/llm/data/decisionTrees.ts +15 -13
- package/src/domains/llm/data/index.ts +2 -0
- package/src/domains/llm/data/toolCatalog.ts +190 -0
- package/src/domains/llm/formatters/capabilities.ts +50 -0
- package/src/domains/llm/formatters/index.ts +1 -0
- package/src/domains/llm/index.ts +3 -2
- package/src/domains/llm/mcp/index.ts +1 -1
- package/src/domains/llm/mcp/specs.ts +59 -52
- package/src/domains/llm/types.ts +40 -0
- package/src/domains/modifier/commands/index.ts +2 -1
- package/src/domains/modifier/commands/list.ts +5 -38
- package/src/domains/modifier/commands/lookup.ts +8 -79
- package/src/domains/modifier/commands/sample.ts +53 -0
- package/src/domains/modifier/formatters/index.ts +2 -1
- package/src/domains/modifier/formatters/sample.ts +15 -0
- package/src/domains/modifier/index.ts +7 -3
- package/src/domains/modifier/mcp/specs.ts +38 -70
- package/src/domains/modifier/operations/index.ts +2 -1
- package/src/domains/modifier/operations/lookup.ts +9 -0
- package/src/domains/modifier/operations/sample.ts +32 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.test.ts +26 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.ts +7 -2
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +15 -2
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +77 -0
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.ts +6 -58
- package/src/domains/ontology/formatters/show.ts +7 -1
- package/src/domains/ontology/mcp/specs.ts +7 -84
- package/src/domains/ontology/stories.test.ts +36 -0
- package/src/domains/ontology/stories.ts +108 -0
- package/src/domains/refs/operations/index.ts +1 -1
- package/src/domains/refs/operations/paths.ts +29 -1
- package/src/domains/refs/operations/updateRefs.test.ts +6 -3
- package/src/domains/refs/operations/updateRefs.ts +2 -36
- package/src/domains/setup/commands/skills.test.ts +6 -2
- package/src/domains/setup/commands/skills.ts +6 -2
- package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
- package/src/domains/setup/helpers/runSetupTask.ts +62 -70
- package/src/domains/setup/operations/setupMcp.test.ts +6 -1
- package/src/domains/shared/bootStore.ts +92 -50
- package/src/domains/shared/compactUri.test.ts +11 -0
- package/src/domains/shared/compactUri.ts +10 -5
- package/src/domains/shared/context.ts +2 -2
- package/src/domains/shared/contracts.ts +12 -0
- package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
- package/src/domains/shared/createInteractivePromptSession.ts +236 -0
- package/src/domains/shared/emptyErrorRecovery.test.ts +109 -0
- package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +206 -0
- package/src/domains/shared/loaders/gitLoader.ts +31 -0
- package/src/domains/shared/loaders/index.ts +9 -0
- package/src/domains/shared/loaders/localLoader.ts +87 -0
- package/src/domains/shared/loaders/readPackageDir.ts +147 -0
- package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
- package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
- package/src/domains/shared/lookupMany.test.ts +120 -0
- package/src/domains/shared/lookupMany.ts +43 -3
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/packages.ts +14 -120
- package/src/domains/shared/pickRandom.ts +22 -0
- package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/runtime.ts +7 -63
- package/src/domains/shared/sampleFormatters.ts +47 -0
- package/src/domains/shared/semanticPackage.ts +112 -0
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +100 -0
- package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
- package/src/domains/shared/stories/compileLookupTool.ts +96 -0
- package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
- package/src/domains/shared/stories/compileReadCommand.ts +48 -0
- package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
- package/src/domains/shared/stories/compileReadTool.ts +38 -0
- package/src/domains/shared/stories/condense.test.ts +18 -0
- package/src/domains/shared/stories/condense.ts +15 -0
- package/src/domains/shared/stories/index.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.ts +22 -0
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +102 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +76 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +51 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +80 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +108 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +76 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +40 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +146 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +253 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +43 -0
- package/src/domains/shared/stories/pack/index.ts +28 -0
- package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +308 -0
- package/src/domains/shared/stories/pack/types.ts +104 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +240 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +408 -0
- package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
- package/src/domains/shared/stories/projectCliParameters.ts +57 -0
- package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
- package/src/domains/shared/stories/projectToolParams.ts +32 -0
- package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
- package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
- package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
- package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
- package/src/domains/shared/stories/types.ts +171 -0
- package/src/domains/shared/suggestions/detectCrossDomain.test.ts +43 -0
- package/src/domains/shared/suggestions/detectCrossDomain.ts +116 -0
- package/src/domains/shared/suggestions/expandGlob.test.ts +92 -0
- package/src/domains/shared/suggestions/expandGlob.ts +52 -0
- package/src/domains/shared/suggestions/expandLookupQueries.ts +57 -0
- package/src/domains/shared/suggestions/index.ts +7 -0
- package/src/domains/shared/suggestions/listDomainNames.ts +61 -0
- package/src/domains/shared/suggestions/suggestNames.test.ts +70 -0
- package/src/domains/shared/suggestions/suggestNames.ts +130 -0
- package/src/domains/shared/types/index.ts +1 -0
- package/src/domains/shared/types/runtime.ts +4 -1
- package/src/domains/shared/types/sample.ts +17 -0
- package/src/domains/skill/commands/index.ts +3 -1
- package/src/domains/skill/commands/list.test.ts +6 -2
- package/src/domains/skill/commands/list.ts +5 -63
- package/src/domains/skill/commands/lookup.test.ts +118 -0
- package/src/domains/skill/commands/lookup.ts +14 -0
- package/src/domains/skill/formatters/index.ts +1 -0
- package/src/domains/skill/formatters/lookup.test.ts +46 -0
- package/src/domains/skill/formatters/lookup.ts +57 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +48 -16
- package/src/domains/skill/index.ts +7 -3
- package/src/domains/skill/mcp/specs.ts +10 -29
- package/src/domains/skill/operations/discover.test.ts +61 -30
- package/src/domains/skill/operations/discover.ts +28 -37
- package/src/domains/skill/operations/index.ts +2 -1
- package/src/domains/skill/operations/list.test.ts +15 -7
- package/src/domains/skill/operations/lookup.test.ts +91 -0
- package/src/domains/skill/operations/lookup.ts +70 -0
- package/src/domains/skill/stories.test.ts +43 -0
- package/src/domains/skill/stories.ts +103 -0
- package/src/domains/skill/types.ts +8 -0
- package/src/domains/standard/commands/index.ts +2 -1
- package/src/domains/standard/commands/list.ts +5 -84
- package/src/domains/standard/commands/lookup.ts +8 -93
- package/src/domains/standard/commands/sample.ts +57 -0
- package/src/domains/standard/formatters/index.ts +2 -1
- package/src/domains/standard/formatters/sample.ts +15 -0
- package/src/domains/standard/index.ts +8 -1
- package/src/domains/standard/mcp/specs.ts +44 -152
- package/src/domains/standard/operations/index.ts +2 -1
- package/src/domains/standard/operations/lookup.ts +9 -0
- package/src/domains/standard/operations/sample.ts +35 -0
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +15 -2
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +33 -0
- package/src/domains/standard/orchestration/standardEmptyError.ts +16 -6
- package/src/domains/standard/stories.test.ts +24 -0
- package/src/domains/standard/stories.ts +151 -0
- package/src/domains/tier/commands/list.ts +5 -55
- package/src/domains/tier/mcp/specs.ts +5 -31
- package/src/domains/tier/stories.test.ts +22 -0
- package/src/domains/tier/stories.ts +52 -0
- package/src/domains/token/commands/index.ts +2 -1
- package/src/domains/token/commands/sample.ts +53 -0
- package/src/domains/token/formatters/index.ts +2 -1
- package/src/domains/token/formatters/sample.ts +16 -0
- package/src/domains/token/index.ts +8 -1
- package/src/domains/token/mcp/specs.ts +52 -2
- package/src/domains/token/operations/index.ts +2 -1
- package/src/domains/token/operations/lookup.ts +9 -0
- package/src/domains/token/operations/sample.ts +32 -0
- package/src/domains/token/orchestration/resolveTokenLookup.ts +15 -2
- package/src/domains/token/orchestration/tokenEmptyError.test.ts +33 -0
- package/src/domains/token/orchestration/tokenEmptyError.ts +5 -2
- package/src/domains/trace/commands/clear.ts +71 -0
- package/src/domains/trace/commands/index.ts +5 -0
- package/src/domains/trace/commands/sessions.ts +32 -0
- package/src/domains/trace/commands/show.ts +145 -0
- package/src/domains/trace/formatters/index.ts +9 -0
- package/src/domains/trace/formatters/sessions.ts +80 -0
- package/src/domains/trace/formatters/show.ts +122 -0
- package/src/domains/trace/index.ts +17 -0
- package/src/domains/trace/operations/index.ts +8 -0
- package/src/domains/trace/operations/listSessions.test.ts +55 -0
- package/src/domains/trace/operations/listSessions.ts +52 -0
- package/src/domains/trace/operations/readTraceLog.test.ts +100 -0
- package/src/domains/trace/operations/readTraceLog.ts +104 -0
- package/src/domains/trace/tracePlugin.test.ts +171 -0
- package/src/domains/trace/tracePlugin.ts +159 -0
- package/src/domains/trace/types.ts +65 -0
- package/src/error/PragmaError.ts +15 -2
- package/src/error/types.ts +15 -1
- package/src/mcp/error/serializeErrorPayload.ts +7 -0
- package/src/mcp/error/types.ts +8 -0
- package/src/mcp/registerResources.test.ts +405 -75
- package/src/mcp/registerResources.ts +5 -299
- package/src/mcp/resources/buildGraphIndex.ts +167 -0
- package/src/mcp/resources/buildResourceList.test.ts +97 -0
- package/src/mcp/resources/buildResourceList.ts +176 -0
- package/src/mcp/resources/classifyEntity.test.ts +40 -0
- package/src/mcp/resources/classifyEntity.ts +27 -0
- package/src/mcp/resources/constants.ts +105 -0
- package/src/mcp/resources/displayPredicates.ts +62 -0
- package/src/mcp/resources/pickFirstValue.test.ts +29 -0
- package/src/mcp/resources/pickFirstValue.ts +26 -0
- package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
- package/src/mcp/resources/pickPrimaryType.ts +26 -0
- package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
- package/src/mcp/resources/rankUriCompletions.ts +64 -0
- package/src/mcp/resources/readEntity.ts +169 -0
- package/src/mcp/resources/registerResources.ts +126 -0
- package/src/mcp/resources/types.ts +128 -0
- package/src/mcp/tools/index.ts +9 -0
- package/src/mcp/tools/registerTools.test.ts +84 -12
- package/src/mcp/types.ts +4 -0
- package/src/mcp/utils/wrapTool.test.ts +1 -0
- package/src/pipeline/collectCommands.ts +15 -2
- package/src/pipeline/renderError.ts +15 -0
- package/src/pipeline/resolveCommandKind.ts +8 -1
- package/src/pipeline/runCli.ts +51 -6
- package/src/testing/createTestMcpClient.ts +5 -1
- package/src/testing/graphqlFixtures.ts +72 -0
- package/src/testing/index.ts +17 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/executor-execution.test.ts +101 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +11 -5
- package/src/testing/integration/parity.test.ts +17 -12
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +15 -0
- package/src/testing/standardParityFixtures.ts +92 -0
- package/src/testing/storyFixtures.ts +63 -0
- package/src/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
2
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
3
|
-
import { createTestMcpClient } from "#testing";
|
|
4
|
-
import { P } from "../domains/shared/prefixes.js";
|
|
3
|
+
import { createTestMcpClient, createTestStore } from "#testing";
|
|
4
|
+
import { P, PREFIX_MAP } from "../domains/shared/prefixes.js";
|
|
5
|
+
import type { PragmaRuntime } from "../domains/shared/runtime.js";
|
|
6
|
+
import createClientFromRuntime from "../testing/helpers/createTestMcpClient.js";
|
|
7
|
+
import {
|
|
8
|
+
ABOX_PER_CLASS_LIMIT,
|
|
9
|
+
COMPLETION_LIMIT,
|
|
10
|
+
} from "./resources/constants.js";
|
|
5
11
|
|
|
6
12
|
let client: Client;
|
|
7
13
|
let cleanup: () => Promise<void>;
|
|
@@ -22,102 +28,409 @@ interface ResourceContent {
|
|
|
22
28
|
text: string;
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
interface ListedMeta {
|
|
32
|
+
"pragma/box"?: string;
|
|
33
|
+
"pragma/category"?: string;
|
|
34
|
+
"pragma/type"?: string | null;
|
|
35
|
+
"pragma/instanceCount"?: number;
|
|
36
|
+
"pragma/instancesShown"?: number;
|
|
37
|
+
"pragma/truncated"?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
function parseContents(result: { contents: unknown[] }): unknown {
|
|
26
|
-
const first = result.contents
|
|
41
|
+
const first = result.contents.at(0) as ResourceContent;
|
|
27
42
|
return JSON.parse(first.text);
|
|
28
43
|
}
|
|
29
44
|
|
|
45
|
+
function metaOf(resource: { _meta?: unknown }): ListedMeta {
|
|
46
|
+
return (resource._meta ?? {}) as ListedMeta;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Build an MCP client over a store with the given TTL and extra prefixes.
|
|
51
|
+
* Lets foreign/overlapping namespaces be registered, which the shared
|
|
52
|
+
* `createTestMcpClient` (fixed prefix set) cannot express.
|
|
53
|
+
*/
|
|
54
|
+
async function createClientForTtl(
|
|
55
|
+
ttl: string,
|
|
56
|
+
prefixes?: Record<string, string>,
|
|
57
|
+
): Promise<{ client: Client; cleanup: () => Promise<void> }> {
|
|
58
|
+
const { store, cleanup: closeStore } = await createTestStore({
|
|
59
|
+
ttl,
|
|
60
|
+
prefixes,
|
|
61
|
+
});
|
|
62
|
+
const runtime: PragmaRuntime = {
|
|
63
|
+
store,
|
|
64
|
+
config: { tier: undefined, channel: "normal" },
|
|
65
|
+
cwd: process.cwd(),
|
|
66
|
+
packages: [],
|
|
67
|
+
dispose: () => closeStore(),
|
|
68
|
+
};
|
|
69
|
+
const { client: scopedClient, cleanup: closeClient } =
|
|
70
|
+
await createClientFromRuntime(runtime);
|
|
71
|
+
return {
|
|
72
|
+
client: scopedClient,
|
|
73
|
+
cleanup: async () => {
|
|
74
|
+
await closeClient();
|
|
75
|
+
runtime.dispose();
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
30
80
|
// =============================================================================
|
|
31
|
-
// Resource
|
|
81
|
+
// Resource template
|
|
32
82
|
// =============================================================================
|
|
33
83
|
|
|
34
|
-
describe("resource
|
|
35
|
-
it("registers 1 resource template", async () => {
|
|
84
|
+
describe("resource template", () => {
|
|
85
|
+
it("registers 1 resource template with the {+uri} pattern", async () => {
|
|
36
86
|
const { resourceTemplates } = await client.listResourceTemplates();
|
|
37
87
|
expect(resourceTemplates).toHaveLength(1);
|
|
38
|
-
expect(resourceTemplates
|
|
88
|
+
expect(resourceTemplates.at(0)?.uriTemplate).toBe("{+uri}");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("documents the TBox/ABox split in the template description", async () => {
|
|
92
|
+
const { resourceTemplates } = await client.listResourceTemplates();
|
|
93
|
+
expect(resourceTemplates.at(0)?.description).toMatch(/TBox/);
|
|
39
94
|
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// =============================================================================
|
|
98
|
+
// Resource listing — presence
|
|
99
|
+
// =============================================================================
|
|
40
100
|
|
|
41
|
-
|
|
101
|
+
describe("resource listing — presence", () => {
|
|
102
|
+
it("lists known instances, OWL classes, and code standards", async () => {
|
|
42
103
|
const { resources } = await client.listResources();
|
|
43
104
|
const uris = resources.map((r) => r.uri);
|
|
44
|
-
expect(uris).toContain(
|
|
45
|
-
expect(uris).toContain(
|
|
46
|
-
expect(uris).toContain(
|
|
105
|
+
expect(uris).toContain(`${P.ds}global.component.button`);
|
|
106
|
+
expect(uris).toContain(`${P.ds}global`);
|
|
107
|
+
expect(uris).toContain(`${P.ds}UIBlock`);
|
|
108
|
+
expect(uris).toContain(`${P.ds}Component`);
|
|
109
|
+
expect(uris).toContain(`${P.cs}react_folder`);
|
|
110
|
+
expect(uris).toContain(`${P.cs}code_purity`);
|
|
47
111
|
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// Resource listing — TBox/ABox separation
|
|
116
|
+
// =============================================================================
|
|
48
117
|
|
|
49
|
-
|
|
118
|
+
describe("resource listing — TBox/ABox separation", () => {
|
|
119
|
+
it("tags classes as tbox/class in _meta", async () => {
|
|
50
120
|
const { resources } = await client.listResources();
|
|
51
|
-
const
|
|
52
|
-
expect(
|
|
53
|
-
expect(
|
|
121
|
+
const uiBlock = resources.find((r) => r.uri === `${P.ds}UIBlock`);
|
|
122
|
+
expect(metaOf(uiBlock ?? {})["pragma/box"]).toBe("tbox");
|
|
123
|
+
expect(metaOf(uiBlock ?? {})["pragma/category"]).toBe("class");
|
|
54
124
|
});
|
|
55
125
|
|
|
56
|
-
it("
|
|
126
|
+
it("tags properties as tbox/property in _meta", async () => {
|
|
57
127
|
const { resources } = await client.listResources();
|
|
58
|
-
const
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
128
|
+
const tier = resources.find((r) => r.uri === `${P.ds}tier`);
|
|
129
|
+
expect(metaOf(tier ?? {})["pragma/box"]).toBe("tbox");
|
|
130
|
+
expect(metaOf(tier ?? {})["pragma/category"]).toBe("property");
|
|
61
131
|
});
|
|
62
132
|
|
|
63
|
-
it("
|
|
133
|
+
it("tags individuals as abox/individual with their class in _meta", async () => {
|
|
64
134
|
const { resources } = await client.listResources();
|
|
65
135
|
const button = resources.find(
|
|
66
|
-
(r) => r.uri ===
|
|
136
|
+
(r) => r.uri === `${P.ds}global.component.button`,
|
|
67
137
|
);
|
|
68
|
-
expect(button
|
|
138
|
+
expect(metaOf(button ?? {})["pragma/box"]).toBe("abox");
|
|
139
|
+
expect(metaOf(button ?? {})["pragma/category"]).toBe("individual");
|
|
140
|
+
expect(metaOf(button ?? {})["pragma/type"]).toBe(`${P.ds}Component`);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("places all TBox schema before any ABox individual", async () => {
|
|
144
|
+
const { resources } = await client.listResources();
|
|
145
|
+
const firstAbox = resources.findIndex(
|
|
146
|
+
(r) => metaOf(r)["pragma/box"] === "abox",
|
|
147
|
+
);
|
|
148
|
+
const lastTbox = resources.reduce(
|
|
149
|
+
(acc, r, i) => (metaOf(r)["pragma/box"] === "tbox" ? i : acc),
|
|
150
|
+
-1,
|
|
151
|
+
);
|
|
152
|
+
expect(firstAbox).toBeGreaterThan(-1);
|
|
153
|
+
expect(lastTbox).toBeGreaterThan(-1);
|
|
154
|
+
expect(lastTbox).toBeLessThan(firstAbox);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("ranks schema above individuals via annotation priority", async () => {
|
|
158
|
+
const { resources } = await client.listResources();
|
|
159
|
+
const cls = resources.find((r) => r.uri === `${P.ds}Component`);
|
|
160
|
+
const individual = resources.find(
|
|
161
|
+
(r) => r.uri === `${P.ds}global.component.button`,
|
|
162
|
+
);
|
|
163
|
+
const clsPriority = cls?.annotations?.priority ?? 0;
|
|
164
|
+
const individualPriority = individual?.annotations?.priority ?? 1;
|
|
165
|
+
expect(clsPriority).toBeGreaterThan(individualPriority);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// =============================================================================
|
|
170
|
+
// Resource listing — names, descriptions, grouping
|
|
171
|
+
// =============================================================================
|
|
172
|
+
|
|
173
|
+
describe("resource listing — names and grouping", () => {
|
|
174
|
+
it("prefixes class names with their category", async () => {
|
|
175
|
+
const { resources } = await client.listResources();
|
|
176
|
+
const cls = resources.find((r) => r.uri === `${P.ds}Component`);
|
|
177
|
+
expect(cls?.name).toBe("Class · Component");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("prefixes property names with their category", async () => {
|
|
181
|
+
const { resources } = await client.listResources();
|
|
182
|
+
const prop = resources.find((r) => r.uri === `${P.ds}tier`);
|
|
183
|
+
expect(prop?.name).toBe("Property · tier");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("groups individuals under their class label in the name", async () => {
|
|
187
|
+
const { resources } = await client.listResources();
|
|
188
|
+
const button = resources.find(
|
|
189
|
+
(r) => r.uri === `${P.ds}global.component.button`,
|
|
190
|
+
);
|
|
191
|
+
expect(button?.name).toBe("Component · Button");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("surfaces instance counts on class entries", async () => {
|
|
195
|
+
const { resources } = await client.listResources();
|
|
196
|
+
const cls = resources.find((r) => r.uri === `${P.ds}Component`);
|
|
197
|
+
expect(metaOf(cls ?? {})["pragma/instanceCount"]).toBe(4);
|
|
198
|
+
expect(metaOf(cls ?? {})["pragma/truncated"]).toBe(false);
|
|
199
|
+
expect(cls?.description).toMatch(/4 individuals/);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("leads an individual description with its class label", async () => {
|
|
203
|
+
const { resources } = await client.listResources();
|
|
204
|
+
const button = resources.find(
|
|
205
|
+
(r) => r.uri === `${P.ds}global.component.button`,
|
|
206
|
+
);
|
|
207
|
+
expect(button?.description).toMatch(/^Component/);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("marks resources with assistant audience", async () => {
|
|
211
|
+
const { resources } = await client.listResources();
|
|
212
|
+
const button = resources.find(
|
|
213
|
+
(r) => r.uri === `${P.ds}global.component.button`,
|
|
214
|
+
);
|
|
215
|
+
expect(button?.annotations?.audience).toEqual(["assistant"]);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// =============================================================================
|
|
220
|
+
// Resource listing — capping the ABox long tail
|
|
221
|
+
// =============================================================================
|
|
222
|
+
|
|
223
|
+
describe("resource listing — capping", () => {
|
|
224
|
+
it("caps individuals per class and reports the remainder", async () => {
|
|
225
|
+
const overflow = ABOX_PER_CLASS_LIMIT + 5;
|
|
226
|
+
const individuals = Array.from(
|
|
227
|
+
{ length: overflow },
|
|
228
|
+
(_, i) => `ds:widget_${i} a ds:Gadget ; ds:name "Widget ${i}" .`,
|
|
229
|
+
).join("\n");
|
|
230
|
+
const ttl = `@prefix ds: <${PREFIX_MAP.ds}> .
|
|
231
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
232
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
233
|
+
|
|
234
|
+
ds:Gadget a owl:Class ; rdfs:label "Gadget" .
|
|
235
|
+
ds:name a owl:DatatypeProperty .
|
|
236
|
+
|
|
237
|
+
${individuals}
|
|
238
|
+
`;
|
|
239
|
+
const scoped = await createClientForTtl(ttl);
|
|
240
|
+
try {
|
|
241
|
+
const { resources } = await scoped.client.listResources();
|
|
242
|
+
const shown = resources.filter(
|
|
243
|
+
(r) => metaOf(r)["pragma/type"] === `${P.ds}Gadget`,
|
|
244
|
+
);
|
|
245
|
+
expect(shown).toHaveLength(ABOX_PER_CLASS_LIMIT);
|
|
246
|
+
|
|
247
|
+
const cls = resources.find((r) => r.uri === `${P.ds}Gadget`);
|
|
248
|
+
expect(metaOf(cls ?? {})["pragma/instanceCount"]).toBe(overflow);
|
|
249
|
+
expect(metaOf(cls ?? {})["pragma/instancesShown"]).toBe(
|
|
250
|
+
ABOX_PER_CLASS_LIMIT,
|
|
251
|
+
);
|
|
252
|
+
expect(metaOf(cls ?? {})["pragma/truncated"]).toBe(true);
|
|
253
|
+
expect(cls?.description).toMatch(
|
|
254
|
+
new RegExp(`showing ${ABOX_PER_CLASS_LIMIT} of ${overflow}`),
|
|
255
|
+
);
|
|
256
|
+
} finally {
|
|
257
|
+
await scoped.cleanup();
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// =============================================================================
|
|
263
|
+
// Resource listing — foreign namespace label fallback
|
|
264
|
+
// =============================================================================
|
|
265
|
+
|
|
266
|
+
describe("resource listing — foreign namespace label fallback", () => {
|
|
267
|
+
const ttl = `
|
|
268
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
269
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
270
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
271
|
+
@prefix dcterms: <http://purl.org/dc/terms/> .
|
|
272
|
+
@prefix schema: <http://schema.org/> .
|
|
273
|
+
@prefix ex: <http://foreign.example/> .
|
|
274
|
+
|
|
275
|
+
ex:Widget a owl:Class ; skos:prefLabel "Foreign Widget" .
|
|
276
|
+
ex:gadget a ex:Widget ; dcterms:title "Shiny Gadget" .
|
|
277
|
+
ex:thing a ex:Widget ; schema:name "Plain Thing" .
|
|
278
|
+
`;
|
|
279
|
+
|
|
280
|
+
it("resolves a foreign class name via skos:prefLabel", async () => {
|
|
281
|
+
const scoped = await createClientForTtl(ttl, {
|
|
282
|
+
ex: "http://foreign.example/",
|
|
283
|
+
});
|
|
284
|
+
try {
|
|
285
|
+
const { resources } = await scoped.client.listResources();
|
|
286
|
+
const cls = resources.find((r) => r.uri === "ex:Widget");
|
|
287
|
+
expect(cls?.name).toBe("Class · Foreign Widget");
|
|
288
|
+
} finally {
|
|
289
|
+
await scoped.cleanup();
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("resolves foreign individual names via dcterms:title and schema:name", async () => {
|
|
294
|
+
const scoped = await createClientForTtl(ttl, {
|
|
295
|
+
ex: "http://foreign.example/",
|
|
296
|
+
});
|
|
297
|
+
try {
|
|
298
|
+
const { resources } = await scoped.client.listResources();
|
|
299
|
+
const gadget = resources.find((r) => r.uri === "ex:gadget");
|
|
300
|
+
const thing = resources.find((r) => r.uri === "ex:thing");
|
|
301
|
+
expect(gadget?.name).toBe("Foreign Widget · Shiny Gadget");
|
|
302
|
+
expect(thing?.name).toBe("Foreign Widget · Plain Thing");
|
|
303
|
+
} finally {
|
|
304
|
+
await scoped.cleanup();
|
|
305
|
+
}
|
|
69
306
|
});
|
|
70
307
|
});
|
|
71
308
|
|
|
72
309
|
// =============================================================================
|
|
73
|
-
//
|
|
310
|
+
// Resource listing — longest-namespace prefix resolution
|
|
311
|
+
// =============================================================================
|
|
312
|
+
|
|
313
|
+
describe("resource listing — longest-namespace prefix resolution", () => {
|
|
314
|
+
it("compacts with the longest matching namespace", async () => {
|
|
315
|
+
const ttl = `
|
|
316
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
317
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
318
|
+
@prefix base: <http://ex.org/> .
|
|
319
|
+
@prefix sub: <http://ex.org/sub/> .
|
|
320
|
+
|
|
321
|
+
base:Thing a owl:Class ; rdfs:label "Thing" .
|
|
322
|
+
sub:Widget a owl:Class ; rdfs:label "Widget" .
|
|
323
|
+
`;
|
|
324
|
+
// `base` is registered before `sub`; first-match compaction would
|
|
325
|
+
// mis-compact to "base:sub/Widget".
|
|
326
|
+
const scoped = await createClientForTtl(ttl, {
|
|
327
|
+
base: "http://ex.org/",
|
|
328
|
+
sub: "http://ex.org/sub/",
|
|
329
|
+
});
|
|
330
|
+
try {
|
|
331
|
+
const { resources } = await scoped.client.listResources();
|
|
332
|
+
const uris = resources.map((r) => r.uri);
|
|
333
|
+
expect(uris).toContain("sub:Widget");
|
|
334
|
+
expect(uris).not.toContain("base:sub/Widget");
|
|
335
|
+
} finally {
|
|
336
|
+
await scoped.cleanup();
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// =============================================================================
|
|
342
|
+
// Autocomplete
|
|
343
|
+
// =============================================================================
|
|
344
|
+
|
|
345
|
+
async function completeUri(value: string): Promise<{
|
|
346
|
+
values: string[];
|
|
347
|
+
total?: number;
|
|
348
|
+
}> {
|
|
349
|
+
const result = await client.complete({
|
|
350
|
+
ref: { type: "ref/resource", uri: "{+uri}" },
|
|
351
|
+
argument: { name: "uri", value },
|
|
352
|
+
});
|
|
353
|
+
return result.completion;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
describe("autocomplete", () => {
|
|
357
|
+
it("matches a URI substring, not just a prefix", async () => {
|
|
358
|
+
const { values } = await completeUri("button");
|
|
359
|
+
expect(values).toContain(`${P.ds}global.component.button`);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("matches a human label fragment", async () => {
|
|
363
|
+
const { values } = await completeUri("Card");
|
|
364
|
+
expect(values).toContain(`${P.ds}global.component.card`);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("returns compacted URIs, not full URIs", async () => {
|
|
368
|
+
const { values } = await completeUri("button");
|
|
369
|
+
expect(values.every((v) => !v.startsWith("http"))).toBe(true);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("ranks an exact label match above a substring match", async () => {
|
|
373
|
+
const { values } = await completeUri("component");
|
|
374
|
+
const exactIdx = values.indexOf(`${P.ds}Component`);
|
|
375
|
+
const substringIdx = values.indexOf(`${P.ds}global.component.button`);
|
|
376
|
+
expect(exactIdx).toBeGreaterThan(-1);
|
|
377
|
+
expect(substringIdx).toBeGreaterThan(-1);
|
|
378
|
+
expect(exactIdx).toBeLessThan(substringIdx);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("caps the number of suggestions", async () => {
|
|
382
|
+
const { values } = await completeUri("");
|
|
383
|
+
expect(values.length).toBeLessThanOrEqual(COMPLETION_LIMIT);
|
|
384
|
+
expect(values).toHaveLength(COMPLETION_LIMIT);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// =============================================================================
|
|
389
|
+
// Read — component instance (ABox)
|
|
74
390
|
// =============================================================================
|
|
75
391
|
|
|
76
392
|
describe("read component instance", () => {
|
|
77
|
-
it("returns
|
|
393
|
+
it("returns an abox individual with types, label, and class pointer", async () => {
|
|
78
394
|
const result = await client.readResource({
|
|
79
|
-
uri:
|
|
395
|
+
uri: `${P.ds}global.component.button`,
|
|
80
396
|
});
|
|
81
397
|
const entity = parseContents(result) as {
|
|
82
|
-
uri: string;
|
|
83
398
|
prefixed: string;
|
|
399
|
+
box: string;
|
|
400
|
+
category: string;
|
|
84
401
|
types: string[];
|
|
85
402
|
label: string | null;
|
|
86
|
-
|
|
403
|
+
instanceOf: string | null;
|
|
87
404
|
};
|
|
88
405
|
expect(entity.prefixed).toBe(`${P.ds}global.component.button`);
|
|
406
|
+
expect(entity.box).toBe("abox");
|
|
407
|
+
expect(entity.category).toBe("individual");
|
|
89
408
|
expect(entity.types).toContain(`${P.ds}Component`);
|
|
90
409
|
expect(entity.label).toBe("Button");
|
|
410
|
+
expect(entity.instanceOf).toBe(`${P.ds}Component`);
|
|
91
411
|
});
|
|
92
412
|
|
|
93
413
|
it("resolves level-1 URI objects to summaries", async () => {
|
|
94
414
|
const result = await client.readResource({
|
|
95
|
-
uri:
|
|
415
|
+
uri: `${P.ds}global.component.button`,
|
|
96
416
|
});
|
|
97
417
|
const entity = parseContents(result) as {
|
|
98
418
|
properties: {
|
|
99
419
|
predicate: string;
|
|
100
|
-
values: {
|
|
101
|
-
type: string;
|
|
102
|
-
uri?: string;
|
|
103
|
-
prefixed?: string;
|
|
104
|
-
label?: string | null;
|
|
105
|
-
}[];
|
|
420
|
+
values: { type: string; prefixed?: string }[];
|
|
106
421
|
}[];
|
|
107
422
|
};
|
|
108
|
-
|
|
109
423
|
const tierProp = entity.properties.find(
|
|
110
424
|
(p) => p.predicate === `${P.ds}tier`,
|
|
111
425
|
);
|
|
112
|
-
|
|
113
|
-
const tierValue = tierProp?.values[0];
|
|
426
|
+
const tierValue = tierProp?.values.at(0);
|
|
114
427
|
expect(tierValue?.type).toBe("uri");
|
|
115
428
|
expect(tierValue?.prefixed).toBe(`${P.ds}global`);
|
|
116
429
|
});
|
|
117
430
|
|
|
118
431
|
it("includes literal values", async () => {
|
|
119
432
|
const result = await client.readResource({
|
|
120
|
-
uri:
|
|
433
|
+
uri: `${P.ds}global.component.button`,
|
|
121
434
|
});
|
|
122
435
|
const entity = parseContents(result) as {
|
|
123
436
|
properties: {
|
|
@@ -125,53 +438,85 @@ describe("read component instance", () => {
|
|
|
125
438
|
values: { type: string; value?: string }[];
|
|
126
439
|
}[];
|
|
127
440
|
};
|
|
128
|
-
|
|
129
441
|
const nameProp = entity.properties.find(
|
|
130
442
|
(p) => p.predicate === `${P.ds}name`,
|
|
131
443
|
);
|
|
132
|
-
expect(nameProp).
|
|
133
|
-
expect(nameProp?.values
|
|
134
|
-
|
|
444
|
+
expect(nameProp?.values.at(0)?.type).toBe("literal");
|
|
445
|
+
expect(nameProp?.values.at(0)?.value).toBe("Button");
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it("keeps URL-shaped literals as literals, not URIs", async () => {
|
|
449
|
+
const result = await client.readResource({
|
|
450
|
+
uri: `${P.ds}global.component.button`,
|
|
451
|
+
});
|
|
452
|
+
const entity = parseContents(result) as {
|
|
453
|
+
properties: {
|
|
454
|
+
predicate: string;
|
|
455
|
+
values: { type: string; value?: string }[];
|
|
456
|
+
}[];
|
|
457
|
+
};
|
|
458
|
+
const figma = entity.properties.find(
|
|
459
|
+
(p) => p.predicate === `${P.ds}figmaLink`,
|
|
460
|
+
);
|
|
461
|
+
const value = figma?.values.at(0);
|
|
462
|
+
expect(value?.type).toBe("literal");
|
|
463
|
+
expect(value?.value).toBe("https://figma.com/design/example/Button");
|
|
135
464
|
});
|
|
136
465
|
});
|
|
137
466
|
|
|
138
467
|
// =============================================================================
|
|
139
|
-
// Read OWL
|
|
468
|
+
// Read — OWL class (TBox)
|
|
140
469
|
// =============================================================================
|
|
141
470
|
|
|
142
471
|
describe("read OWL class", () => {
|
|
143
|
-
it("returns class with
|
|
144
|
-
const result = await client.readResource({
|
|
145
|
-
uri: `pragma:${P.ds}UIBlock`,
|
|
146
|
-
});
|
|
472
|
+
it("returns a tbox class enriched with schema relations", async () => {
|
|
473
|
+
const result = await client.readResource({ uri: `${P.ds}UIBlock` });
|
|
147
474
|
const entity = parseContents(result) as {
|
|
475
|
+
box: string;
|
|
476
|
+
category: string;
|
|
148
477
|
types: string[];
|
|
149
|
-
|
|
478
|
+
subClasses?: string[];
|
|
479
|
+
instanceCount?: number;
|
|
480
|
+
declaredProperties?: string[];
|
|
150
481
|
properties: {
|
|
151
482
|
predicate: string;
|
|
152
483
|
values: { type: string; value?: string }[];
|
|
153
484
|
}[];
|
|
154
485
|
};
|
|
486
|
+
expect(entity.box).toBe("tbox");
|
|
487
|
+
expect(entity.category).toBe("class");
|
|
155
488
|
expect(entity.types).toContain(`${P.owl}Class`);
|
|
489
|
+
expect(entity.subClasses).toContain(`${P.ds}Component`);
|
|
490
|
+
expect(entity.subClasses).toContain(`${P.ds}Subcomponent`);
|
|
491
|
+
expect(entity.declaredProperties).toContain(`${P.ds}tier`);
|
|
492
|
+
expect(entity.instanceCount).toBe(0);
|
|
493
|
+
|
|
156
494
|
const labelProp = entity.properties.find(
|
|
157
495
|
(p) => p.predicate === `${P.rdfs}label`,
|
|
158
496
|
);
|
|
159
|
-
expect(labelProp?.values
|
|
497
|
+
expect(labelProp?.values.at(0)?.value).toBe("UI Block");
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it("reports superclasses and instance count for a subclass", async () => {
|
|
501
|
+
const result = await client.readResource({ uri: `${P.ds}Component` });
|
|
502
|
+
const entity = parseContents(result) as {
|
|
503
|
+
superClasses?: string[];
|
|
504
|
+
instanceCount?: number;
|
|
505
|
+
};
|
|
506
|
+
expect(entity.superClasses).toContain(`${P.ds}UIBlock`);
|
|
507
|
+
expect(entity.instanceCount).toBe(4);
|
|
160
508
|
});
|
|
161
509
|
});
|
|
162
510
|
|
|
163
511
|
// =============================================================================
|
|
164
|
-
// Read code
|
|
512
|
+
// Read — code standard and tier
|
|
165
513
|
// =============================================================================
|
|
166
514
|
|
|
167
515
|
describe("read code standard", () => {
|
|
168
516
|
it("returns standard with properties", async () => {
|
|
169
|
-
const result = await client.readResource({
|
|
170
|
-
uri: `pragma:${P.cs}code_purity`,
|
|
171
|
-
});
|
|
517
|
+
const result = await client.readResource({ uri: `${P.cs}code_purity` });
|
|
172
518
|
const entity = parseContents(result) as {
|
|
173
519
|
types: string[];
|
|
174
|
-
label: string | null;
|
|
175
520
|
properties: {
|
|
176
521
|
predicate: string;
|
|
177
522
|
values: { type: string; value?: string }[];
|
|
@@ -182,41 +527,28 @@ describe("read code standard", () => {
|
|
|
182
527
|
const nameProp = entity.properties.find(
|
|
183
528
|
(p) => p.predicate === `${P.cs}name`,
|
|
184
529
|
);
|
|
185
|
-
expect(nameProp?.values
|
|
530
|
+
expect(nameProp?.values.at(0)?.value).toBe("code/function/purity");
|
|
186
531
|
|
|
187
532
|
const doProp = entity.properties.find((p) => p.predicate === `${P.cs}do`);
|
|
188
|
-
expect(doProp).toBeDefined();
|
|
189
533
|
expect(doProp?.values.length).toBeGreaterThan(0);
|
|
190
|
-
|
|
191
|
-
const dontProp = entity.properties.find(
|
|
192
|
-
(p) => p.predicate === `${P.cs}dont`,
|
|
193
|
-
);
|
|
194
|
-
expect(dontProp).toBeDefined();
|
|
195
534
|
});
|
|
196
535
|
});
|
|
197
536
|
|
|
198
|
-
// =============================================================================
|
|
199
|
-
// Read tiers
|
|
200
|
-
// =============================================================================
|
|
201
|
-
|
|
202
537
|
describe("read tier", () => {
|
|
203
538
|
it("returns tier with name", async () => {
|
|
204
|
-
const result = await client.readResource({
|
|
205
|
-
uri: `pragma:${P.ds}apps_lxd`,
|
|
206
|
-
});
|
|
539
|
+
const result = await client.readResource({ uri: `${P.ds}apps_lxd` });
|
|
207
540
|
const entity = parseContents(result) as {
|
|
208
541
|
types: string[];
|
|
209
542
|
properties: {
|
|
210
543
|
predicate: string;
|
|
211
|
-
values: { type: string; value?: string
|
|
544
|
+
values: { type: string; value?: string }[];
|
|
212
545
|
}[];
|
|
213
546
|
};
|
|
214
547
|
expect(entity.types).toContain(`${P.ds}Tier`);
|
|
215
|
-
|
|
216
548
|
const nameProp = entity.properties.find(
|
|
217
549
|
(p) => p.predicate === `${P.ds}name`,
|
|
218
550
|
);
|
|
219
|
-
expect(nameProp?.values
|
|
551
|
+
expect(nameProp?.values.at(0)?.value).toBe("apps/lxd");
|
|
220
552
|
});
|
|
221
553
|
});
|
|
222
554
|
|
|
@@ -226,10 +558,8 @@ describe("read tier", () => {
|
|
|
226
558
|
|
|
227
559
|
describe("error handling", () => {
|
|
228
560
|
it("returns text/plain error for unknown entity", async () => {
|
|
229
|
-
const result = await client.readResource({
|
|
230
|
-
|
|
231
|
-
});
|
|
232
|
-
const first = result.contents[0] as ResourceContent;
|
|
561
|
+
const result = await client.readResource({ uri: `${P.ds}nonexistent` });
|
|
562
|
+
const first = result.contents.at(0) as ResourceContent;
|
|
233
563
|
expect(first.mimeType).toBe("text/plain");
|
|
234
564
|
expect(first.text).toContain("ENTITY_NOT_FOUND");
|
|
235
565
|
});
|