@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
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { VALID_CHANNELS } from "../constants.js";
|
|
3
|
+
import { parsePackageEntry } from "../domains/refs/operations/parseRef.js";
|
|
4
|
+
import { DEFAULT_CONFIG } from "./defaultConfig.js";
|
|
5
|
+
|
|
6
|
+
describe("DEFAULT_CONFIG", () => {
|
|
7
|
+
it("declares a valid channel", () => {
|
|
8
|
+
expect(VALID_CHANNELS).toContain(DEFAULT_CONFIG.channel);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("declares at least one package, all parseable", () => {
|
|
12
|
+
expect(DEFAULT_CONFIG.packages.length).toBeGreaterThan(0);
|
|
13
|
+
for (const entry of DEFAULT_CONFIG.packages) {
|
|
14
|
+
const ref = parsePackageEntry(entry);
|
|
15
|
+
expect(ref.pkg.length).toBeGreaterThan(0);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("carries the canonical design-system package by default", () => {
|
|
20
|
+
const names = DEFAULT_CONFIG.packages.map((entry) =>
|
|
21
|
+
typeof entry === "string" ? entry : entry.name,
|
|
22
|
+
);
|
|
23
|
+
expect(names).toContain("@canonical/design-system");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("does not declare fields the document must not layer", () => {
|
|
27
|
+
// tier/trace/framework defaults are semantic absences, and packages
|
|
28
|
+
// must reach the merge via DEFAULT_PACKAGES (see defaultConfig.ts).
|
|
29
|
+
expect(DEFAULT_CONFIG.tier).toBeUndefined();
|
|
30
|
+
expect(DEFAULT_CONFIG.trace).toBeUndefined();
|
|
31
|
+
expect(DEFAULT_CONFIG.framework).toBeUndefined();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Channel } from "../constants.js";
|
|
2
|
+
import type { RawPackageEntry } from "../domains/refs/operations/parseRef.js";
|
|
3
|
+
import { PragmaError } from "../error/PragmaError.js";
|
|
4
|
+
import defaultsDocument from "./defaults.json" with { type: "json" };
|
|
5
|
+
import parseConfigValues from "./parseConfigValues.js";
|
|
6
|
+
import type { ConfigFileValues } from "./types.js";
|
|
7
|
+
|
|
8
|
+
/** The built-in defaults with the fields every install relies on present. */
|
|
9
|
+
interface DefaultConfig extends ConfigFileValues {
|
|
10
|
+
/** Release channel active when no config file sets one. */
|
|
11
|
+
readonly channel: Channel;
|
|
12
|
+
/** Semantic packages loaded when no config or global refs declare any. */
|
|
13
|
+
readonly packages: ReadonlyArray<RawPackageEntry>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validate the embedded defaults document into the built-in config layer.
|
|
18
|
+
*
|
|
19
|
+
* The document goes through the same parser as user config files, so an
|
|
20
|
+
* invalid edit to `defaults.json` fails loudly at first import (and in
|
|
21
|
+
* tests) instead of silently changing behavior.
|
|
22
|
+
*
|
|
23
|
+
* @throws PragmaError with code `CONFIG_ERROR` when the document is
|
|
24
|
+
* invalid or misses a required default.
|
|
25
|
+
*/
|
|
26
|
+
function parseDefaultsDocument(): DefaultConfig {
|
|
27
|
+
const values = parseConfigValues(
|
|
28
|
+
JSON.stringify(defaultsDocument),
|
|
29
|
+
"built-in defaults (src/config/defaults.json)",
|
|
30
|
+
);
|
|
31
|
+
if (values.channel === undefined) {
|
|
32
|
+
throw PragmaError.configError("Built-in defaults must declare a channel.", {
|
|
33
|
+
recovery: { message: "Restore `channel` in src/config/defaults.json." },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (values.packages === undefined || values.packages.length === 0) {
|
|
37
|
+
throw PragmaError.configError(
|
|
38
|
+
"Built-in defaults must declare at least one semantic package.",
|
|
39
|
+
{
|
|
40
|
+
recovery: {
|
|
41
|
+
message: "Restore `packages` in src/config/defaults.json.",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return { ...values, channel: values.channel, packages: values.packages };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The built-in configuration layer, sourced from the embedded declarative
|
|
51
|
+
* `defaults.json` document rather than hardcoded values.
|
|
52
|
+
*
|
|
53
|
+
* `packages` is deliberately NOT surfaced through the layered `pick()` in
|
|
54
|
+
* `readConfigLayers` — it feeds `DEFAULT_PACKAGES` (and from there the
|
|
55
|
+
* ref merge), where global `refs.json` entries still merge over it by
|
|
56
|
+
* package name. Sourcing it as an ordinary config layer would make it
|
|
57
|
+
* look user-declared and shut global refs out.
|
|
58
|
+
*/
|
|
59
|
+
export const DEFAULT_CONFIG: DefaultConfig = parseDefaultsDocument();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"channel": "normal",
|
|
3
|
+
"packages": [
|
|
4
|
+
{
|
|
5
|
+
"name": "@canonical/design-system",
|
|
6
|
+
"source": "git+https://github.com/canonical/design-system.git#main"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "@canonical/anatomy-dsl",
|
|
10
|
+
"source": "git+https://github.com/canonical/anatomy-dsl.git#main"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "@canonical/code-standards",
|
|
14
|
+
"source": "git+https://github.com/canonical/web-code-standards.git#main"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import findProjectConfigPath from "./findProjectConfigPath.js";
|
|
6
|
+
|
|
7
|
+
describe("findProjectConfigPath", () => {
|
|
8
|
+
let root: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
root = mkdtempSync(join(tmpdir(), "pragma-find-config-"));
|
|
12
|
+
mkdirSync(join(root, ".git"));
|
|
13
|
+
mkdirSync(join(root, "packages/app/src"), { recursive: true });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
rmSync(root, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("finds the config in the starting directory", () => {
|
|
21
|
+
writeFileSync(join(root, "packages/app/pragma.config.json"), "{}");
|
|
22
|
+
expect(findProjectConfigPath(join(root, "packages/app"))).toBe(
|
|
23
|
+
join(root, "packages/app/pragma.config.json"),
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("walks up to the nearest config", () => {
|
|
28
|
+
writeFileSync(join(root, "pragma.config.json"), "{}");
|
|
29
|
+
expect(findProjectConfigPath(join(root, "packages/app/src"))).toBe(
|
|
30
|
+
join(root, "pragma.config.json"),
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("prefers the nearest config over a higher one", () => {
|
|
35
|
+
writeFileSync(join(root, "pragma.config.json"), "{}");
|
|
36
|
+
writeFileSync(join(root, "packages/app/pragma.config.json"), "{}");
|
|
37
|
+
expect(findProjectConfigPath(join(root, "packages/app/src"))).toBe(
|
|
38
|
+
join(root, "packages/app/pragma.config.json"),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("stops at the repository root (.git) without escaping", () => {
|
|
43
|
+
// The fixture root has .git and no config; nothing above it may leak in.
|
|
44
|
+
expect(findProjectConfigPath(join(root, "packages/app/src"))).toBe(
|
|
45
|
+
undefined,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("checks the .git directory's own level before stopping", () => {
|
|
50
|
+
writeFileSync(join(root, "pragma.config.json"), "{}");
|
|
51
|
+
expect(findProjectConfigPath(root)).toBe(join(root, "pragma.config.json"));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Find the nearest `pragma.config.json` at or above `cwd`.
|
|
7
|
+
*
|
|
8
|
+
* Walks up the directory tree so pragma works from anywhere inside a
|
|
9
|
+
* project. The walk is bounded: it stops after checking the first
|
|
10
|
+
* directory that contains `.git` (the repository root), after checking
|
|
11
|
+
* the home directory, and at the filesystem root — a config outside the
|
|
12
|
+
* repository or home never leaks in.
|
|
13
|
+
*
|
|
14
|
+
* @param cwd - Directory to start from.
|
|
15
|
+
* @returns Absolute path of the nearest config file, or `undefined`.
|
|
16
|
+
* @note Impure — probes the filesystem while walking up.
|
|
17
|
+
*/
|
|
18
|
+
export default function findProjectConfigPath(cwd: string): string | undefined {
|
|
19
|
+
const home = homedir();
|
|
20
|
+
let dir = resolve(cwd);
|
|
21
|
+
|
|
22
|
+
while (true) {
|
|
23
|
+
const candidate = join(dir, "pragma.config.json");
|
|
24
|
+
if (existsSync(candidate)) {
|
|
25
|
+
return candidate;
|
|
26
|
+
}
|
|
27
|
+
if (existsSync(join(dir, ".git")) || dir === home) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const parent = dirname(dir);
|
|
31
|
+
if (parent === dir) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
dir = parent;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/config/index.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
/** @module Configuration
|
|
1
|
+
/** @module Configuration reading, writing, layered resolution, and paths. */
|
|
2
2
|
export { default as configExists } from "./configExists.js";
|
|
3
|
+
export { default as findProjectConfigPath } from "./findProjectConfigPath.js";
|
|
4
|
+
export { default as mergeConfigFileUpdate } from "./mergeConfigFileUpdate.js";
|
|
5
|
+
export { default as parseConfigValues } from "./parseConfigValues.js";
|
|
3
6
|
export { default as readConfig } from "./readConfig.js";
|
|
7
|
+
export { default as readConfigLayers } from "./readConfigLayers.js";
|
|
4
8
|
export { default as resolveConfigPath } from "./resolveConfigPath.js";
|
|
5
|
-
export
|
|
9
|
+
export { default as resolveGlobalConfigPath } from "./resolveGlobalConfigPath.js";
|
|
10
|
+
export { default as resolveWriteConfigPath } from "./resolveWriteConfigPath.js";
|
|
11
|
+
export type {
|
|
12
|
+
ConfigFileValues,
|
|
13
|
+
ConfigLayer,
|
|
14
|
+
ConfigLayers,
|
|
15
|
+
ConfigOrigin,
|
|
16
|
+
ConfigOrigins,
|
|
17
|
+
ConfigScope,
|
|
18
|
+
ConfigUpdate,
|
|
19
|
+
PragmaConfig,
|
|
20
|
+
} from "./types.js";
|
|
6
21
|
export { default as writeConfig } from "./writeConfig.js";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import type { ConfigUpdate } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Compute a config file's new contents after applying an update.
|
|
6
|
+
*
|
|
7
|
+
* Reads the target file itself (not the merged layers — a write must
|
|
8
|
+
* never copy values from another layer into this file), applies the
|
|
9
|
+
* update per field (`undefined` with the key present removes the field),
|
|
10
|
+
* and returns the serialized JSON. Unknown keys already in the file are
|
|
11
|
+
* preserved verbatim.
|
|
12
|
+
*
|
|
13
|
+
* @param configPath - Absolute path of the config file to update.
|
|
14
|
+
* @param update - Fields to set or remove.
|
|
15
|
+
* @returns The file's new contents, newline-terminated.
|
|
16
|
+
* @throws The raw filesystem/parse error when the existing file is
|
|
17
|
+
* present but unreadable or not valid JSON — never silently clobbers.
|
|
18
|
+
* @note Impure — reads the current file contents.
|
|
19
|
+
*/
|
|
20
|
+
export default function mergeConfigFileUpdate(
|
|
21
|
+
configPath: string,
|
|
22
|
+
update: ConfigUpdate,
|
|
23
|
+
): string {
|
|
24
|
+
let existing: Record<string, unknown> = {};
|
|
25
|
+
try {
|
|
26
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
27
|
+
existing = JSON.parse(raw) as Record<string, unknown>;
|
|
28
|
+
} catch (err: unknown) {
|
|
29
|
+
// Only swallow "file not found" — surface parse or permission errors.
|
|
30
|
+
const isNotFound =
|
|
31
|
+
err instanceof Error &&
|
|
32
|
+
"code" in err &&
|
|
33
|
+
(err as NodeJS.ErrnoException).code === "ENOENT";
|
|
34
|
+
if (!isNotFound) {
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
applyField(existing, update, "tier");
|
|
40
|
+
applyField(existing, update, "channel");
|
|
41
|
+
applyField(existing, update, "packages");
|
|
42
|
+
applyField(existing, update, "trace");
|
|
43
|
+
applyField(existing, update, "framework");
|
|
44
|
+
|
|
45
|
+
return `${JSON.stringify(existing, null, 2)}\n`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function applyField(
|
|
49
|
+
existing: Record<string, unknown>,
|
|
50
|
+
update: ConfigUpdate,
|
|
51
|
+
field: keyof ConfigUpdate,
|
|
52
|
+
): void {
|
|
53
|
+
if (update[field] !== undefined) {
|
|
54
|
+
existing[field] = update[field];
|
|
55
|
+
} else if (field in update) {
|
|
56
|
+
delete existing[field];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Channel,
|
|
3
|
+
type Framework,
|
|
4
|
+
VALID_CHANNELS,
|
|
5
|
+
VALID_FRAMEWORKS,
|
|
6
|
+
} from "../constants.js";
|
|
7
|
+
import {
|
|
8
|
+
parsePackageEntry,
|
|
9
|
+
type RawPackageEntry,
|
|
10
|
+
} from "../domains/refs/operations/parseRef.js";
|
|
11
|
+
import type { StoryPackDefinition } from "../domains/shared/stories/pack/types.js";
|
|
12
|
+
import validateStoryPackDefinition from "../domains/shared/stories/pack/validateStoryPackDefinition.js";
|
|
13
|
+
import { PragmaError } from "../error/PragmaError.js";
|
|
14
|
+
import type { ConfigFileValues } from "./types.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Type guard: check whether a value is a recognized channel string.
|
|
18
|
+
*
|
|
19
|
+
* @param value - Candidate value.
|
|
20
|
+
* @returns `true` if value is a valid Channel.
|
|
21
|
+
*/
|
|
22
|
+
function isValidChannel(value: unknown): value is Channel {
|
|
23
|
+
return typeof value === "string" && VALID_CHANNELS.includes(value as Channel);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Type guard: check whether a value is a recognized framework string.
|
|
28
|
+
*
|
|
29
|
+
* @param value - Candidate value.
|
|
30
|
+
* @returns `true` if value is a valid Framework.
|
|
31
|
+
*/
|
|
32
|
+
function isValidFramework(value: unknown): value is Framework {
|
|
33
|
+
return (
|
|
34
|
+
typeof value === "string" && VALID_FRAMEWORKS.includes(value as Framework)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parse and validate one config file's raw contents.
|
|
40
|
+
*
|
|
41
|
+
* Returns only the fields the file actually sets — presence drives layer
|
|
42
|
+
* merging in `readConfigLayers`. An empty or whitespace-only file sets
|
|
43
|
+
* nothing.
|
|
44
|
+
*
|
|
45
|
+
* @param raw - The file's raw text.
|
|
46
|
+
* @param sourcePath - Absolute path, used in error messages.
|
|
47
|
+
* @returns The values this file declares.
|
|
48
|
+
* @throws PragmaError with code `CONFIG_ERROR` on invalid JSON or values.
|
|
49
|
+
*/
|
|
50
|
+
export default function parseConfigValues(
|
|
51
|
+
raw: string,
|
|
52
|
+
sourcePath: string,
|
|
53
|
+
): ConfigFileValues {
|
|
54
|
+
const trimmed = raw.trim();
|
|
55
|
+
if (trimmed === "") {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let parsed: Record<string, unknown>;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(trimmed) as Record<string, unknown>;
|
|
62
|
+
} catch {
|
|
63
|
+
throw PragmaError.configError(`Invalid JSON in ${sourcePath}.`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const tier = typeof parsed.tier === "string" ? parsed.tier : undefined;
|
|
67
|
+
|
|
68
|
+
let channel: Channel | undefined;
|
|
69
|
+
if (parsed.channel !== undefined) {
|
|
70
|
+
if (!isValidChannel(parsed.channel)) {
|
|
71
|
+
throw PragmaError.configError(
|
|
72
|
+
`Invalid channel "${String(parsed.channel)}".`,
|
|
73
|
+
{ validOptions: [...VALID_CHANNELS] },
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
channel = parsed.channel;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const trace = typeof parsed.trace === "boolean" ? parsed.trace : undefined;
|
|
80
|
+
|
|
81
|
+
let framework: Framework | undefined;
|
|
82
|
+
if (parsed.framework !== undefined) {
|
|
83
|
+
if (!isValidFramework(parsed.framework)) {
|
|
84
|
+
throw PragmaError.configError(
|
|
85
|
+
`Invalid framework "${String(parsed.framework)}".`,
|
|
86
|
+
{ validOptions: [...VALID_FRAMEWORKS] },
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
framework = parsed.framework;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const packages = parsePackagesField(parsed.packages);
|
|
93
|
+
const stories = parseStoriesField(parsed.stories, sourcePath);
|
|
94
|
+
const prefixes = parsePrefixesField(parsed.prefixes);
|
|
95
|
+
const detail = typeof parsed.detail === "string" ? parsed.detail : undefined;
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
...(tier !== undefined ? { tier } : {}),
|
|
99
|
+
...(channel !== undefined ? { channel } : {}),
|
|
100
|
+
...(packages ? { packages } : {}),
|
|
101
|
+
...(trace !== undefined ? { trace } : {}),
|
|
102
|
+
...(framework !== undefined ? { framework } : {}),
|
|
103
|
+
...(stories ? { stories } : {}),
|
|
104
|
+
...(prefixes ? { prefixes } : {}),
|
|
105
|
+
...(detail !== undefined ? { detail } : {}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Validate and parse the experimental `stories` config field.
|
|
111
|
+
*
|
|
112
|
+
* @returns The validated definitions, or `undefined` when absent.
|
|
113
|
+
* @throws PragmaError if the field is present but malformed.
|
|
114
|
+
*/
|
|
115
|
+
function parseStoriesField(
|
|
116
|
+
raw: unknown,
|
|
117
|
+
sourcePath: string,
|
|
118
|
+
): ReadonlyArray<StoryPackDefinition> | undefined {
|
|
119
|
+
if (raw === undefined || raw === null) return undefined;
|
|
120
|
+
if (!Array.isArray(raw)) {
|
|
121
|
+
throw PragmaError.configError(
|
|
122
|
+
'"stories" must be an array of story-pack definitions.',
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return raw.map((entry, index) =>
|
|
126
|
+
validateStoryPackDefinition(entry, `${sourcePath} (stories[${index}])`),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate and parse the `prefixes` config field.
|
|
132
|
+
*
|
|
133
|
+
* @returns The prefix map, or `undefined` when absent.
|
|
134
|
+
* @throws PragmaError if the field is present but malformed.
|
|
135
|
+
*/
|
|
136
|
+
function parsePrefixesField(
|
|
137
|
+
raw: unknown,
|
|
138
|
+
): Readonly<Record<string, string>> | undefined {
|
|
139
|
+
if (raw === undefined || raw === null) return undefined;
|
|
140
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
141
|
+
throw PragmaError.configError(
|
|
142
|
+
'"prefixes" must be an object mapping prefix to namespace IRI.',
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const entries = Object.entries(raw as Record<string, unknown>);
|
|
146
|
+
for (const [prefix, namespace] of entries) {
|
|
147
|
+
if (typeof namespace !== "string" || !namespace.includes("://")) {
|
|
148
|
+
throw PragmaError.configError(
|
|
149
|
+
`Invalid namespace for prefix "${prefix}": expected an absolute IRI.`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return Object.fromEntries(entries) as Record<string, string>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Packages field parsing
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Validate and parse the `packages` config field.
|
|
162
|
+
*
|
|
163
|
+
* @returns The validated array, or `undefined` when the field is absent.
|
|
164
|
+
* @throws PragmaError if the field is present but malformed.
|
|
165
|
+
*/
|
|
166
|
+
function parsePackagesField(
|
|
167
|
+
raw: unknown,
|
|
168
|
+
): ReadonlyArray<RawPackageEntry> | undefined {
|
|
169
|
+
if (raw === undefined || raw === null) return undefined;
|
|
170
|
+
|
|
171
|
+
if (!Array.isArray(raw)) {
|
|
172
|
+
throw PragmaError.configError(
|
|
173
|
+
'"packages" must be an array of strings or { name, source? } objects.',
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// An explicitly empty list means "not configured": ref resolution falls
|
|
178
|
+
// back to global refs/defaults for an empty list (see mergeAndParseRefs),
|
|
179
|
+
// so dropping the field here keeps provenance in config show/doctor/info
|
|
180
|
+
// consistent with the behavior instead of reporting a project/global
|
|
181
|
+
// origin that resolution then ignores.
|
|
182
|
+
if (raw.length === 0) return undefined;
|
|
183
|
+
|
|
184
|
+
const entries: RawPackageEntry[] = [];
|
|
185
|
+
for (const item of raw) {
|
|
186
|
+
if (typeof item === "string") {
|
|
187
|
+
entries.push(item);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
191
|
+
const obj = item as Record<string, unknown>;
|
|
192
|
+
if (typeof obj.name !== "string" || obj.name.length === 0) {
|
|
193
|
+
throw PragmaError.configError(
|
|
194
|
+
'Each object in "packages" must have a non-empty "name" string.',
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const entry: { name: string; source?: string } = { name: obj.name };
|
|
198
|
+
if (obj.source !== undefined) {
|
|
199
|
+
if (typeof obj.source !== "string") {
|
|
200
|
+
throw PragmaError.configError(
|
|
201
|
+
`Invalid source for "${obj.name}": expected a string.`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
entry.source = obj.source;
|
|
205
|
+
}
|
|
206
|
+
// Validate format eagerly — fail fast on bad config
|
|
207
|
+
parsePackageEntry(entry);
|
|
208
|
+
entries.push(entry);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
throw PragmaError.configError(
|
|
212
|
+
'Each entry in "packages" must be a string or { name, source? } object.',
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return entries;
|
|
217
|
+
}
|