@canonical/pragma-cli 0.29.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 +128 -47
- 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/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 +217 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -153
- package/src/config/readConfigLayers.test.ts +109 -0
- package/src/config/readConfigLayers.ts +118 -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 +83 -1
- package/src/config/writeConfig.ts +25 -55
- package/src/constants.ts +3 -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 +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/mcp/specs.ts +16 -196
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +260 -0
- package/src/domains/config/commands/channel.test.ts +30 -3
- package/src/domains/config/commands/channel.ts +25 -8
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +32 -1
- package/src/domains/config/commands/framework.ts +26 -8
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +20 -2
- package/src/domains/config/commands/tier.ts +25 -8
- package/src/domains/config/commands/trace.test.ts +19 -2
- package/src/domains/config/commands/trace.ts +6 -3
- 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 +14 -11
- 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 +7 -7
- package/src/domains/config/mcp/specs.ts +33 -53
- 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/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +1 -0
- package/src/domains/config/tasks/setChannel.ts +13 -28
- package/src/domains/config/tasks/setFramework.ts +11 -30
- package/src/domains/config/tasks/setTier.ts +10 -29
- package/src/domains/config/tasks/setTrace.ts +10 -28
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- 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/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +133 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +32 -26
- 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 +160 -7
- package/src/domains/doctor/operations/runChecks.test.ts +15 -6
- package/src/domains/doctor/operations/runChecks.ts +2 -0
- 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/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/build.test.ts +5 -3
- package/src/domains/graphql/commands/build.ts +8 -16
- package/src/domains/graphql/commands/check.test.ts +5 -3
- package/src/domains/graphql/commands/check.ts +7 -13
- package/src/domains/graphql/commands/index.ts +1 -0
- package/src/domains/graphql/commands/serve.test.ts +46 -0
- package/src/domains/graphql/commands/serve.ts +141 -0
- package/src/domains/graphql/constants.ts +10 -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 +2 -0
- 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/helpers/routeGraphql.test.ts +43 -0
- package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
- package/src/domains/graphql/index.ts +9 -6
- package/src/domains/graphql/operations/compileSchema.test.ts +23 -47
- package/src/domains/graphql/operations/compileSchema.ts +23 -25
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- 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 +10 -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/collectInfo.test.ts +14 -3
- package/src/domains/info/operations/collectInfo.ts +12 -15
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +24 -4
- 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 +55 -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/list.ts +5 -38
- package/src/domains/modifier/commands/lookup.ts +8 -79
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/mcp/specs.ts +15 -94
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +86 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/commands/show.ts +6 -58
- 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 +361 -64
- 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/mcp/specs.ts +7 -84
- 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.test.ts +36 -0
- package/src/domains/ontology/stories.ts +153 -0
- package/src/domains/refs/operations/updateRefs.ts +2 -38
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- 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/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/setup/operations/setupMcp.test.ts +6 -1
- 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 +57 -18
- 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 +11 -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 +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.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +158 -12
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/index.ts +4 -1
- package/src/domains/shared/loaders/localLoader.ts +2 -4
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +82 -9
- 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 +135 -0
- package/src/domains/shared/lookupMany.ts +50 -2
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/packages.ts +10 -17
- 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/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/runtime.ts +4 -63
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +34 -3
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +102 -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 +151 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +111 -0
- 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 +81 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +154 -0
- 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 +406 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +158 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +50 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +751 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +724 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +55 -0
- package/src/domains/shared/stories/pack/index.ts +42 -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/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +381 -0
- package/src/domains/shared/stories/pack/types.ts +224 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +742 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +859 -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 +179 -0
- package/src/domains/shared/suggestions/expandGlob.ts +1 -0
- package/src/domains/shared/suggestions/suggestNames.ts +43 -10
- 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/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 +0 -1
- 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/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 +18 -2
- package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/domains/trace/tracePlugin.test.ts +1 -1
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +382 -74
- package/src/mcp/registerResources.ts +5 -348
- 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 +22 -5
- package/src/mcp/tools/registerTools.test.ts +167 -34
- package/src/mcp/types.ts +4 -0
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +35 -13
- 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 +202 -25
- package/src/testing/cli.ts +30 -2
- package/src/testing/createTestMcpClient.ts +4 -1
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +7 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +103 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +136 -82
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +26 -0
- package/src/testing/standardParityFixtures.ts +35 -0
- package/src/testing/store.ts +4 -4
- package/src/testing/storyFixtures.ts +63 -0
- 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 -90
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -99
- 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 -244
- 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 -32
- 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/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 -59
- 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 -38
- 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/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createStore } from "@canonical/ke";
|
|
1
|
+
import { createStore, type InlineSource } from "@canonical/ke";
|
|
3
2
|
import {
|
|
4
3
|
CompilationError,
|
|
5
4
|
type CompilerResult,
|
|
@@ -9,15 +8,14 @@ import {
|
|
|
9
8
|
hashSources,
|
|
10
9
|
} from "@canonical/ke-graphql";
|
|
11
10
|
import { PragmaError } from "#error";
|
|
12
|
-
import { resolveSourceFiles } from "../helpers/index.js";
|
|
13
11
|
|
|
14
12
|
export interface CompileSchemaOptions {
|
|
15
|
-
/** TTL
|
|
16
|
-
readonly sources: readonly
|
|
13
|
+
/** In-memory TTL sources (content + provenance path) to compile. */
|
|
14
|
+
readonly sources: readonly InlineSource[];
|
|
17
15
|
/** Prefix map handed to both the ke store and the compiler. */
|
|
18
16
|
readonly prefixes: Readonly<Record<string, string>>;
|
|
19
|
-
/** Working directory
|
|
20
|
-
readonly cwd
|
|
17
|
+
/** Working directory, passed through to the store (unused for inline). */
|
|
18
|
+
readonly cwd?: string;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export interface CompileSchemaResult {
|
|
@@ -25,7 +23,7 @@ export interface CompileSchemaResult {
|
|
|
25
23
|
readonly status: "ok" | "failed";
|
|
26
24
|
/** All compiler diagnostics, including those carried by a CompilationError. */
|
|
27
25
|
readonly diagnostics: readonly Diagnostic[];
|
|
28
|
-
/**
|
|
26
|
+
/** Provenance paths of the sources that were loaded. */
|
|
29
27
|
readonly files: readonly string[];
|
|
30
28
|
/** Fingerprint of the raw source contents (matches ke's onLoad hashing). */
|
|
31
29
|
readonly sourcesHash: string;
|
|
@@ -34,42 +32,42 @@ export interface CompileSchemaResult {
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
/**
|
|
37
|
-
* Compile TTL sources into a GraphQL schema via the ke-graphql
|
|
35
|
+
* Compile in-memory TTL sources into a GraphQL schema via the ke-graphql
|
|
36
|
+
* pipeline.
|
|
38
37
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* diagnostics; the store is always disposed.
|
|
38
|
+
* Hashes the raw source contents (the exact bytes ke loads, so the
|
|
39
|
+
* artifact's `sourcesHash` matches what `createSchemaPlugin` computes from
|
|
40
|
+
* ke's onLoad), boots a throwaway ke store, and runs the compiler. A
|
|
41
|
+
* {@link CompilationError} (schema composition failed) is converted into a
|
|
42
|
+
* `"failed"` result carrying its diagnostics; the store is always disposed.
|
|
45
43
|
*
|
|
46
|
-
* @note Impure —
|
|
44
|
+
* @note Impure — boots a ke store (WASM + TTL).
|
|
47
45
|
*
|
|
48
|
-
* @param options -
|
|
46
|
+
* @param options - In-memory sources and prefixes.
|
|
49
47
|
* @returns The compile outcome with diagnostics, files, and sources hash.
|
|
50
|
-
* @throws PragmaError with code `INVALID_INPUT` when no sources
|
|
48
|
+
* @throws PragmaError with code `INVALID_INPUT` when given no sources.
|
|
51
49
|
* @throws PragmaError with code `STORE_ERROR` when the store fails to load.
|
|
52
50
|
*/
|
|
53
51
|
export default async function compileSchema(
|
|
54
52
|
options: CompileSchemaOptions,
|
|
55
53
|
): Promise<CompileSchemaResult> {
|
|
56
|
-
const
|
|
54
|
+
const { sources } = options;
|
|
57
55
|
|
|
58
|
-
if (
|
|
59
|
-
throw PragmaError.invalidInput("sources",
|
|
56
|
+
if (sources.length === 0) {
|
|
57
|
+
throw PragmaError.invalidInput("sources", "(empty)", {
|
|
60
58
|
recovery: {
|
|
61
|
-
message: "Provide at least one
|
|
59
|
+
message: "Provide at least one TTL source.",
|
|
62
60
|
},
|
|
63
61
|
});
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
const
|
|
67
|
-
const
|
|
64
|
+
const sourcesHash = hashSources(sources.map((source) => source.content));
|
|
65
|
+
const files = sources.map((source) => source.path ?? "inline");
|
|
68
66
|
|
|
69
67
|
let store: Awaited<ReturnType<typeof createStore>>;
|
|
70
68
|
try {
|
|
71
69
|
store = await createStore({
|
|
72
|
-
sources: [...
|
|
70
|
+
sources: [...sources],
|
|
73
71
|
prefixes: options.prefixes,
|
|
74
72
|
cwd: options.cwd,
|
|
75
73
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { EX_NAMESPACE, GRAPHQL_CLEAN_TTL } from "#testing";
|
|
3
|
+
import { DEFAULT_PREFIX_MAP } from "../../shared/prefixes.js";
|
|
4
|
+
import createSchemaServer, { type SchemaServer } from "./createSchemaServer.js";
|
|
5
|
+
|
|
6
|
+
const PREFIXES = { ...DEFAULT_PREFIX_MAP, ex: EX_NAMESPACE };
|
|
7
|
+
|
|
8
|
+
describe("createSchemaServer", () => {
|
|
9
|
+
let server: SchemaServer | undefined;
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
server?.dispose();
|
|
13
|
+
server = undefined;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("compiles inline sources and serves an introspectable schema", async () => {
|
|
17
|
+
server = await createSchemaServer({
|
|
18
|
+
sources: [
|
|
19
|
+
{ content: GRAPHQL_CLEAN_TTL, format: "turtle", path: "clean.ttl" },
|
|
20
|
+
],
|
|
21
|
+
prefixes: PREFIXES,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const response = await server.handler(
|
|
25
|
+
new Request("http://localhost/graphql", {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: { "content-type": "application/json" },
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
query: "{ __schema { queryType { name } } }",
|
|
30
|
+
}),
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(response.status).toBe(200);
|
|
35
|
+
const body = (await response.json()) as {
|
|
36
|
+
data?: { __schema?: { queryType?: { name?: string } } };
|
|
37
|
+
};
|
|
38
|
+
expect(body.data?.__schema?.queryType?.name).toBe("Query");
|
|
39
|
+
expect(Array.isArray(server.diagnostics)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("throws a STORE_ERROR on invalid TTL", async () => {
|
|
43
|
+
await expect(
|
|
44
|
+
createSchemaServer({
|
|
45
|
+
sources: [{ content: "not turtle .", format: "turtle", path: "x.ttl" }],
|
|
46
|
+
prefixes: PREFIXES,
|
|
47
|
+
}),
|
|
48
|
+
).rejects.toMatchObject({ code: "STORE_ERROR" });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot a persistent ke store over in-memory TTL sources with the
|
|
3
|
+
* ke-graphql schema plugin attached, and build the `/http` fetch handler
|
|
4
|
+
* (GraphiQL + GraphQL, CORS, incremental delivery) over the compiled
|
|
5
|
+
* schema. The caller owns the returned `dispose`.
|
|
6
|
+
*
|
|
7
|
+
* @note Impure — boots a ke store (WASM + TTL) and compiles the schema.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createStore, type InlineSource, type Plugin } from "@canonical/ke";
|
|
11
|
+
import {
|
|
12
|
+
createSchemaPlugin,
|
|
13
|
+
type Diagnostic,
|
|
14
|
+
type SchemaPluginApi,
|
|
15
|
+
} from "@canonical/ke-graphql";
|
|
16
|
+
import { createGraphQLHandler } from "@canonical/ke-graphql/http";
|
|
17
|
+
import { PragmaError } from "#error";
|
|
18
|
+
|
|
19
|
+
export interface CreateSchemaServerOptions {
|
|
20
|
+
/** In-memory TTL sources to load into the store. */
|
|
21
|
+
readonly sources: readonly InlineSource[];
|
|
22
|
+
/** Prefix map handed to both the ke store and the compiler. */
|
|
23
|
+
readonly prefixes: Readonly<Record<string, string>>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SchemaServer {
|
|
27
|
+
/** Runtime-neutral fetch handler over the compiled schema. */
|
|
28
|
+
readonly handler: ReturnType<typeof createGraphQLHandler>;
|
|
29
|
+
/** Compiler diagnostics produced while building the schema. */
|
|
30
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
31
|
+
/** Releases the underlying ke store. */
|
|
32
|
+
readonly dispose: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default async function createSchemaServer(
|
|
36
|
+
options: CreateSchemaServerOptions,
|
|
37
|
+
): Promise<SchemaServer> {
|
|
38
|
+
const plugin = createSchemaPlugin({ incremental: true });
|
|
39
|
+
|
|
40
|
+
let store: Awaited<ReturnType<typeof createStore>>;
|
|
41
|
+
try {
|
|
42
|
+
store = await createStore({
|
|
43
|
+
sources: [...options.sources],
|
|
44
|
+
prefixes: options.prefixes,
|
|
45
|
+
// biome-ignore lint/suspicious/noExplicitAny: Plugin generic variance
|
|
46
|
+
plugins: [plugin as Plugin<any>],
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw PragmaError.storeError(
|
|
50
|
+
error instanceof Error ? error.message : String(error),
|
|
51
|
+
{ recovery: { message: "Check the TTL syntax of the sources." } },
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const api = store.api<SchemaPluginApi>("ke-graphql");
|
|
56
|
+
if (!api) {
|
|
57
|
+
store.dispose();
|
|
58
|
+
throw PragmaError.storeError("ke-graphql plugin did not register its API", {
|
|
59
|
+
recovery: { message: "This is an internal error; please report it." },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const handler = createGraphQLHandler(api.schema, {
|
|
64
|
+
context: () => api.createContext(store),
|
|
65
|
+
graphiql: true,
|
|
66
|
+
cors: true,
|
|
67
|
+
incremental: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
handler,
|
|
72
|
+
diagnostics: api.diagnostics,
|
|
73
|
+
dispose: () => store.dispose(),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
mkdtempSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
12
|
+
import { EX_NAMESPACE, GRAPHQL_CLEAN_TTL, GRAPHQL_FATAL_TTL } from "#testing";
|
|
13
|
+
import { DEFAULT_PREFIX_MAP } from "../../shared/prefixes.js";
|
|
14
|
+
import type { SemanticPackage } from "../../shared/semanticPackage.js";
|
|
15
|
+
import ensureSchemaArtifact, {
|
|
16
|
+
resolveSchemaArtifactPath,
|
|
17
|
+
} from "./ensureSchemaArtifact.js";
|
|
18
|
+
|
|
19
|
+
const PREFIXES = { ...DEFAULT_PREFIX_MAP, ex: EX_NAMESPACE };
|
|
20
|
+
|
|
21
|
+
let dataDir: string;
|
|
22
|
+
let cwd: string;
|
|
23
|
+
let originalXdgData: string | undefined;
|
|
24
|
+
|
|
25
|
+
beforeAll(() => {
|
|
26
|
+
dataDir = mkdtempSync(join(tmpdir(), "pragma-schema-artifact-data-"));
|
|
27
|
+
cwd = mkdtempSync(join(tmpdir(), "pragma-schema-artifact-cwd-"));
|
|
28
|
+
originalXdgData = process.env.XDG_DATA_HOME;
|
|
29
|
+
process.env.XDG_DATA_HOME = dataDir;
|
|
30
|
+
// The global test setup disables artifact emission; this suite tests it.
|
|
31
|
+
delete process.env.PRAGMA_SCHEMA_ARTIFACT;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterAll(() => {
|
|
35
|
+
if (originalXdgData === undefined) {
|
|
36
|
+
delete process.env.XDG_DATA_HOME;
|
|
37
|
+
} else {
|
|
38
|
+
process.env.XDG_DATA_HOME = originalXdgData;
|
|
39
|
+
}
|
|
40
|
+
process.env.PRAGMA_SCHEMA_ARTIFACT = "off";
|
|
41
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
42
|
+
rmSync(cwd, { recursive: true, force: true });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
delete process.env.PRAGMA_SCHEMA_ARTIFACT;
|
|
47
|
+
rmSync(join(dataDir, "pragma"), { recursive: true, force: true });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function makeRuntime(ttl: string): Parameters<typeof ensureSchemaArtifact>[0] {
|
|
51
|
+
const pkg: SemanticPackage = {
|
|
52
|
+
name: "@test/ontology",
|
|
53
|
+
version: "1.0.0",
|
|
54
|
+
source: "local",
|
|
55
|
+
graphs: [{ path: "ontology.ttl", content: ttl, format: "turtle" }],
|
|
56
|
+
skills: [],
|
|
57
|
+
stories: [],
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
cwd,
|
|
61
|
+
packages: [pkg],
|
|
62
|
+
store: { prefixes: PREFIXES } as never,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("resolveSchemaArtifactPath", () => {
|
|
67
|
+
it("uses the XDG data dir when no project config is in tree", () => {
|
|
68
|
+
expect(resolveSchemaArtifactPath(cwd)).toBe(
|
|
69
|
+
join(dataDir, "pragma", "schema.graphql"),
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("uses .pragma/ beside the project config when one is in tree", () => {
|
|
74
|
+
const project = mkdtempSync(join(tmpdir(), "pragma-schema-project-"));
|
|
75
|
+
try {
|
|
76
|
+
writeFileSync(join(project, "pragma.config.json"), "{}", "utf-8");
|
|
77
|
+
const nested = join(project, "src");
|
|
78
|
+
mkdirSync(nested);
|
|
79
|
+
expect(resolveSchemaArtifactPath(nested)).toBe(
|
|
80
|
+
join(project, ".pragma", "schema.graphql"),
|
|
81
|
+
);
|
|
82
|
+
} finally {
|
|
83
|
+
rmSync(project, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("ensureSchemaArtifact", () => {
|
|
89
|
+
it("writes the artifact with a source-hash header on first use", async () => {
|
|
90
|
+
const result = await ensureSchemaArtifact(makeRuntime(GRAPHQL_CLEAN_TTL));
|
|
91
|
+
|
|
92
|
+
expect(result.status).toBe("written");
|
|
93
|
+
expect(existsSync(result.path)).toBe(true);
|
|
94
|
+
|
|
95
|
+
const content = readFileSync(result.path, "utf-8");
|
|
96
|
+
expect(content.startsWith("# pragma:sources ")).toBe(true);
|
|
97
|
+
expect(content).toContain("type Thing");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("is fresh on a second run with unchanged sources", async () => {
|
|
101
|
+
const runtime = makeRuntime(GRAPHQL_CLEAN_TTL);
|
|
102
|
+
await ensureSchemaArtifact(runtime);
|
|
103
|
+
|
|
104
|
+
const second = await ensureSchemaArtifact(runtime);
|
|
105
|
+
expect(second.status).toBe("fresh");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("rewrites when the sources change", async () => {
|
|
109
|
+
await ensureSchemaArtifact(makeRuntime(GRAPHQL_CLEAN_TTL));
|
|
110
|
+
|
|
111
|
+
const changed = await ensureSchemaArtifact(
|
|
112
|
+
makeRuntime(`${GRAPHQL_CLEAN_TTL}\n# changed`),
|
|
113
|
+
);
|
|
114
|
+
expect(changed.status).toBe("written");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("leaves a stale artifact in place when compilation fails", async () => {
|
|
118
|
+
const first = await ensureSchemaArtifact(makeRuntime(GRAPHQL_CLEAN_TTL));
|
|
119
|
+
const before = readFileSync(first.path, "utf-8");
|
|
120
|
+
|
|
121
|
+
const failed = await ensureSchemaArtifact(makeRuntime(GRAPHQL_FATAL_TTL));
|
|
122
|
+
expect(failed.status).toBe("failed");
|
|
123
|
+
expect(readFileSync(first.path, "utf-8")).toBe(before);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("compiles from the parseable sources when one is malformed", async () => {
|
|
127
|
+
const pkg: SemanticPackage = {
|
|
128
|
+
name: "@test/ontology",
|
|
129
|
+
version: "1.0.0",
|
|
130
|
+
source: "local",
|
|
131
|
+
graphs: [
|
|
132
|
+
{ path: "clean.ttl", content: GRAPHQL_CLEAN_TTL, format: "turtle" },
|
|
133
|
+
{
|
|
134
|
+
path: "broken.ttl",
|
|
135
|
+
content: "this is not turtle .",
|
|
136
|
+
format: "turtle",
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
skills: [],
|
|
140
|
+
stories: [],
|
|
141
|
+
};
|
|
142
|
+
const result = await ensureSchemaArtifact({
|
|
143
|
+
cwd,
|
|
144
|
+
packages: [pkg],
|
|
145
|
+
store: { prefixes: PREFIXES } as never,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(result.status).toBe("written");
|
|
149
|
+
expect(readFileSync(result.path, "utf-8")).toContain("type Thing");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("skips when no sources resolve", async () => {
|
|
153
|
+
const result = await ensureSchemaArtifact({
|
|
154
|
+
cwd,
|
|
155
|
+
packages: [],
|
|
156
|
+
store: { prefixes: PREFIXES } as never,
|
|
157
|
+
});
|
|
158
|
+
expect(result.status).toBe("skipped");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("skips when disabled via PRAGMA_SCHEMA_ARTIFACT=off", async () => {
|
|
162
|
+
process.env.PRAGMA_SCHEMA_ARTIFACT = "off";
|
|
163
|
+
const result = await ensureSchemaArtifact(makeRuntime(GRAPHQL_CLEAN_TTL));
|
|
164
|
+
expect(result.status).toBe("skipped");
|
|
165
|
+
expect(existsSync(result.path)).toBe(false);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash-gated `schema.graphql` artifact.
|
|
3
|
+
*
|
|
4
|
+
* pragma keeps one compiled GraphQL schema per data set so downstream
|
|
5
|
+
* tooling (codegen, IDEs, `graphql serve` clients) always has a current
|
|
6
|
+
* SDL without anyone running `pragma graphql build` by hand:
|
|
7
|
+
*
|
|
8
|
+
* - **Location** — next to the project's `pragma.config.json`
|
|
9
|
+
* (`<projectRoot>/.pragma/schema.graphql`) when one is in tree, else the
|
|
10
|
+
* XDG data dir (`$XDG_DATA_HOME/pragma/schema.graphql`).
|
|
11
|
+
* - **Emission** — on first use of pragma (any command that boots the
|
|
12
|
+
* runtime) the artifact is generated if missing.
|
|
13
|
+
* - **Renewal** — the artifact's first line records the hash of the TTL
|
|
14
|
+
* sources it was compiled from (`# pragma:sources <hash>`); it is
|
|
15
|
+
* recompiled only when the resolved sources no longer match that hash.
|
|
16
|
+
*
|
|
17
|
+
* Failure is never fatal: a schema that does not compile leaves the
|
|
18
|
+
* previous artifact in place and the boot proceeds with a warning.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
closeSync,
|
|
23
|
+
existsSync,
|
|
24
|
+
mkdirSync,
|
|
25
|
+
openSync,
|
|
26
|
+
readSync,
|
|
27
|
+
writeFileSync,
|
|
28
|
+
} from "node:fs";
|
|
29
|
+
import { dirname, join } from "node:path";
|
|
30
|
+
import { createStore, definePlugin, type InlineSource } from "@canonical/ke";
|
|
31
|
+
import { hashSources } from "@canonical/ke-graphql";
|
|
32
|
+
import { findProjectConfigPath } from "#config";
|
|
33
|
+
import { dataRoot } from "../../refs/operations/paths.js";
|
|
34
|
+
import type { PragmaRuntime } from "../../shared/runtime.js";
|
|
35
|
+
import compileSchema from "./compileSchema.js";
|
|
36
|
+
|
|
37
|
+
/** First-line marker carrying the source fingerprint. */
|
|
38
|
+
const HASH_HEADER = "# pragma:sources ";
|
|
39
|
+
|
|
40
|
+
/** Environment kill-switch (CI, tests, air-gapped boots). */
|
|
41
|
+
const DISABLE_ENV = "PRAGMA_SCHEMA_ARTIFACT";
|
|
42
|
+
|
|
43
|
+
export interface SchemaArtifactResult {
|
|
44
|
+
/** Absolute path of the artifact. */
|
|
45
|
+
readonly path: string;
|
|
46
|
+
/**
|
|
47
|
+
* What happened: `fresh` (hash matched, nothing to do), `written`
|
|
48
|
+
* (compiled and saved), `failed` (compilation failed, artifact left
|
|
49
|
+
* as-is), `skipped` (no sources, or disabled via env).
|
|
50
|
+
*/
|
|
51
|
+
readonly status: "fresh" | "written" | "failed" | "skipped";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Where the schema artifact lives for a working directory: project-local
|
|
56
|
+
* (`.pragma/` beside the nearest `pragma.config.json`) when the project is
|
|
57
|
+
* configured, global XDG data dir otherwise.
|
|
58
|
+
*/
|
|
59
|
+
export function resolveSchemaArtifactPath(cwd: string): string {
|
|
60
|
+
const configPath = findProjectConfigPath(cwd);
|
|
61
|
+
return configPath
|
|
62
|
+
? join(dirname(configPath), ".pragma", "schema.graphql")
|
|
63
|
+
: join(dataRoot(), "schema.graphql");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Ensure the schema artifact exists and matches the runtime's resolved
|
|
68
|
+
* TTL sources, recompiling only on hash mismatch.
|
|
69
|
+
*
|
|
70
|
+
* @note Impure — reads and writes the artifact; compiles via a throwaway
|
|
71
|
+
* ke store when stale.
|
|
72
|
+
*/
|
|
73
|
+
export default async function ensureSchemaArtifact(
|
|
74
|
+
runtime: Pick<PragmaRuntime, "cwd" | "packages" | "store">,
|
|
75
|
+
): Promise<SchemaArtifactResult> {
|
|
76
|
+
const path = resolveSchemaArtifactPath(runtime.cwd);
|
|
77
|
+
|
|
78
|
+
if (process.env[DISABLE_ENV] === "off") {
|
|
79
|
+
return { path, status: "skipped" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Sort by provenance path so the fingerprint is stable across boots
|
|
83
|
+
// regardless of package resolution order.
|
|
84
|
+
const sources: InlineSource[] = runtime.packages
|
|
85
|
+
.flatMap((pkg) =>
|
|
86
|
+
pkg.graphs.map((graph) => ({
|
|
87
|
+
content: graph.content,
|
|
88
|
+
format: "turtle" as const,
|
|
89
|
+
path: graph.path,
|
|
90
|
+
})),
|
|
91
|
+
)
|
|
92
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
93
|
+
|
|
94
|
+
if (sources.length === 0) {
|
|
95
|
+
return { path, status: "skipped" };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const hash = hashSources(sources.map((source) => source.content));
|
|
99
|
+
|
|
100
|
+
if (existsSync(path) && readFirstLine(path) === `${HASH_HEADER}${hash}`) {
|
|
101
|
+
return { path, status: "fresh" };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Mirror boot's "one bad file cannot break boot": compile from the
|
|
105
|
+
// parseable sources only. The runtime already warned about any malformed
|
|
106
|
+
// graph while booting the main store, so the filter here is silent.
|
|
107
|
+
// The fingerprint above covers ALL sources, so fixing a malformed file
|
|
108
|
+
// changes the hash and triggers recompilation.
|
|
109
|
+
const parseable = await filterParseableSources(sources);
|
|
110
|
+
if (parseable.length === 0) {
|
|
111
|
+
return { path, status: "failed" };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const outcome = await compileSchema({
|
|
115
|
+
sources: parseable,
|
|
116
|
+
prefixes: runtime.store.prefixes,
|
|
117
|
+
cwd: runtime.cwd,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (outcome.status === "failed" || !outcome.compiled) {
|
|
121
|
+
return { path, status: "failed" };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
125
|
+
writeFileSync(
|
|
126
|
+
path,
|
|
127
|
+
`${HASH_HEADER}${hash}\n${outcome.compiled.sdl}`,
|
|
128
|
+
"utf-8",
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return { path, status: "written" };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Read just the artifact's header line — the freshness probe runs on every
|
|
136
|
+
* booted invocation, so it must not scale with the SDL's size.
|
|
137
|
+
*/
|
|
138
|
+
function readFirstLine(path: string, max = 96): string {
|
|
139
|
+
const fd = openSync(path, "r");
|
|
140
|
+
try {
|
|
141
|
+
const buffer = Buffer.alloc(max);
|
|
142
|
+
const bytes = readSync(fd, buffer, 0, max, 0);
|
|
143
|
+
const text = buffer.subarray(0, bytes).toString("utf-8");
|
|
144
|
+
const newline = text.indexOf("\n");
|
|
145
|
+
return newline === -1 ? text : text.slice(0, newline);
|
|
146
|
+
} finally {
|
|
147
|
+
closeSync(fd);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Drop sources the Turtle parser rejects, keeping the rest — the same
|
|
153
|
+
* tolerance the main store's graph loader applies at boot. Probes with a
|
|
154
|
+
* throwaway store so the strict `graphql check` semantics of
|
|
155
|
+
* {@link compileSchema} stay untouched.
|
|
156
|
+
*/
|
|
157
|
+
async function filterParseableSources(
|
|
158
|
+
sources: readonly InlineSource[],
|
|
159
|
+
): Promise<InlineSource[]> {
|
|
160
|
+
const parseable: InlineSource[] = [];
|
|
161
|
+
const probe = definePlugin({
|
|
162
|
+
name: "pragma-schema-artifact-probe",
|
|
163
|
+
onReady(ctx) {
|
|
164
|
+
for (const source of sources) {
|
|
165
|
+
try {
|
|
166
|
+
ctx.load(source.content, { format: source.format });
|
|
167
|
+
parseable.push(source);
|
|
168
|
+
} catch {
|
|
169
|
+
// Already warned at boot by the main store's graph loader.
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const store = await createStore({ sources: [], plugins: [probe] });
|
|
176
|
+
store.dispose();
|
|
177
|
+
return parseable;
|
|
178
|
+
}
|
|
@@ -5,3 +5,13 @@ export type {
|
|
|
5
5
|
CompileSchemaResult,
|
|
6
6
|
} from "./compileSchema.js";
|
|
7
7
|
export { default as compileSchema } from "./compileSchema.js";
|
|
8
|
+
export type {
|
|
9
|
+
CreateSchemaServerOptions,
|
|
10
|
+
SchemaServer,
|
|
11
|
+
} from "./createSchemaServer.js";
|
|
12
|
+
export { default as createSchemaServer } from "./createSchemaServer.js";
|
|
13
|
+
export type { SchemaArtifactResult } from "./ensureSchemaArtifact.js";
|
|
14
|
+
export {
|
|
15
|
+
default as ensureSchemaArtifact,
|
|
16
|
+
resolveSchemaArtifactPath,
|
|
17
|
+
} from "./ensureSchemaArtifact.js";
|
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
|
|
9
9
|
function createUpgradeData(overrides: Partial<UpgradeData> = {}): UpgradeData {
|
|
10
10
|
return {
|
|
11
|
-
pm
|
|
11
|
+
// `pm` is the install label, which already carries the scope (as the
|
|
12
|
+
// command populates it from source.label).
|
|
13
|
+
pm: "bun (global)",
|
|
12
14
|
current: "0.18.0",
|
|
13
15
|
latest: "0.19.0",
|
|
14
16
|
command: "bun update -g @canonical/pragma-cli",
|
|
@@ -21,9 +23,10 @@ function createUpgradeData(overrides: Partial<UpgradeData> = {}): UpgradeData {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
describe("renderUpgradePlain", () => {
|
|
24
|
-
it("shows PM info", () => {
|
|
26
|
+
it("shows PM info without doubling the scope suffix", () => {
|
|
25
27
|
const output = renderUpgradePlain(createUpgradeData());
|
|
26
|
-
expect(output).toContain("bun (global)");
|
|
28
|
+
expect(output).toContain("Installed via: bun (global)");
|
|
29
|
+
expect(output).not.toContain("(global) (global)");
|
|
27
30
|
});
|
|
28
31
|
|
|
29
32
|
it("shows version transition", () => {
|
|
@@ -107,7 +110,7 @@ describe("renderUpgradeJson", () => {
|
|
|
107
110
|
it("produces valid JSON", () => {
|
|
108
111
|
const data = createUpgradeData();
|
|
109
112
|
const parsed = JSON.parse(renderUpgradeJson(data));
|
|
110
|
-
expect(parsed.pm).toBe("bun");
|
|
113
|
+
expect(parsed.pm).toBe("bun (global)");
|
|
111
114
|
expect(parsed.current).toBe("0.18.0");
|
|
112
115
|
expect(parsed.latest).toBe("0.19.0");
|
|
113
116
|
});
|
|
@@ -12,7 +12,9 @@ import type { UpgradeData } from "../types.js";
|
|
|
12
12
|
function renderUpgradePlain(data: UpgradeData): string {
|
|
13
13
|
const lines: string[] = [];
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// `data.pm` is the install label (e.g. "bun (global)"), which already carries
|
|
16
|
+
// the scope — do not append another "(global)".
|
|
17
|
+
lines.push(`Installed via: ${data.pm}`);
|
|
16
18
|
|
|
17
19
|
if (data.offline) {
|
|
18
20
|
lines.push("Could not reach registry");
|